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}