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::{groups::GroupOpsBounds, 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    for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
 297    for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
 298{
 299    type RunningInstance = (LCCCS<C1>, Witness<C1::ScalarField>);
 300    type IncomingInstance = (CCCS<C1>, Witness<C1::ScalarField>);
 301    type MultiInstance = (Vec<Self::RunningInstance>, Vec<Self::IncomingInstance>);
 302
 303    /// Creates a new LCCS instance for the given state, which satisfies the HyperNova.CCS. This
 304    /// method can be used to generate the 'other' LCCS instances to be folded in the multi-folding
 305    /// step.
 306    fn new_running_instance(
 307        &self,
 308        mut rng: impl RngCore,
 309        state: Vec<C1::ScalarField>,
 310        external_inputs: Vec<C1::ScalarField>,
 311    ) -> Result<Self::RunningInstance, Error> {
 312        let r1cs_z = self.new_instance_generic(state, external_inputs)?;
 313        // compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we
 314        // assign them directly to w_i, u_i.
 315        let (U_i, W_i) = self
 316            .ccs
 317            .to_lcccs::<_, _, CS1, H>(&mut rng, &self.cs_pp, &r1cs_z)?;
 318
 319        #[cfg(test)]
 320        self.ccs.check_relation(&W_i, &U_i)?;
 321
 322        Ok((U_i, W_i))
 323    }
 324
 325    /// Creates a new CCCS instance for the given state, which satisfies the HyperNova.CCS. This
 326    /// method can be used to generate the 'other' CCCS instances to be folded in the multi-folding
 327    /// step.
 328    fn new_incoming_instance(
 329        &self,
 330        mut rng: impl RngCore,
 331        state: Vec<C1::ScalarField>,
 332        external_inputs: Vec<C1::ScalarField>,
 333    ) -> Result<Self::IncomingInstance, Error> {
 334        let r1cs_z = self.new_instance_generic(state, external_inputs)?;
 335        // compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we
 336        // assign them directly to w_i, u_i.
 337        let (u_i, w_i) = self
 338            .ccs
 339            .to_cccs::<_, _, CS1, H>(&mut rng, &self.cs_pp, &r1cs_z)?;
 340
 341        #[cfg(test)]
 342        self.ccs.check_relation(&w_i, &u_i)?;
 343
 344        Ok((u_i, w_i))
 345    }
 346}
 347
 348impl<C1, GC1, C2, GC2, FC, CS1, CS2, const MU: usize, const NU: usize, const H: bool>
 349    HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, MU, NU, H>
 350where
 351    C1: CurveGroup,
 352    GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
 353    C2: CurveGroup,
 354    GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
 355    FC: FCircuit<C1::ScalarField>,
 356    CS1: CommitmentScheme<C1, H>,
 357    CS2: CommitmentScheme<C2, H>,
 358    <C1 as CurveGroup>::BaseField: PrimeField,
 359    <C2 as CurveGroup>::BaseField: PrimeField,
 360    <C1 as Group>::ScalarField: Absorb,
 361    <C2 as Group>::ScalarField: Absorb,
 362    C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
 363    for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
 364    for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
 365{
 366    /// internal helper for new_running_instance & new_incoming_instance methods, returns the R1CS
 367    /// z=[u,x,w] vector to be used to create the LCCCS & CCCS fresh instances.
 368    fn new_instance_generic(
 369        &self,
 370        state: Vec<C1::ScalarField>,
 371        external_inputs: Vec<C1::ScalarField>,
 372    ) -> Result<Vec<C1::ScalarField>, Error> {
 373        // prepare the initial dummy instances
 374        let U_i = LCCCS::<C1>::dummy(&self.ccs);
 375        let mut u_i = CCCS::<C1>::dummy(&self.ccs);
 376        let (_, cf_U_i): (CycleFoldWitness<C2>, CycleFoldCommittedInstance<C2>) =
 377            self.cf_r1cs.dummy_witness_instance();
 378
 379        let sponge = PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config);
 380
 381        u_i.x = vec![
 382            U_i.hash(
 383                &sponge,
 384                self.pp_hash,
 385                C1::ScalarField::zero(), // i
 386                &self.z_0,
 387                &state,
 388            ),
 389            cf_U_i.hash_cyclefold(&sponge, self.pp_hash),
 390        ];
 391        let us = vec![u_i.clone(); NU - 1];
 392
 393        let z_i1 = self
 394            .F
 395            .step_native(0, state.clone(), external_inputs.clone())?;
 396
 397        // compute u_{i+1}.x
 398        let U_i1 = LCCCS::dummy(&self.ccs);
 399        let u_i1_x = U_i1.hash(
 400            &sponge,
 401            self.pp_hash,
 402            C1::ScalarField::one(), // i+1, where i=0
 403            &self.z_0,
 404            &z_i1,
 405        );
 406
 407        let cf_u_i1_x = cf_U_i.hash_cyclefold(&sponge, self.pp_hash);
 408        let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC, MU, NU> {
 409            _c2: PhantomData,
 410            _gc2: PhantomData,
 411            poseidon_config: self.poseidon_config.clone(),
 412            ccs: self.ccs.clone(),
 413            pp_hash: Some(self.pp_hash),
 414            i: Some(C1::ScalarField::zero()),
 415            i_usize: Some(0),
 416            z_0: Some(self.z_0.clone()),
 417            z_i: Some(state.clone()),
 418            external_inputs: Some(external_inputs),
 419            U_i: Some(U_i.clone()),
 420            Us: None,
 421            u_i_C: Some(u_i.C),
 422            us: Some(us),
 423            U_i1_C: Some(U_i1.C),
 424            F: self.F.clone(),
 425            x: Some(u_i1_x),
 426            nimfs_proof: None,
 427
 428            // cyclefold values
 429            cf_u_i_cmW: None,
 430            cf_U_i: None,
 431            cf_x: Some(cf_u_i1_x),
 432            cf_cmT: None,
 433        };
 434
 435        let (cs, _) = augmented_f_circuit.compute_cs_ccs()?;
 436
 437        #[cfg(test)]
 438        assert!(cs.is_satisfied()?);
 439
 440        let (r1cs_w_i1, r1cs_x_i1) = extract_w_x::<C1::ScalarField>(&cs); // includes 1 and public inputs
 441
 442        #[cfg(test)]
 443        assert_eq!(r1cs_x_i1[0], augmented_f_circuit.x.unwrap());
 444
 445        let r1cs_z = [
 446            vec![C1::ScalarField::one()],
 447            r1cs_x_i1.clone(),
 448            r1cs_w_i1.clone(),
 449        ]
 450        .concat();
 451        Ok(r1cs_z)
 452    }
 453}
 454
 455impl<C1, GC1, C2, GC2, FC, CS1, CS2, const MU: usize, const NU: usize, const H: bool>
 456    FoldingScheme<C1, C2, FC> for HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, MU, NU, H>
 457where
 458    C1: CurveGroup,
 459    GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
 460    C2: CurveGroup,
 461    GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
 462    FC: FCircuit<C1::ScalarField>,
 463    CS1: CommitmentScheme<C1, H>,
 464    CS2: CommitmentScheme<C2, H>,
 465    <C1 as CurveGroup>::BaseField: PrimeField,
 466    <C2 as CurveGroup>::BaseField: PrimeField,
 467    <C1 as Group>::ScalarField: Absorb,
 468    <C2 as Group>::ScalarField: Absorb,
 469    C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
 470    for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
 471    for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
 472{
 473    /// Reuse Nova's PreprocessorParam.
 474    type PreprocessorParam = PreprocessorParam<C1, C2, FC, CS1, CS2, H>;
 475    type ProverParam = ProverParams<C1, C2, CS1, CS2, H>;
 476    type VerifierParam = VerifierParams<C1, C2, CS1, CS2, H>;
 477    type RunningInstance = (LCCCS<C1>, Witness<C1::ScalarField>);
 478    type IncomingInstance = (CCCS<C1>, Witness<C1::ScalarField>);
 479    type MultiCommittedInstanceWithWitness =
 480        (Vec<Self::RunningInstance>, Vec<Self::IncomingInstance>);
 481    type CFInstance = (CycleFoldCommittedInstance<C2>, CycleFoldWitness<C2>);
 482    type IVCProof = IVCProof<C1, C2>;
 483
 484    fn pp_deserialize_with_mode<R: std::io::prelude::Read>(
 485        mut reader: R,
 486        compress: ark_serialize::Compress,
 487        validate: ark_serialize::Validate,
 488        fc_params: FC::Params,
 489    ) -> Result<Self::ProverParam, Error> {
 490        let poseidon_config = poseidon_canonical_config::<C1::ScalarField>();
 491
 492        // generate the r1cs & cf_r1cs needed for the VerifierParams. In this way we avoid needing
 493        // to serialize them, saving significant space in the VerifierParams serialized size.
 494
 495        // main circuit R1CS:
 496        let f_circuit = FC::new(fc_params)?;
 497        let augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC, MU, NU>::empty(
 498            &poseidon_config,
 499            f_circuit.clone(),
 500            None,
 501        )?;
 502        let ccs = augmented_F_circuit.ccs;
 503
 504        let cs_pp = CS1::ProverParams::deserialize_with_mode(&mut reader, compress, validate)?;
 505        let cf_cs_pp = CS2::ProverParams::deserialize_with_mode(&mut reader, compress, validate)?;
 506
 507        Ok(ProverParams {
 508            poseidon_config,
 509            cs_pp,
 510            cf_cs_pp,
 511            ccs: Some(ccs),
 512        })
 513    }
 514
 515    fn vp_deserialize_with_mode<R: std::io::prelude::Read>(
 516        mut reader: R,
 517        compress: ark_serialize::Compress,
 518        validate: ark_serialize::Validate,
 519        fc_params: FC::Params,
 520    ) -> Result<Self::VerifierParam, Error> {
 521        let poseidon_config = poseidon_canonical_config::<C1::ScalarField>();
 522
 523        // generate the r1cs & cf_r1cs needed for the VerifierParams. In this way we avoid needing
 524        // to serialize them, saving significant space in the VerifierParams serialized size.
 525
 526        // main circuit R1CS:
 527        let f_circuit = FC::new(fc_params)?;
 528        let augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC, MU, NU>::empty(
 529            &poseidon_config,
 530            f_circuit.clone(),
 531            None,
 532        )?;
 533        let ccs = augmented_F_circuit.ccs;
 534
 535        // CycleFold circuit R1CS
 536        let cf_circuit = HyperNovaCycleFoldCircuit::<C1, GC1, MU, NU>::empty();
 537        let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
 538
 539        let cs_vp = CS1::VerifierParams::deserialize_with_mode(&mut reader, compress, validate)?;
 540        let cf_cs_vp = CS2::VerifierParams::deserialize_with_mode(&mut reader, compress, validate)?;
 541
 542        Ok(VerifierParams {
 543            poseidon_config,
 544            ccs,
 545            cf_r1cs,
 546            cs_vp,
 547            cf_cs_vp,
 548        })
 549    }
 550
 551    fn preprocess(
 552        mut rng: impl RngCore,
 553        prep_param: &Self::PreprocessorParam,
 554    ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> {
 555        if MU < 1 || NU < 1 {
 556            return Err(Error::CantBeZero("mu,nu".to_string()));
 557        }
 558
 559        let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC, MU, NU>::empty(
 560            &prep_param.poseidon_config,
 561            prep_param.F.clone(),
 562            None,
 563        )?;
 564        let ccs = augmented_f_circuit.ccs.clone();
 565
 566        let cf_circuit = HyperNovaCycleFoldCircuit::<C1, GC1, MU, NU>::empty();
 567        let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
 568
 569        // if cs params exist, use them, if not, generate new ones
 570        let cs_pp: CS1::ProverParams;
 571        let cs_vp: CS1::VerifierParams;
 572        let cf_cs_pp: CS2::ProverParams;
 573        let cf_cs_vp: CS2::VerifierParams;
 574        if prep_param.cs_pp.is_some()
 575            && prep_param.cf_cs_pp.is_some()
 576            && prep_param.cs_vp.is_some()
 577            && prep_param.cf_cs_vp.is_some()
 578        {
 579            cs_pp = prep_param.clone().cs_pp.unwrap();
 580            cs_vp = prep_param.clone().cs_vp.unwrap();
 581            cf_cs_pp = prep_param.clone().cf_cs_pp.unwrap();
 582            cf_cs_vp = prep_param.clone().cf_cs_vp.unwrap();
 583        } else {
 584            (cs_pp, cs_vp) = CS1::setup(&mut rng, ccs.n - ccs.l - 1)?;
 585            (cf_cs_pp, cf_cs_vp) = CS2::setup(&mut rng, cf_r1cs.A.n_cols - cf_r1cs.l - 1)?;
 586        }
 587
 588        let pp = ProverParams::<C1, C2, CS1, CS2, H> {
 589            poseidon_config: prep_param.poseidon_config.clone(),
 590            cs_pp,
 591            cf_cs_pp,
 592            ccs: Some(ccs.clone()),
 593        };
 594        let vp = VerifierParams::<C1, C2, CS1, CS2, H> {
 595            poseidon_config: prep_param.poseidon_config.clone(),
 596            ccs,
 597            cf_r1cs,
 598            cs_vp: cs_vp.clone(),
 599            cf_cs_vp: cf_cs_vp.clone(),
 600        };
 601        Ok((pp, vp))
 602    }
 603
 604    /// Initializes the HyperNova+CycleFold's IVC for the given parameters and initial state `z_0`.
 605    fn init(
 606        params: &(Self::ProverParam, Self::VerifierParam),
 607        F: FC,
 608        z_0: Vec<C1::ScalarField>,
 609    ) -> Result<Self, Error> {
 610        let (pp, vp) = params;
 611        if MU < 1 || NU < 1 {
 612            return Err(Error::CantBeZero("mu,nu".to_string()));
 613        }
 614
 615        // `sponge` is for digest computation.
 616        let sponge = PoseidonSponge::<C1::ScalarField>::new(&pp.poseidon_config);
 617
 618        // prepare the HyperNova's AugmentedFCircuit and CycleFold's circuits and obtain its CCS
 619        // and R1CS respectively
 620        let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC, MU, NU>::empty(
 621            &pp.poseidon_config,
 622            F.clone(),
 623            pp.ccs.clone(),
 624        )?;
 625        let ccs = augmented_f_circuit.ccs.clone();
 626
 627        let cf_circuit = HyperNovaCycleFoldCircuit::<C1, GC1, MU, NU>::empty();
 628        let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
 629
 630        // compute the public params hash
 631        let pp_hash = vp.pp_hash()?;
 632
 633        // setup the dummy instances
 634        let W_dummy = Witness::<C1::ScalarField>::dummy(&ccs);
 635        let U_dummy = LCCCS::<C1>::dummy(&ccs);
 636        let w_dummy = W_dummy.clone();
 637        let mut u_dummy = CCCS::<C1>::dummy(&ccs);
 638        let (cf_W_dummy, cf_U_dummy): (CycleFoldWitness<C2>, CycleFoldCommittedInstance<C2>) =
 639            cf_r1cs.dummy_witness_instance();
 640        u_dummy.x = vec![
 641            U_dummy.hash(&sponge, pp_hash, C1::ScalarField::zero(), &z_0, &z_0),
 642            cf_U_dummy.hash_cyclefold(&sponge, pp_hash),
 643        ];
 644
 645        // W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the
 646        // R1CS that we're working with.
 647        Ok(Self {
 648            _gc1: PhantomData,
 649            _c2: PhantomData,
 650            _gc2: PhantomData,
 651            ccs,
 652            cf_r1cs,
 653            poseidon_config: pp.poseidon_config.clone(),
 654            cs_pp: pp.cs_pp.clone(),
 655            cf_cs_pp: pp.cf_cs_pp.clone(),
 656            F,
 657            pp_hash,
 658            i: C1::ScalarField::zero(),
 659            z_0: z_0.clone(),
 660            z_i: z_0,
 661            W_i: W_dummy,
 662            U_i: U_dummy,
 663            w_i: w_dummy,
 664            u_i: u_dummy,
 665            // cyclefold running instance
 666            cf_W_i: cf_W_dummy,
 667            cf_U_i: cf_U_dummy,
 668        })
 669    }
 670
 671    /// Implements IVC.P of HyperNova+CycleFold
 672    fn prove_step(
 673        &mut self,
 674        mut rng: impl RngCore,
 675        external_inputs: Vec<C1::ScalarField>,
 676        other_instances: Option<Self::MultiCommittedInstanceWithWitness>,
 677    ) -> Result<(), Error> {
 678        // ensure that commitments are blinding if user has specified so.
 679
 680        if H {
 681            let blinding_commitments = if self.i == C1::ScalarField::zero() {
 682                vec![self.w_i.r_w]
 683            } else {
 684                vec![self.w_i.r_w, self.W_i.r_w]
 685            };
 686            if blinding_commitments.contains(&C1::ScalarField::zero()) {
 687                return Err(Error::IncorrectBlinding(
 688                    H,
 689                    format!("{:?}", blinding_commitments),
 690                ));
 691            }
 692        }
 693
 694        // `sponge` is for digest computation.
 695        let sponge = PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config);
 696
 697        let (Us, Ws, us, ws) = if MU > 1 || NU > 1 {
 698            let other_instances = other_instances.ok_or(Error::MissingOtherInstances(MU, NU))?;
 699
 700            #[allow(clippy::type_complexity)]
 701            let (lcccs, cccs): (
 702                Vec<(LCCCS<C1>, Witness<C1::ScalarField>)>,
 703                Vec<(CCCS<C1>, Witness<C1::ScalarField>)>,
 704            ) = other_instances;
 705
 706            // recall, mu & nu is the number of all the LCCCS & CCCS respectively, including the
 707            // running and incoming instances that are not part of the 'other_instances', hence the +1
 708            // in the couple of following checks.
 709            if lcccs.len() + 1 != MU {
 710                return Err(Error::NotSameLength(
 711                    "other_instances.lcccs.len()".to_string(),
 712                    lcccs.len(),
 713                    "hypernova.mu".to_string(),
 714                    MU,
 715                ));
 716            }
 717            if cccs.len() + 1 != NU {
 718                return Err(Error::NotSameLength(
 719                    "other_instances.cccs.len()".to_string(),
 720                    cccs.len(),
 721                    "hypernova.nu".to_string(),
 722                    NU,
 723                ));
 724            }
 725
 726            let (Us, Ws): (Vec<LCCCS<C1>>, Vec<Witness<C1::ScalarField>>) =
 727                lcccs.into_iter().unzip();
 728            let (us, ws): (Vec<CCCS<C1>>, Vec<Witness<C1::ScalarField>>) = cccs.into_iter().unzip();
 729            (Some(Us), Some(Ws), Some(us), Some(ws))
 730        } else {
 731            (None, None, None, None)
 732        };
 733
 734        let augmented_f_circuit: AugmentedFCircuit<C1, C2, GC2, FC, MU, NU>;
 735
 736        if self.z_i.len() != self.F.state_len() {
 737            return Err(Error::NotSameLength(
 738                "z_i.len()".to_string(),
 739                self.z_i.len(),
 740                "F.state_len()".to_string(),
 741                self.F.state_len(),
 742            ));
 743        }
 744        if external_inputs.len() != self.F.external_inputs_len() {
 745            return Err(Error::NotSameLength(
 746                "F.external_inputs_len()".to_string(),
 747                self.F.external_inputs_len(),
 748                "external_inputs.len()".to_string(),
 749                external_inputs.len(),
 750            ));
 751        }
 752
 753        if self.i > C1::ScalarField::from_le_bytes_mod_order(&usize::MAX.to_le_bytes()) {
 754            return Err(Error::MaxStep);
 755        }
 756
 757        let i_usize;
 758
 759        #[cfg(target_pointer_width = "64")]
 760        {
 761            let mut i_bytes: [u8; 8] = [0; 8];
 762            i_bytes.copy_from_slice(&self.i.into_bigint().to_bytes_le()[..8]);
 763            i_usize = usize::from_le_bytes(i_bytes);
 764        }
 765
 766        #[cfg(target_pointer_width = "32")]
 767        {
 768            let mut i_bytes: [u8; 4] = [0; 4];
 769            i_bytes.copy_from_slice(&self.i.into_bigint().to_bytes_le()[..4]);
 770            i_usize = usize::from_le_bytes(i_bytes);
 771        }
 772
 773        let z_i1 = self
 774            .F
 775            .step_native(i_usize, self.z_i.clone(), external_inputs.clone())?;
 776
 777        // u_{i+1}.x[1] = H(cf_U_{i+1})
 778        let cf_u_i1_x: C1::ScalarField;
 779        let (U_i1, mut W_i1);
 780
 781        if self.i == C1::ScalarField::zero() {
 782            W_i1 = Witness::<C1::ScalarField>::dummy(&self.ccs);
 783            W_i1.r_w = self.W_i.r_w;
 784            U_i1 = LCCCS::dummy(&self.ccs);
 785
 786            let u_i1_x = U_i1.hash(
 787                &sponge,
 788                self.pp_hash,
 789                C1::ScalarField::one(),
 790                &self.z_0,
 791                &z_i1,
 792            );
 793
 794            // hash the initial (dummy) CycleFold instance, which is used as the 2nd public
 795            // input in the AugmentedFCircuit
 796            cf_u_i1_x = self.cf_U_i.hash_cyclefold(&sponge, self.pp_hash);
 797
 798            augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC, MU, NU> {
 799                _c2: PhantomData,
 800                _gc2: PhantomData,
 801                poseidon_config: self.poseidon_config.clone(),
 802                ccs: self.ccs.clone(),
 803                pp_hash: Some(self.pp_hash),
 804                i: Some(C1::ScalarField::zero()),
 805                i_usize: Some(0),
 806                z_0: Some(self.z_0.clone()),
 807                z_i: Some(self.z_i.clone()),
 808                external_inputs: Some(external_inputs.clone()),
 809                U_i: Some(self.U_i.clone()),
 810                Us: Us.clone(),
 811                u_i_C: Some(self.u_i.C),
 812                us: us.clone(),
 813                U_i1_C: Some(U_i1.C),
 814                F: self.F.clone(),
 815                x: Some(u_i1_x),
 816                nimfs_proof: None,
 817
 818                // cyclefold values
 819                cf_u_i_cmW: None,
 820                cf_U_i: None,
 821                cf_x: Some(cf_u_i1_x),
 822                cf_cmT: None,
 823            };
 824        } else {
 825            let mut transcript_p: PoseidonSponge<C1::ScalarField> =
 826                PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config);
 827            transcript_p.absorb(&self.pp_hash);
 828
 829            let (all_Us, all_us, all_Ws, all_ws) = if MU > 1 || NU > 1 {
 830                (
 831                    [vec![self.U_i.clone()], Us.clone().unwrap()].concat(),
 832                    [vec![self.u_i.clone()], us.clone().unwrap()].concat(),
 833                    [vec![self.W_i.clone()], Ws.unwrap()].concat(),
 834                    [vec![self.w_i.clone()], ws.unwrap()].concat(),
 835                )
 836            } else {
 837                (
 838                    vec![self.U_i.clone()],
 839                    vec![self.u_i.clone()],
 840                    vec![self.W_i.clone()],
 841                    vec![self.w_i.clone()],
 842                )
 843            };
 844
 845            let (rho, nimfs_proof);
 846            (nimfs_proof, U_i1, W_i1, rho) = NIMFS::<C1, PoseidonSponge<C1::ScalarField>>::prove(
 847                &mut transcript_p,
 848                &self.ccs,
 849                &all_Us,
 850                &all_us,
 851                &all_Ws,
 852                &all_ws,
 853            )?;
 854
 855            // sanity check: check the folded instance relation
 856            #[cfg(test)]
 857            self.ccs.check_relation(&W_i1, &U_i1)?;
 858
 859            let u_i1_x = U_i1.hash(
 860                &sponge,
 861                self.pp_hash,
 862                self.i + C1::ScalarField::one(),
 863                &self.z_0,
 864                &z_i1,
 865            );
 866
 867            let rho_bits = rho.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec();
 868            let rho_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap();
 869
 870            // CycleFold part:
 871            // get the vector used as public inputs 'x' in the CycleFold circuit.
 872            // Place the random values and the points coordinates as the public input x:
 873            // In Nova, this is: x == [r, p1, p2, p3].
 874            // In multifolding schemes such as HyperNova, this is:
 875            // computed_x = [r, p_0, p_1, p_2, ..., p_n],
 876            // where each p_i is in fact p_i.to_constraint_field()
 877            let cf_u_i_x = [
 878                vec![rho_Fq],
 879                all_Us
 880                    .iter()
 881                    .flat_map(|Us_i| get_cm_coordinates(&Us_i.C))
 882                    .collect(),
 883                all_us
 884                    .iter()
 885                    .flat_map(|us_i| get_cm_coordinates(&us_i.C))
 886                    .collect(),
 887                get_cm_coordinates(&U_i1.C),
 888            ]
 889            .concat();
 890
 891            let cf_circuit = HyperNovaCycleFoldCircuit::<C1, GC1, MU, NU> {
 892                _gc: PhantomData,
 893                r_bits: Some(rho_bits.clone()),
 894                points: Some(
 895                    [
 896                        all_Us.iter().map(|Us_i| Us_i.C).collect::<Vec<_>>(),
 897                        all_us.iter().map(|us_i| us_i.C).collect::<Vec<_>>(),
 898                    ]
 899                    .concat(),
 900                ),
 901                x: Some(cf_u_i_x.clone()),
 902            };
 903
 904            let (_cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) = fold_cyclefold_circuit::<
 905                HyperNovaCycleFoldConfig<C1, MU, NU>,
 906                C1,
 907                GC1,
 908                C2,
 909                GC2,
 910                CS2,
 911                H,
 912            >(
 913                &mut transcript_p,
 914                self.cf_r1cs.clone(),
 915                self.cf_cs_pp.clone(),
 916                self.pp_hash,
 917                self.cf_W_i.clone(), // CycleFold running instance witness
 918                self.cf_U_i.clone(), // CycleFold running instance
 919                cf_u_i_x,
 920                cf_circuit,
 921                &mut rng,
 922            )?;
 923
 924            cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, self.pp_hash);
 925
 926            augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC, MU, NU> {
 927                _c2: PhantomData,
 928                _gc2: PhantomData,
 929                poseidon_config: self.poseidon_config.clone(),
 930                ccs: self.ccs.clone(),
 931                pp_hash: Some(self.pp_hash),
 932                i: Some(self.i),
 933                i_usize: Some(i_usize),
 934                z_0: Some(self.z_0.clone()),
 935                z_i: Some(self.z_i.clone()),
 936                external_inputs: Some(external_inputs),
 937                U_i: Some(self.U_i.clone()),
 938                Us: Us.clone(),
 939                u_i_C: Some(self.u_i.C),
 940                us: us.clone(),
 941                U_i1_C: Some(U_i1.C),
 942                F: self.F.clone(),
 943                x: Some(u_i1_x),
 944                nimfs_proof: Some(nimfs_proof),
 945
 946                // cyclefold values
 947                cf_u_i_cmW: Some(cf_u_i.cmW),
 948                cf_U_i: Some(self.cf_U_i.clone()),
 949                cf_x: Some(cf_u_i1_x),
 950                cf_cmT: Some(cf_cmT),
 951            };
 952
 953            // assign the next round instances
 954            self.cf_W_i = cf_W_i1;
 955            self.cf_U_i = cf_U_i1;
 956        }
 957
 958        let (cs, _) = augmented_f_circuit.compute_cs_ccs()?;
 959
 960        #[cfg(test)]
 961        assert!(cs.is_satisfied()?);
 962
 963        let (r1cs_w_i1, r1cs_x_i1) = extract_w_x::<C1::ScalarField>(&cs); // includes 1 and public inputs
 964
 965        let r1cs_z = [
 966            vec![C1::ScalarField::one()],
 967            r1cs_x_i1.clone(),
 968            r1cs_w_i1.clone(),
 969        ]
 970        .concat();
 971        // compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we
 972        // assign them directly to w_i, u_i.
 973        let (u_i, w_i) = self
 974            .ccs
 975            .to_cccs::<_, C1, CS1, H>(&mut rng, &self.cs_pp, &r1cs_z)?;
 976        self.u_i = u_i.clone();
 977        self.w_i = w_i.clone();
 978
 979        // set values for next iteration
 980        self.i += C1::ScalarField::one();
 981        // assign z_{i+1} into z_i
 982        self.z_i = z_i1.clone();
 983        self.U_i = U_i1.clone();
 984        self.W_i = W_i1.clone();
 985
 986        #[cfg(test)]
 987        {
 988            // check the new LCCCS instance relation
 989            self.ccs.check_relation(&self.W_i, &self.U_i)?;
 990            // check the new CCCS instance relation
 991            self.ccs.check_relation(&self.w_i, &self.u_i)?;
 992        }
 993
 994        Ok(())
 995    }
 996
 997    fn state(&self) -> Vec<C1::ScalarField> {
 998        self.z_i.clone()
 999    }
