/// Implements the scheme described in [Nova](https://eprint.iacr.org/2021/370.pdf) and /// [CycleFold](https://eprint.iacr.org/2023/1192.pdf). use ark_crypto_primitives::{ crh::{poseidon::CRH, CRHScheme}, sponge::{poseidon::PoseidonConfig, Absorb}, }; use ark_ec::{AffineRepr, CurveGroup, Group}; use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar}; use ark_std::fmt::Debug; use ark_std::{One, Zero}; use core::marker::PhantomData; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; use crate::ccs::r1cs::{extract_r1cs, extract_w_x, R1CS}; use crate::commitment::CommitmentProver; use crate::folding::circuits::nonnative::point_to_nonnative_limbs; use crate::frontend::FCircuit; use crate::utils::vec::is_zero_vec; use crate::Error; use crate::FoldingScheme; pub mod circuits; pub mod cyclefold; pub mod decider_eth; pub mod decider_eth_circuit; pub mod nifs; pub mod traits; use circuits::{AugmentedFCircuit, ChallengeGadget, CF2}; use cyclefold::{CycleFoldChallengeGadget, CycleFoldCircuit}; use nifs::NIFS; use traits::NovaR1CS; #[cfg(test)] use cyclefold::CF_IO_LEN; #[derive(Debug, Clone, Eq, PartialEq)] pub struct CommittedInstance { pub cmE: C, pub u: C::ScalarField, pub cmW: C, pub x: Vec, } impl CommittedInstance { pub fn dummy(io_len: usize) -> Self { Self { cmE: C::zero(), u: C::ScalarField::zero(), cmW: C::zero(), x: vec![C::ScalarField::zero(); io_len], } } } impl CommittedInstance where ::ScalarField: Absorb, ::BaseField: ark_ff::PrimeField, { /// hash implements the committed instance hash compatible with the gadget implemented in /// nova/circuits.rs::CommittedInstanceVar.hash. /// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U_i` is the /// `CommittedInstance`. pub fn hash( &self, poseidon_config: &PoseidonConfig, i: C::ScalarField, z_0: Vec, z_i: Vec, ) -> Result { let (cmE_x, cmE_y) = point_to_nonnative_limbs::(self.cmE)?; let (cmW_x, cmW_y) = point_to_nonnative_limbs::(self.cmW)?; CRH::::evaluate( poseidon_config, vec![ vec![i], z_0, z_i, vec![self.u], self.x.clone(), cmE_x, cmE_y, cmW_x, cmW_y, ] .concat(), ) .map_err(|e| Error::Other(e.to_string())) } } #[derive(Debug, Clone, Eq, PartialEq)] pub struct Witness { pub E: Vec, pub rE: C::ScalarField, pub W: Vec, pub rW: C::ScalarField, } impl Witness where ::ScalarField: Absorb, { pub fn new(w: Vec, e_len: usize) -> Self { // note: at the current version, we don't use the blinding factors and we set them to 0 // always. Self { E: vec![C::ScalarField::zero(); e_len], rE: C::ScalarField::zero(), W: w, rW: C::ScalarField::zero(), } } pub fn commit>( &self, params: &CP::Params, x: Vec, ) -> Result, Error> { let mut cmE = C::zero(); if !is_zero_vec::(&self.E) { cmE = CP::commit(params, &self.E, &self.rE)?; } let cmW = CP::commit(params, &self.W, &self.rW)?; Ok(CommittedInstance { cmE, u: C::ScalarField::one(), cmW, x, }) } } #[derive(Debug, Clone)] pub struct ProverParams where C1: CurveGroup, C2: CurveGroup, CP1: CommitmentProver, CP2: CommitmentProver, { pub poseidon_config: PoseidonConfig, pub cm_params: CP1::Params, pub cf_cm_params: CP2::Params, } #[derive(Debug, Clone)] pub struct VerifierParams { pub poseidon_config: PoseidonConfig, pub r1cs: R1CS, pub cf_r1cs: R1CS, } /// Implements Nova+CycleFold's IVC, described in [Nova](https://eprint.iacr.org/2021/370.pdf) and /// [CycleFold](https://eprint.iacr.org/2023/1192.pdf), following the FoldingScheme trait #[derive(Clone, Debug)] pub struct Nova where C1: CurveGroup, GC1: CurveVar>, C2: CurveGroup, GC2: CurveVar>, FC: FCircuit, CP1: CommitmentProver, CP2: CommitmentProver, { _gc1: PhantomData, _c2: PhantomData, _gc2: PhantomData, /// R1CS of the Augmented Function circuit pub r1cs: R1CS, /// R1CS of the CycleFold circuit pub cf_r1cs: R1CS, pub poseidon_config: PoseidonConfig, /// CommitmentProver::Params over C1 pub cm_params: CP1::Params, /// CycleFold CommitmentProver::Params, over C2 pub cf_cm_params: CP2::Params, /// F circuit, the circuit that is being folded pub F: FC, pub i: C1::ScalarField, /// initial state pub z_0: Vec, /// current i-th state pub z_i: Vec, /// Nova instances pub w_i: Witness, pub u_i: CommittedInstance, pub W_i: Witness, pub U_i: CommittedInstance, /// CycleFold running instance pub cf_W_i: Witness, pub cf_U_i: CommittedInstance, } impl FoldingScheme for Nova where C1: CurveGroup, GC1: CurveVar>, C2: CurveGroup, GC2: CurveVar>, FC: FCircuit, CP1: CommitmentProver, CP2: CommitmentProver, ::BaseField: PrimeField, ::BaseField: PrimeField, ::ScalarField: Absorb, ::ScalarField: Absorb, C1: CurveGroup, for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>, for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, { type PreprocessorParam = (Self::ProverParam, FC); type ProverParam = ProverParams; type VerifierParam = VerifierParams; type CommittedInstanceWithWitness = (CommittedInstance, Witness); type CFCommittedInstanceWithWitness = (CommittedInstance, Witness); fn preprocess( prep_param: &Self::PreprocessorParam, ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> { let (prover_params, F_circuit) = prep_param; let (r1cs, cf_r1cs) = get_r1cs::(&prover_params.poseidon_config, *F_circuit)?; let verifier_params = VerifierParams:: { poseidon_config: prover_params.poseidon_config.clone(), r1cs, cf_r1cs, }; Ok((prover_params.clone(), verifier_params)) } /// Initializes the Nova+CycleFold's IVC for the given parameters and initial state `z_0`. fn init(pp: &Self::ProverParam, F: FC, z_0: Vec) -> Result { // prepare the circuit to obtain its R1CS let cs = ConstraintSystem::::new_ref(); let cs2 = ConstraintSystem::::new_ref(); let augmented_F_circuit = AugmentedFCircuit::::empty(&pp.poseidon_config, F); let cf_circuit = CycleFoldCircuit::::empty(); augmented_F_circuit.generate_constraints(cs.clone())?; cs.finalize(); let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?; let r1cs = extract_r1cs::(&cs); cf_circuit.generate_constraints(cs2.clone())?; cs2.finalize(); let cs2 = cs2.into_inner().ok_or(Error::NoInnerConstraintSystem)?; let cf_r1cs = extract_r1cs::(&cs2); // setup the dummy instances let (w_dummy, u_dummy) = r1cs.dummy_instance(); let (cf_w_dummy, cf_u_dummy) = cf_r1cs.dummy_instance(); // W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the // R1CS that we're working with. Ok(Self { _gc1: PhantomData, _c2: PhantomData, _gc2: PhantomData, r1cs, cf_r1cs, poseidon_config: pp.poseidon_config.clone(), cm_params: pp.cm_params.clone(), cf_cm_params: pp.cf_cm_params.clone(), F, i: C1::ScalarField::zero(), z_0: z_0.clone(), z_i: z_0, w_i: w_dummy.clone(), u_i: u_dummy.clone(), W_i: w_dummy, U_i: u_dummy, // cyclefold running instance cf_W_i: cf_w_dummy.clone(), cf_U_i: cf_u_dummy.clone(), }) } /// Implements IVC.P of Nova+CycleFold fn prove_step(&mut self) -> Result<(), Error> { let augmented_F_circuit: AugmentedFCircuit; let cfW_circuit: CycleFoldCircuit; let cfE_circuit: CycleFoldCircuit; let z_i1 = self.F.step_native(self.z_i.clone())?; // compute T and cmT for AugmentedFCircuit let (T, cmT) = self.compute_cmT()?; // r_bits is the r used to the RLC of the F' instances let r_bits = ChallengeGadget::::get_challenge_native( &self.poseidon_config, self.U_i.clone(), self.u_i.clone(), cmT, )?; let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)) .ok_or(Error::OutOfBounds)?; // fold Nova instances let (W_i1, U_i1): (Witness, CommittedInstance) = NIFS::::fold_instances( r_Fr, &self.W_i, &self.U_i, &self.w_i, &self.u_i, &T, cmT, )?; // folded instance output (public input, x) // u_{i+1}.x = H(i+1, z_0, z_{i+1}, U_{i+1}) let u_i1_x = U_i1.hash( &self.poseidon_config, self.i + C1::ScalarField::one(), self.z_0.clone(), z_i1.clone(), )?; if self.i == C1::ScalarField::zero() { // base case augmented_F_circuit = AugmentedFCircuit:: { _gc2: PhantomData, poseidon_config: self.poseidon_config.clone(), i: Some(C1::ScalarField::zero()), // = i=0 z_0: Some(self.z_0.clone()), // = z_i z_i: Some(self.z_i.clone()), u_i: Some(self.u_i.clone()), // = dummy U_i: Some(self.U_i.clone()), // = dummy U_i1: Some(U_i1.clone()), cmT: Some(cmT), F: self.F, x: Some(u_i1_x), cf1_u_i: None, cf2_u_i: None, cf_U_i: None, cf1_U_i1: None, cf_U_i1: None, cf1_cmT: None, cf2_cmT: None, cf1_r_nonnat: None, cf2_r_nonnat: None, }; #[cfg(test)] NIFS::::verify_folded_instance(r_Fr, &self.U_i, &self.u_i, &U_i1, &cmT)?; } else { // CycleFold part: // get the vector used as public inputs 'x' in the CycleFold circuit // cyclefold circuit for cmW let cfW_u_i_x = [ get_cm_coordinates(&self.U_i.cmW), get_cm_coordinates(&self.u_i.cmW), get_cm_coordinates(&U_i1.cmW), ] .concat(); // cyclefold circuit for cmE let cfE_u_i_x = [ get_cm_coordinates(&self.U_i.cmE), get_cm_coordinates(&self.u_i.cmE), get_cm_coordinates(&U_i1.cmE), ] .concat(); cfW_circuit = CycleFoldCircuit:: { _gc: PhantomData, r_bits: Some(r_bits.clone()), p1: Some(self.U_i.clone().cmW), p2: Some(self.u_i.clone().cmW), p3: Some(U_i1.clone().cmW), x: Some(cfW_u_i_x.clone()), }; cfE_circuit = CycleFoldCircuit:: { _gc: PhantomData, r_bits: Some(r_bits.clone()), p1: Some(self.U_i.clone().cmE), p2: Some(cmT), p3: Some(U_i1.clone().cmE), x: Some(cfE_u_i_x.clone()), }; // fold self.cf_U_i + cfW_U -> folded running with cfW let (_cfW_w_i, cfW_u_i, cfW_W_i1, cfW_U_i1, cfW_cmT, cfW_r1_Fq) = self .fold_cyclefold_circuit( self.cf_W_i.clone(), // CycleFold running instance witness self.cf_U_i.clone(), // CycleFold running instance cfW_u_i_x, cfW_circuit, )?; // fold [the output from folding self.cf_U_i + cfW_U] + cfE_U = folded_running_with_cfW + cfE let (_cfE_w_i, cfE_u_i, cf_W_i1, cf_U_i1, cf_cmT, cf_r2_Fq) = self.fold_cyclefold_circuit(cfW_W_i1, cfW_U_i1.clone(), cfE_u_i_x, cfE_circuit)?; augmented_F_circuit = AugmentedFCircuit:: { _gc2: PhantomData, poseidon_config: self.poseidon_config.clone(), i: Some(self.i), z_0: Some(self.z_0.clone()), z_i: Some(self.z_i.clone()), u_i: Some(self.u_i.clone()), U_i: Some(self.U_i.clone()), U_i1: Some(U_i1.clone()), cmT: Some(cmT), F: self.F, x: Some(u_i1_x), // cyclefold values cf1_u_i: Some(cfW_u_i.clone()), cf2_u_i: Some(cfE_u_i.clone()), cf_U_i: Some(self.cf_U_i.clone()), cf1_U_i1: Some(cfW_U_i1.clone()), cf_U_i1: Some(cf_U_i1.clone()), cf1_cmT: Some(cfW_cmT), cf2_cmT: Some(cf_cmT), cf1_r_nonnat: Some(cfW_r1_Fq), cf2_r_nonnat: Some(cf_r2_Fq), }; self.cf_W_i = cf_W_i1.clone(); self.cf_U_i = cf_U_i1.clone(); #[cfg(test)] { self.cf_r1cs.check_instance_relation(&_cfW_w_i, &cfW_u_i)?; self.cf_r1cs.check_instance_relation(&_cfE_w_i, &cfE_u_i)?; self.cf_r1cs .check_relaxed_instance_relation(&self.cf_W_i, &self.cf_U_i)?; } } let cs = ConstraintSystem::::new_ref(); augmented_F_circuit.generate_constraints(cs.clone())?; let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?; let (w_i1, x_i1) = extract_w_x::(&cs); if x_i1[0] != u_i1_x { return Err(Error::NotEqual); } #[cfg(test)] if x_i1.len() != 1 { return Err(Error::NotExpectedLength(x_i1.len(), 1)); } // set values for next iteration self.i += C1::ScalarField::one(); self.z_i = z_i1.clone(); self.w_i = Witness::::new(w_i1, self.r1cs.A.n_rows); self.u_i = self.w_i.commit::(&self.cm_params, vec![u_i1_x])?; self.W_i = W_i1.clone(); self.U_i = U_i1.clone(); #[cfg(test)] { self.r1cs.check_instance_relation(&self.w_i, &self.u_i)?; self.r1cs .check_relaxed_instance_relation(&self.W_i, &self.U_i)?; } Ok(()) } fn state(&self) -> Vec { self.z_i.clone() } fn instances( &self, ) -> ( Self::CommittedInstanceWithWitness, Self::CommittedInstanceWithWitness, Self::CFCommittedInstanceWithWitness, ) { ( (self.U_i.clone(), self.W_i.clone()), (self.u_i.clone(), self.w_i.clone()), (self.cf_U_i.clone(), self.cf_W_i.clone()), ) } /// Implements IVC.V of Nova+CycleFold fn verify( vp: Self::VerifierParam, z_0: Vec, // initial state z_i: Vec, // last state num_steps: C1::ScalarField, running_instance: Self::CommittedInstanceWithWitness, incoming_instance: Self::CommittedInstanceWithWitness, cyclefold_instance: Self::CFCommittedInstanceWithWitness, ) -> Result<(), Error> { let (U_i, W_i) = running_instance; let (u_i, w_i) = incoming_instance; let (cf_U_i, cf_W_i) = cyclefold_instance; if u_i.x.len() != 1 || U_i.x.len() != 1 { return Err(Error::IVCVerificationFail); } // check that u_i's output points to the running instance // u_i.X == H(i, z_0, z_i, U_i) let expected_u_i_x = U_i.hash(&vp.poseidon_config, num_steps, z_0, z_i.clone())?; if expected_u_i_x != u_i.x[0] { return Err(Error::IVCVerificationFail); } // check u_i.cmE==0, u_i.u==1 (=u_i is a un-relaxed instance) if !u_i.cmE.is_zero() || !u_i.u.is_one() { return Err(Error::IVCVerificationFail); } // check R1CS satisfiability vp.r1cs.check_instance_relation(&w_i, &u_i)?; // check RelaxedR1CS satisfiability vp.r1cs.check_relaxed_instance_relation(&W_i, &U_i)?; // check CycleFold RelaxedR1CS satisfiability vp.cf_r1cs .check_relaxed_instance_relation(&cf_W_i, &cf_U_i)?; Ok(()) } } impl Nova where C1: CurveGroup, GC1: CurveVar>, C2: CurveGroup, GC2: CurveVar>, FC: FCircuit, CP1: CommitmentProver, CP2: CommitmentProver, ::BaseField: PrimeField, ::ScalarField: Absorb, ::ScalarField: Absorb, C1: CurveGroup, { // computes T and cmT for the AugmentedFCircuit fn compute_cmT(&self) -> Result<(Vec, C1), Error> { NIFS::::compute_cmT( &self.cm_params, &self.r1cs, &self.w_i, &self.u_i, &self.W_i, &self.U_i, ) } // computes T* and cmT* for the CycleFoldCircuit fn compute_cf_cmT( &self, cf_w_i: &Witness, cf_u_i: &CommittedInstance, cf_W_i: &Witness, cf_U_i: &CommittedInstance, ) -> Result<(Vec, C2), Error> { NIFS::::compute_cyclefold_cmT( &self.cf_cm_params, &self.cf_r1cs, cf_w_i, cf_u_i, cf_W_i, cf_U_i, ) } } impl Nova where C1: CurveGroup, GC1: CurveVar>, C2: CurveGroup, GC2: CurveVar>, FC: FCircuit, CP1: CommitmentProver, CP2: CommitmentProver, ::BaseField: PrimeField, ::BaseField: PrimeField, ::ScalarField: Absorb, ::ScalarField: Absorb, C1: CurveGroup, for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>, for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, { // folds the given cyclefold circuit and its instances #[allow(clippy::type_complexity)] fn fold_cyclefold_circuit( &self, cf_W_i: Witness, // witness of the running instance cf_U_i: CommittedInstance, // running instance cf_u_i_x: Vec, cf_circuit: CycleFoldCircuit, ) -> Result< ( Witness, CommittedInstance, // u_i Witness, // W_i1 CommittedInstance, // U_i1 C2, // cmT C2::ScalarField, // r_Fq ), Error, > { let cs2 = ConstraintSystem::::new_ref(); cf_circuit.generate_constraints(cs2.clone())?; let cs2 = cs2.into_inner().ok_or(Error::NoInnerConstraintSystem)?; let (cf_w_i, cf_x_i) = extract_w_x::(&cs2); if cf_x_i != cf_u_i_x { return Err(Error::NotEqual); } #[cfg(test)] if cf_x_i.len() != CF_IO_LEN { return Err(Error::NotExpectedLength(cf_x_i.len(), CF_IO_LEN)); } // fold cyclefold instances let cf_w_i = Witness::::new(cf_w_i.clone(), self.cf_r1cs.A.n_rows); let cf_u_i: CommittedInstance = cf_w_i.commit::(&self.cf_cm_params, cf_x_i.clone())?; // compute T* and cmT* for CycleFoldCircuit let (cf_T, cf_cmT) = self.compute_cf_cmT(&cf_w_i, &cf_u_i, &cf_W_i, &cf_U_i)?; let cf_r_bits = CycleFoldChallengeGadget::::get_challenge_native( &self.poseidon_config, cf_U_i.clone(), cf_u_i.clone(), cf_cmT, )?; let cf_r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&cf_r_bits)) .ok_or(Error::OutOfBounds)?; let (cf_W_i1, cf_U_i1) = NIFS::::fold_instances( cf_r_Fq, &cf_W_i, &cf_U_i, &cf_w_i, &cf_u_i, &cf_T, cf_cmT, )?; Ok((cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, cf_r_Fq)) } } /// helper method to get the r1cs from the ConstraintSynthesizer pub fn get_r1cs_from_cs( circuit: impl ConstraintSynthesizer, ) -> Result, Error> { let cs = ConstraintSystem::::new_ref(); circuit.generate_constraints(cs.clone())?; cs.finalize(); let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?; let r1cs = extract_r1cs::(&cs); Ok(r1cs) } /// helper method to get the R1CS for both the AugmentedFCircuit and the CycleFold circuit #[allow(clippy::type_complexity)] pub fn get_r1cs( poseidon_config: &PoseidonConfig, F_circuit: FC, ) -> Result<(R1CS, R1CS), Error> where C1: CurveGroup, GC1: CurveVar>, C2: CurveGroup, GC2: CurveVar>, FC: FCircuit, ::BaseField: PrimeField, ::BaseField: PrimeField, ::ScalarField: Absorb, ::ScalarField: Absorb, C1: CurveGroup, for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>, for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, { let augmented_F_circuit = AugmentedFCircuit::::empty(poseidon_config, F_circuit); let cf_circuit = CycleFoldCircuit::::empty(); let r1cs = get_r1cs_from_cs::(augmented_F_circuit)?; let cf_r1cs = get_r1cs_from_cs::(cf_circuit)?; Ok((r1cs, cf_r1cs)) } /// helper method to get the pedersen params length for both the AugmentedFCircuit and the /// CycleFold circuit pub fn get_pedersen_params_len( poseidon_config: &PoseidonConfig, F_circuit: FC, ) -> Result<(usize, usize), Error> where C1: CurveGroup, GC1: CurveVar>, C2: CurveGroup, GC2: CurveVar>, FC: FCircuit, ::BaseField: PrimeField, ::BaseField: PrimeField, ::ScalarField: Absorb, ::ScalarField: Absorb, C1: CurveGroup, for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>, for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, { let (r1cs, cf_r1cs) = get_r1cs::(poseidon_config, F_circuit)?; Ok((r1cs.A.n_rows, cf_r1cs.A.n_rows)) } pub(crate) fn get_cm_coordinates(cm: &C) -> Vec { let zero = (&C::BaseField::zero(), &C::BaseField::one()); let cm = cm.into_affine(); let (cm_x, cm_y) = cm.xy().unwrap_or(zero); vec![*cm_x, *cm_y] } #[cfg(test)] pub mod tests { use super::*; use ark_pallas::{constraints::GVar, Fr, Projective}; use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2}; use crate::commitment::pedersen::Pedersen; use crate::frontend::tests::CubicFCircuit; use crate::transcript::poseidon::poseidon_test_config; /// This test tests the Nova+CycleFold IVC, and by consequence it is also testing the /// AugmentedFCircuit #[test] fn test_ivc() { type NOVA = Nova< Projective, GVar, Projective2, GVar2, CubicFCircuit, Pedersen, Pedersen, >; let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_test_config::(); let F_circuit = CubicFCircuit::::new(()); let z_0 = vec![Fr::from(3_u32)]; let (cm_len, cf_cm_len) = get_pedersen_params_len::>( &poseidon_config, F_circuit, ) .unwrap(); let pedersen_params = Pedersen::::new_params(&mut rng, cm_len); let cf_pedersen_params = Pedersen::::new_params(&mut rng, cf_cm_len); let prover_params = ProverParams::, Pedersen> { poseidon_config: poseidon_config.clone(), cm_params: pedersen_params, cf_cm_params: cf_pedersen_params, }; let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap(); let num_steps: usize = 3; for _ in 0..num_steps { nova.prove_step().unwrap(); } assert_eq!(Fr::from(num_steps as u32), nova.i); let verifier_params = VerifierParams:: { poseidon_config, r1cs: nova.clone().r1cs, cf_r1cs: nova.clone().cf_r1cs, }; let (running_instance, incoming_instance, cyclefold_instance) = nova.instances(); NOVA::verify( verifier_params, z_0, nova.z_i, nova.i, running_instance, incoming_instance, cyclefold_instance, ) .unwrap(); } }