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