arnaucube
arnaucube/grapevine-sonobePublic Self-hosted repository
grapevine-sonobe/src/circom.rs
src/circom.rsRaw
  1use ark_circom::circom::CircomCircuit;
  2use ark_ff::PrimeField;
  3use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar};
  4use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError};
  5use ark_std::fmt::Debug;
  6use num_bigint::BigInt;
  7use sonobe::{frontend::FCircuit, Error as SonobeError};
  8use std::path::PathBuf;
  9
 10use crate::errors::GrapevineError;
 11use crate::utils::wrapper::{CircomPrivateInput, CircomWrapper};
 12
 13// Define Circom FCircuit
 14#[derive(Clone, Debug)]
 15pub struct GrapevineFCircuit<F: PrimeField> {
 16    circom_wrapper: CircomWrapper<F>,
 17    private_input: CircomPrivateInput,
 18}
 19
 20impl<F: PrimeField> GrapevineFCircuit<F> {
 21    pub fn set_private_input(&mut self, input: CircomPrivateInput) {
 22        self.private_input = input;
 23    }
 24}
 25
 26impl<F: PrimeField> FCircuit<F> for GrapevineFCircuit<F> {
 27    type Params = (PathBuf, PathBuf);
 28
 29    fn new(params: Self::Params) -> Self {
 30        let (r1cs_path, wasm_path) = params;
 31        let circom_wrapper = CircomWrapper::new(r1cs_path, wasm_path);
 32        Self {
 33            circom_wrapper,
 34            private_input: CircomPrivateInput::empty(false),
 35        }
 36    }
 37
 38    fn state_len(&self) -> usize {
 39        4
 40    }
 41
 42    fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, SonobeError> {
 43        // convert ivc_input from ark ff to BigInt
 44        let ivc_input = z_i
 45            .iter()
 46            .map(|val| CircomWrapper::ark_primefield_to_num_bigint(*val))
 47            .collect::<Vec<BigInt>>();
 48        let mut inputs = vec![("ivc_input".to_string(), ivc_input)];
 49
 50        // set the private inputs
 51        if self.private_input.uninitialized() {
 52            return Err(SonobeError::Other("Private input not set".to_string()));
 53        }
 54        let private_input = CircomWrapper::<F>::marshal_private_inputs(&self.private_input);
 55        inputs.extend(private_input);
 56
 57        // calculate witness
 58        let witness = self.circom_wrapper.extract_witness(&inputs).map_err(|e| {
 59            SonobeError::WitnessCalculationError(format!("Failed to calculate witness: {}", e))
 60        })?;
 61
 62        // extract the z_i1 (next state) from witvec
 63        let z_i1 = witness[1..1 + self.state_len()].to_vec();
 64        Ok(z_i1)
 65    }
 66
 67    fn generate_step_constraints(
 68        &self,
 69        cs: ConstraintSystemRef<F>,
 70        _i: usize,
 71        z_i: Vec<FpVar<F>>,
 72    ) -> Result<Vec<FpVar<F>>, SynthesisError> {
 73        // convert ivc input from FpVar to ark ff to BigInt
 74        let ivc_input: Vec<BigInt> = z_i
 75            .iter()
 76            .map(|val| CircomWrapper::ark_primefield_to_num_bigint(val.value().unwrap()))
 77            .collect();
 78        let mut inputs = vec![("ivc_input".to_string(), ivc_input)];
 79
 80        // set the private inputs
 81        if self.private_input.uninitialized() {
 82            return Err(SynthesisError::AssignmentMissing);
 83        }
 84        let private_input = CircomWrapper::<F>::marshal_private_inputs(&self.private_input);
 85        inputs.extend(private_input);
 86
 87        println!("Inputs: {:?}", inputs);
 88
 89        // extract r1cs and witness
 90        let (r1cs, witness) = self
 91            .circom_wrapper
 92            .extract_r1cs_and_witness(&inputs)
 93            .map_err(|_| SynthesisError::AssignmentMissing)?;
 94
 95        println!("Wire map len: {:?}", r1cs.clone().wire_mapping.unwrap().len());
 96        println!("Constraints len: {:?}", r1cs.clone().constraints.len());
 97        println!("Witness len: {:?}", witness.clone().unwrap().len());
 98
 99        // Initialize CircomCircuit
100        let circom_circuit = CircomCircuit {
101            r1cs,
102            witness: witness.clone(),
103            inputs_already_computed: false,
104        };
105
106        circom_circuit
107            .generate_constraints(cs.clone())
108            .map_err(|_| SynthesisError::Unsatisfiable)?;
109
110        if !cs.is_satisfied().unwrap() {
111            return Err(SynthesisError::Unsatisfiable);
112        };
113
114        let w = witness.ok_or(SynthesisError::Unsatisfiable)?;
115
116        let z_i1: Vec<FpVar<F>> =
117            Vec::<FpVar<F>>::new_witness(cs.clone(), || Ok(w[1..1 + self.state_len()].to_vec()))?;
118
119        Ok(z_i1)
120    }
121}
122
123#[cfg(test)]
124mod test {
125    use super::*;
126    use crate::params::test_nova_setup;
127    use crate::utils::{
128        inputs::{get_z0, random_f_bigint},
129        wrapper::CircomPrivateInput,
130    };
131    use ark_pallas::{constraints::GVar, Fr, Projective};
132    use ark_r1cs_std::alloc::AllocVar;
133    use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
134    use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2};
135    use lazy_static::lazy_static;
136    use num_bigint::BigInt;
137    use sonobe::{
138        commitment::pedersen::Pedersen, folding::nova::Nova,
139        transcript::poseidon::poseidon_test_config, Error, FoldingScheme,
140    };
141    use std::env::current_dir;
142    use std::time::Instant;
143
144    lazy_static! {
145        pub static ref R1CS_PATH: PathBuf = PathBuf::from("./circom/artifacts/grapevine.r1cs");
146        pub static ref WASM_PATH: PathBuf = PathBuf::from("./circom/artifacts/grapevine.wasm");
147        pub static ref PHRASE: String = String::from("This is a secret");
148        pub static ref USERNAMES: [String; 5] = [
149            String::from("alice"),
150            String::from("bob"),
151            String::from("charlie"),
152            String::from("david"),
153            String::from("eve")
154        ];
155        pub static ref AUTH_SECRETS: [BigInt; 5] = (0..5)
156            .map(|_| random_f_bigint::<Fr>())
157            .collect::<Vec<BigInt>>()
158            .try_into()
159            .unwrap();
160    }
161
162    #[test]
163    fn test_step_native() {
164        // define inputs
165        let step_0_inputs = CircomPrivateInput {
166            phrase: Some(String::from(&*PHRASE)),
167            usernames: [None, Some(String::from(&*USERNAMES[0]))],
168            auth_secrets: [None, Some(AUTH_SECRETS[0].clone())],
169            chaff: false,
170        };
171        let z_0 = get_z0();
172
173        // initialize new Grapevine function circuit
174        let mut f_circuit = GrapevineFCircuit::<Fr>::new((R1CS_PATH.clone(), WASM_PATH.clone()));
175        f_circuit.set_private_input(step_0_inputs);
176
177        let z_1 = f_circuit.step_native(0, z_0.to_vec()).unwrap();
178        println!("z_1: {:?}", z_1);
179    }
180
181    #[test]
182    fn test_step_constraints() {
183        // initialize new Grapevine function circuit
184        let mut f_circuit = GrapevineFCircuit::<Fr>::new((R1CS_PATH.clone(), WASM_PATH.clone()));
185
186        // define inputs
187        let step_0_inputs = CircomPrivateInput {
188            phrase: Some(String::from(&*PHRASE)),
189            usernames: [None, Some(String::from(&*USERNAMES[0]))],
190            auth_secrets: [None, Some(AUTH_SECRETS[0].clone())],
191            chaff: false,
192        };
193        f_circuit.set_private_input(step_0_inputs);
194
195        // assign z0
196        let cs = ConstraintSystem::<Fr>::new_ref();
197        let z_0_var = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(get_z0())).unwrap();
198
199        // compute constraints for step 0
200        let cs = ConstraintSystem::<Fr>::new_ref();
201        let z_1_var = f_circuit
202            .generate_step_constraints(cs.clone(), 1, z_0_var)
203            .unwrap();
204        println!("z_1: {:?}", z_1_var);
205
206        // assert_eq!(z_i1_var.value().unwrap(), vec![Fr::from(38), Fr::from(1)]);
207    }
208
209    #[test]
210    fn test_multiple_steps_native() {
211        // initialize new Grapevine function circuit
212        let mut f_circuit = GrapevineFCircuit::<Fr>::new((R1CS_PATH.clone(), WASM_PATH.clone()));
213
214        /*  DEGREE 1  */
215        // define degree 1 logic inputs
216        let inputs = CircomPrivateInput {
217            phrase: Some(String::from(&*PHRASE)),
218            usernames: [None, Some(String::from(&*USERNAMES[0]))],
219            auth_secrets: [None, Some(AUTH_SECRETS[0].clone())],
220            chaff: false,
221        };
222        f_circuit.set_private_input(inputs);
223
224        // compute step 0 (degree 1 logic step)
225        let z_i = f_circuit.step_native(0, get_z0().to_vec()).unwrap();
226
227        // define degree 1 chaff inputs
228        let inputs = CircomPrivateInput::empty(true);
229        f_circuit.set_private_input(inputs);
230
231        // compute step 1 (degree 1 chaff step)
232        let z_i = f_circuit.step_native(1, z_i.to_vec()).unwrap();
233        println!("z_i: {:?}", z_i);
234
235        /*  DEGREE 2  */
236        // define degree 2 logic inputs
237        let inputs = CircomPrivateInput {
238            phrase: None,
239            usernames: [
240                Some(String::from(&*USERNAMES[0])),
241                Some(String::from(&*USERNAMES[1])),
242            ],
243            auth_secrets: [Some(AUTH_SECRETS[0].clone()), Some(AUTH_SECRETS[1].clone())],
244            chaff: false,
245        };
246        f_circuit.set_private_input(inputs);
247
248        // compute step 2 (degree 2 logic step)
249        let z_i = f_circuit.step_native(2, z_i.to_vec()).unwrap();
250
251        // define degree 2 chaff inputs
252        let inputs = CircomPrivateInput::empty(true);
253        f_circuit.set_private_input(inputs);
254
255        // compute step 3 (degree 2 chaff step)
256        let z_i = f_circuit.step_native(3, z_i.to_vec()).unwrap();
257
258        /*  DEGREE 3  */
259        // define degree 3 logic inputs
260        let inputs = CircomPrivateInput {
261            phrase: None,
262            usernames: [
263                Some(String::from(&*USERNAMES[1])),
264                Some(String::from(&*USERNAMES[2])),
265            ],
266            auth_secrets: [Some(AUTH_SECRETS[1].clone()), Some(AUTH_SECRETS[2].clone())],
267            chaff: false,
268        };
269        f_circuit.set_private_input(inputs);
270
271        // compute step 4 (degree 3 logic step)
272        let z_i = f_circuit.step_native(4, z_i.to_vec()).unwrap();
273
274        // define degree 3 chaff inputs
275        let inputs = CircomPrivateInput::empty(true);
276        f_circuit.set_private_input(inputs);
277
278        // compute step 5 (degree 3 chaff step)
279        let z_i = f_circuit.step_native(5, z_i.to_vec()).unwrap();
280
281        /* RESULT */
282        // @todo: compute hashes natively
283        assert_eq!(z_i[0], Fr::from(3));
284        assert_eq!(z_i[3], Fr::from(0));
285    }
286
287    // #[test]
288    // fn test_multiple_steps_constraints() {
289    //     // initialize new Grapevine function circuit
290    //     let mut f_circuit = GrapevineFCircuit::<Fr>::new((R1CS_PATH.clone(), WASM_PATH.clone()));
291    //
292    //     /*  DEGREE 1  */
293    //     // define degree 1 logic inputs
294    //     let inputs = CircomPrivateInput {
295    //         phrase: Some(String::from(&*PHRASE)),
296    //         usernames: [None, Some(String::from(&*USERNAMES[0]))],
297    //         auth_secrets: [None, Some(AUTH_SECRETS[0].clone())],
298    //         chaff: false
299    //     };
300    //     f_circuit.set_private_input(inputs);
301    //
302    //     // assign z0
303    //     let cs = ConstraintSystem::<Fr>::new_ref();
304    //     let z_0_var = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(get_z0())).unwrap();
305    //
306    //     // compute constraints for step 0 (degree 1 logic step)
307    //     let cs = ConstraintSystem::<Fr>::new_ref();
308    //     let z_1_var = f_circuit
309    //         .generate_step_constraints(cs.clone(), 0, z_0_var)
310    //         .unwrap();
311    //     println!("z_1: {:?}", z_1_var);
312    //
313    //     // define degree 1 chaff inputs
314    //     let inputs = CircomPrivateInput::empty(true);
315    //     f_circuit.set_private_input(inputs);
316    //
317    //     // compute step 1 (degree 1 chaff step)
318    //     let z_i = f_circuit.step_native(1, z_i.to_vec()).unwrap();
319    //     println!("z_i: {:?}", z_i);
320    //
321    //     // define inputs
322    //     let step_0_inputs = CircomPrivateInput {
323    //         phrase: Some(String::from(&*PHRASE)),
324    //         usernames: [None, Some(String::from(&*USERNAMES[0]))],
325    //         auth_secrets: [None, Some(AUTH_SECRETS[0].clone())],
326    //         chaff: false
327    //     };
328    //
329    //     // initialize new Grapevine function circuit
330    //     let mut f_circuit = GrapevineFCircuit::<Fr>::new((R1CS_PATH.clone(), WASM_PATH.clone()));
331    //     f_circuit.set_private_input(step_0_inputs);
332    //
333    // }
334
335    #[test]
336    fn test_full_one_step() {
337        // initialize new Grapevine function circuit
338        let mut f_circuit = GrapevineFCircuit::<Fr>::new((R1CS_PATH.clone(), WASM_PATH.clone()));
339
340        // Get test params
341        let (prover_params, verifier_params) =
342            test_nova_setup::<GrapevineFCircuit<Fr>>(f_circuit.clone());
343        
344
345        // define inputs
346        // define inputs
347        let step_0_inputs = CircomPrivateInput {
348            phrase: Some(String::from(&*PHRASE)),
349            usernames: [None, Some(String::from(&*USERNAMES[0]))],
350            auth_secrets: [None, Some(AUTH_SECRETS[0].clone())],
351            chaff: false,
352        };
353        // let z_0 = get_z0();
354    }
355
356    #[test]
357    fn test_full() {
358        let num_steps = 10;
359        let initial_state = vec![Fr::from(19), Fr::from(0)];
360
361        let r1cs_path = PathBuf::from("./circom/artifacts/grapevine.r1cs");
362        let wasm_path = PathBuf::from("./circom/artifacts/grapevine.wasm");
363
364        let f_circuit = GrapevineFCircuit::<Fr>::new((r1cs_path, wasm_path));
365
366        let start = Instant::now();
367        println!("Generating params...");
368        let (prover_params, verifier_params) =
369            test_nova_setup::<GrapevineFCircuit<Fr>>(f_circuit.clone());
370        println!("Generated params: {:?}", start.elapsed());
371        type NOVA = Nova<
372            Projective,
373            GVar,
374            Projective2,
375            GVar2,
376            GrapevineFCircuit<Fr>,
377            Pedersen<Projective>,
378            Pedersen<Projective2>,
379        >;
380
381        let start = Instant::now();
382        println!("Initializing folding scheme...");
383        let mut folding_scheme =
384            NOVA::init(&prover_params, f_circuit, initial_state.clone()).unwrap();
385        println!("Initialized folding scheme: {:?}", start.elapsed());
386
387        for i in 0..num_steps {
388            let start = Instant::now();
389            folding_scheme.prove_step().unwrap();
390            println!("Proved step {}: {:?}", i, start.elapsed());
391        }
392
393        let (running_instance, incoming_instance, cyclefold_instance) = folding_scheme.instances();
394
395        println!("Running IVC Verifier...");
396        let start = Instant::now();
397        NOVA::verify(
398            verifier_params,
399            initial_state.clone(),
400            folding_scheme.state(),
401            Fr::from(num_steps as u32),
402            running_instance,
403            incoming_instance,
404            cyclefold_instance,
405        )
406        .unwrap();
407        println!("Verified: {:?}", start.elapsed());
408    }
409}