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