sonobe/folding-schemes/src/folding/hypernova/mod.rs
folding-schemes/src/folding/hypernova/mod.rsRaw
1/// Implements the scheme described in [HyperNova](https://eprint.iacr.org/2023/573.pdf)
2use ark_crypto_primitives::sponge::{
3 poseidon::{PoseidonConfig, PoseidonSponge},
4 Absorb, CryptographicSponge,
5};
6use ark_ec::{CurveGroup, Group};
7use ark_ff::{BigInteger, PrimeField};
8use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget};
9use ark_std::rand::RngCore;
10use ark_std::{One, Zero};
11use core::marker::PhantomData;
12use std::fmt::Debug;
13
14pub mod cccs;
15pub mod circuits;
16use circuits::AugmentedFCircuit;
17pub mod lcccs;
18pub mod nimfs;
19pub mod utils;
20use cccs::CCCS;
21use lcccs::LCCCS;
22use nimfs::NIMFS;
23
24use crate::arith::{
25 ccs::CCS,
26 r1cs::{extract_w_x, R1CS},
27};
28use crate::commitment::CommitmentScheme;
29use crate::folding::circuits::{
30 cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit},
31 CF2,
32};
33use crate::folding::nova::{
34 get_r1cs_from_cs, traits::NovaR1CS, CommittedInstance, PreprocessorParam,
35 Witness as NovaWitness,
36};
37use crate::frontend::FCircuit;
38use crate::utils::{get_cm_coordinates, pp_hash};
39use crate::Error;
40use crate::FoldingScheme;
41
42/// Witness for the LCCCS & CCCS, containing the w vector, and the r_w used as randomness in the Pedersen commitment.
43#[derive(Debug, Clone, Eq, PartialEq)]
44pub struct Witness<F: PrimeField> {
45 pub w: Vec<F>,
46 pub r_w: F,
47}
48
49impl<F: PrimeField> Witness<F> {
50 pub fn new(w: Vec<F>) -> Self {
51 // note: at the current version, we don't use the blinding factors and we set them to 0
52 // always.
53 Self { w, r_w: F::zero() }
54 }
55 pub fn dummy(ccs: &CCS<F>) -> Self {
56 Witness::<F>::new(vec![F::zero(); ccs.n - ccs.l - 1])
57 }
58}
59
60#[derive(Debug, Clone)]
61pub struct ProverParams<C1, C2, CS1, CS2>
62where
63 C1: CurveGroup,
64 C2: CurveGroup,
65 CS1: CommitmentScheme<C1>,
66 CS2: CommitmentScheme<C2>,
67{
68 pub poseidon_config: PoseidonConfig<C1::ScalarField>,
69 pub cs_params: CS1::ProverParams,
70 pub cf_cs_params: CS2::ProverParams,
71 // if ccs is set, it will be used, if not, it will be computed at runtime
72 pub ccs: Option<CCS<C1::ScalarField>>,
73}
74
75#[derive(Debug, Clone)]
76pub struct VerifierParams<
77 C1: CurveGroup,
78 C2: CurveGroup,
79 CS1: CommitmentScheme<C1>,
80 CS2: CommitmentScheme<C2>,
81> {
82 pub poseidon_config: PoseidonConfig<C1::ScalarField>,
83 pub ccs: CCS<C1::ScalarField>,
84 pub cf_r1cs: R1CS<C2::ScalarField>,
85 pub cs_vp: CS1::VerifierParams,
86 pub cf_cs_vp: CS2::VerifierParams,
87}
88
89impl<C1, C2, CS1, CS2> VerifierParams<C1, C2, CS1, CS2>
90where
91 C1: CurveGroup,
92 C2: CurveGroup,
93 CS1: CommitmentScheme<C1>,
94 CS2: CommitmentScheme<C2>,
95{
96 /// returns the hash of the public parameters of HyperNova
97 pub fn pp_hash(&self) -> Result<C1::ScalarField, Error> {
98 pp_hash::<C1, C2, CS1, CS2>(
99 &self.ccs,
100 &self.cf_r1cs,
101 &self.cs_vp,
102 &self.cf_cs_vp,
103 &self.poseidon_config,
104 )
105 }
106}
107
108/// Implements HyperNova+CycleFold's IVC, described in
109/// [HyperNova](https://eprint.iacr.org/2023/573.pdf) and
110/// [CycleFold](https://eprint.iacr.org/2023/1192.pdf), following the FoldingScheme trait
111#[derive(Clone, Debug)]
112pub struct HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2>
113where
114 C1: CurveGroup,
115 GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
116 C2: CurveGroup,
117 GC2: CurveVar<C2, CF2<C2>>,
118 FC: FCircuit<C1::ScalarField>,
119 CS1: CommitmentScheme<C1>,
120 CS2: CommitmentScheme<C2>,
121{
122 _gc1: PhantomData<GC1>,
123 _c2: PhantomData<C2>,
124 _gc2: PhantomData<GC2>,
125
126 /// CCS of the Augmented Function circuit
127 pub ccs: CCS<C1::ScalarField>,
128 /// R1CS of the CycleFold circuit
129 pub cf_r1cs: R1CS<C2::ScalarField>,
130 pub poseidon_config: PoseidonConfig<C1::ScalarField>,
131 /// CommitmentScheme::ProverParams over C1
132 pub cs_params: CS1::ProverParams,
133 /// CycleFold CommitmentScheme::ProverParams, over C2
134 pub cf_cs_params: CS2::ProverParams,
135 /// F circuit, the circuit that is being folded
136 pub F: FC,
137 /// public params hash
138 pub pp_hash: C1::ScalarField,
139 pub i: C1::ScalarField,
140 /// initial state
141 pub z_0: Vec<C1::ScalarField>,
142 /// current i-th state
143 pub z_i: Vec<C1::ScalarField>,
144 /// HyperNova instances
145 pub W_i: Witness<C1::ScalarField>,
146 pub U_i: LCCCS<C1>,
147 pub w_i: Witness<C1::ScalarField>,
148 pub u_i: CCCS<C1>,
149
150 /// CycleFold running instance
151 pub cf_W_i: NovaWitness<C2>,
152 pub cf_U_i: CommittedInstance<C2>,
153}
154
155impl<C1, GC1, C2, GC2, FC, CS1, CS2> FoldingScheme<C1, C2, FC>
156 for HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2>
157where
158 C1: CurveGroup,
159 GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
160 C2: CurveGroup,
161 GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
162 FC: FCircuit<C1::ScalarField>,
163 CS1: CommitmentScheme<C1>,
164 CS2: CommitmentScheme<C2>,
165 <C1 as CurveGroup>::BaseField: PrimeField,
166 <C2 as CurveGroup>::BaseField: PrimeField,
167 <C1 as Group>::ScalarField: Absorb,
168 <C2 as Group>::ScalarField: Absorb,
169 C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
170 for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
171 for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
172{
173 type PreprocessorParam = PreprocessorParam<C1, C2, FC, CS1, CS2>;
174 type ProverParam = ProverParams<C1, C2, CS1, CS2>;
175 type VerifierParam = VerifierParams<C1, C2, CS1, CS2>;
176 type RunningInstance = (LCCCS<C1>, Witness<C1::ScalarField>);
177 type IncomingInstance = (CCCS<C1>, Witness<C1::ScalarField>);
178 type CFInstance = (CommittedInstance<C2>, NovaWitness<C2>);
179
180 fn preprocess(
181 mut rng: impl RngCore,
182 prep_param: &Self::PreprocessorParam,
183 ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> {
184 let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC>::empty(
185 &prep_param.poseidon_config,
186 prep_param.F.clone(),
187 None,
188 )?;
189 let ccs = augmented_f_circuit.ccs.clone();
190
191 let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty();
192 let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
193
194 // if cs params exist, use them, if not, generate new ones
195 let cs_pp: CS1::ProverParams;
196 let cs_vp: CS1::VerifierParams;
197 let cf_cs_pp: CS2::ProverParams;
198 let cf_cs_vp: CS2::VerifierParams;
199 if prep_param.cs_pp.is_some()
200 && prep_param.cf_cs_pp.is_some()
201 && prep_param.cs_vp.is_some()
202 && prep_param.cf_cs_vp.is_some()
203 {
204 cs_pp = prep_param.clone().cs_pp.unwrap();
205 cs_vp = prep_param.clone().cs_vp.unwrap();
206 cf_cs_pp = prep_param.clone().cf_cs_pp.unwrap();
207 cf_cs_vp = prep_param.clone().cf_cs_vp.unwrap();
208 } else {
209 (cs_pp, cs_vp) = CS1::setup(&mut rng, ccs.n - ccs.l - 1)?;
210 (cf_cs_pp, cf_cs_vp) = CS2::setup(&mut rng, cf_r1cs.A.n_cols - cf_r1cs.l - 1)?;
211 }
212
213 let pp = ProverParams::<C1, C2, CS1, CS2> {
214 poseidon_config: prep_param.poseidon_config.clone(),
215 cs_params: cs_pp.clone(),
216 cf_cs_params: cf_cs_pp.clone(),
217 ccs: Some(ccs.clone()),
218 };
219 let vp = VerifierParams::<C1, C2, CS1, CS2> {
220 poseidon_config: prep_param.poseidon_config.clone(),
221 ccs,
222 cf_r1cs,
223 cs_vp: cs_vp.clone(),
224 cf_cs_vp: cf_cs_vp.clone(),
225 };
226 Ok((pp, vp))
227 }
228
229 /// Initializes the HyperNova+CycleFold's IVC for the given parameters and initial state `z_0`.
230 fn init(
231 params: (Self::ProverParam, Self::VerifierParam),
232 F: FC,
233 z_0: Vec<C1::ScalarField>,
234 ) -> Result<Self, Error> {
235 let (pp, vp) = params;
236 // `sponge` is for digest computation.
237 let sponge = PoseidonSponge::<C1::ScalarField>::new(&pp.poseidon_config);
238
239 // prepare the HyperNova's AugmentedFCircuit and CycleFold's circuits and obtain its CCS
240 // and R1CS respectively
241 let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC>::empty(
242 &pp.poseidon_config,
243 F.clone(),
244 pp.ccs.clone(),
245 )?;
246 let ccs = augmented_f_circuit.ccs.clone();
247
248 let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty();
249 let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
250
251 // compute the public params hash
252 let pp_hash = vp.pp_hash()?;
253
254 // setup the dummy instances
255 let W_dummy = Witness::<C1::ScalarField>::dummy(&ccs);
256 let U_dummy = LCCCS::<C1>::dummy(ccs.l, ccs.t, ccs.s);
257 let w_dummy = W_dummy.clone();
258 let mut u_dummy = CCCS::<C1>::dummy(ccs.l);
259 let (cf_W_dummy, cf_U_dummy): (NovaWitness<C2>, CommittedInstance<C2>) =
260 cf_r1cs.dummy_instance();
261 u_dummy.x = vec![
262 U_dummy.hash(
263 &sponge,
264 pp_hash,
265 C1::ScalarField::zero(),
266 z_0.clone(),
267 z_0.clone(),
268 ),
269 cf_U_dummy.hash_cyclefold(&sponge, pp_hash),
270 ];
271
272 // W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the
273 // R1CS that we're working with.
274 Ok(Self {
275 _gc1: PhantomData,
276 _c2: PhantomData,
277 _gc2: PhantomData,
278 ccs,
279 cf_r1cs,
280 poseidon_config: pp.poseidon_config.clone(),
281 cs_params: pp.cs_params.clone(),
282 cf_cs_params: pp.cf_cs_params.clone(),
283 F,
284 pp_hash,
285 i: C1::ScalarField::zero(),
286 z_0: z_0.clone(),
287 z_i: z_0,
288 W_i: W_dummy,
289 U_i: U_dummy,
290 w_i: w_dummy,
291 u_i: u_dummy,
292 // cyclefold running instance
293 cf_W_i: cf_W_dummy,
294 cf_U_i: cf_U_dummy,
295 })
296 }
297
298 /// Implements IVC.P of HyperNova+CycleFold
299 fn prove_step(
300 &mut self,
301 mut rng: impl RngCore,
302 external_inputs: Vec<C1::ScalarField>,
303 ) -> Result<(), Error> {
304 // `sponge` is for digest computation.
305 let sponge = PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config);
306
307 let augmented_f_circuit: AugmentedFCircuit<C1, C2, GC2, FC>;
308
309 if self.z_i.len() != self.F.state_len() {
310 return Err(Error::NotSameLength(
311 "z_i.len()".to_string(),
312 self.z_i.len(),
313 "F.state_len()".to_string(),
314 self.F.state_len(),
315 ));
316 }
317 if external_inputs.len() != self.F.external_inputs_len() {
318 return Err(Error::NotSameLength(
319 "F.external_inputs_len()".to_string(),
320 self.F.external_inputs_len(),
321 "external_inputs.len()".to_string(),
322 external_inputs.len(),
323 ));
324 }
325
326 if self.i > C1::ScalarField::from_le_bytes_mod_order(&usize::MAX.to_le_bytes()) {
327 return Err(Error::MaxStep);
328 }
329
330 let mut i_bytes: [u8; 8] = [0; 8];
331 i_bytes.copy_from_slice(&self.i.into_bigint().to_bytes_le()[..8]);
332 let i_usize: usize = usize::from_le_bytes(i_bytes);
333
334 let z_i1 = self
335 .F
336 .step_native(i_usize, self.z_i.clone(), external_inputs.clone())?;
337
338 // u_{i+1}.x[1] = H(cf_U_{i+1})
339 let cf_u_i1_x: C1::ScalarField;
340 let (U_i1, W_i1);
341
342 if self.i == C1::ScalarField::zero() {
343 W_i1 = Witness::<C1::ScalarField>::dummy(&self.ccs);
344 U_i1 = LCCCS::dummy(self.ccs.l, self.ccs.t, self.ccs.s);
345
346 let u_i1_x = U_i1.hash(
347 &sponge,
348 self.pp_hash,
349 C1::ScalarField::one(),
350 self.z_0.clone(),
351 z_i1.clone(),
352 );
353
354 // hash the initial (dummy) CycleFold instance, which is used as the 2nd public
355 // input in the AugmentedFCircuit
356 cf_u_i1_x = self.cf_U_i.hash_cyclefold(&sponge, self.pp_hash);
357
358 augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
359 _c2: PhantomData,
360 _gc2: PhantomData,
361 poseidon_config: self.poseidon_config.clone(),
362 ccs: self.ccs.clone(),
363 pp_hash: Some(self.pp_hash),
364 i: Some(C1::ScalarField::zero()),
365 i_usize: Some(0),
366 z_0: Some(self.z_0.clone()),
367 z_i: Some(self.z_i.clone()),
368 external_inputs: Some(external_inputs.clone()),
369 u_i_C: Some(self.u_i.C),
370 U_i: Some(self.U_i.clone()),
371 U_i1_C: Some(U_i1.C),
372 F: self.F.clone(),
373 x: Some(u_i1_x),
374 nimfs_proof: None,
375
376 // cyclefold values
377 cf_u_i_cmW: None,
378 cf_U_i: None,
379 cf_x: Some(cf_u_i1_x),
380 cf_cmT: None,
381 };
382 } else {
383 let mut transcript_p: PoseidonSponge<C1::ScalarField> =
384 PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config);
385 transcript_p.absorb(&self.pp_hash);
386 let (rho_bits, nimfs_proof);
387 (nimfs_proof, U_i1, W_i1, rho_bits) =
388 NIMFS::<C1, PoseidonSponge<C1::ScalarField>>::prove(
389 &mut transcript_p,
390 &self.ccs,
391 &[self.U_i.clone()],
392 &[self.u_i.clone()],
393 &[self.W_i.clone()],
394 &[self.w_i.clone()],
395 )?;
396
397 // sanity check: check the folded instance relation
398 #[cfg(test)]
399 U_i1.check_relation(&self.ccs, &W_i1)?;
400
401 let u_i1_x = U_i1.hash(
402 &sponge,
403 self.pp_hash,
404 self.i + C1::ScalarField::one(),
405 self.z_0.clone(),
406 z_i1.clone(),
407 );
408
409 let rho_Fq = C2::ScalarField::from_bigint(BigInteger::from_bits_le(&rho_bits))
410 .ok_or(Error::OutOfBounds)?;
411 // CycleFold part:
412 // get the vector used as public inputs 'x' in the CycleFold circuit
413 // cyclefold circuit for cmW
414 let cf_u_i_x = [
415 vec![rho_Fq],
416 get_cm_coordinates(&self.U_i.C),
417 get_cm_coordinates(&self.u_i.C),
418 get_cm_coordinates(&U_i1.C),
419 ]
420 .concat();
421
422 let cf_circuit = CycleFoldCircuit::<C1, GC1> {
423 _gc: PhantomData,
424 r_bits: Some(rho_bits.clone()),
425 p1: Some(self.U_i.clone().C),
426 p2: Some(self.u_i.clone().C),
427 x: Some(cf_u_i_x.clone()),
428 };
429
430 let (_cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) =
431 fold_cyclefold_circuit::<C1, GC1, C2, GC2, FC, CS1, CS2>(
432 &mut transcript_p,
433 self.cf_r1cs.clone(),
434 self.cf_cs_params.clone(),
435 self.pp_hash,
436 self.cf_W_i.clone(), // CycleFold running instance witness
437 self.cf_U_i.clone(), // CycleFold running instance
438 cf_u_i_x,
439 cf_circuit,
440 )?;
441
442 cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, self.pp_hash);
443
444 augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
445 _c2: PhantomData,
446 _gc2: PhantomData,
447 poseidon_config: self.poseidon_config.clone(),
448 ccs: self.ccs.clone(),
449 pp_hash: Some(self.pp_hash),
450 i: Some(self.i),
451 i_usize: Some(i_usize),
452 z_0: Some(self.z_0.clone()),
453 z_i: Some(self.z_i.clone()),
454 external_inputs: Some(external_inputs),
455 u_i_C: Some(self.u_i.C),
456 U_i: Some(self.U_i.clone()),
457 U_i1_C: Some(U_i1.C),
458 F: self.F.clone(),
459 x: Some(u_i1_x),
460 nimfs_proof: Some(nimfs_proof),
461
462 // cyclefold values
463 cf_u_i_cmW: Some(cf_u_i.cmW),
464 cf_U_i: Some(self.cf_U_i.clone()),
465 cf_x: Some(cf_u_i1_x),
466 cf_cmT: Some(cf_cmT),
467 };
468
469 // assign the next round instances
470 self.cf_W_i = cf_W_i1;
471 self.cf_U_i = cf_U_i1;
472 }
473
474 let (cs, _) = augmented_f_circuit.compute_cs_ccs()?;
475
476 #[cfg(test)]
477 assert!(cs.is_satisfied()?);
478
479 let (r1cs_w_i1, r1cs_x_i1) = extract_w_x::<C1::ScalarField>(&cs); // includes 1 and public inputs
480
481 let r1cs_z = [
482 vec![C1::ScalarField::one()],
483 r1cs_x_i1.clone(),
484 r1cs_w_i1.clone(),
485 ]
486 .concat();
487 // compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we
488 // assign them directly to w_i, u_i.
489 let (u_i, w_i) = self
490 .ccs
491 .to_cccs::<_, C1, CS1>(&mut rng, &self.cs_params, &r1cs_z)?;
492 self.u_i = u_i.clone();
493 self.w_i = w_i.clone();
494
495 // set values for next iteration
496 self.i += C1::ScalarField::one();
497 // assign z_{i+1} into z_i
498 self.z_i = z_i1.clone();
499 self.U_i = U_i1.clone();
500 self.W_i = W_i1.clone();
501
502 #[cfg(test)]
503 {
504 // check the new LCCCS instance relation
505 self.U_i.check_relation(&self.ccs, &self.W_i)?;
506 // check the new CCCS instance relation
507 self.u_i.check_relation(&self.ccs, &self.w_i)?;
508 }
509
510 Ok(())
511 }
512
513 fn state(&self) -> Vec<C1::ScalarField> {
514 self.z_i.clone()
515 }
516
517 fn instances(
518 &self,
519 ) -> (
520 Self::RunningInstance,
521 Self::IncomingInstance,
522 Self::CFInstance,
523 ) {
524 (
525 (self.U_i.clone(), self.W_i.clone()),
526 (self.u_i.clone(), self.w_i.clone()),
527 (self.cf_U_i.clone(), self.cf_W_i.clone()),
528 )
529 }
530
531 /// Implements IVC.V of HyperNova+CycleFold. Notice that this method does not include the
532 /// commitments verification, which is done in the Decider.
533 fn verify(
534 vp: Self::VerifierParam,
535 z_0: Vec<C1::ScalarField>, // initial state
536 z_i: Vec<C1::ScalarField>, // last state
537 num_steps: C1::ScalarField,
538 running_instance: Self::RunningInstance,
539 incoming_instance: Self::IncomingInstance,
540 cyclefold_instance: Self::CFInstance,
541 ) -> Result<(), Error> {
542 if num_steps == C1::ScalarField::zero() {
543 if z_0 != z_i {
544 return Err(Error::IVCVerificationFail);
545 }
546 return Ok(());
547 }
548 // `sponge` is for digest computation.
549 let sponge = PoseidonSponge::<C1::ScalarField>::new(&vp.poseidon_config);
550
551 let (U_i, W_i) = running_instance;
552 let (u_i, w_i) = incoming_instance;
553 let (cf_U_i, cf_W_i) = cyclefold_instance;
554 if u_i.x.len() != 2 || U_i.x.len() != 2 {
555 return Err(Error::IVCVerificationFail);
556 }
557
558 let pp_hash = vp.pp_hash()?;
559
560 // check that u_i's output points to the running instance
561 // u_i.X[0] == H(i, z_0, z_i, U_i)
562 let expected_u_i_x = U_i.hash(&sponge, pp_hash, num_steps, z_0, z_i.clone());
563 if expected_u_i_x != u_i.x[0] {
564 return Err(Error::IVCVerificationFail);
565 }
566 // u_i.X[1] == H(cf_U_i)
567 let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&sponge, pp_hash);
568 if expected_cf_u_i_x != u_i.x[1] {
569 return Err(Error::IVCVerificationFail);
570 }
571
572 // check LCCCS satisfiability
573 U_i.check_relation(&vp.ccs, &W_i)?;
574 // check CCCS satisfiability
575 u_i.check_relation(&vp.ccs, &w_i)?;
576
577 // check CycleFold's RelaxedR1CS satisfiability
578 vp.cf_r1cs
579 .check_relaxed_instance_relation(&cf_W_i, &cf_U_i)?;
580
581 Ok(())
582 }
583}
584
585#[cfg(test)]
586mod tests {
587 use crate::commitment::kzg::KZG;
588 use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
589 use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
590
591 use super::*;
592 use crate::commitment::pedersen::Pedersen;
593 use crate::frontend::tests::CubicFCircuit;
594 use crate::transcript::poseidon::poseidon_canonical_config;
595
596 #[test]
597 pub fn test_ivc() {
598 let poseidon_config = poseidon_canonical_config::<Fr>();
599
600 let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
601
602 // run the test using Pedersen commitments on both sides of the curve cycle
603 test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>>(
604 poseidon_config.clone(),
605 F_circuit,
606 );
607 // run the test using KZG for the commitments on the main curve, and Pedersen for the
608 // commitments on the secondary curve
609 test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>>(poseidon_config, F_circuit);
610 }
611
612 // test_ivc allowing to choose the CommitmentSchemes
613 fn test_ivc_opt<CS1: CommitmentScheme<Projective>, CS2: CommitmentScheme<Projective2>>(
614 poseidon_config: PoseidonConfig<Fr>,
615 F_circuit: CubicFCircuit<Fr>,
616 ) {
617 let mut rng = ark_std::test_rng();
618
619 type HN<CS1, CS2> =
620 HyperNova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2>;
621
622 let prep_param =
623 PreprocessorParam::<Projective, Projective2, CubicFCircuit<Fr>, CS1, CS2>::new(
624 poseidon_config.clone(),
625 F_circuit,
626 );
627 let (prover_params, verifier_params) = HN::preprocess(&mut rng, &prep_param).unwrap();
628
629 let z_0 = vec![Fr::from(3_u32)];
630 let mut hypernova = HN::init(
631 (prover_params, verifier_params.clone()),
632 F_circuit,
633 z_0.clone(),
634 )
635 .unwrap();
636
637 let num_steps: usize = 3;
638 for _ in 0..num_steps {
639 hypernova.prove_step(&mut rng, vec![]).unwrap();
640 }
641 assert_eq!(Fr::from(num_steps as u32), hypernova.i);
642
643 let (running_instance, incoming_instance, cyclefold_instance) = hypernova.instances();
644 HN::verify(
645 verifier_params,
646 z_0,
647 hypernova.z_i,
648 hypernova.i,
649 running_instance,
650 incoming_instance,
651 cyclefold_instance,
652 )
653 .unwrap();
654 }
655}