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}