arnaucube
arnaucube/sonobePublic Self-hosted repository
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}