1
- use ark_circom:: circom:: { CircomCircuit , R1CS as CircomR1CS } ;
1
+ use ark_circom:: circom:: R1CS as CircomR1CS ;
2
2
use ark_ff:: PrimeField ;
3
- use ark_r1cs_std:: alloc:: AllocVar ;
4
- use ark_r1cs_std:: fields:: fp:: FpVar ;
5
- use ark_r1cs_std:: fields:: fp:: FpVar :: Var ;
6
- use ark_r1cs_std:: R1CSVar ;
7
- use ark_relations:: r1cs:: { ConstraintSynthesizer , ConstraintSystemRef , SynthesisError } ;
3
+ use ark_r1cs_std:: {
4
+ fields:: fp:: { AllocatedFp , FpVar } ,
5
+ R1CSVar ,
6
+ } ;
7
+ use ark_relations:: {
8
+ lc,
9
+ r1cs:: { ConstraintSystemRef , SynthesisError , Variable } ,
10
+ } ;
8
11
use ark_std:: fmt:: Debug ;
9
12
use folding_schemes:: { frontend:: FCircuit , utils:: PathOrBin , Error } ;
10
- use num_bigint:: BigInt ;
13
+ use num_bigint:: { BigInt , BigUint } ;
11
14
12
15
pub mod utils;
13
16
use crate :: utils:: { VecF , VecFpVar } ;
@@ -17,7 +20,7 @@ use utils::CircomWrapper;
17
20
/// The parameter `EIL` indicates the length of the ExternalInputs vector of field elements.
18
21
#[ derive( Clone , Debug ) ]
19
22
pub struct CircomFCircuit < F : PrimeField , const SL : usize , const EIL : usize > {
20
- circom_wrapper : CircomWrapper < F > ,
23
+ circom_wrapper : CircomWrapper ,
21
24
r1cs : CircomR1CS < F > ,
22
25
}
23
26
@@ -54,76 +57,107 @@ impl<F: PrimeField, const SL: usize, const EIL: usize> FCircuit<F> for CircomFCi
54
57
#[ cfg( test) ]
55
58
assert_eq ! ( external_inputs. 0 . len( ) , EIL ) ;
56
59
57
- let input_values = self . fpvars_to_bigints ( & z_i) ? ;
60
+ let input_values = Self :: fpvars_to_bigints ( & z_i) ;
58
61
let mut inputs_map = vec ! [ ( "ivc_input" . to_string( ) , input_values) ] ;
59
62
60
63
if EIL > 0 {
61
- let external_inputs_bi = self . fpvars_to_bigints ( & external_inputs. 0 ) ? ;
64
+ let external_inputs_bi = Self :: fpvars_to_bigints ( & external_inputs. 0 ) ;
62
65
inputs_map. push ( ( "external_inputs" . to_string ( ) , external_inputs_bi) ) ;
63
66
}
64
67
68
+ // The layout of `witness` is as follows:
69
+ // [
70
+ // 1, // The constant 1 is implicitly allocated by Arkworks
71
+ // ...z_{i + 1}, // The next state marked as `signal output` in the circom circuit
72
+ // ...z_i, // The current state marked as `signal input` in the circom circuit
73
+ // ...external_inputs, // The optional external inputs marked as `external input` in the circom circuit
74
+ // ...aux, // The intermediate witnesses
75
+ // ]
76
+ // Here, 1, z_i, and external_inputs have already been allocated in the
77
+ // constraint system, while z_{i + 1} and aux are yet to be allocated.
65
78
let witness = self
66
79
. circom_wrapper
67
- . extract_witness ( & inputs_map)
80
+ . extract_witness ( inputs_map)
68
81
. map_err ( |_| SynthesisError :: AssignmentMissing ) ?;
69
82
70
- // Since public inputs are already allocated variables, we will tell `circom-compat` to not re-allocate those
71
- let mut already_allocated_public_inputs = vec ! [ ] ;
72
- for var in z_i. iter ( ) {
73
- match var {
74
- Var ( var) => already_allocated_public_inputs. push ( var. variable ) ,
75
- _ => return Err ( SynthesisError :: Unsatisfiable ) , // allocated z_i should be Var
76
- }
83
+ // In order to convert the indexes of variables in the circom circuit to
84
+ // those in the arkworks circuit, we adopt the tricks from
85
+ // https://github.com/arnaucube/circom-compat/pull/1
86
+
87
+ // Since our cs might already have allocated constraints,
88
+ // We store a mapping between circom's defined indexes and the newly obtained cs indexes
89
+ let mut circom_index_to_cs_index = vec ! [ ] ;
90
+
91
+ // Constant 1 at idx 0 is already allocated by arkworks
92
+ circom_index_to_cs_index. push ( Variable :: One ) ;
93
+
94
+ // Allocate the next state (1..1 + SL) as witness, and at the same time,
95
+ // record the allocated variable's index in `circom_index_to_cs_index`.
96
+ // Cf. https://github.com/arnaucube/circom-compat/blob/22c8f5/src/circom/circuit.rs#L56-L86
97
+ let mut z_i1 = vec ! [ ] ;
98
+ for & w in witness. iter ( ) . skip ( 1 ) . take ( SL ) {
99
+ let v = cs. new_witness_variable ( || Ok ( w) ) ?;
100
+ circom_index_to_cs_index. push ( v) ;
101
+ z_i1. push ( FpVar :: Var ( AllocatedFp :: new ( Some ( w) , v, cs. clone ( ) ) ) ) ;
77
102
}
78
103
79
- // Initializes the CircomCircuit.
80
- let circom_circuit = CircomCircuit {
81
- r1cs : self . r1cs . clone ( ) ,
82
- witness : Some ( witness. clone ( ) ) ,
83
- public_inputs_indexes : already_allocated_public_inputs,
84
- allocate_inputs_as_witnesses : true ,
85
- } ;
104
+ // `z_i` and `external_inputs` have already been allocated as witness,
105
+ // so we just record their indexes in `circom_index_to_cs_index`.
106
+ // Cf. https://github.com/arnaucube/circom-compat/blob/22c8f5/src/circom/circuit.rs#L89-L95
107
+ for v in z_i. iter ( ) . chain ( & external_inputs. 0 ) {
108
+ match v {
109
+ FpVar :: Var ( v) => circom_index_to_cs_index. push ( v. variable ) ,
110
+ // safe because `z_i` and `external_inputs` are allocated as
111
+ // witness (not constant)
112
+ _ => unreachable ! ( ) ,
113
+ } ;
114
+ }
86
115
87
- // Generates the constraints for the circom_circuit.
88
- circom_circuit. generate_constraints ( cs. clone ( ) ) ?;
116
+ // Allocate the remaining aux variables as witness.
117
+ // Also, record their indexes in `circom_index_to_cs_index`.
118
+ // Cf. https://github.com/arnaucube/circom-compat/blob/22c8f5/src/circom/circuit.rs#L106-L121
119
+ for w in witness. into_iter ( ) . skip ( circom_index_to_cs_index. len ( ) ) {
120
+ circom_index_to_cs_index. push ( cs. new_witness_variable ( || Ok ( w) ) ?) ;
121
+ }
122
+
123
+ let fold_lc = |lc, & ( i, coeff) | lc + ( coeff, circom_index_to_cs_index[ i] ) ;
89
124
90
- // TODO: https://github.com/privacy-scaling-explorations/sonobe/issues/104
91
- // We disable checking constraints for now
92
- // Checks for constraint satisfaction.
93
- // if !cs.is_satisfied().unwrap() {
94
- // return Err(SynthesisError::Unsatisfiable);
95
- // }
125
+ // Generates the constraints for the circom_circuit.
126
+ for ( a, b, c) in & self . r1cs . constraints {
127
+ cs. enforce_constraint (
128
+ a. iter ( ) . fold ( lc ! ( ) , fold_lc) ,
129
+ b. iter ( ) . fold ( lc ! ( ) , fold_lc) ,
130
+ c. iter ( ) . fold ( lc ! ( ) , fold_lc) ,
131
+ ) ?;
132
+ }
96
133
97
- // Extracts the z_i1(next state) from the witness vector.
98
- let z_i1: Vec < FpVar < F > > =
99
- Vec :: < FpVar < F > > :: new_witness ( cs. clone ( ) , || Ok ( witness[ 1 ..1 + SL ] . to_vec ( ) ) ) ?;
134
+ #[ cfg( test) ]
135
+ if !cs. is_in_setup_mode ( ) && !cs. is_satisfied ( ) ? {
136
+ return Err ( SynthesisError :: Unsatisfiable ) ;
137
+ }
100
138
101
139
Ok ( z_i1)
102
140
}
103
141
}
104
142
105
143
impl < F : PrimeField , const SL : usize , const EIL : usize > CircomFCircuit < F , SL , EIL > {
106
- fn fpvars_to_bigints ( & self , fpvars : & [ FpVar < F > ] ) -> Result < Vec < BigInt > , SynthesisError > {
107
- let mut input_values = Vec :: new ( ) ;
108
- // converts each FpVar to PrimeField value, then to num_bigint::BigInt.
109
- for fp_var in fpvars. iter ( ) {
110
- // extracts the PrimeField value from FpVar.
111
- let primefield_value = fp_var. value ( ) ?;
112
- // converts the PrimeField value to num_bigint::BigInt.
113
- let num_bigint_value = self
114
- . circom_wrapper
115
- . ark_primefield_to_num_bigint ( primefield_value) ;
116
- input_values. push ( num_bigint_value) ;
117
- }
118
- Ok ( input_values)
144
+ fn fpvars_to_bigints ( fpvars : & [ FpVar < F > ] ) -> Vec < BigInt > {
145
+ fpvars
146
+ . value ( )
147
+ . unwrap_or ( vec ! [ F :: zero( ) ; fpvars. len( ) ] )
148
+ . into_iter ( )
149
+ . map ( Into :: < BigUint > :: into)
150
+ . map ( BigInt :: from)
151
+ . collect ( )
119
152
}
120
153
}
121
154
122
155
#[ cfg( test) ]
123
156
pub mod tests {
124
157
use super :: * ;
125
158
use ark_bn254:: Fr ;
126
- use ark_relations:: r1cs:: ConstraintSystem ;
159
+ use ark_r1cs_std:: alloc:: AllocVar ;
160
+ use ark_relations:: r1cs:: { ConstraintSynthesizer , ConstraintSystem } ;
127
161
use std:: path:: PathBuf ;
128
162
129
163
/// Native implementation of `src/circom/test_folder/cubic_circuit.r1cs`
0 commit comments