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