1000
1001    fn ivc_proof(&self) -> Self::IVCProof {
1002        Self::IVCProof {
1003            i: self.i,
1004            z_0: self.z_0.clone(),
1005            z_i: self.z_i.clone(),
1006            W_i: self.W_i.clone(),
1007            U_i: self.U_i.clone(),
1008            w_i: self.w_i.clone(),
1009            u_i: self.u_i.clone(),
1010            cf_W_i: self.cf_W_i.clone(),
1011            cf_U_i: self.cf_U_i.clone(),
1012        }
1013    }
1014
1015    fn from_ivc_proof(
1016        ivc_proof: Self::IVCProof,
1017        fcircuit_params: FC::Params,
1018        params: (Self::ProverParam, Self::VerifierParam),
1019    ) -> Result<Self, Error> {
1020        let IVCProof {
1021            i,
1022            z_0,
1023            z_i,
1024            W_i,
1025            U_i,
1026            w_i,
1027            u_i,
1028            cf_W_i,
1029            cf_U_i,
1030        } = ivc_proof;
1031        let (pp, vp) = params;
1032
1033        let f_circuit = FC::new(fcircuit_params).unwrap();
1034        let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC, MU, NU>::empty(
1035            &pp.poseidon_config,
1036            f_circuit.clone(),
1037            None,
1038        )?;
1039        let cf_circuit = HyperNovaCycleFoldCircuit::<C1, GC1, MU, NU>::empty();
1040
1041        let ccs = augmented_f_circuit.ccs.clone();
1042        let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
1043
1044        Ok(Self {
1045            _gc1: PhantomData,
1046            _c2: PhantomData,
1047            _gc2: PhantomData,
1048            ccs,
1049            cf_r1cs,
1050            poseidon_config: pp.poseidon_config,
1051            cs_pp: pp.cs_pp,
1052            cf_cs_pp: pp.cf_cs_pp,
1053            F: f_circuit,
1054            pp_hash: vp.pp_hash()?,
1055            i,
1056            z_0,
1057            z_i,
1058            w_i,
1059            u_i,
1060            W_i,
1061            U_i,
1062            cf_W_i,
1063            cf_U_i,
1064        })
1065    }
1066
1067    /// Implements IVC.V of Hyp.clone()erNova+CycleFold. Notice that this method does not include the
1068    /// commitments verification, which is done in the Decider.
1069    fn verify(vp: Self::VerifierParam, ivc_proof: Self::IVCProof) -> Result<(), Error> {
1070        let Self::IVCProof {
1071            i: num_steps,
1072            z_0,
1073            z_i,
1074            W_i,
1075            U_i,
1076            w_i,
1077            u_i,
1078            cf_W_i,
1079            cf_U_i,
1080        } = ivc_proof;
1081
1082        if num_steps == C1::ScalarField::zero() {
1083            if z_0 != z_i {
1084                return Err(Error::IVCVerificationFail);
1085            }
1086            return Ok(());
1087        }
1088        // `sponge` is for digest computation.
1089        let sponge = PoseidonSponge::<C1::ScalarField>::new(&vp.poseidon_config);
1090
1091        if u_i.x.len() != 2 || U_i.x.len() != 2 {
1092            return Err(Error::IVCVerificationFail);
1093        }
1094
1095        let pp_hash = vp.pp_hash()?;
1096
1097        // check that u_i's output points to the running instance
1098        // u_i.X[0] == H(i, z_0, z_i, U_i)
1099        let expected_u_i_x = U_i.hash(&sponge, pp_hash, num_steps, &z_0, &z_i);
1100        if expected_u_i_x != u_i.x[0] {
1101            return Err(Error::IVCVerificationFail);
1102        }
1103        // u_i.X[1] == H(cf_U_i)
1104        let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&sponge, pp_hash);
1105        if expected_cf_u_i_x != u_i.x[1] {
1106            return Err(Error::IVCVerificationFail);
1107        }
1108
1109        // check LCCCS satisfiability
1110        vp.ccs.check_relation(&W_i, &U_i)?;
1111        // check CCCS satisfiability
1112        vp.ccs.check_relation(&w_i, &u_i)?;
1113
1114        // check CycleFold's RelaxedR1CS satisfiability
1115        vp.cf_r1cs.check_relation(&cf_W_i, &cf_U_i)?;
1116
1117        Ok(())
1118    }
1119}
1120
1121#[cfg(test)]
1122mod tests {
1123    use crate::commitment::kzg::KZG;
1124    use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
1125    use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
1126    use ark_std::UniformRand;
1127
1128    use super::*;
1129    use crate::commitment::pedersen::Pedersen;
1130    use crate::frontend::utils::CubicFCircuit;
1131    use crate::transcript::poseidon::poseidon_canonical_config;
1132
1133    #[test]
1134    pub fn test_ivc() {
1135        let poseidon_config = poseidon_canonical_config::<Fr>();
1136
1137        let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
1138
1139        // run the test using Pedersen commitments on both sides of the curve cycle
1140        test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>, false>(
1141            poseidon_config.clone(),
1142            F_circuit,
1143        );
1144
1145        test_ivc_opt::<Pedersen<Projective, true>, Pedersen<Projective2, true>, true>(
1146            poseidon_config.clone(),
1147            F_circuit,
1148        );
1149
1150        // run the test using KZG for the commitments on the main curve, and Pedersen for the
1151        // commitments on the secondary curve
1152        test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>, false>(poseidon_config, F_circuit);
1153    }
1154
1155    #[allow(clippy::type_complexity)]
1156    // test_ivc allowing to choose the CommitmentSchemes
1157    pub fn test_ivc_opt<
1158        CS1: CommitmentScheme<Projective, H>,
1159        CS2: CommitmentScheme<Projective2, H>,
1160        const H: bool,
1161    >(
1162        poseidon_config: PoseidonConfig<Fr>,
1163        F_circuit: CubicFCircuit<Fr>,
1164    ) {
1165        let mut rng = ark_std::test_rng();
1166
1167        const MU: usize = 2;
1168        const NU: usize = 3;
1169
1170        type HN<CS1, CS2, const H: bool> =
1171            HyperNova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, MU, NU, H>;
1172
1173        let prep_param =
1174            PreprocessorParam::<Projective, Projective2, CubicFCircuit<Fr>, CS1, CS2, H>::new(
1175                poseidon_config.clone(),
1176                F_circuit,
1177            );
1178        let hypernova_params = HN::preprocess(&mut rng, &prep_param).unwrap();
1179
1180        let z_0 = vec![Fr::from(3_u32)];
1181        let mut hypernova = HN::init(&hypernova_params, F_circuit, z_0.clone()).unwrap();
1182
1183        let (w_i_blinding, W_i_blinding) = if H {
1184            (Fr::rand(&mut rng), Fr::rand(&mut rng))
1185        } else {
1186            (Fr::zero(), Fr::zero())
1187        };
1188        hypernova.w_i.r_w = w_i_blinding;
1189        hypernova.W_i.r_w = W_i_blinding;
1190
1191        let num_steps: usize = 3;
1192        for _ in 0..num_steps {
1193            // prepare some new instances to fold in the multifolding step
1194            let mut lcccs = vec![];
1195            for j in 0..MU - 1 {
1196                let instance_state = vec![Fr::from(j as u32 + 85_u32)];
1197                let (U, W) = hypernova
1198                    .new_running_instance(&mut rng, instance_state, vec![])
1199                    .unwrap();
1200                lcccs.push((U, W));
1201            }
1202            let mut cccs = vec![];
1203            for j in 0..NU - 1 {
1204                let instance_state = vec![Fr::from(j as u32 + 15_u32)];
1205                let (u, w) = hypernova
1206                    .new_incoming_instance(&mut rng, instance_state, vec![])
1207                    .unwrap();
1208                cccs.push((u, w));
1209            }
1210
1211            hypernova
1212                .prove_step(&mut rng, vec![], Some((lcccs, cccs)))
1213                .unwrap();
1214        }
1215        assert_eq!(Fr::from(num_steps as u32), hypernova.i);
1216
1217        let ivc_proof = hypernova.ivc_proof();
1218        HN::verify(
1219            hypernova_params.1.clone(), // verifier_params
1220            ivc_proof,
1221        )
1222        .unwrap();
1223    }
1224}