Skip to main content

frontend/worldcoin_mpc/
circuits.rs

1#![allow(clippy::type_complexity)]
2use ark_std::log2;
3use shared_types::Field;
4
5use crate::{
6    hyrax_worldcoin_mpc::mpc_prover::MPCCircuitConstData,
7    layouter::builder::{Circuit, CircuitBuilder, LayerVisibility},
8    worldcoin_mpc::{
9        components::WorldcoinMpcComponents,
10        parameters::{GR4_ELEM_BIT_LENGTH, GR4_MULTIPLICATION_WIRINGS},
11    },
12};
13use remainder::layer::gate::BinaryOperation;
14
15use super::{
16    data::MPCCircuitInputData,
17    parameters::{ENCODING_MATRIX_NUM_VARS_COLS, ENCODING_MATRIX_NUM_VARS_ROWS},
18};
19
20/// The input layer for quotients and multiplicities
21pub const MPC_AUXILIARY_LAYER: &str = "Auxiliary"; // Should be private + depend on party.
22/// all other public inputs, such as the evaluation points, the encoding matrix,
23/// the lookup_table_values. Should (probably) be public + common among parties.
24pub const MPC_AUXILIARY_INVARIANT_LAYER: &str = "Auxiliary Invariant";
25pub const MPC_AUXILIARY_INVARIANT_LAYER_2: &str = "Auxiliary Invariant 2";
26/// The input layer for shares_reduced_modulo_gr4_modulus, public
27pub const MPC_SHARES_LAYER: &str = "Shares";
28/// The private input layer for the slope (private and generated randomly).
29pub const MPC_SLOPES_LAYER: &str = "Slopes";
30/// The private input layer for the iris code (private), obtained through
31/// running the iriscode circuit
32pub const MPC_IRISCODE_INPUT_LAYER: &str = "Iris Code Input";
33/// The private input layer for the mask code (private),  obtained through
34/// running the iriscode circuit
35pub const MPC_MASKCODE_INPUT_LAYER: &str = "Mask Code Input";
36
37pub const MPC_IRISCODE_SHRED: &str = "Iris Code Input";
38pub const MPC_MASKCODE_SHRED: &str = "Mask Code Input";
39pub const MPC_SLOPES_SHRED: &str = "Slopes";
40pub const MPC_QUOTIENTS_SHRED: &str = "Quotients";
41pub const MPC_SHARES_SHRED: &str = "Shares Reduced Modulo GR4 Modulus";
42pub const MPC_MULTIPLICITIES_SHARES_SHRED: &str = "Multiplicities Shares";
43pub const MPC_MULTIPLICITIES_SLOPES_SHRED: &str = "Multiplicities Slopes";
44pub const MPC_MULTIPLICITIES_QUOTIENTS_SHRED: &str = "Multiplicities Quotients";
45pub const MPC_ENCODING_MATRIX_SHRED: &str = "Encoding Matrix";
46pub const MPC_EVALUATION_POINTS_SHRED: &str = "Evaluation Points";
47pub const MPC_LOOKUP_TABLE_VALUES_SHRED: &str = "Lookup Table Values";
48pub const MPC_LOOKUP_TABLE_VALUES_SHRED_2_19: &str = "Lookup Table Values 2^19";
49
50/// Builds the mpc circuit.
51/// The full circuit spec can be referenced here:
52/// <https://www.notion.so/MPC-Circuit-Builders-8fe15c8d8f4b4db18cbf85344ccbb9df?pvs=4>
53///
54/// To summerize, this circuit takes in slope, iris code and mask code as private inputs,
55/// computes the masked iris code, encodes the masked iris code to a GR4 element.
56/// Then, the circuit does a GR4 multiplication between the evaluation points and the slopes.
57/// The result is then summed with the masked iris code to get the computed shares.
58/// The quotients, shares_reduced_modulo_gr4_modulus are to count for congruency (modulo 2^16).
59pub fn build_circuit<F: Field, const NUM_IRIS_4_CHUNKS: usize>(
60    layer_visibility: LayerVisibility,
61) -> Circuit<F> {
62    let mut builder = CircuitBuilder::<F>::new();
63
64    let num_vars_dataparallel = log2(NUM_IRIS_4_CHUNKS) as usize;
65    // the 2 extra num_vars are for the 4 chunks of the iris code (every 4 iris code values
66    // are combined into a single GR4 elements)
67    let num_vars = num_vars_dataparallel + 2;
68
69    let auxilary_input_layer_node = builder.add_input_layer(MPC_AUXILIARY_LAYER, layer_visibility);
70    let shares_input_layer_node =
71        builder.add_input_layer(MPC_SHARES_LAYER, LayerVisibility::Public);
72    let auxiliary_invariant_public_input_layer_node =
73        builder.add_input_layer(MPC_AUXILIARY_INVARIANT_LAYER, LayerVisibility::Public);
74    let auxiliary_invariant_public_input_layer_node_2 =
75        builder.add_input_layer(MPC_AUXILIARY_INVARIANT_LAYER_2, LayerVisibility::Public);
76    let slope_input_layer_node = builder.add_input_layer(MPC_SLOPES_LAYER, layer_visibility);
77    let iris_code_input_layer_node =
78        builder.add_input_layer(MPC_IRISCODE_INPUT_LAYER, layer_visibility);
79    let mask_code_input_layer_node =
80        builder.add_input_layer(MPC_MASKCODE_INPUT_LAYER, layer_visibility);
81
82    let iris_code =
83        builder.add_input_shred(MPC_IRISCODE_SHRED, num_vars, &iris_code_input_layer_node);
84    let mask_code =
85        builder.add_input_shred(MPC_MASKCODE_SHRED, num_vars, &mask_code_input_layer_node);
86    let slopes = builder.add_input_shred(MPC_SLOPES_SHRED, num_vars, &slope_input_layer_node);
87    let quotients =
88        builder.add_input_shred(MPC_QUOTIENTS_SHRED, num_vars, &auxilary_input_layer_node);
89    let shares_reduced_modulo_gr4_modulus =
90        builder.add_input_shred(MPC_SHARES_SHRED, num_vars, &shares_input_layer_node);
91    let multiplicities_shares = builder.add_input_shred(
92        MPC_MULTIPLICITIES_SHARES_SHRED,
93        GR4_ELEM_BIT_LENGTH as usize,
94        &auxilary_input_layer_node,
95    );
96    let multiplicities_slopes = builder.add_input_shred(
97        MPC_MULTIPLICITIES_SLOPES_SHRED,
98        GR4_ELEM_BIT_LENGTH as usize,
99        &auxilary_input_layer_node,
100    );
101    let multiplicities_quotients = builder.add_input_shred(
102        MPC_MULTIPLICITIES_QUOTIENTS_SHRED,
103        19,
104        &auxilary_input_layer_node,
105    );
106
107    let encoding_matrix = builder.add_input_shred(
108        MPC_ENCODING_MATRIX_SHRED,
109        ENCODING_MATRIX_NUM_VARS_ROWS + ENCODING_MATRIX_NUM_VARS_COLS,
110        &auxiliary_invariant_public_input_layer_node,
111    );
112    let evaluation_points = builder.add_input_shred(
113        MPC_EVALUATION_POINTS_SHRED,
114        num_vars,
115        &auxiliary_invariant_public_input_layer_node,
116    );
117
118    let lookup_table_values = builder.add_input_shred(
119        MPC_LOOKUP_TABLE_VALUES_SHRED,
120        GR4_ELEM_BIT_LENGTH as usize,
121        &auxiliary_invariant_public_input_layer_node,
122    );
123    let fiat_shamir_challenge_node = builder.add_fiat_shamir_challenge_node(1);
124    let lookup_table = builder.add_lookup_table(&lookup_table_values, &fiat_shamir_challenge_node);
125
126    let lookup_table_values_2_19 = builder.add_input_shred(
127        MPC_LOOKUP_TABLE_VALUES_SHRED_2_19,
128        19,
129        &auxiliary_invariant_public_input_layer_node_2,
130    );
131    let lookup_table_2_19 =
132        builder.add_lookup_table(&lookup_table_values_2_19, &fiat_shamir_challenge_node);
133
134    let masked_iris_code =
135        WorldcoinMpcComponents::masked_iris_code(&mut builder, &iris_code, &mask_code);
136    // The matrix multiplication node encodes the masked iris code to a GR4 element
137    let encoded_masked_iris_code = builder.add_matmult_node(
138        &masked_iris_code,
139        (num_vars_dataparallel, ENCODING_MATRIX_NUM_VARS_ROWS),
140        &encoding_matrix,
141        (ENCODING_MATRIX_NUM_VARS_ROWS, ENCODING_MATRIX_NUM_VARS_COLS),
142    );
143
144    // The gate node performs a GR4 multiplication between the evaluation points and the slopes
145    let evaluation_points_times_slopes = builder.add_gate_node(
146        &evaluation_points,
147        &slopes,
148        GR4_MULTIPLICATION_WIRINGS.to_vec(),
149        BinaryOperation::Mul,
150        Some(num_vars_dataparallel),
151    );
152
153    let computed_shares = WorldcoinMpcComponents::sum(
154        &mut builder,
155        &encoded_masked_iris_code,
156        &evaluation_points_times_slopes,
157    );
158
159    let _congruence = WorldcoinMpcComponents::congruence(
160        &mut builder,
161        &quotients,
162        &computed_shares,
163        &shares_reduced_modulo_gr4_modulus,
164    );
165
166    let _lookup_constraint_shares = builder.add_lookup_constraint(
167        &lookup_table,
168        &shares_reduced_modulo_gr4_modulus,
169        &multiplicities_shares,
170    );
171
172    let _lookup_constraint_slopes =
173        builder.add_lookup_constraint(&lookup_table, &slopes, &multiplicities_slopes);
174
175    let _lookup_constraint_quotients =
176        builder.add_lookup_constraint(&lookup_table_2_19, &quotients, &multiplicities_quotients);
177
178    builder.build_without_layer_combination().unwrap()
179}
180
181/// Generates a mapping from Layer IDs to their respective MLEs,
182/// by attaching the `MPCInputBuilderMetadata` onto a circuit that is
183/// described through the `input_builder_metadata` of an MPC secret share circuit.
184pub fn mpc_attach_data<F: Field>(
185    circuit: &mut Circuit<F>,
186    mpc_aux_data: MPCCircuitConstData<F>,
187    mpc_input_data: MPCCircuitInputData<F>,
188) {
189    circuit.set_input(MPC_IRISCODE_SHRED, mpc_input_data.iris_codes);
190    circuit.set_input(MPC_MASKCODE_SHRED, mpc_input_data.masks);
191    circuit.set_input(MPC_SLOPES_SHRED, mpc_input_data.slopes);
192    circuit.set_input(MPC_QUOTIENTS_SHRED, mpc_input_data.quotients);
193    circuit.set_input(
194        MPC_SHARES_SHRED,
195        mpc_input_data.shares_reduced_modulo_gr4_modulus,
196    );
197    circuit.set_input(
198        MPC_MULTIPLICITIES_SHARES_SHRED,
199        mpc_input_data.multiplicities_shares,
200    );
201    circuit.set_input(
202        MPC_MULTIPLICITIES_SLOPES_SHRED,
203        mpc_input_data.multiplicities_slopes,
204    );
205    circuit.set_input(
206        MPC_MULTIPLICITIES_QUOTIENTS_SHRED,
207        mpc_input_data.multiplicities_quotients,
208    );
209    circuit.set_input(MPC_ENCODING_MATRIX_SHRED, mpc_aux_data.encoding_matrix);
210    circuit.set_input(MPC_EVALUATION_POINTS_SHRED, mpc_aux_data.evaluation_points);
211    circuit.set_input(
212        MPC_LOOKUP_TABLE_VALUES_SHRED,
213        mpc_aux_data.lookup_table_values,
214    );
215    circuit.set_input(
216        MPC_LOOKUP_TABLE_VALUES_SHRED_2_19,
217        mpc_aux_data.lookup_table_values_2_19,
218    );
219}