arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/folding/nova/mod.rs
folding-schemes/src/folding/nova/mod.rsRaw
   1/// Implements the scheme described in [Nova](https://eprint.iacr.org/2021/370.pdf) and
   2/// [CycleFold](https://eprint.iacr.org/2023/1192.pdf).
   3use ark_crypto_primitives::sponge::{
   4    poseidon::{PoseidonConfig, PoseidonSponge},
   5    Absorb, CryptographicSponge,
   6};
   7use ark_ec::{CurveGroup, Group};
   8use ark_ff::{BigInteger, PrimeField};
   9use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget};
  10use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
  11use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Valid};
  12use ark_std::fmt::Debug;
  13use ark_std::rand::RngCore;
  14use ark_std::{One, UniformRand, Zero};
  15use core::marker::PhantomData;
  16use decider_eth_circuit::WitnessVar;
  17
  18use crate::folding::circuits::cyclefold::{
  19    fold_cyclefold_circuit, CycleFoldCircuit, CycleFoldCommittedInstance, CycleFoldConfig,
  20    CycleFoldWitness,
  21};
  22use crate::folding::{
  23    circuits::{CF1, CF2},
  24    traits::Dummy,
  25};
  26use crate::frontend::FCircuit;
  27use crate::transcript::{poseidon::poseidon_canonical_config, AbsorbNonNative, Transcript};
  28use crate::utils::vec::is_zero_vec;
  29use crate::Error;
  30use crate::FoldingScheme;
  31use crate::{
  32    arith::r1cs::{extract_r1cs, extract_w_x, R1CS},
  33    constants::NOVA_N_BITS_RO,
  34    utils::{get_cm_coordinates, pp_hash},
  35};
  36use crate::{arith::Arith, commitment::CommitmentScheme};
  37
  38use circuits::{AugmentedFCircuit, ChallengeGadget, CommittedInstanceVar};
  39use nifs::NIFS;
  40
  41pub mod circuits;
  42pub mod nifs;
  43pub mod serialize;
  44pub mod traits;
  45pub mod zk;
  46
  47// offchain decider
  48pub mod decider;
  49pub mod decider_circuits;
  50// onchain decider
  51pub mod decider_eth;
  52pub mod decider_eth_circuit;
  53
  54use super::traits::{CommittedInstanceOps, WitnessOps};
  55
  56/// Configuration for Nova's CycleFold circuit
  57pub struct NovaCycleFoldConfig<C: CurveGroup> {
  58    _c: PhantomData<C>,
  59}
  60
  61impl<C: CurveGroup> CycleFoldConfig for NovaCycleFoldConfig<C> {
  62    const RANDOMNESS_BIT_LENGTH: usize = NOVA_N_BITS_RO;
  63    // Number of points to be folded in the CycleFold circuit, in Nova's case, this is a fixed
  64    // amount:
  65    // 2 points to be folded.
  66    const N_INPUT_POINTS: usize = 2;
  67    type C = C;
  68    type F = C::BaseField;
  69}
  70
  71/// CycleFold circuit for computing random linear combinations of group elements
  72/// in Nova instances.
  73pub type NovaCycleFoldCircuit<C, GC> = CycleFoldCircuit<NovaCycleFoldConfig<C>, GC>;
  74
  75#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
  76pub struct CommittedInstance<C: CurveGroup> {
  77    pub cmE: C,
  78    pub u: C::ScalarField,
  79    pub cmW: C,
  80    pub x: Vec<C::ScalarField>,
  81}
  82
  83impl<C: CurveGroup> Dummy<usize> for CommittedInstance<C> {
  84    fn dummy(io_len: usize) -> Self {
  85        Self {
  86            cmE: C::zero(),
  87            u: CF1::<C>::zero(),
  88            cmW: C::zero(),
  89            x: vec![CF1::<C>::zero(); io_len],
  90        }
  91    }
  92}
  93
  94impl<C: CurveGroup> Dummy<&R1CS<CF1<C>>> for CommittedInstance<C> {
  95    fn dummy(r1cs: &R1CS<CF1<C>>) -> Self {
  96        Self::dummy(r1cs.l)
  97    }
  98}
  99
 100impl<C: CurveGroup> Absorb for CommittedInstance<C>
 101where
 102    C::ScalarField: Absorb,
 103{
 104    fn to_sponge_bytes(&self, dest: &mut Vec<u8>) {
 105        C::ScalarField::batch_to_sponge_bytes(&self.to_sponge_field_elements_as_vec(), dest);
 106    }
 107
 108    fn to_sponge_field_elements<F: PrimeField>(&self, dest: &mut Vec<F>) {
 109        self.u.to_sponge_field_elements(dest);
 110        self.x.to_sponge_field_elements(dest);
 111        // We cannot call `to_native_sponge_field_elements(dest)` directly, as
 112        // `to_native_sponge_field_elements` needs `F` to be `C::ScalarField`,
 113        // but here `F` is a generic `PrimeField`.
 114        self.cmE
 115            .to_native_sponge_field_elements_as_vec()
 116            .to_sponge_field_elements(dest);
 117        self.cmW
 118            .to_native_sponge_field_elements_as_vec()
 119            .to_sponge_field_elements(dest);
 120    }
 121}
 122
 123impl<C: CurveGroup> CommittedInstanceOps<C> for CommittedInstance<C> {
 124    type Var = CommittedInstanceVar<C>;
 125
 126    fn get_commitments(&self) -> Vec<C> {
 127        vec![self.cmW, self.cmE]
 128    }
 129
 130    fn is_incoming(&self) -> bool {
 131        self.cmE == C::zero() && self.u == One::one()
 132    }
 133}
 134
 135#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
 136pub struct Witness<C: CurveGroup> {
 137    pub E: Vec<C::ScalarField>,
 138    pub rE: C::ScalarField,
 139    pub W: Vec<C::ScalarField>,
 140    pub rW: C::ScalarField,
 141}
 142
 143impl<C: CurveGroup> Witness<C> {
 144    pub fn new<const H: bool>(w: Vec<C::ScalarField>, e_len: usize, mut rng: impl RngCore) -> Self {
 145        let (rW, rE) = if H {
 146            (
 147                C::ScalarField::rand(&mut rng),
 148                C::ScalarField::rand(&mut rng),
 149            )
 150        } else {
 151            (C::ScalarField::zero(), C::ScalarField::zero())
 152        };
 153
 154        Self {
 155            E: vec![C::ScalarField::zero(); e_len],
 156            rE,
 157            W: w,
 158            rW,
 159        }
 160    }
 161
 162    pub fn commit<CS: CommitmentScheme<C, HC>, const HC: bool>(
 163        &self,
 164        params: &CS::ProverParams,
 165        x: Vec<C::ScalarField>,
 166    ) -> Result<CommittedInstance<C>, Error> {
 167        let mut cmE = C::zero();
 168        if !is_zero_vec::<C::ScalarField>(&self.E) {
 169            cmE = CS::commit(params, &self.E, &self.rE)?;
 170        }
 171        let cmW = CS::commit(params, &self.W, &self.rW)?;
 172        Ok(CommittedInstance {
 173            cmE,
 174            u: C::ScalarField::one(),
 175            cmW,
 176            x,
 177        })
 178    }
 179}
 180
 181impl<C: CurveGroup> Dummy<&R1CS<CF1<C>>> for Witness<C> {
 182    fn dummy(r1cs: &R1CS<CF1<C>>) -> Self {
 183        Self {
 184            E: vec![C::ScalarField::zero(); r1cs.A.n_rows],
 185            rE: C::ScalarField::zero(),
 186            W: vec![C::ScalarField::zero(); r1cs.A.n_cols - 1 - r1cs.l],
 187            rW: C::ScalarField::zero(),
 188        }
 189    }
 190}
 191
 192impl<C: CurveGroup> WitnessOps<C::ScalarField> for Witness<C> {
 193    type Var = WitnessVar<C>;
 194
 195    fn get_openings(&self) -> Vec<(&[C::ScalarField], C::ScalarField)> {
 196        vec![(&self.W, self.rW), (&self.E, self.rE)]
 197    }
 198}
 199
 200#[derive(Debug, Clone)]
 201pub struct PreprocessorParam<C1, C2, FC, CS1, CS2, const H: bool = false>
 202where
 203    C1: CurveGroup,
 204    C2: CurveGroup,
 205    FC: FCircuit<C1::ScalarField>,
 206    CS1: CommitmentScheme<C1, H>,
 207    CS2: CommitmentScheme<C2, H>,
 208{
 209    pub poseidon_config: PoseidonConfig<C1::ScalarField>,
 210    pub F: FC,
 211    // cs params if not provided, will be generated at the preprocess method
 212    pub cs_pp: Option<CS1::ProverParams>,
 213    pub cs_vp: Option<CS1::VerifierParams>,
 214    pub cf_cs_pp: Option<CS2::ProverParams>,
 215    pub cf_cs_vp: Option<CS2::VerifierParams>,
 216}
 217
 218impl<C1, C2, FC, CS1, CS2, const H: bool> PreprocessorParam<C1, C2, FC, CS1, CS2, H>
 219where
 220    C1: CurveGroup,
 221    C2: CurveGroup,
 222    FC: FCircuit<C1::ScalarField>,
 223    CS1: CommitmentScheme<C1, H>,
 224    CS2: CommitmentScheme<C2, H>,
 225{
 226    pub fn new(poseidon_config: PoseidonConfig<C1::ScalarField>, F: FC) -> Self {
 227        Self {
 228            poseidon_config,
 229            F,
 230            cs_pp: None,
 231            cs_vp: None,
 232            cf_cs_pp: None,
 233            cf_cs_vp: None,
 234        }
 235    }
 236}
 237
 238/// Proving parameters for Nova-based IVC
 239#[derive(Debug, Clone)]
 240pub struct ProverParams<C1, C2, CS1, CS2, const H: bool = false>
 241where
 242    C1: CurveGroup,
 243    C2: CurveGroup,
 244    CS1: CommitmentScheme<C1, H>,
 245    CS2: CommitmentScheme<C2, H>,
 246{
 247    /// Poseidon sponge configuration
 248    pub poseidon_config: PoseidonConfig<C1::ScalarField>,
 249    /// Proving parameters of the underlying commitment scheme over C1
 250    pub cs_pp: CS1::ProverParams,
 251    /// Proving parameters of the underlying commitment scheme over C2
 252    pub cf_cs_pp: CS2::ProverParams,
 253}
 254
 255impl<C1, C2, CS1, CS2, const H: bool> Valid for ProverParams<C1, C2, CS1, CS2, H>
 256where
 257    C1: CurveGroup,
 258    C2: CurveGroup,
 259    CS1: CommitmentScheme<C1, H>,
 260    CS2: CommitmentScheme<C2, H>,
 261{
 262    fn check(&self) -> Result<(), ark_serialize::SerializationError> {
 263        self.poseidon_config.full_rounds.check()?;
 264        self.poseidon_config.partial_rounds.check()?;
 265        self.poseidon_config.alpha.check()?;
 266        self.poseidon_config.ark.check()?;
 267        self.poseidon_config.mds.check()?;
 268        self.poseidon_config.rate.check()?;
 269        self.poseidon_config.capacity.check()?;
 270        self.cs_pp.check()?;
 271        self.cf_cs_pp.check()?;
 272        Ok(())
 273    }
 274}
 275impl<C1, C2, CS1, CS2, const H: bool> CanonicalSerialize for ProverParams<C1, C2, CS1, CS2, H>
 276where
 277    C1: CurveGroup,
 278    C2: CurveGroup,
 279    CS1: CommitmentScheme<C1, H>,
 280    CS2: CommitmentScheme<C2, H>,
 281{
 282    fn serialize_with_mode<W: std::io::prelude::Write>(
 283        &self,
 284        mut writer: W,
 285        compress: ark_serialize::Compress,
 286    ) -> Result<(), ark_serialize::SerializationError> {
 287        self.cs_pp.serialize_with_mode(&mut writer, compress)?;
 288        self.cf_cs_pp.serialize_with_mode(&mut writer, compress)
 289    }
 290
 291    fn serialized_size(&self, compress: ark_serialize::Compress) -> usize {
 292        self.cs_pp.serialized_size(compress) + self.cf_cs_pp.serialized_size(compress)
 293    }
 294}
 295impl<C1, C2, CS1, CS2, const H: bool> CanonicalDeserialize for ProverParams<C1, C2, CS1, CS2, H>
 296where
 297    C1: CurveGroup,
 298    C2: CurveGroup,
 299    CS1: CommitmentScheme<C1, H>,
 300    CS2: CommitmentScheme<C2, H>,
 301{
 302    fn deserialize_with_mode<R: std::io::prelude::Read>(
 303        mut reader: R,
 304        compress: ark_serialize::Compress,
 305        validate: ark_serialize::Validate,
 306    ) -> Result<Self, ark_serialize::SerializationError> {
 307        let cs_pp = CS1::ProverParams::deserialize_with_mode(&mut reader, compress, validate)?;
 308        let cf_cs_pp = CS2::ProverParams::deserialize_with_mode(&mut reader, compress, validate)?;
 309        Ok(ProverParams {
 310            poseidon_config: poseidon_canonical_config::<C1::ScalarField>(),
 311            cs_pp,
 312            cf_cs_pp,
 313        })
 314    }
 315}
 316
 317/// Verification parameters for Nova-based IVC
 318#[derive(Debug, Clone)]
 319pub struct VerifierParams<C1, C2, CS1, CS2, const H: bool = false>
 320where
 321    C1: CurveGroup,
 322    C2: CurveGroup,
 323    CS1: CommitmentScheme<C1, H>,
 324    CS2: CommitmentScheme<C2, H>,
 325{
 326    /// Poseidon sponge configuration
 327    pub poseidon_config: PoseidonConfig<C1::ScalarField>,
 328    /// R1CS of the Augmented step circuit
 329    pub r1cs: R1CS<C1::ScalarField>,
 330    /// R1CS of the CycleFold circuit
 331    pub cf_r1cs: R1CS<C2::ScalarField>,
 332    /// Verification parameters of the underlying commitment scheme over C1
 333    pub cs_vp: CS1::VerifierParams,
 334    /// Verification parameters of the underlying commitment scheme over C2
 335    pub cf_cs_vp: CS2::VerifierParams,
 336}
 337
 338impl<C1, C2, CS1, CS2, const H: bool> Valid for VerifierParams<C1, C2, CS1, CS2, H>
 339where
 340    C1: CurveGroup,
 341    C2: CurveGroup,
 342    CS1: CommitmentScheme<C1, H>,
 343    CS2: CommitmentScheme<C2, H>,
 344{
 345    fn check(&self) -> Result<(), ark_serialize::SerializationError> {
 346        self.poseidon_config.full_rounds.check()?;
 347        self.poseidon_config.partial_rounds.check()?;
 348        self.poseidon_config.alpha.check()?;
 349        self.poseidon_config.ark.check()?;
 350        self.poseidon_config.mds.check()?;
 351        self.poseidon_config.rate.check()?;
 352        self.poseidon_config.capacity.check()?;
 353        self.r1cs.check()?;
 354        self.cf_r1cs.check()?;
 355        self.cs_vp.check()?;
 356        self.cf_cs_vp.check()?;
 357        Ok(())
 358    }
 359}
 360impl<C1, C2, CS1, CS2, const H: bool> CanonicalSerialize for VerifierParams<C1, C2, CS1, CS2, H>
 361where
 362    C1: CurveGroup,
 363    C2: CurveGroup,
 364    CS1: CommitmentScheme<C1, H>,
 365    CS2: CommitmentScheme<C2, H>,
 366{
 367    fn serialize_with_mode<W: std::io::prelude::Write>(
 368        &self,
 369        mut writer: W,
 370        compress: ark_serialize::Compress,
 371    ) -> Result<(), ark_serialize::SerializationError> {
 372        self.r1cs.serialize_with_mode(&mut writer, compress)?;
 373        self.cf_r1cs.serialize_with_mode(&mut writer, compress)?;
 374        self.cs_vp.serialize_with_mode(&mut writer, compress)?;
 375        self.cf_cs_vp.serialize_with_mode(&mut writer, compress)
 376    }
 377
 378    fn serialized_size(&self, compress: ark_serialize::Compress) -> usize {
 379        self.r1cs.serialized_size(compress)
 380            + self.cf_r1cs.serialized_size(compress)
 381            + self.cs_vp.serialized_size(compress)
 382            + self.cf_cs_vp.serialized_size(compress)
 383    }
 384}
 385impl<C1, C2, CS1, CS2, const H: bool> CanonicalDeserialize for VerifierParams<C1, C2, CS1, CS2, H>
 386where
 387    C1: CurveGroup,
 388    C2: CurveGroup,
 389    CS1: CommitmentScheme<C1, H>,
 390    CS2: CommitmentScheme<C2, H>,
 391{
 392    fn deserialize_with_mode<R: std::io::prelude::Read>(
 393        mut reader: R,
 394        compress: ark_serialize::Compress,
 395        validate: ark_serialize::Validate,
 396    ) -> Result<Self, ark_serialize::SerializationError> {
 397        let r1cs = R1CS::deserialize_with_mode(&mut reader, compress, validate)?;
 398        let cf_r1cs = R1CS::deserialize_with_mode(&mut reader, compress, validate)?;
 399        let cs_vp = CS1::VerifierParams::deserialize_with_mode(&mut reader, compress, validate)?;
 400        let cf_cs_vp = CS2::VerifierParams::deserialize_with_mode(&mut reader, compress, validate)?;
 401        Ok(VerifierParams {
 402            poseidon_config: poseidon_canonical_config::<C1::ScalarField>(),
 403            r1cs,
 404            cf_r1cs,
 405            cs_vp,
 406            cf_cs_vp,
 407        })
 408    }
 409}
 410
 411impl<C1, C2, CS1, CS2, const H: bool> VerifierParams<C1, C2, CS1, CS2, H>
 412where
 413    C1: CurveGroup,
 414    C2: CurveGroup,
 415    CS1: CommitmentScheme<C1, H>,
 416    CS2: CommitmentScheme<C2, H>,
 417{
 418    /// returns the hash of the public parameters of Nova
 419    pub fn pp_hash(&self) -> Result<C1::ScalarField, Error> {
 420        pp_hash::<C1, C2, CS1, CS2, H>(
 421            &self.r1cs,
 422            &self.cf_r1cs,
 423            &self.cs_vp,
 424            &self.cf_cs_vp,
 425            &self.poseidon_config,
 426        )
 427    }
 428}
 429
 430/// Implements Nova+CycleFold's IVC, described in [Nova](https://eprint.iacr.org/2021/370.pdf) and
 431/// [CycleFold](https://eprint.iacr.org/2023/1192.pdf), following the FoldingScheme trait
 432/// The `H` const generic specifies whether the homorphic commitment scheme is blinding
 433#[derive(Clone, Debug)]
 434pub struct Nova<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool = false>
 435where
 436    C1: CurveGroup,
 437    GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
 438    C2: CurveGroup,
 439    GC2: CurveVar<C2, CF2<C2>>,
 440    FC: FCircuit<C1::ScalarField>,
 441    CS1: CommitmentScheme<C1, H>,
 442    CS2: CommitmentScheme<C2, H>,
 443{
 444    _gc1: PhantomData<GC1>,
 445    _c2: PhantomData<C2>,
 446    _gc2: PhantomData<GC2>,
 447    /// R1CS of the Augmented Function circuit
 448    pub r1cs: R1CS<C1::ScalarField>,
 449    /// R1CS of the CycleFold circuit
 450    pub cf_r1cs: R1CS<C2::ScalarField>,
 451    pub poseidon_config: PoseidonConfig<C1::ScalarField>,
 452    /// CommitmentScheme::ProverParams over C1
 453    pub cs_pp: CS1::ProverParams,
 454    /// CycleFold CommitmentScheme::ProverParams, over C2
 455    pub cf_cs_pp: CS2::ProverParams,
 456    /// F circuit, the circuit that is being folded
 457    pub F: FC,
 458    /// public params hash
 459    pub pp_hash: C1::ScalarField,
 460    pub i: C1::ScalarField,
 461    /// initial state
 462    pub z_0: Vec<C1::ScalarField>,
 463    /// current i-th state
 464    pub z_i: Vec<C1::ScalarField>,
 465    /// Nova instances
 466    pub w_i: Witness<C1>,
 467    pub u_i: CommittedInstance<C1>,
 468    pub W_i: Witness<C1>,
 469    pub U_i: CommittedInstance<C1>,
 470
 471    /// CycleFold running instance
 472    pub cf_W_i: CycleFoldWitness<C2>,
 473    pub cf_U_i: CycleFoldCommittedInstance<C2>,
 474}
 475
 476impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> FoldingScheme<C1, C2, FC>
 477    for Nova<C1, GC1, C2, GC2, FC, CS1, CS2, H>
 478where
 479    C1: CurveGroup,
 480    GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
 481    C2: CurveGroup,
 482    GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
 483    FC: FCircuit<C1::ScalarField>,
 484    CS1: CommitmentScheme<C1, H>,
 485    CS2: CommitmentScheme<C2, H>,
 486    <C1 as CurveGroup>::BaseField: PrimeField,
 487    <C2 as CurveGroup>::BaseField: PrimeField,
 488    <C1 as Group>::ScalarField: Absorb,
 489    <C2 as Group>::ScalarField: Absorb,
 490    C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
 491    for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
 492    for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
 493{
 494    type PreprocessorParam = PreprocessorParam<C1, C2, FC, CS1, CS2, H>;
 495    type ProverParam = ProverParams<C1, C2, CS1, CS2, H>;
 496    type VerifierParam = VerifierParams<C1, C2, CS1, CS2, H>;
 497    type RunningInstance = (CommittedInstance<C1>, Witness<C1>);
 498    type IncomingInstance = (CommittedInstance<C1>, Witness<C1>);
 499    type MultiCommittedInstanceWithWitness = ();
 500    type CFInstance = (CycleFoldCommittedInstance<C2>, CycleFoldWitness<C2>);
 501
 502    fn preprocess(
 503        mut rng: impl RngCore,
 504        prep_param: &Self::PreprocessorParam,
 505    ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> {
 506        let (r1cs, cf_r1cs) =
 507            get_r1cs::<C1, GC1, C2, GC2, FC>(&prep_param.poseidon_config, prep_param.F.clone())?;
 508
 509        // if cs params exist, use them, if not, generate new ones
 510        let cs_pp: CS1::ProverParams;
 511        let cs_vp: CS1::VerifierParams;
 512        let cf_cs_pp: CS2::ProverParams;
 513        let cf_cs_vp: CS2::VerifierParams;
 514        if prep_param.cs_pp.is_some()
 515            && prep_param.cf_cs_pp.is_some()
 516            && prep_param.cs_vp.is_some()
 517            && prep_param.cf_cs_vp.is_some()
 518        {
 519            cs_pp = prep_param.clone().cs_pp.unwrap();
 520            cs_vp = prep_param.clone().cs_vp.unwrap();
 521            cf_cs_pp = prep_param.clone().cf_cs_pp.unwrap();
 522            cf_cs_vp = prep_param.clone().cf_cs_vp.unwrap();
 523        } else {
 524            (cs_pp, cs_vp) = CS1::setup(&mut rng, r1cs.A.n_rows)?;
 525            (cf_cs_pp, cf_cs_vp) = CS2::setup(&mut rng, cf_r1cs.A.n_rows)?;
 526        }
 527
 528        let prover_params = ProverParams::<C1, C2, CS1, CS2, H> {
 529            poseidon_config: prep_param.poseidon_config.clone(),
 530            cs_pp: cs_pp.clone(),
 531            cf_cs_pp: cf_cs_pp.clone(),
 532        };
 533        let verifier_params = VerifierParams::<C1, C2, CS1, CS2, H> {
 534            poseidon_config: prep_param.poseidon_config.clone(),
 535            r1cs,
 536            cf_r1cs,
 537            cs_vp,
 538            cf_cs_vp,
 539        };
 540
 541        Ok((prover_params, verifier_params))
 542    }
 543
 544    /// Initializes the Nova+CycleFold's IVC for the given parameters and initial state `z_0`.
 545    fn init(
 546        params: &(Self::ProverParam, Self::VerifierParam),
 547        F: FC,
 548        z_0: Vec<C1::ScalarField>,
 549    ) -> Result<Self, Error> {
 550        let (pp, vp) = params;
 551
 552        // prepare the circuit to obtain its R1CS
 553        let cs = ConstraintSystem::<C1::ScalarField>::new_ref();
 554        let cs2 = ConstraintSystem::<C1::BaseField>::new_ref();
 555
 556        let augmented_F_circuit =
 557            AugmentedFCircuit::<C1, C2, GC2, FC>::empty(&pp.poseidon_config, F.clone());
 558        let cf_circuit = NovaCycleFoldCircuit::<C1, GC1>::empty();
 559
 560        augmented_F_circuit.generate_constraints(cs.clone())?;
 561        cs.finalize();
 562        let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
 563        let r1cs = extract_r1cs::<C1::ScalarField>(&cs);
 564
 565        cf_circuit.generate_constraints(cs2.clone())?;
 566        cs2.finalize();
 567        let cs2 = cs2.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
 568        let cf_r1cs = extract_r1cs::<C1::BaseField>(&cs2);
 569
 570        // compute the public params hash
 571        let pp_hash = vp.pp_hash()?;
 572
 573        // setup the dummy instances
 574        let (W_dummy, U_dummy) = r1cs.dummy_witness_instance();
 575        let (w_dummy, u_dummy) = r1cs.dummy_witness_instance();
 576        let (cf_W_dummy, cf_U_dummy) = cf_r1cs.dummy_witness_instance();
 577
 578        // W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the
 579        // R1CS that we're working with.
 580        Ok(Self {
 581            _gc1: PhantomData,
 582            _c2: PhantomData,
 583            _gc2: PhantomData,
 584            r1cs,
 585            cf_r1cs,
 586            poseidon_config: pp.poseidon_config.clone(),
 587            cs_pp: pp.cs_pp.clone(),
 588            cf_cs_pp: pp.cf_cs_pp.clone(),
 589            F,
 590            pp_hash,
 591            i: C1::ScalarField::zero(),
 592            z_0: z_0.clone(),
 593            z_i: z_0,
 594            w_i: w_dummy,
 595            u_i: u_dummy,
 596            W_i: W_dummy,
 597            U_i: U_dummy,
 598            // cyclefold running instance
 599            cf_W_i: cf_W_dummy,
 600            cf_U_i: cf_U_dummy,
 601        })
 602    }
 603
 604    /// Implements IVC.P of Nova+CycleFold
 605    fn prove_step(
 606        &mut self,
 607        mut rng: impl RngCore,
 608        external_inputs: Vec<C1::ScalarField>,
 609        // Nova does not support multi-instances folding
 610        _other_instances: Option<Self::MultiCommittedInstanceWithWitness>,
 611    ) -> Result<(), Error> {
 612        // ensure that commitments are blinding if user has specified so.
 613        if H && self.i >= C1::ScalarField::one() {
 614            let blinding_commitments = if self.i == C1::ScalarField::one() {
 615                // blinding values of the running instances are zero at the first iteration
 616                vec![self.w_i.rW, self.w_i.rE]
 617            } else {
 618                vec![self.w_i.rW, self.w_i.rE, self.W_i.rW, self.W_i.rE]
 619            };
 620            if blinding_commitments.contains(&C1::ScalarField::zero()) {
 621                return Err(Error::IncorrectBlinding(
 622                    H,
 623                    format!("{:?}", blinding_commitments),
 624                ));
 625            }
 626        }
 627        // `sponge` is for digest computation.
 628        let sponge = PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config);
 629        // `transcript` is for challenge generation.
 630        let mut transcript = sponge.clone();
 631
 632        let augmented_F_circuit: AugmentedFCircuit<C1, C2, GC2, FC>;
 633
 634        // Nova does not support (by design) multi-instances folding
 635        if _other_instances.is_some() {
 636            return Err(Error::NoMultiInstances);
 637        }
 638
 639        if self.z_i.len() != self.F.state_len() {
 640            return Err(Error::NotSameLength(
 641                "z_i.len()".to_string(),
 642                self.z_i.len(),
 643                "F.state_len()".to_string(),
 644                self.F.state_len(),
 645            ));
 646        }
 647        if external_inputs.len() != self.F.external_inputs_len() {
 648            return Err(Error::NotSameLength(
 649                "F.external_inputs_len()".to_string(),
 650                self.F.external_inputs_len(),
 651                "external_inputs.len()".to_string(),
 652                external_inputs.len(),
 653            ));
 654        }
 655
 656        if self.i > C1::ScalarField::from_le_bytes_mod_order(&usize::MAX.to_le_bytes()) {
 657            return Err(Error::MaxStep);
 658        }
 659
 660        let i_usize;
 661
 662        #[cfg(target_pointer_width = "64")]
 663        {
 664            let mut i_bytes: [u8; 8] = [0; 8];
 665            i_bytes.copy_from_slice(&self.i.into_bigint().to_bytes_le()[..8]);
 666            i_usize = usize::from_le_bytes(i_bytes);
 667        }
 668
 669        #[cfg(target_pointer_width = "32")]
 670        {
 671            let mut i_bytes: [u8; 4] = [0; 4];
 672            i_bytes.copy_from_slice(&self.i.into_bigint().to_bytes_le()[..4]);
 673            i_usize = usize::from_le_bytes(i_bytes);
 674        }
 675
 676        let z_i1 = self
 677            .F
 678            .step_native(i_usize, self.z_i.clone(), external_inputs.clone())?;
 679
 680        // compute T and cmT for AugmentedFCircuit
 681        let (T, cmT) = self.compute_cmT()?;
 682
 683        // r_bits is the r used to the RLC of the F' instances
 684        let r_bits = ChallengeGadget::<C1>::get_challenge_native(
 685            &mut transcript,
 686            self.pp_hash,
 687            self.U_i.clone(),
 688            self.u_i.clone(),
 689            cmT,
 690        );
 691        let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits))
 692            .ok_or(Error::OutOfBounds)?;
 693        let r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&r_bits))
 694            .ok_or(Error::OutOfBounds)?;
 695
 696        // fold Nova instances
 697        let (W_i1, U_i1): (Witness<C1>, CommittedInstance<C1>) =
 698            NIFS::<C1, CS1, H>::fold_instances(
 699                r_Fr, &self.W_i, &self.U_i, &self.w_i, &self.u_i, &T, cmT,
 700            )?;
 701
 702        // folded instance output (public input, x)
 703        // u_{i+1}.x[0] = H(i+1, z_0, z_{i+1}, U_{i+1})
 704        let u_i1_x = U_i1.hash(
 705            &sponge,
 706            self.pp_hash,
 707            self.i + C1::ScalarField::one(),
 708            &self.z_0,
 709            &z_i1,
 710        );
 711        // u_{i+1}.x[1] = H(cf_U_{i+1})
 712        let cf_u_i1_x: C1::ScalarField;
 713
 714        if self.i == C1::ScalarField::zero() {
 715            cf_u_i1_x = self.cf_U_i.hash_cyclefold(&sponge, self.pp_hash);
 716            // base case
 717            augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
 718                _gc2: PhantomData,
 719                poseidon_config: self.poseidon_config.clone(),
 720                pp_hash: Some(self.pp_hash),
 721                i: Some(C1::ScalarField::zero()), // = i=0
 722                i_usize: Some(0),
 723                z_0: Some(self.z_0.clone()), // = z_i
 724                z_i: Some(self.z_i.clone()),
 725                external_inputs: Some(external_inputs.clone()),
 726                u_i_cmW: Some(self.u_i.cmW), // = dummy
 727                U_i: Some(self.U_i.clone()), // = dummy
 728                U_i1_cmE: Some(U_i1.cmE),
 729                U_i1_cmW: Some(U_i1.cmW),
 730                cmT: Some(cmT),
 731                F: self.F.clone(),
 732                x: Some(u_i1_x),
 733                cf1_u_i_cmW: None,
 734                cf2_u_i_cmW: None,
 735                cf_U_i: None,
 736                cf1_cmT: None,
 737                cf2_cmT: None,
 738                cf_x: Some(cf_u_i1_x),
 739            };
 740
 741            #[cfg(test)]
 742            NIFS::<C1, CS1, H>::verify_folded_instance(r_Fr, &self.U_i, &self.u_i, &U_i1, &cmT)?;
 743        } else {
 744            // CycleFold part:
 745            // get the vector used as public inputs 'x' in the CycleFold circuit
 746            // cyclefold circuit for cmW
 747            let cfW_u_i_x = [
 748                vec![r_Fq],
 749                get_cm_coordinates(&self.U_i.cmW),
 750                get_cm_coordinates(&self.u_i.cmW),
 751                get_cm_coordinates(&U_i1.cmW),
 752            ]
 753            .concat();
 754            // cyclefold circuit for cmE
 755            let cfE_u_i_x = [
 756                vec![r_Fq],
 757                get_cm_coordinates(&self.U_i.cmE),
 758                get_cm_coordinates(&cmT),
 759                get_cm_coordinates(&U_i1.cmE),
 760            ]
 761            .concat();
 762
 763            let cfW_circuit = NovaCycleFoldCircuit::<C1, GC1> {
 764                _gc: PhantomData,
 765                r_bits: Some(r_bits.clone()),
 766                points: Some(vec![self.U_i.clone().cmW, self.u_i.clone().cmW]),
 767                x: Some(cfW_u_i_x.clone()),
 768            };
 769            let cfE_circuit = NovaCycleFoldCircuit::<C1, GC1> {
 770                _gc: PhantomData,
 771                r_bits: Some(r_bits.clone()),
 772                points: Some(vec![self.U_i.clone().cmE, cmT]),
 773                x: Some(cfE_u_i_x.clone()),
 774            };
 775
 776            // fold self.cf_U_i + cfW_U -> folded running with cfW
 777            let (_cfW_w_i, cfW_u_i, cfW_W_i1, cfW_U_i1, cfW_cmT, _) = self.fold_cyclefold_circuit(
 778                &mut transcript,
 779                self.cf_W_i.clone(), // CycleFold running instance witness
 780                self.cf_U_i.clone(), // CycleFold running instance
 781                cfW_u_i_x,
 782                cfW_circuit,
 783                &mut rng,
 784            )?;
 785            // fold [the output from folding self.cf_U_i + cfW_U] + cfE_U = folded_running_with_cfW + cfE
 786            let (_cfE_w_i, cfE_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) = self.fold_cyclefold_circuit(
 787                &mut transcript,
 788                cfW_W_i1,
 789                cfW_U_i1.clone(),
 790                cfE_u_i_x,
 791                cfE_circuit,
 792                &mut rng,
 793            )?;
 794
 795            cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, self.pp_hash);
 796
 797            augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
 798                _gc2: PhantomData,
 799                poseidon_config: self.poseidon_config.clone(),
 800                pp_hash: Some(self.pp_hash),
 801                i: Some(self.i),
 802                i_usize: Some(i_usize),
 803                z_0: Some(self.z_0.clone()),
 804                z_i: Some(self.z_i.clone()),
 805                external_inputs: Some(external_inputs.clone()),
 806                u_i_cmW: Some(self.u_i.cmW),
 807                U_i: Some(self.U_i.clone()),
 808                U_i1_cmE: Some(U_i1.cmE),
 809                U_i1_cmW: Some(U_i1.cmW),
 810                cmT: Some(cmT),
 811                F: self.F.clone(),
 812                x: Some(u_i1_x),
 813                // cyclefold values
 814                cf1_u_i_cmW: Some(cfW_u_i.cmW),
 815                cf2_u_i_cmW: Some(cfE_u_i.cmW),
 816                cf_U_i: Some(self.cf_U_i.clone()),
 817                cf1_cmT: Some(cfW_cmT),
 818                cf2_cmT: Some(cf_cmT),
 819                cf_x: Some(cf_u_i1_x),
 820            };
 821
 822            self.cf_W_i = cf_W_i1;
 823            self.cf_U_i = cf_U_i1;
 824
 825            #[cfg(test)]
 826            {
 827                cfW_u_i.check_incoming()?;
 828                cfE_u_i.check_incoming()?;
 829                self.cf_r1cs.check_relation(&_cfW_w_i, &cfW_u_i)?;
 830                self.cf_r1cs.check_relation(&_cfE_w_i, &cfE_u_i)?;
 831                self.cf_r1cs.check_relation(&self.cf_W_i, &self.cf_U_i)?;
 832            }
 833        }
 834
 835        let cs = ConstraintSystem::<C1::ScalarField>::new_ref();
 836
 837        augmented_F_circuit.generate_constraints(cs.clone())?;
 838
 839        #[cfg(test)]
 840        assert!(cs.is_satisfied().unwrap());
 841
 842        let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
 843        let (w_i1, x_i1) = extract_w_x::<C1::ScalarField>(&cs);
 844        if x_i1[0] != u_i1_x || x_i1[1] != cf_u_i1_x {
 845            return Err(Error::NotEqual);
 846        }
 847
 848        #[cfg(test)]
 849        if x_i1.len() != 2 {
 850            return Err(Error::NotExpectedLength(x_i1.len(), 2));
 851        }
 852
 853        // set values for next iteration
 854        self.i += C1::ScalarField::one();
 855        self.z_i = z_i1;
 856        self.w_i = Witness::<C1>::new::<H>(w_i1, self.r1cs.A.n_rows, &mut rng);
 857        self.u_i = self.w_i.commit::<CS1, H>(&self.cs_pp, x_i1)?;
 858        self.W_i = W_i1;
 859        self.U_i = U_i1;
 860
 861        #[cfg(test)]
 862        {
 863            self.u_i.check_incoming()?;
 864            self.r1cs.check_relation(&self.w_i, &self.u_i)?;
 865            self.r1cs.check_relation(&self.W_i, &self.U_i)?;
 866        }
 867
 868        Ok(())
 869    }
 870
 871    fn state(&self) -> Vec<C1::ScalarField> {
 872        self.z_i.clone()
 873    }
 874
 875    fn instances(
 876        &self,
 877    ) -> (
 878        Self::RunningInstance,
 879        Self::IncomingInstance,
 880        Self::CFInstance,
 881    ) {
 882        (
 883            (self.U_i.clone(), self.W_i.clone()),
 884            (self.u_i.clone(), self.w_i.clone()),
 885            (self.cf_U_i.clone(), self.cf_W_i.clone()),
 886        )
 887    }
 888
 889    /// Implements IVC.V of Nova+CycleFold. Notice that this method does not include the
 890    /// commitments verification, which is done in the Decider.
 891    fn verify(
 892        vp: Self::VerifierParam,
 893        z_0: Vec<C1::ScalarField>, // initial state
 894        z_i: Vec<C1::ScalarField>, // last state
 895        num_steps: C1::ScalarField,
 896        running_instance: Self::RunningInstance,
 897        incoming_instance: Self::IncomingInstance,
 898        cyclefold_instance: Self::CFInstance,
 899    ) -> Result<(), Error> {
 900        let sponge = PoseidonSponge::<C1::ScalarField>::new(&vp.poseidon_config);
 901
 902        if num_steps == C1::ScalarField::zero() {
 903            if z_0 != z_i {
 904                return Err(Error::IVCVerificationFail);
 905            }
 906            return Ok(());
 907        }
 908
 909        let (U_i, W_i) = running_instance;
 910        let (u_i, w_i) = incoming_instance;
 911        let (cf_U_i, cf_W_i) = cyclefold_instance;
 912
 913        if u_i.x.len() != 2 || U_i.x.len() != 2 {
 914            return Err(Error::IVCVerificationFail);
 915        }
 916
 917        let pp_hash = vp.pp_hash()?;
 918
 919        // check that u_i's output points to the running instance
 920        // u_i.X[0] == H(i, z_0, z_i, U_i)
 921        let expected_u_i_x = U_i.hash(&sponge, pp_hash, num_steps, &z_0, &z_i);
 922        if expected_u_i_x != u_i.x[0] {
 923            return Err(Error::IVCVerificationFail);
 924        }
 925        // u_i.X[1] == H(cf_U_i)
 926        let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&sponge, pp_hash);
 927        if expected_cf_u_i_x != u_i.x[1] {
 928            return Err(Error::IVCVerificationFail);
 929        }
 930
 931        // check R1CS satisfiability, which is equivalent to checking if `u_i`
 932        // is an incoming instance and if `w_i` and `u_i` satisfy RelaxedR1CS
 933        u_i.check_incoming()?;
 934        vp.r1cs.check_relation(&w_i, &u_i)?;
 935        // check RelaxedR1CS satisfiability
 936        vp.r1cs.check_relation(&W_i, &U_i)?;
 937
 938        // check CycleFold RelaxedR1CS satisfiability
 939        vp.cf_r1cs.check_relation(&cf_W_i, &cf_U_i)?;
 940
 941        Ok(())
 942    }
 943}
 944
 945impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> Nova<C1, GC1, C2, GC2, FC, CS1, CS2, H>
 946where
 947    C1: CurveGroup,
 948    GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
 949    C2: CurveGroup,
 950    GC2: CurveVar<C2, CF2<C2>>,
 951    FC: FCircuit<C1::ScalarField>,
 952    CS1: CommitmentScheme<C1, H>,
 953    CS2: CommitmentScheme<C2, H>,
 954    <C2 as CurveGroup>::BaseField: PrimeField,
 955    <C1 as Group>::ScalarField: Absorb,
 956    <C2 as Group>::ScalarField: Absorb,
 957    C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
 958{
 959    // computes T and cmT for the AugmentedFCircuit
 960    fn compute_cmT(&self) -> Result<(Vec<C1::ScalarField>, C1), Error> {
 961        NIFS::<C1, CS1, H>::compute_cmT(
 962            &self.cs_pp,
 963            &self.r1cs,
 964            &self.w_i,
 965            &self.u_i,
 966            &self.W_i,
 967            &self.U_i,
 968        )
 969    }
 970}
 971
 972impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> Nova<C1, GC1, C2, GC2, FC, CS1, CS2, H>
 973where
 974    C1: CurveGroup,
 975    GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
 976    C2: CurveGroup,
 977    GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
 978    FC: FCircuit<C1::ScalarField>,
 979    CS1: CommitmentScheme<C1, H>,
 980    CS2: CommitmentScheme<C2, H>,
 981    <C1 as CurveGroup>::BaseField: PrimeField,
 982    <C2 as CurveGroup>::BaseField: PrimeField,
 983    <C1 as Group>::ScalarField: Absorb,
 984    <C2 as Group>::ScalarField: Absorb,
 985    C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
 986    for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
 987    for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
 988{
 989    // folds the given cyclefold circuit and its instances
 990    #[allow(clippy::type_complexity)]
 991    fn fold_cyclefold_circuit<T: Transcript<C1::ScalarField>>(
 992        &self,
 993        transcript: &mut T,
 994        cf_W_i: CycleFoldWitness<C2>, // witness of the running instance
 995        cf_U_i: CycleFoldCommittedInstance<C2>, // running instance
 996        cf_u_i_x: Vec<C2::ScalarField>,
 997        cf_circuit: NovaCycleFoldCircuit<C1, GC1>,
 998        rng: &mut impl RngCore,
 999    ) -> Result<
1000        (
1001            CycleFoldWitness<C2>,
1002            CycleFoldCommittedInstance<C2>, // u_i
1003            CycleFoldWitness<C2>,           // W_i1
1004            CycleFoldCommittedInstance<C2>, // U_i1
1005            C2,                             // cmT
1006            C2::ScalarField,                // r_Fq
1007        ),
1008        Error,
1009    > {
1010        fold_cyclefold_circuit::<NovaCycleFoldConfig<C1>, C1, GC1, C2, GC2, CS2, H>(
1011            transcript,
1012            self.cf_r1cs.clone(),
1013            self.cf_cs_pp.clone(),
1014            self.pp_hash,
1015            cf_W_i,
1016            cf_U_i,
1017            cf_u_i_x,
1018            cf_circuit,
1019            rng,
1020        )
1021    }
1022}
1023
1024/// helper method to get the r1cs from the ConstraintSynthesizer
1025pub fn get_r1cs_from_cs<F: PrimeField>(
1026    circuit: impl ConstraintSynthesizer<F>,
1027) -> Result<R1CS<F>, Error> {
1028    let cs = ConstraintSystem::<F>::new_ref();
1029    circuit.generate_constraints(cs.clone())?;
1030    cs.finalize();
1031    let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
1032    let r1cs = extract_r1cs::<F>(&cs);
1033    Ok(r1cs)
1034}
1035
1036/// helper method to get the R1CS for both the AugmentedFCircuit and the CycleFold circuit
1037#[allow(clippy::type_complexity)]
1038pub fn get_r1cs<C1, GC1, C2, GC2, FC>(
1039    poseidon_config: &PoseidonConfig<C1::ScalarField>,
1040    F_circuit: FC,
1041) -> Result<(R1CS<C1::ScalarField>, R1CS<C2::ScalarField>), Error>
1042where
1043    C1: CurveGroup,
1044    GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
1045    C2: CurveGroup,
1046    GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
1047    FC: FCircuit<C1::ScalarField>,
1048    <C1 as CurveGroup>::BaseField: PrimeField,
1049    <C2 as CurveGroup>::BaseField: PrimeField,
1050    <C1 as Group>::ScalarField: Absorb,
1051    <C2 as Group>::ScalarField: Absorb,
1052    C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
1053    for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
1054    for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
1055{
1056    let augmented_F_circuit =
1057        AugmentedFCircuit::<C1, C2, GC2, FC>::empty(poseidon_config, F_circuit);
1058    let cf_circuit = NovaCycleFoldCircuit::<C1, GC1>::empty();
1059    let r1cs = get_r1cs_from_cs::<C1::ScalarField>(augmented_F_circuit)?;
1060    let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
1061    Ok((r1cs, cf_r1cs))
1062}
1063
1064/// helper method to get the pedersen params length for both the AugmentedFCircuit and the
1065/// CycleFold circuit
1066pub fn get_cs_params_len<C1, GC1, C2, GC2, FC>(
1067    poseidon_config: &PoseidonConfig<C1::ScalarField>,
1068    F_circuit: FC,
1069) -> Result<(usize, usize), Error>
1070where
1071    C1: CurveGroup,
1072    GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
1073    C2: CurveGroup,
1074    GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
1075    FC: FCircuit<C1::ScalarField>,
1076    <C1 as CurveGroup>::BaseField: PrimeField,
1077    <C2 as CurveGroup>::BaseField: PrimeField,
1078    <C1 as Group>::ScalarField: Absorb,
1079    <C2 as Group>::ScalarField: Absorb,
1080    C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
1081    for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
1082    for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
1083{
1084    let (r1cs, cf_r1cs) = get_r1cs::<C1, GC1, C2, GC2, FC>(poseidon_config, F_circuit)?;
1085    Ok((r1cs.A.n_rows, cf_r1cs.A.n_rows))
1086}
1087
1088#[cfg(test)]
1089pub mod tests {
1090    use crate::commitment::kzg::KZG;
1091    use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
1092    use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
1093
1094    use super::*;
1095    use crate::commitment::pedersen::Pedersen;
1096    use crate::frontend::utils::CubicFCircuit;
1097    use crate::transcript::poseidon::poseidon_canonical_config;
1098
1099    /// This test tests the Nova+CycleFold IVC, and by consequence it is also testing the
1100    /// AugmentedFCircuit
1101    #[test]
1102    fn test_ivc() {
1103        let poseidon_config = poseidon_canonical_config::<Fr>();
1104
1105        let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
1106
1107        // run the test using Pedersen commitments on both sides of the curve cycle
1108        test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>, false>(
1109            poseidon_config.clone(),
1110            F_circuit,
1111            3,
1112        );
1113
1114        test_ivc_opt::<Pedersen<Projective, true>, Pedersen<Projective2, true>, true>(
1115            poseidon_config.clone(),
1116            F_circuit,
1117            3,
1118        );
1119
1120        // run the test using KZG for the commitments on the main curve, and Pedersen for the
1121        // commitments on the secondary curve
1122        test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>, false>(poseidon_config, F_circuit, 3);
1123    }
1124
1125    // test_ivc allowing to choose the CommitmentSchemes
1126    #[allow(clippy::type_complexity)]
1127    pub(crate) fn test_ivc_opt<
1128        CS1: CommitmentScheme<Projective, H>,
1129        CS2: CommitmentScheme<Projective2, H>,
1130        const H: bool,
1131    >(
1132        poseidon_config: PoseidonConfig<Fr>,
1133        F_circuit: CubicFCircuit<Fr>,
1134        num_steps: usize,
1135    ) -> (
1136        Vec<Fr>,
1137        Nova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, H>,
1138    ) {
1139        let mut rng = ark_std::test_rng();
1140
1141        let prep_param =
1142            PreprocessorParam::<Projective, Projective2, CubicFCircuit<Fr>, CS1, CS2, H> {
1143                poseidon_config,
1144                F: F_circuit,
1145                cs_pp: None,
1146                cs_vp: None,
1147                cf_cs_pp: None,
1148                cf_cs_vp: None,
1149            };
1150        let nova_params = Nova::<
1151            Projective,
1152            GVar,
1153            Projective2,
1154            GVar2,
1155            CubicFCircuit<Fr>,
1156            CS1,
1157            CS2,
1158            H,
1159        >::preprocess(&mut rng, &prep_param)
1160        .unwrap();
1161
1162        let z_0 = vec![Fr::from(3_u32)];
1163        let mut nova =
1164            Nova::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, H>::init(
1165                &nova_params,
1166                F_circuit,
1167                z_0.clone(),
1168            )
1169            .unwrap();
1170
1171        for _ in 0..num_steps {
1172            nova.prove_step(&mut rng, vec![], None).unwrap();
1173        }
1174        assert_eq!(Fr::from(num_steps as u32), nova.i);
1175
1176        let (running_instance, incoming_instance, cyclefold_instance) = nova.instances();
1177        Nova::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, H>::verify(
1178            nova_params.1, // Nova's verifier params
1179            z_0.clone(),
1180            nova.z_i.clone(),
1181            nova.i,
1182            running_instance,
1183            incoming_instance,
1184            cyclefold_instance,
1185        )
1186        .unwrap();
1187
1188        (z_0, nova)
1189    }
1190}