sonobe/src/folding/nova/mod.rs
src/folding/nova/mod.rsRaw
1/// Implements the scheme described in [Nova](https://eprint.iacr.org/2021/370.pdf)
2use ark_crypto_primitives::{
3 crh::{poseidon::CRH, CRHScheme},
4 sponge::{poseidon::PoseidonConfig, Absorb},
5};
6use ark_ec::{AffineRepr, CurveGroup, Group};
7use ark_ff::{BigInteger, PrimeField};
8use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar};
9use ark_std::fmt::Debug;
10use ark_std::{One, Zero};
11use core::marker::PhantomData;
12
13use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
14
15use crate::ccs::r1cs::{extract_r1cs, extract_w_x, R1CS};
16use crate::commitment::CommitmentProver;
17use crate::folding::circuits::nonnative::point_to_nonnative_limbs;
18use crate::frontend::FCircuit;
19use crate::utils::vec::is_zero_vec;
20use crate::Error;
21use crate::FoldingScheme;
22
23pub mod circuits;
24pub mod cyclefold;
25pub mod decider_eth_circuit;
26pub mod nifs;
27pub mod traits;
28
29use circuits::{AugmentedFCircuit, ChallengeGadget, CF2};
30use cyclefold::{CycleFoldChallengeGadget, CycleFoldCircuit};
31use nifs::NIFS;
32use traits::NovaR1CS;
33
34#[cfg(test)]
35use cyclefold::CF_IO_LEN;
36
37#[derive(Debug, Clone, Eq, PartialEq)]
38pub struct CommittedInstance<C: CurveGroup> {
39 pub cmE: C,
40 pub u: C::ScalarField,
41 pub cmW: C,
42 pub x: Vec<C::ScalarField>,
43}
44
45impl<C: CurveGroup> CommittedInstance<C> {
46 pub fn dummy(io_len: usize) -> Self {
47 Self {
48 cmE: C::zero(),
49 u: C::ScalarField::zero(),
50 cmW: C::zero(),
51 x: vec![C::ScalarField::zero(); io_len],
52 }
53 }
54}
55
56impl<C: CurveGroup> CommittedInstance<C>
57where
58 <C as Group>::ScalarField: Absorb,
59 <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
60{
61 /// hash implements the committed instance hash compatible with the gadget implemented in
62 /// nova/circuits.rs::CommittedInstanceVar.hash.
63 /// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U_i` is the
64 /// `CommittedInstance`.
65 pub fn hash(
66 &self,
67 poseidon_config: &PoseidonConfig<C::ScalarField>,
68 i: C::ScalarField,
69 z_0: Vec<C::ScalarField>,
70 z_i: Vec<C::ScalarField>,
71 ) -> Result<C::ScalarField, Error> {
72 let (cmE_x, cmE_y) = point_to_nonnative_limbs::<C>(self.cmE)?;
73 let (cmW_x, cmW_y) = point_to_nonnative_limbs::<C>(self.cmW)?;
74
75 CRH::<C::ScalarField>::evaluate(
76 poseidon_config,
77 vec![
78 vec![i],
79 z_0,
80 z_i,
81 vec![self.u],
82 self.x.clone(),
83 cmE_x,
84 cmE_y,
85 cmW_x,
86 cmW_y,
87 ]
88 .concat(),
89 )
90 .map_err(|e| Error::Other(e.to_string()))
91 }
92}
93
94#[derive(Debug, Clone, Eq, PartialEq)]
95pub struct Witness<C: CurveGroup> {
96 pub E: Vec<C::ScalarField>,
97 pub rE: C::ScalarField,
98 pub W: Vec<C::ScalarField>,
99 pub rW: C::ScalarField,
100}
101
102impl<C: CurveGroup> Witness<C>
103where
104 <C as Group>::ScalarField: Absorb,
105{
106 pub fn new(w: Vec<C::ScalarField>, e_len: usize) -> Self {
107 // note: at the current version, we don't use the blinding factors and we set them to 0
108 // always.
109 Self {
110 E: vec![C::ScalarField::zero(); e_len],
111 rE: C::ScalarField::zero(),
112 W: w,
113 rW: C::ScalarField::zero(),
114 }
115 }
116 pub fn commit<CP: CommitmentProver<C>>(
117 &self,
118 params: &CP::Params,
119 x: Vec<C::ScalarField>,
120 ) -> Result<CommittedInstance<C>, Error> {
121 let mut cmE = C::zero();
122 if !is_zero_vec::<C::ScalarField>(&self.E) {
123 cmE = CP::commit(params, &self.E, &self.rE)?;
124 }
125 let cmW = CP::commit(params, &self.W, &self.rW)?;
126 Ok(CommittedInstance {
127 cmE,
128 u: C::ScalarField::one(),
129 cmW,
130 x,
131 })
132 }
133}
134
135#[derive(Debug, Clone)]
136pub struct ProverParams<C1, C2, CP1, CP2>
137where
138 C1: CurveGroup,
139 C2: CurveGroup,
140 CP1: CommitmentProver<C1>,
141 CP2: CommitmentProver<C2>,
142{
143 pub poseidon_config: PoseidonConfig<C1::ScalarField>,
144 pub cm_params: CP1::Params,
145 pub cf_cm_params: CP2::Params,
146}
147
148#[derive(Debug, Clone)]
149pub struct VerifierParams<C1: CurveGroup, C2: CurveGroup> {
150 pub poseidon_config: PoseidonConfig<C1::ScalarField>,
151 pub r1cs: R1CS<C1::ScalarField>,
152 pub cf_r1cs: R1CS<C2::ScalarField>,
153}
154
155/// Implements Nova+CycleFold's IVC, described in [Nova](https://eprint.iacr.org/2021/370.pdf) and
156/// [CycleFold](https://eprint.iacr.org/2023/1192.pdf), following the FoldingScheme trait
157#[derive(Clone, Debug)]
158pub struct Nova<C1, GC1, C2, GC2, FC, CP1, CP2>
159where
160 C1: CurveGroup,
161 GC1: CurveVar<C1, CF2<C1>>,
162 C2: CurveGroup,
163 GC2: CurveVar<C2, CF2<C2>>,
164 FC: FCircuit<C1::ScalarField>,
165 CP1: CommitmentProver<C1>,
166 CP2: CommitmentProver<C2>,
167{
168 _gc1: PhantomData<GC1>,
169 _c2: PhantomData<C2>,
170 _gc2: PhantomData<GC2>,
171 /// R1CS of the Augmented Function circuit
172 pub r1cs: R1CS<C1::ScalarField>,
173 /// R1CS of the CycleFold circuit
174 pub cf_r1cs: R1CS<C2::ScalarField>,
175 pub poseidon_config: PoseidonConfig<C1::ScalarField>,
176 /// CommitmentProver::Params over C1
177 pub cm_params: CP1::Params,
178 /// CycleFold CommitmentProver::Params, over C2
179 pub cf_cm_params: CP2::Params,
180 /// F circuit, the circuit that is being folded
181 pub F: FC,
182 pub i: C1::ScalarField,
183 /// initial state
184 pub z_0: Vec<C1::ScalarField>,
185 /// current i-th state
186 pub z_i: Vec<C1::ScalarField>,
187 /// Nova instances
188 pub w_i: Witness<C1>,
189 pub u_i: CommittedInstance<C1>,
190 pub W_i: Witness<C1>,
191 pub U_i: CommittedInstance<C1>,
192
193 /// CycleFold running instance
194 pub cf_W_i: Witness<C2>,
195 pub cf_U_i: CommittedInstance<C2>,
196}
197
198impl<C1, GC1, C2, GC2, FC, CP1, CP2> FoldingScheme<C1, C2, FC>
199 for Nova<C1, GC1, C2, GC2, FC, CP1, CP2>
200where
201 C1: CurveGroup,
202 GC1: CurveVar<C1, CF2<C1>>,
203 C2: CurveGroup,
204 GC2: CurveVar<C2, CF2<C2>>,
205 FC: FCircuit<C1::ScalarField>,
206 CP1: CommitmentProver<C1>,
207 CP2: CommitmentProver<C2>,
208 <C1 as CurveGroup>::BaseField: PrimeField,
209 <C2 as CurveGroup>::BaseField: PrimeField,
210 <C1 as Group>::ScalarField: Absorb,
211 <C2 as Group>::ScalarField: Absorb,
212 C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
213 for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
214 for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
215{
216 type PreprocessorParam = (Self::ProverParam, FC);
217 type ProverParam = ProverParams<C1, C2, CP1, CP2>;
218 type VerifierParam = VerifierParams<C1, C2>;
219 type Witness = Witness<C1>;
220 type CommittedInstanceWithWitness = (CommittedInstance<C1>, Witness<C1>);
221 type CFCommittedInstanceWithWitness = (CommittedInstance<C2>, Witness<C2>);
222 type CommittedInstance = CommittedInstance<C1>;
223
224 fn preprocess(
225 prep_param: &Self::PreprocessorParam,
226 ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> {
227 let (prover_params, F_circuit) = prep_param;
228
229 let (r1cs, cf_r1cs) =
230 get_r1cs::<C1, GC1, C2, GC2, FC>(&prover_params.poseidon_config, *F_circuit)?;
231
232 let verifier_params = VerifierParams::<C1, C2> {
233 poseidon_config: prover_params.poseidon_config.clone(),
234 r1cs,
235 cf_r1cs,
236 };
237 Ok((prover_params.clone(), verifier_params))
238 }
239
240 /// Initializes the Nova+CycleFold's IVC for the given parameters and initial state `z_0`.
241 fn init(pp: &Self::ProverParam, F: FC, z_0: Vec<C1::ScalarField>) -> Result<Self, Error> {
242 // prepare the circuit to obtain its R1CS
243 let cs = ConstraintSystem::<C1::ScalarField>::new_ref();
244 let cs2 = ConstraintSystem::<C1::BaseField>::new_ref();
245
246 let augmented_F_circuit =
247 AugmentedFCircuit::<C1, C2, GC2, FC>::empty(&pp.poseidon_config, F);
248 let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty();
249
250 augmented_F_circuit.generate_constraints(cs.clone())?;
251 cs.finalize();
252 let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
253 let r1cs = extract_r1cs::<C1::ScalarField>(&cs);
254
255 cf_circuit.generate_constraints(cs2.clone())?;
256 cs2.finalize();
257 let cs2 = cs2.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
258 let cf_r1cs = extract_r1cs::<C1::BaseField>(&cs2);
259
260 // setup the dummy instances
261 let (w_dummy, u_dummy) = r1cs.dummy_instance();
262 let (cf_w_dummy, cf_u_dummy) = cf_r1cs.dummy_instance();
263
264 // W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the
265 // R1CS that we're working with.
266 Ok(Self {
267 _gc1: PhantomData,
268 _c2: PhantomData,
269 _gc2: PhantomData,
270 r1cs,
271 cf_r1cs,
272 poseidon_config: pp.poseidon_config.clone(),
273 cm_params: pp.cm_params.clone(),
274 cf_cm_params: pp.cf_cm_params.clone(),
275 F,
276 i: C1::ScalarField::zero(),
277 z_0: z_0.clone(),
278 z_i: z_0,
279 w_i: w_dummy.clone(),
280 u_i: u_dummy.clone(),
281 W_i: w_dummy,
282 U_i: u_dummy,
283 // cyclefold running instance
284 cf_W_i: cf_w_dummy.clone(),
285 cf_U_i: cf_u_dummy.clone(),
286 })
287 }
288
289 /// Implements IVC.P of Nova+CycleFold
290 fn prove_step(&mut self) -> Result<(), Error> {
291 let augmented_F_circuit: AugmentedFCircuit<C1, C2, GC2, FC>;
292 let cf_circuit: CycleFoldCircuit<C1, GC1>;
293
294 let z_i1 = self.F.step_native(self.z_i.clone())?;
295
296 // compute T and cmT for AugmentedFCircuit
297 let (T, cmT) = self.compute_cmT()?;
298
299 let r_bits = ChallengeGadget::<C1>::get_challenge_native(
300 &self.poseidon_config,
301 self.u_i.clone(),
302 self.U_i.clone(),
303 cmT,
304 )?;
305 let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits))
306 .ok_or(Error::OutOfBounds)?;
307
308 // fold Nova instances
309 let (W_i1, U_i1): (Witness<C1>, CommittedInstance<C1>) = NIFS::<C1, CP1>::fold_instances(
310 r_Fr, &self.w_i, &self.u_i, &self.W_i, &self.U_i, &T, cmT,
311 )?;
312
313 // folded instance output (public input, x)
314 // u_{i+1}.x = H(i+1, z_0, z_{i+1}, U_{i+1})
315 let u_i1_x = U_i1.hash(
316 &self.poseidon_config,
317 self.i + C1::ScalarField::one(),
318 self.z_0.clone(),
319 z_i1.clone(),
320 )?;
321
322 if self.i == C1::ScalarField::zero() {
323 // base case
324 augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
325 _gc2: PhantomData,
326 poseidon_config: self.poseidon_config.clone(),
327 i: Some(C1::ScalarField::zero()), // = i=0
328 z_0: Some(self.z_0.clone()), // = z_i
329 z_i: Some(self.z_i.clone()),
330 u_i: Some(self.u_i.clone()), // = dummy
331 U_i: Some(self.U_i.clone()), // = dummy
332 U_i1: Some(U_i1.clone()),
333 cmT: Some(cmT),
334 F: self.F,
335 x: Some(u_i1_x),
336 cf_u_i: None,
337 cf_U_i: None,
338 cf_U_i1: None,
339 cf_cmT: None,
340 cf_r_nonnat: None,
341 };
342
343 #[cfg(test)]
344 NIFS::<C1, CP1>::verify_folded_instance(r_Fr, &self.u_i, &self.U_i, &U_i1, &cmT)?;
345 } else {
346 // CycleFold part:
347 // get the vector used as public inputs 'x' in the CycleFold circuit
348 let cf_u_i_x = [
349 get_committed_instance_coordinates(&self.u_i),
350 get_committed_instance_coordinates(&self.U_i),
351 get_committed_instance_coordinates(&U_i1),
352 ]
353 .concat();
354
355 cf_circuit = CycleFoldCircuit::<C1, GC1> {
356 _gc: PhantomData,
357 r_bits: Some(r_bits.clone()),
358 cmT: Some(cmT),
359 u_i: Some(self.u_i.clone()),
360 U_i: Some(self.U_i.clone()),
361 U_i1: Some(U_i1.clone()),
362 x: Some(cf_u_i_x.clone()),
363 };
364
365 let cs2 = ConstraintSystem::<C1::BaseField>::new_ref();
366 cf_circuit.generate_constraints(cs2.clone())?;
367
368 let cs2 = cs2.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
369 let (cf_w_i, cf_x_i) = extract_w_x::<C1::BaseField>(&cs2);
370 if cf_x_i != cf_u_i_x {
371 return Err(Error::NotEqual);
372 }
373
374 #[cfg(test)]
375 if cf_x_i.len() != CF_IO_LEN {
376 return Err(Error::NotExpectedLength(cf_x_i.len(), CF_IO_LEN));
377 }
378
379 // fold cyclefold instances
380 let cf_w_i = Witness::<C2>::new(cf_w_i.clone(), self.cf_r1cs.A.n_rows);
381 let cf_u_i: CommittedInstance<C2> =
382 cf_w_i.commit::<CP2>(&self.cf_cm_params, cf_x_i.clone())?;
383
384 // compute T* and cmT* for CycleFoldCircuit
385 let (cf_T, cf_cmT) = self.compute_cf_cmT(&cf_w_i, &cf_u_i)?;
386
387 let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_native(
388 &self.poseidon_config,
389 cf_u_i.clone(),
390 self.cf_U_i.clone(),
391 cf_cmT,
392 )?;
393 let cf_r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&cf_r_bits))
394 .ok_or(Error::OutOfBounds)?;
395
396 let (cf_W_i1, cf_U_i1) = NIFS::<C2, CP2>::fold_instances(
397 cf_r_Fq,
398 &self.cf_W_i,
399 &self.cf_U_i,
400 &cf_w_i,
401 &cf_u_i,
402 &cf_T,
403 cf_cmT,
404 )?;
405
406 augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
407 _gc2: PhantomData,
408 poseidon_config: self.poseidon_config.clone(),
409 i: Some(self.i),
410 z_0: Some(self.z_0.clone()),
411 z_i: Some(self.z_i.clone()),
412 u_i: Some(self.u_i.clone()),
413 U_i: Some(self.U_i.clone()),
414 U_i1: Some(U_i1.clone()),
415 cmT: Some(cmT),
416 F: self.F,
417 x: Some(u_i1_x),
418 // cyclefold values
419 cf_u_i: Some(cf_u_i.clone()),
420 cf_U_i: Some(self.cf_U_i.clone()),
421 cf_U_i1: Some(cf_U_i1.clone()),
422 cf_cmT: Some(cf_cmT),
423 cf_r_nonnat: Some(cf_r_Fq),
424 };
425
426 self.cf_W_i = cf_W_i1.clone();
427 self.cf_U_i = cf_U_i1.clone();
428
429 #[cfg(test)]
430 {
431 self.cf_r1cs.check_instance_relation(&cf_w_i, &cf_u_i)?;
432 self.cf_r1cs
433 .check_relaxed_instance_relation(&self.cf_W_i, &self.cf_U_i)?;
434 }
435 }
436
437 let cs = ConstraintSystem::<C1::ScalarField>::new_ref();
438
439 augmented_F_circuit.generate_constraints(cs.clone())?;
440
441 let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
442 let (w_i1, x_i1) = extract_w_x::<C1::ScalarField>(&cs);
443 if x_i1[0] != u_i1_x {
444 return Err(Error::NotEqual);
445 }
446
447 #[cfg(test)]
448 if x_i1.len() != 1 {
449 return Err(Error::NotExpectedLength(x_i1.len(), 1));
450 }
451
452 // set values for next iteration
453 self.i += C1::ScalarField::one();
454 self.z_i = z_i1.clone();
455 self.w_i = Witness::<C1>::new(w_i1, self.r1cs.A.n_rows);
456 self.u_i = self.w_i.commit::<CP1>(&self.cm_params, vec![u_i1_x])?;
457 self.W_i = W_i1.clone();
458 self.U_i = U_i1.clone();
459
460 #[cfg(test)]
461 {
462 self.r1cs.check_instance_relation(&self.w_i, &self.u_i)?;
463 self.r1cs
464 .check_relaxed_instance_relation(&self.W_i, &self.U_i)?;
465 }
466
467 Ok(())
468 }
469
470 fn state(&self) -> Vec<C1::ScalarField> {
471 self.z_i.clone()
472 }
473 fn instances(
474 &self,
475 ) -> (
476 Self::CommittedInstanceWithWitness,
477 Self::CommittedInstanceWithWitness,
478 Self::CFCommittedInstanceWithWitness,
479 ) {
480 (
481 (self.U_i.clone(), self.W_i.clone()),
482 (self.u_i.clone(), self.w_i.clone()),
483 (self.cf_U_i.clone(), self.cf_W_i.clone()),
484 )
485 }
486
487 /// Implements IVC.V of Nova+CycleFold
488 fn verify(
489 vp: Self::VerifierParam,
490 z_0: Vec<C1::ScalarField>, // initial state
491 z_i: Vec<C1::ScalarField>, // last state
492 num_steps: C1::ScalarField,
493 running_instance: Self::CommittedInstanceWithWitness,
494 incomming_instance: Self::CommittedInstanceWithWitness,
495 cyclefold_instance: Self::CFCommittedInstanceWithWitness,
496 ) -> Result<(), Error> {
497 let (U_i, W_i) = running_instance;
498 let (u_i, w_i) = incomming_instance;
499 let (cf_U_i, cf_W_i) = cyclefold_instance;
500
501 if u_i.x.len() != 1 || U_i.x.len() != 1 {
502 return Err(Error::IVCVerificationFail);
503 }
504
505 // check that u_i's output points to the running instance
506 // u_i.X == H(i, z_0, z_i, U_i)
507 let expected_u_i_x = U_i.hash(&vp.poseidon_config, num_steps, z_0, z_i.clone())?;
508 if expected_u_i_x != u_i.x[0] {
509 return Err(Error::IVCVerificationFail);
510 }
511
512 // check u_i.cmE==0, u_i.u==1 (=u_i is a un-relaxed instance)
513 if !u_i.cmE.is_zero() || !u_i.u.is_one() {
514 return Err(Error::IVCVerificationFail);
515 }
516
517 // check R1CS satisfiability
518 vp.r1cs.check_instance_relation(&w_i, &u_i)?;
519 // check RelaxedR1CS satisfiability
520 vp.r1cs.check_relaxed_instance_relation(&W_i, &U_i)?;
521
522 // check CycleFold RelaxedR1CS satisfiability
523 vp.cf_r1cs
524 .check_relaxed_instance_relation(&cf_W_i, &cf_U_i)?;
525
526 Ok(())
527 }
528}
529
530impl<C1, GC1, C2, GC2, FC, CP1, CP2> Nova<C1, GC1, C2, GC2, FC, CP1, CP2>
531where
532 C1: CurveGroup,
533 GC1: CurveVar<C1, CF2<C1>>,
534 C2: CurveGroup,
535 GC2: CurveVar<C2, CF2<C2>>,
536 FC: FCircuit<C1::ScalarField>,
537 CP1: CommitmentProver<C1>,
538 CP2: CommitmentProver<C2>,
539 <C2 as CurveGroup>::BaseField: PrimeField,
540 <C1 as Group>::ScalarField: Absorb,
541 <C2 as Group>::ScalarField: Absorb,
542 C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
543{
544 // computes T and cmT for the AugmentedFCircuit
545 fn compute_cmT(&self) -> Result<(Vec<C1::ScalarField>, C1), Error> {
546 NIFS::<C1, CP1>::compute_cmT(
547 &self.cm_params,
548 &self.r1cs,
549 &self.w_i,
550 &self.u_i,
551 &self.W_i,
552 &self.U_i,
553 )
554 }
555 // computes T* and cmT* for the CycleFoldCircuit
556 fn compute_cf_cmT(
557 &self,
558 cf_w_i: &Witness<C2>,
559 cf_u_i: &CommittedInstance<C2>,
560 ) -> Result<(Vec<C2::ScalarField>, C2), Error> {
561 NIFS::<C2, CP2>::compute_cyclefold_cmT(
562 &self.cf_cm_params,
563 &self.cf_r1cs,
564 cf_w_i,
565 cf_u_i,
566 &self.cf_W_i,
567 &self.cf_U_i,
568 )
569 }
570}
571
572/// helper method to get the r1cs from the ConstraintSynthesizer
573pub fn get_r1cs_from_cs<F: PrimeField>(
574 circuit: impl ConstraintSynthesizer<F>,
575) -> Result<R1CS<F>, Error> {
576 let cs = ConstraintSystem::<F>::new_ref();
577 circuit.generate_constraints(cs.clone())?;
578 cs.finalize();
579 let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
580 let r1cs = extract_r1cs::<F>(&cs);
581 Ok(r1cs)
582}
583
584/// helper method to get the R1CS for both the AugmentedFCircuit and the CycleFold circuit
585#[allow(clippy::type_complexity)]
586pub fn get_r1cs<C1, GC1, C2, GC2, FC>(
587 poseidon_config: &PoseidonConfig<C1::ScalarField>,
588 F_circuit: FC,
589) -> Result<(R1CS<C1::ScalarField>, R1CS<C2::ScalarField>), Error>
590where
591 C1: CurveGroup,
592 GC1: CurveVar<C1, CF2<C1>>,
593 C2: CurveGroup,
594 GC2: CurveVar<C2, CF2<C2>>,
595 FC: FCircuit<C1::ScalarField>,
596 <C1 as CurveGroup>::BaseField: PrimeField,
597 <C2 as CurveGroup>::BaseField: PrimeField,
598 <C1 as Group>::ScalarField: Absorb,
599 <C2 as Group>::ScalarField: Absorb,
600 C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
601 for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
602 for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
603{
604 let augmented_F_circuit =
605 AugmentedFCircuit::<C1, C2, GC2, FC>::empty(poseidon_config, F_circuit);
606 let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty();
607 let r1cs = get_r1cs_from_cs::<C1::ScalarField>(augmented_F_circuit)?;
608 let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
609 Ok((r1cs, cf_r1cs))
610}
611
612/// helper method to get the pedersen params length for both the AugmentedFCircuit and the
613/// CycleFold circuit
614pub fn get_pedersen_params_len<C1, GC1, C2, GC2, FC>(
615 poseidon_config: &PoseidonConfig<C1::ScalarField>,
616 F_circuit: FC,
617) -> Result<(usize, usize), Error>
618where
619 C1: CurveGroup,
620 GC1: CurveVar<C1, CF2<C1>>,
621 C2: CurveGroup,
622 GC2: CurveVar<C2, CF2<C2>>,
623 FC: FCircuit<C1::ScalarField>,
624 <C1 as CurveGroup>::BaseField: PrimeField,
625 <C2 as CurveGroup>::BaseField: PrimeField,
626 <C1 as Group>::ScalarField: Absorb,
627 <C2 as Group>::ScalarField: Absorb,
628 C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
629 for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
630 for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
631{
632 let (r1cs, cf_r1cs) = get_r1cs::<C1, GC1, C2, GC2, FC>(poseidon_config, F_circuit)?;
633 Ok((r1cs.A.n_rows, cf_r1cs.A.n_rows))
634}
635
636pub(crate) fn get_committed_instance_coordinates<C: CurveGroup>(
637 u: &CommittedInstance<C>,
638) -> Vec<C::BaseField> {
639 let zero = (&C::BaseField::zero(), &C::BaseField::one());
640
641 let cmE = u.cmE.into_affine();
642 let (cmE_x, cmE_y) = cmE.xy().unwrap_or(zero);
643
644 let cmW = u.cmW.into_affine();
645 let (cmW_x, cmW_y) = cmW.xy().unwrap_or(zero);
646 vec![*cmE_x, *cmE_y, *cmW_x, *cmW_y]
647}
648
649#[cfg(test)]
650pub mod tests {
651 use super::*;
652 use ark_pallas::{constraints::GVar, Fr, Projective};
653 use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2};
654
655 use crate::commitment::pedersen::Pedersen;
656 use crate::frontend::tests::CubicFCircuit;
657 use crate::transcript::poseidon::poseidon_test_config;
658
659 #[test]
660 fn test_ivc() {
661 type NOVA = Nova<
662 Projective,
663 GVar,
664 Projective2,
665 GVar2,
666 CubicFCircuit<Fr>,
667 Pedersen<Projective>,
668 Pedersen<Projective2>,
669 >;
670
671 let mut rng = ark_std::test_rng();
672 let poseidon_config = poseidon_test_config::<Fr>();
673
674 let F_circuit = CubicFCircuit::<Fr>::new(());
675 let z_0 = vec![Fr::from(3_u32)];
676
677 let (cm_len, cf_cm_len) =
678 get_pedersen_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>(
679 &poseidon_config,
680 F_circuit,
681 )
682 .unwrap();
683 let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, cm_len);
684 let cf_pedersen_params = Pedersen::<Projective2>::new_params(&mut rng, cf_cm_len);
685
686 let prover_params =
687 ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> {
688 poseidon_config: poseidon_config.clone(),
689 cm_params: pedersen_params,
690 cf_cm_params: cf_pedersen_params,
691 };
692
693 let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap();
694
695 let num_steps: usize = 3;
696 for _ in 0..num_steps {
697 nova.prove_step().unwrap();
698 }
699 assert_eq!(Fr::from(num_steps as u32), nova.i);
700
701 let verifier_params = VerifierParams::<Projective, Projective2> {
702 poseidon_config,
703 r1cs: nova.r1cs,
704 cf_r1cs: nova.cf_r1cs,
705 };
706 NOVA::verify(
707 verifier_params,
708 z_0,
709 nova.z_i,
710 nova.i,
711 (nova.U_i, nova.W_i),
712 (nova.u_i, nova.w_i),
713 (nova.cf_U_i, nova.cf_W_i),
714 )
715 .unwrap();
716 }
717}