Skip to main content

world_id_primitives/
circuit_inputs.rs

1//! Circuit input types for the World ID Protocol circuits.
2//!
3//! This module requires the `circuits` feature and is not available in WASM builds.
4
5use std::collections::HashMap;
6
7use groth16_material::circom::ProofInput;
8use ruint::aliases::U256;
9
10use crate::authenticator::MAX_AUTHENTICATOR_KEYS;
11
12type BaseField = ark_babyjubjub::Fq;
13type ScalarField = ark_babyjubjub::Fr;
14type Affine = ark_babyjubjub::EdwardsAffine;
15
16#[inline]
17pub(crate) fn fq_to_u256_vec(f: ark_babyjubjub::Fq) -> Vec<U256> {
18    vec![f.into()]
19}
20
21#[inline]
22pub(crate) fn fq_seq_to_u256_vec(fs: &[ark_babyjubjub::Fq]) -> Vec<U256> {
23    fs.iter().copied().map(Into::into).collect()
24}
25
26#[inline]
27pub(crate) fn fr_to_u256_vec(f: ark_babyjubjub::Fr) -> Vec<U256> {
28    vec![f.into()]
29}
30
31#[inline]
32pub(crate) fn affine_to_u256_vec(p: ark_babyjubjub::EdwardsAffine) -> Vec<U256> {
33    vec![p.x.into(), p.y.into()]
34}
35
36#[inline]
37pub(crate) fn affine_seq_to_u256_vec(ps: &[ark_babyjubjub::EdwardsAffine]) -> Vec<U256> {
38    ps.iter()
39        .copied()
40        .flat_map(|p| [p.x.into(), p.y.into()])
41        .collect()
42}
43
44/// The input for the circuit of the OPRF Query Proof `π1`.
45///
46/// TODO: Rename attribute names to match the `Credential` type.
47#[derive(Debug, Clone)]
48pub struct QueryProofCircuitInput<const MAX_DEPTH: usize> {
49    /// The `AuthenticatorPublicKeySet` represented as an array of Affine points.
50    pub pk: [Affine; MAX_AUTHENTICATOR_KEYS],
51    /// The index of the public key which will be used to sign the OPRF query from the `AuthenticatorPublicKeySet`.
52    pub pk_index: BaseField,
53    /// The `s` part of the signature of the query with the public key at the `pk_index`.
54    pub s: ScalarField,
55    /// The `r` part of the signature of the query with the public key at the `pk_index`.
56    pub r: Affine,
57    /// The root of the Merkle tree of the `WorldIDRegistry` contract.
58    pub merkle_root: BaseField,
59    /// The depth of the Merkle tree of the `WorldIDRegistry` contract.
60    pub depth: BaseField,
61    /// The leaf index of the World ID in the Merkle tree of the `WorldIDRegistry` contract.
62    ///
63    /// In the `MerkleInclusionProof` type, this is the `leaf_index` field.
64    pub mt_index: BaseField,
65    /// The siblings of the Merkle proof of the account in the `WorldIDRegistry` contract.
66    pub siblings: [BaseField; MAX_DEPTH],
67    /// The (non-inverted) blinding factor of the OPRF query.
68    pub beta: ScalarField,
69    /// The ID of the RP requesting the proof as registered in the `RpRegistry` contract.
70    ///
71    /// TODO: Will require updates once the new `RpRegistry` is launched.
72    pub rp_id: BaseField,
73    /// The action for the proof request. See `ProofRequest` for more details.
74    pub action: BaseField,
75    /// The nonce of the proof request. See `ProofRequest` for more details.
76    pub nonce: BaseField,
77}
78
79impl<const MAX_DEPTH: usize> ProofInput for QueryProofCircuitInput<MAX_DEPTH> {
80    fn prepare_input(&self) -> HashMap<String, Vec<U256>> {
81        let mut map = HashMap::new();
82        map.insert("pk".to_owned(), affine_seq_to_u256_vec(&self.pk));
83        map.insert("pk_index".to_owned(), fq_to_u256_vec(self.pk_index));
84        map.insert("s".to_owned(), fr_to_u256_vec(self.s));
85        map.insert("r".to_owned(), affine_to_u256_vec(self.r));
86        map.insert("merkle_root".to_owned(), fq_to_u256_vec(self.merkle_root));
87        map.insert("depth".to_owned(), fq_to_u256_vec(self.depth));
88        map.insert("mt_index".to_owned(), fq_to_u256_vec(self.mt_index));
89        map.insert("siblings".to_owned(), fq_seq_to_u256_vec(&self.siblings));
90        map.insert("beta".to_owned(), fr_to_u256_vec(self.beta));
91        map.insert("rp_id".to_owned(), fq_to_u256_vec(self.rp_id));
92        map.insert("action".to_owned(), fq_to_u256_vec(self.action));
93        map.insert("nonce".to_owned(), fq_to_u256_vec(self.nonce));
94        map
95    }
96}
97
98/// The input for the circuit of the Uniqueness Proof `π2` (internally also nullifier proof).
99///
100/// Externally, the Nullifier Proof is exposed to RPs as a Uniqueness Proof or a Session Proof respectively.
101#[derive(Debug, Clone)]
102pub struct NullifierProofCircuitInput<const MAX_DEPTH: usize> {
103    /// The input for the circuit of the OPRF Query Proof from which this nullifier proof is constructed.
104    pub query_input: QueryProofCircuitInput<MAX_DEPTH>,
105
106    // SECTION: Credential Inputs
107    /// The id as registered in the `CredentialSchemaIssuerRegistry` representing the (issuer, schema) pair.
108    ///
109    /// This is the `issuer_schema_id` field of the `Credential` type.
110    pub issuer_schema_id: BaseField,
111    /// The public key of the issuer of the credential. This is stored in the `Credential` type in the `issuer` field.
112    pub cred_pk: Affine,
113    /// A specific commitment to particular claims of the `Credential`. In particular:
114    /// [`claims_hash`, `associated_data_commitment`]
115    pub cred_hashes: [BaseField; 2],
116    /// The `genesis_issued_at` attribute of the `Credential`. See the `Credential` type for more details.
117    pub cred_genesis_issued_at: BaseField,
118    /// The `expires_at` attribute of the `Credential`. See the `Credential` type for more details.
119    pub cred_expires_at: BaseField,
120    /// The `s` part of the signature of the credential (signed by the issuer)
121    pub cred_s: ScalarField,
122    /// The `r` part of the signature of the credential (signed by the issuer)
123    pub cred_r: Affine,
124    /// The timestamp from the request.
125    pub current_timestamp: BaseField,
126    /// The `genesis_issued_at_min` attribute of the `Credential`. See the `Credential` type for more details.
127    pub cred_genesis_issued_at_min: BaseField,
128    /// The `cred_user_id_r` blinding factor used to generate the `sub`.
129    pub cred_sub_blinding_factor: BaseField,
130    /// The unique identifier of the `Credential`.
131    pub cred_id: BaseField,
132
133    // SECTION: User Inputs
134    /// The random commitment for future session proofs generated by the Authenticator.
135    ///
136    /// TODO: Rename to match new terms and avoid confusion with World ID <3.0's `identity_commitment`.
137    pub id_commitment_r: BaseField,
138
139    /// The identity commitment for future session proofs.
140    ///
141    /// Is used internally to check for equality against the computed identity commitment.
142    /// If set to 0, all computed identity commitments will be accepted.
143    pub id_commitment: BaseField,
144
145    // SECTION: OPRF Inputs
146    /// The `e` part of the `DLog` equality proof (Fiat-Shamir challenge)
147    pub dlog_e: BaseField,
148    /// The `s` part of the `DLog` equality proof (proof response)
149    pub dlog_s: ScalarField,
150    /// The public key of the OPRF Nodes for the particular RP (this is the `RpNullifierKey`).
151    pub oprf_pk: Affine,
152    /// The combined blinded response after aggregating peer commitments from the OPRF nodes.
153    pub oprf_response_blinded: Affine,
154    /// The unblinded response from from the OPRF nodes.
155    pub oprf_response: Affine,
156
157    // SECTION: RP Inputs
158    /// The hashed signal provided by the RP and committed to by the user. See `ProofRequest` for more details.
159    pub signal_hash: BaseField,
160}
161
162impl<const MAX_DEPTH: usize> ProofInput for NullifierProofCircuitInput<MAX_DEPTH> {
163    fn prepare_input(&self) -> std::collections::HashMap<String, Vec<ruint::aliases::U256>> {
164        let mut map = self.query_input.prepare_input();
165        map.insert(
166            "issuer_schema_id".to_owned(),
167            fq_to_u256_vec(self.issuer_schema_id),
168        );
169        map.insert("cred_pk".to_owned(), affine_to_u256_vec(self.cred_pk));
170        map.insert(
171            "cred_hashes".to_owned(),
172            fq_seq_to_u256_vec(&self.cred_hashes),
173        );
174        map.insert(
175            "cred_genesis_issued_at".to_owned(),
176            fq_to_u256_vec(self.cred_genesis_issued_at),
177        );
178        map.insert(
179            "cred_genesis_issued_at_min".to_owned(),
180            fq_to_u256_vec(self.cred_genesis_issued_at_min),
181        );
182        map.insert(
183            "cred_expires_at".to_owned(),
184            fq_to_u256_vec(self.cred_expires_at),
185        );
186        map.insert("cred_id".to_owned(), fq_to_u256_vec(self.cred_id));
187        map.insert(
188            "cred_user_id_r".to_owned(),
189            fq_to_u256_vec(self.cred_sub_blinding_factor),
190        );
191        map.insert("cred_s".to_owned(), fr_to_u256_vec(self.cred_s));
192        map.insert("cred_r".to_owned(), affine_to_u256_vec(self.cred_r));
193
194        map.insert(
195            "id_commitment_r".to_owned(),
196            fq_to_u256_vec(self.id_commitment_r),
197        );
198        map.insert(
199            "id_commitment".to_owned(),
200            fq_to_u256_vec(self.id_commitment),
201        );
202
203        map.insert("dlog_e".to_owned(), fq_to_u256_vec(self.dlog_e));
204        map.insert("dlog_s".to_owned(), fr_to_u256_vec(self.dlog_s));
205        map.insert("oprf_pk".to_owned(), affine_to_u256_vec(self.oprf_pk));
206        map.insert(
207            "oprf_response_blinded".to_owned(),
208            affine_to_u256_vec(self.oprf_response_blinded),
209        );
210        map.insert(
211            "oprf_response".to_owned(),
212            affine_to_u256_vec(self.oprf_response),
213        );
214        map.insert("signal_hash".to_owned(), fq_to_u256_vec(self.signal_hash));
215        map.insert(
216            "current_timestamp".to_owned(),
217            fq_to_u256_vec(self.current_timestamp),
218        );
219
220        map
221    }
222}