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