arnaucube
arnaucube/sonobePublic Self-hosted repository
Enable hiding commitments in nova and hypernova (#129)

* feat: enable hiding commitments in nova and hypernova

* fix: set blinding values for witness vector

* fix: remove cloning of the cyclefold running instance

* fix: do not re-use blinding values between prove steps

* fix: specify whether the witness should use blinding values using a
const generic

* feat: create a `dummy` method for nova witnesses as well

* chore: clippy - removed unused imports
Pierre pdaixmoreux@gmail.com
Mon, 29 Jul 2024 12:15:15 +0200
commit

21ff3cf1ab825dd1c741cf0f028ac5934ef0f19d

parent

6248a90e89a6890af6603138586aa3e1fb683965

M examples/circom_full_flow.rs → examples/circom_full_flow.rs

@@ -64,7 +64,8 @@

let f_circuit_params = (r1cs_path, wasm_path, 1, 2); let f_circuit = CircomFCircuit::<Fr>::new(f_circuit_params).unwrap(); - pub type N = Nova<G1, GVar, G2, GVar2, CircomFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>>; + pub type N = + Nova<G1, GVar, G2, GVar2, CircomFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>, false>; pub type D = DeciderEth< G1, GVar,
M examples/external_inputs.rs → examples/external_inputs.rs

@@ -181,6 +181,7 @@ GVar2,

ExternalInputsCircuit<Fr>, KZG<'static, Bn254>, Pedersen<Projective2>, + false, >; let mut rng = rand::rngs::OsRng;
M examples/full_flow.rs → examples/full_flow.rs

@@ -81,7 +81,8 @@ let z_0 = vec![Fr::from(3_u32)];

let f_circuit = CubicFCircuit::<Fr>::new(()).unwrap(); - pub type N = Nova<G1, GVar, G2, GVar2, CubicFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>>; + pub type N = + Nova<G1, GVar, G2, GVar2, CubicFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>, false>; pub type D = DeciderEth< G1, GVar,
M examples/multi_inputs.rs → examples/multi_inputs.rs

@@ -137,6 +137,7 @@ GVar2,

MultiInputsFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<Projective2>, + false, >; println!("Prepare Nova ProverParams & VerifierParams");
M examples/sha256.rs → examples/sha256.rs

@@ -119,6 +119,7 @@ GVar2,

Sha256FCircuit<Fr>, KZG<'static, Bn254>, Pedersen<Projective2>, + false, >; let poseidon_config = poseidon_canonical_config::<Fr>();
M folding-schemes/src/folding/circuits/cyclefold.rs → folding-schemes/src/folding/circuits/cyclefold.rs

@@ -16,6 +16,7 @@ use ark_relations::r1cs::{

ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, Namespace, SynthesisError, }; use ark_std::fmt::Debug; +use ark_std::rand::RngCore; use ark_std::Zero; use core::{borrow::Borrow, marker::PhantomData};

@@ -382,7 +383,7 @@ /// Folds the given cyclefold circuit and its instances. This method is abstracted from any folding

/// scheme struct because it is used both by Nova & HyperNova's CycleFold. #[allow(clippy::type_complexity)] #[allow(clippy::too_many_arguments)] -pub fn fold_cyclefold_circuit<C1, GC1, C2, GC2, FC, CS1, CS2>( +pub fn fold_cyclefold_circuit<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool>( _n_points: usize, transcript: &mut impl Transcript<C1::ScalarField>, cf_r1cs: R1CS<C2::ScalarField>,

@@ -392,6 +393,7 @@ cf_W_i: Witness<C2>, // witness of the running instance

cf_U_i: CommittedInstance<C2>, // running instance cf_u_i_x: Vec<C2::ScalarField>, cf_circuit: CycleFoldCircuit<C1, GC1>, + mut rng: impl RngCore, ) -> Result< ( Witness<C2>,

@@ -409,8 +411,8 @@ GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,

C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>, FC: FCircuit<C1::ScalarField>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, <C1 as CurveGroup>::BaseField: PrimeField, <C2 as CurveGroup>::BaseField: PrimeField, <C1 as Group>::ScalarField: Absorb,

@@ -432,11 +434,11 @@ #[cfg(test)]

assert_eq!(cf_x_i.len(), cf_io_len(_n_points)); // fold cyclefold instances - let cf_w_i = Witness::<C2>::new(cf_w_i.clone(), cf_r1cs.A.n_rows); - let cf_u_i: CommittedInstance<C2> = cf_w_i.commit::<CS2>(&cf_cs_params, cf_x_i.clone())?; + let cf_w_i = Witness::<C2>::new::<H>(cf_w_i.clone(), cf_r1cs.A.n_rows, &mut rng); + let cf_u_i: CommittedInstance<C2> = cf_w_i.commit::<CS2, H>(&cf_cs_params, cf_x_i.clone())?; // compute T* and cmT* for CycleFoldCircuit - let (cf_T, cf_cmT) = NIFS::<C2, CS2>::compute_cyclefold_cmT( + let (cf_T, cf_cmT) = NIFS::<C2, CS2, H>::compute_cyclefold_cmT( &cf_cs_params, &cf_r1cs, &cf_w_i,

@@ -455,7 +457,7 @@ );

let cf_r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&cf_r_bits)) .expect("cf_r_bits out of bounds"); - let (cf_W_i1, cf_U_i1) = NIFS::<C2, CS2>::fold_instances( + let (cf_W_i1, cf_U_i1) = NIFS::<C2, CS2, H>::fold_instances( cf_r_Fq, &cf_W_i, &cf_U_i, &cf_w_i, &cf_u_i, &cf_T, cf_cmT, )?; Ok((cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, cf_r_Fq))
M folding-schemes/src/folding/hypernova/cccs.rs → folding-schemes/src/folding/hypernova/cccs.rs

@@ -26,7 +26,7 @@ pub x: Vec<C::ScalarField>,

} impl<F: PrimeField> CCS<F> { - pub fn to_cccs<R: Rng, C: CurveGroup, CS: CommitmentScheme<C>>( + pub fn to_cccs<R: Rng, C: CurveGroup, CS: CommitmentScheme<C, H>, const H: bool>( &self, rng: &mut R, cs_params: &CS::ProverParams,
M folding-schemes/src/folding/hypernova/circuits.rs → folding-schemes/src/folding/hypernova/circuits.rs

@@ -949,7 +949,7 @@ // Create the LCCCS instances out of z_lcccs

let mut lcccs_instances = Vec::new(); for z_i in z_lcccs.iter() { let (inst, _) = ccs - .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .to_lcccs::<_, _, Pedersen<Projective, true>, true>(&mut rng, &pedersen_params, z_i) .unwrap(); lcccs_instances.push(inst); }

@@ -957,7 +957,7 @@ // Create the CCCS instance out of z_cccs

let mut cccs_instances = Vec::new(); for z_i in z_cccs.iter() { let (inst, _) = ccs - .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .to_cccs::<_, _, Pedersen<Projective>, false>(&mut rng, &pedersen_params, z_i) .unwrap(); cccs_instances.push(inst); }

@@ -1045,7 +1045,11 @@ let mut lcccs_instances = Vec::new();

let mut w_lcccs = Vec::new(); for z_i in z_lcccs.iter() { let (running_instance, w) = ccs - .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .to_lcccs::<_, _, Pedersen<Projective, false>, false>( + &mut rng, + &pedersen_params, + z_i, + ) .unwrap(); lcccs_instances.push(running_instance); w_lcccs.push(w);

@@ -1055,7 +1059,7 @@ let mut cccs_instances = Vec::new();

let mut w_cccs = Vec::new(); for z_i in z_cccs.iter() { let (new_instance, w) = ccs - .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .to_cccs::<_, _, Pedersen<Projective>, false>(&mut rng, &pedersen_params, z_i) .unwrap(); cccs_instances.push(new_instance); w_cccs.push(w);

@@ -1139,7 +1143,7 @@ let i = Fr::from(3_u32);

let z_0 = vec![Fr::from(3_u32)]; let z_i = vec![Fr::from(3_u32)]; let (lcccs, _) = ccs - .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z1) + .to_lcccs::<_, _, Pedersen<Projective, true>, true>(&mut rng, &pedersen_params, &z1) .unwrap(); let h = lcccs .clone()

@@ -1378,6 +1382,7 @@ GVar2,

CubicFCircuit<Fr>, Pedersen<Projective>, Pedersen<Projective2>, + false, >( mu + nu, &mut transcript_p,

@@ -1388,6 +1393,7 @@ cf_W_i.clone(), // CycleFold running instance witness

cf_U_i.clone(), // CycleFold running instance cf_u_i_x, // CycleFold incoming instance cf_circuit, + &mut rng, ) .unwrap();

@@ -1439,7 +1445,7 @@ let r1cs_z = [vec![Fr::one()], r1cs_x_i1.clone(), r1cs_w_i1.clone()].concat();

// compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we // assign them directly to w_i, u_i. (u_i, w_i) = ccs - .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &r1cs_z) + .to_cccs::<_, _, Pedersen<Projective>, false>(&mut rng, &pedersen_params, &r1cs_z) .unwrap(); u_i.check_relation(&ccs, &w_i).unwrap();
M folding-schemes/src/folding/hypernova/lcccs.rs → folding-schemes/src/folding/hypernova/lcccs.rs

@@ -30,7 +30,7 @@ pub v: Vec<C::ScalarField>,

} impl<F: PrimeField> CCS<F> { - pub fn to_lcccs<R: Rng, C: CurveGroup, CS: CommitmentScheme<C>>( + pub fn to_lcccs<R: Rng, C: CurveGroup, CS: CommitmentScheme<C, H>, const H: bool>( &self, rng: &mut R, cs_params: &CS::ProverParams,

@@ -221,7 +221,11 @@ let (pedersen_params, _) =

Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); let (lcccs, _) = ccs - .to_lcccs::<_, Projective, Pedersen<Projective>>(&mut rng, &pedersen_params, &z) + .to_lcccs::<_, Projective, Pedersen<Projective, false>, false>( + &mut rng, + &pedersen_params, + &z, + ) .unwrap(); // with our test vector coming from R1CS, v should have length 3 assert_eq!(lcccs.v.len(), 3);

@@ -255,7 +259,7 @@ let (pedersen_params, _) =

Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); // Compute v_j with the right z let (lcccs, _) = ccs - .to_lcccs::<_, Projective, Pedersen<Projective>>(&mut rng, &pedersen_params, &z) + .to_lcccs::<_, Projective, Pedersen<Projective>, false>(&mut rng, &pedersen_params, &z) .unwrap(); // with our test vector coming from R1CS, v should have length 3 assert_eq!(lcccs.v.len(), 3);
M folding-schemes/src/folding/hypernova/mod.rs → folding-schemes/src/folding/hypernova/mod.rs

@@ -61,12 +61,12 @@ }

} #[derive(Debug, Clone)] -pub struct ProverParams<C1, C2, CS1, CS2> +pub struct ProverParams<C1, C2, CS1, CS2, const H: bool> where C1: CurveGroup, C2: CurveGroup, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, { pub poseidon_config: PoseidonConfig<C1::ScalarField>, pub cs_params: CS1::ProverParams,

@@ -81,8 +81,9 @@ #[derive(Debug, Clone)]

pub struct VerifierParams< C1: CurveGroup, C2: CurveGroup, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, + const H: bool, > { pub poseidon_config: PoseidonConfig<C1::ScalarField>, pub ccs: CCS<C1::ScalarField>,

@@ -91,16 +92,16 @@ pub cs_vp: CS1::VerifierParams,

pub cf_cs_vp: CS2::VerifierParams, } -impl<C1, C2, CS1, CS2> VerifierParams<C1, C2, CS1, CS2> +impl<C1, C2, CS1, CS2, const H: bool> VerifierParams<C1, C2, CS1, CS2, H> where C1: CurveGroup, C2: CurveGroup, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, { /// returns the hash of the public parameters of HyperNova pub fn pp_hash(&self) -> Result<C1::ScalarField, Error> { - pp_hash::<C1, C2, CS1, CS2>( + pp_hash::<C1, C2, CS1, CS2, H>( &self.ccs, &self.cf_r1cs, &self.cs_vp,

@@ -114,15 +115,15 @@ /// Implements HyperNova+CycleFold's IVC, described in

/// [HyperNova](https://eprint.iacr.org/2023/573.pdf) and /// [CycleFold](https://eprint.iacr.org/2023/1192.pdf), following the FoldingScheme trait #[derive(Clone, Debug)] -pub struct HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2> +pub struct HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> where C1: CurveGroup, GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<C1::ScalarField>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, { _gc1: PhantomData<GC1>, _c2: PhantomData<C2>,

@@ -159,16 +160,16 @@ pub cf_W_i: NovaWitness<C2>,

pub cf_U_i: CommittedInstance<C2>, } -impl<C1, GC1, C2, GC2, FC, CS1, CS2> MultiFolding<C1, C2, FC> - for HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2> +impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> MultiFolding<C1, C2, FC> + for HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, H> where C1: CurveGroup, GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>, FC: FCircuit<C1::ScalarField>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, <C1 as CurveGroup>::BaseField: PrimeField, <C2 as CurveGroup>::BaseField: PrimeField, <C1 as Group>::ScalarField: Absorb,

@@ -195,7 +196,7 @@ // compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we

// assign them directly to w_i, u_i. let (U_i, W_i) = self .ccs - .to_lcccs::<_, _, CS1>(&mut rng, &self.cs_params, &r1cs_z)?; + .to_lcccs::<_, _, CS1, H>(&mut rng, &self.cs_params, &r1cs_z)?; #[cfg(test)] U_i.check_relation(&self.ccs, &W_i)?;

@@ -217,7 +218,7 @@ // compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we

// assign them directly to w_i, u_i. let (u_i, w_i) = self .ccs - .to_cccs::<_, _, CS1>(&mut rng, &self.cs_params, &r1cs_z)?; + .to_cccs::<_, _, CS1, H>(&mut rng, &self.cs_params, &r1cs_z)?; #[cfg(test)] u_i.check_relation(&self.ccs, &w_i)?;

@@ -226,15 +227,15 @@ Ok((u_i, w_i))

} } -impl<C1, GC1, C2, GC2, FC, CS1, CS2> HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2> +impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, H> where C1: CurveGroup, GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>, FC: FCircuit<C1::ScalarField>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, <C1 as CurveGroup>::BaseField: PrimeField, <C2 as CurveGroup>::BaseField: PrimeField, <C1 as Group>::ScalarField: Absorb,

@@ -333,16 +334,16 @@ Ok(r1cs_z)

} } -impl<C1, GC1, C2, GC2, FC, CS1, CS2> FoldingScheme<C1, C2, FC> - for HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2> +impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> FoldingScheme<C1, C2, FC> + for HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, H> where C1: CurveGroup, GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>, FC: FCircuit<C1::ScalarField>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, <C1 as CurveGroup>::BaseField: PrimeField, <C2 as CurveGroup>::BaseField: PrimeField, <C1 as Group>::ScalarField: Absorb,

@@ -354,9 +355,9 @@ {

/// Reuse Nova's PreprocessorParam, together with two usize values, which are mu & nu /// respectively, which indicate the amount of LCCCS & CCCS instances to be folded at each /// folding step. - type PreprocessorParam = (PreprocessorParam<C1, C2, FC, CS1, CS2>, usize, usize); - type ProverParam = ProverParams<C1, C2, CS1, CS2>; - type VerifierParam = VerifierParams<C1, C2, CS1, CS2>; + type PreprocessorParam = (PreprocessorParam<C1, C2, FC, CS1, CS2, H>, usize, usize); + type ProverParam = ProverParams<C1, C2, CS1, CS2, H>; + type VerifierParam = VerifierParams<C1, C2, CS1, CS2, H>; type RunningInstance = (LCCCS<C1>, Witness<C1::ScalarField>); type IncomingInstance = (CCCS<C1>, Witness<C1::ScalarField>); type MultiCommittedInstanceWithWitness =

@@ -403,7 +404,7 @@ (cs_pp, cs_vp) = CS1::setup(&mut rng, ccs.n - ccs.l - 1)?;

(cf_cs_pp, cf_cs_vp) = CS2::setup(&mut rng, cf_r1cs.A.n_cols - cf_r1cs.l - 1)?; } - let pp = ProverParams::<C1, C2, CS1, CS2> { + let pp = ProverParams::<C1, C2, CS1, CS2, H> { poseidon_config: prep_param.poseidon_config.clone(), cs_params: cs_pp.clone(), cf_cs_params: cf_cs_pp.clone(),

@@ -411,7 +412,7 @@ ccs: Some(ccs.clone()),

mu: *mu, nu: *nu, }; - let vp = VerifierParams::<C1, C2, CS1, CS2> { + let vp = VerifierParams::<C1, C2, CS1, CS2, H> { poseidon_config: prep_param.poseidon_config.clone(), ccs, cf_r1cs,

@@ -505,6 +506,22 @@ mut rng: impl RngCore,

external_inputs: Vec<C1::ScalarField>, other_instances: Option<Self::MultiCommittedInstanceWithWitness>, ) -> Result<(), Error> { + // ensure that commitments are blinding if user has specified so. + + if H { + let blinding_commitments = if self.i == C1::ScalarField::zero() { + vec![self.w_i.r_w] + } else { + vec![self.w_i.r_w, self.W_i.r_w] + }; + if blinding_commitments.contains(&C1::ScalarField::zero()) { + return Err(Error::IncorrectBlinding( + H, + format!("{:?}", blinding_commitments), + )); + } + } + // `sponge` is for digest computation. let sponge = PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config);

@@ -572,10 +589,11 @@ .step_native(i_usize, self.z_i.clone(), external_inputs.clone())?;

// u_{i+1}.x[1] = H(cf_U_{i+1}) let cf_u_i1_x: C1::ScalarField; - let (U_i1, W_i1); + let (U_i1, mut W_i1); if self.i == C1::ScalarField::zero() { W_i1 = Witness::<C1::ScalarField>::dummy(&self.ccs); + W_i1.r_w = self.W_i.r_w; U_i1 = LCCCS::dummy(self.ccs.l, self.ccs.t, self.ccs.s); let u_i1_x = U_i1.hash(

@@ -697,7 +715,7 @@ x: Some(cf_u_i_x.clone()),

}; let (_cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) = - fold_cyclefold_circuit::<C1, GC1, C2, GC2, FC, CS1, CS2>( + fold_cyclefold_circuit::<C1, GC1, C2, GC2, FC, CS1, CS2, H>( self.mu + self.nu, &mut transcript_p, self.cf_r1cs.clone(),

@@ -707,6 +725,7 @@ self.cf_W_i.clone(), // CycleFold running instance witness

self.cf_U_i.clone(), // CycleFold running instance cf_u_i_x, cf_circuit, + &mut rng, )?; cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, self.pp_hash);

@@ -762,7 +781,7 @@ // compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we

// assign them directly to w_i, u_i. let (u_i, w_i) = self .ccs - .to_cccs::<_, C1, CS1>(&mut rng, &self.cs_params, &r1cs_z)?; + .to_cccs::<_, C1, CS1, H>(&mut rng, &self.cs_params, &r1cs_z)?; self.u_i = u_i.clone(); self.w_i = w_i.clone();

@@ -861,6 +880,7 @@ mod tests {

use crate::commitment::kzg::KZG; use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; + use ark_std::UniformRand; use super::*; use crate::commitment::pedersen::Pedersen;

@@ -874,35 +894,71 @@

let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap(); // run the test using Pedersen commitments on both sides of the curve cycle - test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>>( + test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>, false>( + poseidon_config.clone(), + F_circuit, + ); + + test_ivc_opt::<Pedersen<Projective, true>, Pedersen<Projective2, true>, true>( poseidon_config.clone(), F_circuit, ); + // run the test using KZG for the commitments on the main curve, and Pedersen for the // commitments on the secondary curve - test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>>(poseidon_config, F_circuit); + test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>, false>(poseidon_config, F_circuit); } // test_ivc allowing to choose the CommitmentSchemes - fn test_ivc_opt<CS1: CommitmentScheme<Projective>, CS2: CommitmentScheme<Projective2>>( + fn test_ivc_opt< + CS1: CommitmentScheme<Projective, H>, + CS2: CommitmentScheme<Projective2, H>, + const H: bool, + >( poseidon_config: PoseidonConfig<Fr>, F_circuit: CubicFCircuit<Fr>, ) { let mut rng = ark_std::test_rng(); - type HN<CS1, CS2> = - HyperNova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2>; let (mu, nu) = (2, 3); let prep_param = - PreprocessorParam::<Projective, Projective2, CubicFCircuit<Fr>, CS1, CS2>::new( + PreprocessorParam::<Projective, Projective2, CubicFCircuit<Fr>, CS1, CS2, H>::new( poseidon_config.clone(), F_circuit, ); - let hypernova_params = HN::preprocess(&mut rng, &(prep_param, mu, nu)).unwrap(); + let hypernova_params = HyperNova::< + Projective, + GVar, + Projective2, + GVar2, + CubicFCircuit<Fr>, + CS1, + CS2, + H, + >::preprocess(&mut rng, &(prep_param, mu, nu)) + .unwrap(); let z_0 = vec![Fr::from(3_u32)]; - let mut hypernova = HN::init(&hypernova_params, F_circuit, z_0.clone()).unwrap(); + let mut hypernova = HyperNova::< + Projective, + GVar, + Projective2, + GVar2, + CubicFCircuit<Fr>, + CS1, + CS2, + H, + >::init(&hypernova_params, F_circuit, z_0.clone()) + .unwrap(); + + let (w_i_blinding, W_i_blinding) = if H { + (Fr::rand(&mut rng), Fr::rand(&mut rng)) + } else { + (Fr::zero(), Fr::zero()) + }; + hypernova.w_i.r_w = w_i_blinding; + hypernova.W_i.r_w = W_i_blinding; let num_steps: usize = 3; for _ in 0..num_steps {

@@ -932,7 +988,7 @@ }

assert_eq!(Fr::from(num_steps as u32), hypernova.i); let (running_instance, incoming_instance, cyclefold_instance) = hypernova.instances(); - HN::verify( + HyperNova::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, H>::verify( hypernova_params.1, // verifier_params z_0, hypernova.z_i,
M folding-schemes/src/folding/hypernova/nimfs.rs → folding-schemes/src/folding/hypernova/nimfs.rs

@@ -441,10 +441,10 @@ let (pedersen_params, _) =

Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); let (lcccs, w1) = ccs - .to_lcccs::<_, Projective, Pedersen<Projective>>(&mut rng, &pedersen_params, &z1) + .to_lcccs::<_, Projective, Pedersen<Projective>, false>(&mut rng, &pedersen_params, &z1) .unwrap(); let (cccs, w2) = ccs - .to_cccs::<_, Projective, Pedersen<Projective>>(&mut rng, &pedersen_params, &z2) + .to_cccs::<_, Projective, Pedersen<Projective>, false>(&mut rng, &pedersen_params, &z2) .unwrap(); lcccs.check_relation(&ccs, &w1).unwrap();

@@ -484,11 +484,11 @@ let z_2 = get_test_z(4);

// Create the LCCCS instance out of z_1 let (running_instance, w1) = ccs - .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z_1) + .to_lcccs::<_, _, Pedersen<Projective>, false>(&mut rng, &pedersen_params, &z_1) .unwrap(); // Create the CCCS instance out of z_2 let (new_instance, w2) = ccs - .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z_2) + .to_cccs::<_, _, Pedersen<Projective>, false>(&mut rng, &pedersen_params, &z_2) .unwrap(); // Prover's transcript

@@ -540,7 +540,7 @@

// LCCCS witness let z_1 = get_test_z(2); let (mut running_instance, mut w1) = ccs - .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z_1) + .to_lcccs::<_, _, Pedersen<Projective>, false>(&mut rng, &pedersen_params, &z_1) .unwrap(); let poseidon_config = poseidon_canonical_config::<Fr>();

@@ -557,7 +557,7 @@ // CCS witness

let z_2 = get_test_z(i); let (new_instance, w2) = ccs - .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z_2) + .to_cccs::<_, _, Pedersen<Projective>, false>(&mut rng, &pedersen_params, &z_2) .unwrap(); // run the prover side of the multifolding

@@ -621,7 +621,7 @@ let mut lcccs_instances = Vec::new();

let mut w_lcccs = Vec::new(); for z_i in z_lcccs.iter() { let (running_instance, w) = ccs - .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .to_lcccs::<_, _, Pedersen<Projective>, false>(&mut rng, &pedersen_params, z_i) .unwrap(); lcccs_instances.push(running_instance); w_lcccs.push(w);

@@ -631,7 +631,7 @@ let mut cccs_instances = Vec::new();

let mut w_cccs = Vec::new(); for z_i in z_cccs.iter() { let (new_instance, w) = ccs - .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .to_cccs::<_, _, Pedersen<Projective>, false>(&mut rng, &pedersen_params, z_i) .unwrap(); cccs_instances.push(new_instance); w_cccs.push(w);

@@ -717,7 +717,7 @@ let mut lcccs_instances = Vec::new();

let mut w_lcccs = Vec::new(); for z_i in z_lcccs.iter() { let (running_instance, w) = ccs - .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .to_lcccs::<_, _, Pedersen<Projective>, false>(&mut rng, &pedersen_params, z_i) .unwrap(); lcccs_instances.push(running_instance); w_lcccs.push(w);

@@ -727,7 +727,7 @@ let mut cccs_instances = Vec::new();

let mut w_cccs = Vec::new(); for z_i in z_cccs.iter() { let (new_instance, w) = ccs - .to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i) + .to_cccs::<_, _, Pedersen<Projective>, false>(&mut rng, &pedersen_params, z_i) .unwrap(); cccs_instances.push(new_instance); w_cccs.push(w);
M folding-schemes/src/folding/hypernova/utils.rs → folding-schemes/src/folding/hypernova/utils.rs

@@ -242,7 +242,7 @@ // Initialize a multifolding object

let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); let (lcccs_instance, _) = ccs - .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z1) + .to_lcccs::<_, _, Pedersen<Projective>, false>(&mut rng, &pedersen_params, &z1) .unwrap(); let sigmas_thetas =

@@ -292,7 +292,7 @@ // Initialize a multifolding object

let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); let (lcccs_instance, _) = ccs - .to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z1) + .to_lcccs::<_, _, Pedersen<Projective>, false>(&mut rng, &pedersen_params, &z1) .unwrap(); // Compute g(x) with that r_x
M folding-schemes/src/folding/nova/decider_eth.rs → folding-schemes/src/folding/nova/decider_eth.rs

@@ -81,10 +81,10 @@ C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,

for<'b> &'b GC1: GroupOpsBounds<'b, C1, GC1>, for<'b> &'b GC2: GroupOpsBounds<'b, C2, GC2>, // constrain FS into Nova, since this is a Decider specifically for Nova - Nova<C1, GC1, C2, GC2, FC, CS1, CS2>: From<FS>, - crate::folding::nova::ProverParams<C1, C2, CS1, CS2>: + Nova<C1, GC1, C2, GC2, FC, CS1, CS2, false>: From<FS>, + crate::folding::nova::ProverParams<C1, C2, CS1, CS2, false>: From<<FS as FoldingScheme<C1, C2, FC>>::ProverParam>, - crate::folding::nova::VerifierParams<C1, C2, CS1, CS2>: + crate::folding::nova::VerifierParams<C1, C2, CS1, CS2, false>: From<<FS as FoldingScheme<C1, C2, FC>>::VerifierParam>, { type PreprocessorParam = (FS::ProverParam, FS::VerifierParam);

@@ -108,14 +108,17 @@ let (g16_pk, g16_vk) = S::circuit_specific_setup(circuit, &mut rng).unwrap();

// get the FoldingScheme prover & verifier params from Nova #[allow(clippy::type_complexity)] - let nova_pp: - <Nova<C1, GC1, C2, GC2, FC, CS1, CS2> as FoldingScheme<C1, C2, FC>>::ProverParam = - prep_param.0.clone().into() - ; + let nova_pp: <Nova<C1, GC1, C2, GC2, FC, CS1, CS2, false> as FoldingScheme< + C1, + C2, + FC, + >>::ProverParam = prep_param.0.clone().into(); #[allow(clippy::type_complexity)] - let nova_vp: - <Nova<C1, GC1, C2, GC2, FC, CS1, CS2> as FoldingScheme<C1, C2, FC>>::VerifierParam = - prep_param.1.clone().into(); + let nova_vp: <Nova<C1, GC1, C2, GC2, FC, CS1, CS2, false> as FoldingScheme< + C1, + C2, + FC, + >>::VerifierParam = prep_param.1.clone().into(); let pp_hash = nova_vp.pp_hash()?; let pp = (g16_pk, nova_pp.cs_pp);

@@ -338,6 +341,7 @@ GVar2,

CubicFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<Projective2>, + false, >; type D = Decider< Projective,
M folding-schemes/src/folding/nova/decider_eth_circuit.rs → folding-schemes/src/folding/nova/decider_eth_circuit.rs

@@ -193,14 +193,14 @@

/// Circuit that implements the in-circuit checks needed for the onchain (Ethereum's EVM) /// verification. #[derive(Clone, Debug)] -pub struct DeciderEthCircuit<C1, GC1, C2, GC2, CS1, CS2> +pub struct DeciderEthCircuit<C1, GC1, C2, GC2, CS1, CS2, const H: bool = false> where C1: CurveGroup, GC1: CurveVar<C1, CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, { _c1: PhantomData<C1>, _gc1: PhantomData<GC1>,

@@ -246,25 +246,25 @@ pub kzg_c_E: Option<C1::ScalarField>,

pub eval_W: Option<C1::ScalarField>, pub eval_E: Option<C1::ScalarField>, } -impl<C1, GC1, C2, GC2, CS1, CS2> DeciderEthCircuit<C1, GC1, C2, GC2, CS1, CS2> +impl<C1, GC1, C2, GC2, CS1, CS2, const H: bool> DeciderEthCircuit<C1, GC1, C2, GC2, CS1, CS2, H> where C1: CurveGroup, C2: CurveGroup, GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>, - CS1: CommitmentScheme<C1>, + CS1: CommitmentScheme<C1, H>, // enforce that the CS2 is Pedersen commitment scheme, since we're at Ethereum's EVM decider - CS2: CommitmentScheme<C2, ProverParams = PedersenParams<C2>>, + CS2: CommitmentScheme<C2, H, ProverParams = PedersenParams<C2>>, <C1 as Group>::ScalarField: Absorb, <C1 as CurveGroup>::BaseField: PrimeField, { pub fn from_nova<FC: FCircuit<C1::ScalarField>>( - nova: Nova<C1, GC1, C2, GC2, FC, CS1, CS2>, + nova: Nova<C1, GC1, C2, GC2, FC, CS1, CS2, H>, ) -> Result<Self, Error> { let mut transcript = PoseidonSponge::<C1::ScalarField>::new(&nova.poseidon_config); // compute the U_{i+1}, W_{i+1} - let (T, cmT) = NIFS::<C1, CS1>::compute_cmT( + let (T, cmT) = NIFS::<C1, CS1, H>::compute_cmT( &nova.cs_pp, &nova.r1cs.clone(), &nova.w_i.clone(),

@@ -281,7 +281,7 @@ cmT,

); let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)) .ok_or(Error::OutOfBounds)?; - let (W_i1, U_i1) = NIFS::<C1, CS1>::fold_instances( + let (W_i1, U_i1) = NIFS::<C1, CS1, H>::fold_instances( r_Fr, &nova.W_i, &nova.U_i, &nova.w_i, &nova.u_i, &T, cmT, )?;

@@ -376,8 +376,8 @@ Ok(self.z_i.unwrap_or(vec![CF1::<C1>::zero()]))

})?; let u_dummy_native = CommittedInstance::<C1>::dummy(2); - let w_dummy_native = Witness::<C1>::new( - vec![C1::ScalarField::zero(); self.r1cs.A.n_cols - 3 /* (3=2+1, since u_i.x.len=2) */], + let w_dummy_native = Witness::<C1>::dummy( + self.r1cs.A.n_cols - 3, /* (3=2+1, since u_i.x.len=2) */ self.E_len, );

@@ -453,10 +453,8 @@ use crate::folding::circuits::cyclefold::{cf_io_len, CycleFoldCommittedInstanceVar};

use ark_r1cs_std::ToBitsGadget; let cf_u_dummy_native = CommittedInstance::<C2>::dummy(cf_io_len(NOVA_CF_N_POINTS)); - let w_dummy_native = Witness::<C2>::new( - vec![C2::ScalarField::zero(); self.cf_r1cs.A.n_cols - 1 - self.cf_r1cs.l], - self.cf_E_len, - ); + let w_dummy_native = + Witness::<C2>::dummy(self.cf_r1cs.A.n_cols - 1 - self.cf_r1cs.l, self.cf_E_len); let cf_U_i = CycleFoldCommittedInstanceVar::<C2, GC2>::new_witness(cs.clone(), || { Ok(self.cf_U_i.unwrap_or_else(|| cf_u_dummy_native.clone())) })?;

@@ -788,6 +786,7 @@ GVar2,

CubicFCircuit<Fr>, Pedersen<Projective>, Pedersen<Projective2>, + false, >; let prep_param = PreprocessorParam::<

@@ -796,6 +795,7 @@ Projective2,

CubicFCircuit<Fr>, Pedersen<Projective>, Pedersen<Projective2>, + false, >::new(poseidon_config, F_circuit); let nova_params = N::preprocess(&mut rng, &prep_param).unwrap();
M folding-schemes/src/folding/nova/mod.rs → folding-schemes/src/folding/nova/mod.rs

@@ -11,7 +11,7 @@ use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};

use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::fmt::Debug; use ark_std::rand::RngCore; -use ark_std::{One, Zero}; +use ark_std::{One, UniformRand, Zero}; use core::marker::PhantomData; use crate::commitment::CommitmentScheme;

@@ -166,17 +166,37 @@ impl<C: CurveGroup> Witness<C>

where <C as Group>::ScalarField: Absorb, { - pub fn new(w: Vec<C::ScalarField>, e_len: usize) -> Self { - // note: at the current version, we don't use the blinding factors and we set them to 0 - // always. + pub fn new<const H: bool>(w: Vec<C::ScalarField>, e_len: usize, mut rng: impl RngCore) -> Self { + let (rW, rE) = if H { + ( + C::ScalarField::rand(&mut rng), + C::ScalarField::rand(&mut rng), + ) + } else { + (C::ScalarField::zero(), C::ScalarField::zero()) + }; + + Self { + E: vec![C::ScalarField::zero(); e_len], + rE, + W: w, + rW, + } + } + + pub fn dummy(w_len: usize, e_len: usize) -> Self { + let (rW, rE) = (C::ScalarField::zero(), C::ScalarField::zero()); + let w = vec![C::ScalarField::zero(); w_len]; + Self { E: vec![C::ScalarField::zero(); e_len], - rE: C::ScalarField::zero(), + rE, W: w, - rW: C::ScalarField::zero(), + rW, } } - pub fn commit<CS: CommitmentScheme<C>>( + + pub fn commit<CS: CommitmentScheme<C, HC>, const HC: bool>( &self, params: &CS::ProverParams, x: Vec<C::ScalarField>,

@@ -196,13 +216,13 @@ }

} #[derive(Debug, Clone)] -pub struct PreprocessorParam<C1, C2, FC, CS1, CS2> +pub struct PreprocessorParam<C1, C2, FC, CS1, CS2, const H: bool> where C1: CurveGroup, C2: CurveGroup, FC: FCircuit<C1::ScalarField>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, { pub poseidon_config: PoseidonConfig<C1::ScalarField>, pub F: FC,

@@ -213,13 +233,13 @@ pub cf_cs_pp: Option<CS2::ProverParams>,

pub cf_cs_vp: Option<CS2::VerifierParams>, } -impl<C1, C2, FC, CS1, CS2> PreprocessorParam<C1, C2, FC, CS1, CS2> +impl<C1, C2, FC, CS1, CS2, const H: bool> PreprocessorParam<C1, C2, FC, CS1, CS2, H> where C1: CurveGroup, C2: CurveGroup, FC: FCircuit<C1::ScalarField>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, { pub fn new(poseidon_config: PoseidonConfig<C1::ScalarField>, F: FC) -> Self { Self {

@@ -234,12 +254,12 @@ }

} #[derive(Debug, Clone)] -pub struct ProverParams<C1, C2, CS1, CS2> +pub struct ProverParams<C1, C2, CS1, CS2, const H: bool> where C1: CurveGroup, C2: CurveGroup, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, { pub poseidon_config: PoseidonConfig<C1::ScalarField>, pub cs_pp: CS1::ProverParams,

@@ -247,12 +267,12 @@ pub cf_cs_pp: CS2::ProverParams,

} #[derive(Debug, Clone)] -pub struct VerifierParams<C1, C2, CS1, CS2> +pub struct VerifierParams<C1, C2, CS1, CS2, const H: bool> where C1: CurveGroup, C2: CurveGroup, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, { pub poseidon_config: PoseidonConfig<C1::ScalarField>, pub r1cs: R1CS<C1::ScalarField>,

@@ -261,16 +281,16 @@ pub cs_vp: CS1::VerifierParams,

pub cf_cs_vp: CS2::VerifierParams, } -impl<C1, C2, CS1, CS2> VerifierParams<C1, C2, CS1, CS2> +impl<C1, C2, CS1, CS2, const H: bool> VerifierParams<C1, C2, CS1, CS2, H> where C1: CurveGroup, C2: CurveGroup, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, { /// returns the hash of the public parameters of Nova pub fn pp_hash(&self) -> Result<C1::ScalarField, Error> { - pp_hash::<C1, C2, CS1, CS2>( + pp_hash::<C1, C2, CS1, CS2, H>( &self.r1cs, &self.cf_r1cs, &self.cs_vp,

@@ -282,16 +302,17 @@ }

/// Implements Nova+CycleFold's IVC, described in [Nova](https://eprint.iacr.org/2021/370.pdf) and /// [CycleFold](https://eprint.iacr.org/2023/1192.pdf), following the FoldingScheme trait +/// The `H` const generic specifies whether the homorphic commitment scheme is blinding #[derive(Clone, Debug)] -pub struct Nova<C1, GC1, C2, GC2, FC, CS1, CS2> +pub struct Nova<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool = false> where C1: CurveGroup, GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<C1::ScalarField>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, { _gc1: PhantomData<GC1>, _c2: PhantomData<C2>,

@@ -325,16 +346,16 @@ pub cf_W_i: Witness<C2>,

pub cf_U_i: CommittedInstance<C2>, } -impl<C1, GC1, C2, GC2, FC, CS1, CS2> FoldingScheme<C1, C2, FC> - for Nova<C1, GC1, C2, GC2, FC, CS1, CS2> +impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> FoldingScheme<C1, C2, FC> + for Nova<C1, GC1, C2, GC2, FC, CS1, CS2, H> where C1: CurveGroup, GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>, FC: FCircuit<C1::ScalarField>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, <C1 as CurveGroup>::BaseField: PrimeField, <C2 as CurveGroup>::BaseField: PrimeField, <C1 as Group>::ScalarField: Absorb,

@@ -343,9 +364,9 @@ C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,

for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>, for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, { - type PreprocessorParam = PreprocessorParam<C1, C2, FC, CS1, CS2>; - type ProverParam = ProverParams<C1, C2, CS1, CS2>; - type VerifierParam = VerifierParams<C1, C2, CS1, CS2>; + type PreprocessorParam = PreprocessorParam<C1, C2, FC, CS1, CS2, H>; + type ProverParam = ProverParams<C1, C2, CS1, CS2, H>; + type VerifierParam = VerifierParams<C1, C2, CS1, CS2, H>; type RunningInstance = (CommittedInstance<C1>, Witness<C1>); type IncomingInstance = (CommittedInstance<C1>, Witness<C1>); type MultiCommittedInstanceWithWitness = ();

@@ -377,12 +398,12 @@ (cs_pp, cs_vp) = CS1::setup(&mut rng, r1cs.A.n_rows)?;

(cf_cs_pp, cf_cs_vp) = CS2::setup(&mut rng, cf_r1cs.A.n_rows)?; } - let prover_params = ProverParams::<C1, C2, CS1, CS2> { + let prover_params = ProverParams::<C1, C2, CS1, CS2, H> { poseidon_config: prep_param.poseidon_config.clone(), cs_pp: cs_pp.clone(), cf_cs_pp: cf_cs_pp.clone(), }; - let verifier_params = VerifierParams::<C1, C2, CS1, CS2> { + let verifier_params = VerifierParams::<C1, C2, CS1, CS2, H> { poseidon_config: prep_param.poseidon_config.clone(), r1cs, cf_r1cs,

@@ -455,11 +476,26 @@

/// Implements IVC.P of Nova+CycleFold fn prove_step( &mut self, - _rng: impl RngCore, + mut rng: impl RngCore, external_inputs: Vec<C1::ScalarField>, // Nova does not support multi-instances folding _other_instances: Option<Self::MultiCommittedInstanceWithWitness>, ) -> Result<(), Error> { + // ensure that commitments are blinding if user has specified so. + if H && self.i >= C1::ScalarField::one() { + let blinding_commitments = if self.i == C1::ScalarField::one() { + // blinding values of the running instances are zero at the first iteration + vec![self.w_i.rW, self.w_i.rE] + } else { + vec![self.w_i.rW, self.w_i.rE, self.W_i.rW, self.W_i.rE] + }; + if blinding_commitments.contains(&C1::ScalarField::zero()) { + return Err(Error::IncorrectBlinding( + H, + format!("{:?}", blinding_commitments), + )); + } + } // `sponge` is for digest computation. let sponge = PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config); // `transcript` is for challenge generation.

@@ -517,9 +553,10 @@ let r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&r_bits))

.ok_or(Error::OutOfBounds)?; // fold Nova instances - let (W_i1, U_i1): (Witness<C1>, CommittedInstance<C1>) = NIFS::<C1, CS1>::fold_instances( - r_Fr, &self.W_i, &self.U_i, &self.w_i, &self.u_i, &T, cmT, - )?; + let (W_i1, U_i1): (Witness<C1>, CommittedInstance<C1>) = + NIFS::<C1, CS1, H>::fold_instances( + r_Fr, &self.W_i, &self.U_i, &self.w_i, &self.u_i, &T, cmT, + )?; // folded instance output (public input, x) // u_{i+1}.x[0] = H(i+1, z_0, z_{i+1}, U_{i+1})

@@ -561,7 +598,7 @@ cf_x: Some(cf_u_i1_x),

}; #[cfg(test)] - NIFS::<C1, CS1>::verify_folded_instance(r_Fr, &self.U_i, &self.u_i, &U_i1, &cmT)?; + NIFS::<C1, CS1, H>::verify_folded_instance(r_Fr, &self.U_i, &self.u_i, &U_i1, &cmT)?; } else { // CycleFold part: // get the vector used as public inputs 'x' in the CycleFold circuit

@@ -604,6 +641,7 @@ self.cf_W_i.clone(), // CycleFold running instance witness

self.cf_U_i.clone(), // CycleFold running instance cfW_u_i_x, cfW_circuit, + &mut rng, )?; // fold [the output from folding self.cf_U_i + cfW_U] + cfE_U = folded_running_with_cfW + cfE let (_cfE_w_i, cfE_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) = self.fold_cyclefold_circuit(

@@ -612,6 +650,7 @@ cfW_W_i1,

cfW_U_i1.clone(), cfE_u_i_x, cfE_circuit, + &mut rng, )?; cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, self.pp_hash);

@@ -674,8 +713,8 @@

// set values for next iteration self.i += C1::ScalarField::one(); self.z_i = z_i1; - self.w_i = Witness::<C1>::new(w_i1, self.r1cs.A.n_rows); - self.u_i = self.w_i.commit::<CS1>(&self.cs_pp, x_i1)?; + self.w_i = Witness::<C1>::new::<H>(w_i1, self.r1cs.A.n_rows, &mut rng); + self.u_i = self.w_i.commit::<CS1, H>(&self.cs_pp, x_i1)?; self.W_i = W_i1; self.U_i = U_i1;

@@ -767,15 +806,15 @@ Ok(())

} } -impl<C1, GC1, C2, GC2, FC, CS1, CS2> Nova<C1, GC1, C2, GC2, FC, CS1, CS2> +impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> Nova<C1, GC1, C2, GC2, FC, CS1, CS2, H> where C1: CurveGroup, GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<C1::ScalarField>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, <C2 as CurveGroup>::BaseField: PrimeField, <C1 as Group>::ScalarField: Absorb, <C2 as Group>::ScalarField: Absorb,

@@ -783,7 +822,7 @@ C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,

{ // computes T and cmT for the AugmentedFCircuit fn compute_cmT(&self) -> Result<(Vec<C1::ScalarField>, C1), Error> { - NIFS::<C1, CS1>::compute_cmT( + NIFS::<C1, CS1, H>::compute_cmT( &self.cs_pp, &self.r1cs, &self.w_i,

@@ -794,15 +833,15 @@ )

} } -impl<C1, GC1, C2, GC2, FC, CS1, CS2> Nova<C1, GC1, C2, GC2, FC, CS1, CS2> +impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> Nova<C1, GC1, C2, GC2, FC, CS1, CS2, H> where C1: CurveGroup, GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>, FC: FCircuit<C1::ScalarField>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, <C1 as CurveGroup>::BaseField: PrimeField, <C2 as CurveGroup>::BaseField: PrimeField, <C1 as Group>::ScalarField: Absorb,

@@ -820,6 +859,7 @@ cf_W_i: Witness<C2>, // witness of the running instance

cf_U_i: CommittedInstance<C2>, // running instance cf_u_i_x: Vec<C2::ScalarField>, cf_circuit: CycleFoldCircuit<C1, GC1>, + rng: &mut impl RngCore, ) -> Result< ( Witness<C2>,

@@ -831,7 +871,7 @@ C2::ScalarField, // r_Fq

), Error, > { - fold_cyclefold_circuit::<C1, GC1, C2, GC2, FC, CS1, CS2>( + fold_cyclefold_circuit::<C1, GC1, C2, GC2, FC, CS1, CS2, H>( NOVA_CF_N_POINTS, transcript, self.cf_r1cs.clone(),

@@ -841,6 +881,7 @@ cf_W_i,

cf_U_i, cf_u_i_x, cf_circuit, + rng, ) } }

@@ -929,35 +970,60 @@

let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap(); // run the test using Pedersen commitments on both sides of the curve cycle - test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>>( + test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>, false>( + poseidon_config.clone(), + F_circuit, + ); + test_ivc_opt::<Pedersen<Projective, true>, Pedersen<Projective2, true>, true>( poseidon_config.clone(), F_circuit, ); + // run the test using KZG for the commitments on the main curve, and Pedersen for the // commitments on the secondary curve - test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>>(poseidon_config, F_circuit); + test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>, false>(poseidon_config, F_circuit); } // test_ivc allowing to choose the CommitmentSchemes - fn test_ivc_opt<CS1: CommitmentScheme<Projective>, CS2: CommitmentScheme<Projective2>>( + fn test_ivc_opt< + CS1: CommitmentScheme<Projective, H>, + CS2: CommitmentScheme<Projective2, H>, + const H: bool, + >( poseidon_config: PoseidonConfig<Fr>, F_circuit: CubicFCircuit<Fr>, ) { let mut rng = ark_std::test_rng(); - type N<CS1, CS2> = Nova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2>; - let prep_param = PreprocessorParam::<Projective, Projective2, CubicFCircuit<Fr>, CS1, CS2> { - poseidon_config, - F: F_circuit, - cs_pp: None, - cs_vp: None, - cf_cs_pp: None, - cf_cs_vp: None, - }; - let nova_params = N::preprocess(&mut rng, &prep_param).unwrap(); + let prep_param = + PreprocessorParam::<Projective, Projective2, CubicFCircuit<Fr>, CS1, CS2, H> { + poseidon_config, + F: F_circuit, + cs_pp: None, + cs_vp: None, + cf_cs_pp: None, + cf_cs_vp: None, + }; + let nova_params = Nova::< + Projective, + GVar, + Projective2, + GVar2, + CubicFCircuit<Fr>, + CS1, + CS2, + H, + >::preprocess(&mut rng, &prep_param) + .unwrap(); let z_0 = vec![Fr::from(3_u32)]; - let mut nova = N::init(&nova_params, F_circuit, z_0.clone()).unwrap(); + let mut nova = + Nova::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, H>::init( + &nova_params, + F_circuit, + z_0.clone(), + ) + .unwrap(); let num_steps: usize = 3; for _ in 0..num_steps {

@@ -966,7 +1032,7 @@ }

assert_eq!(Fr::from(num_steps as u32), nova.i); let (running_instance, incoming_instance, cyclefold_instance) = nova.instances(); - N::<CS1, CS2>::verify( + Nova::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, H>::verify( nova_params.1, // Nova's verifier params z_0, nova.z_i,
M folding-schemes/src/folding/nova/nifs.rs → folding-schemes/src/folding/nova/nifs.rs

@@ -12,12 +12,13 @@ use crate::Error;

/// Implements the Non-Interactive Folding Scheme described in section 4 of /// [Nova](https://eprint.iacr.org/2021/370.pdf) -pub struct NIFS<C: CurveGroup, CS: CommitmentScheme<C>> { +/// `H` specifies whether the NIFS will use a blinding factor +pub struct NIFS<C: CurveGroup, CS: CommitmentScheme<C, H>, const H: bool = false> { _c: PhantomData<C>, _cp: PhantomData<CS>, } -impl<C: CurveGroup, CS: CommitmentScheme<C>> NIFS<C, CS> +impl<C: CurveGroup, CS: CommitmentScheme<C, H>, const H: bool> NIFS<C, CS, H> where <C as Group>::ScalarField: Absorb, {

@@ -141,10 +142,10 @@ cmT: C,

) -> Result<(Witness<C>, CommittedInstance<C>), Error> { // fold witness // use r_T=0 since we don't need hiding property for cm(T) - let w3 = NIFS::<C, CS>::fold_witness(r, w1, w2, T, C::ScalarField::zero())?; + let w3 = NIFS::<C, CS, H>::fold_witness(r, w1, w2, T, C::ScalarField::zero())?; // fold committed instances - let ci3 = NIFS::<C, CS>::fold_committed_instance(r, ci1, ci2, &cmT); + let ci3 = NIFS::<C, CS, H>::fold_committed_instance(r, ci1, ci2, &cmT); Ok((w3, ci3)) }

@@ -158,7 +159,7 @@ ci1: &CommittedInstance<C>,

ci2: &CommittedInstance<C>, cmT: &C, ) -> CommittedInstance<C> { - NIFS::<C, CS>::fold_committed_instance(r, ci1, ci2, cmT) + NIFS::<C, CS, H>::fold_committed_instance(r, ci1, ci2, cmT) } /// Verify committed folded instance (ci) relations. Notice that this method does not open the

@@ -206,7 +207,7 @@ CryptographicSponge,

}; use ark_ff::{BigInteger, PrimeField}; use ark_pallas::{Fr, Projective}; - use ark_std::{ops::Mul, UniformRand}; + use ark_std::{ops::Mul, test_rng, UniformRand}; use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z}; use crate::commitment::pedersen::{Params as PedersenParams, Pedersen};

@@ -241,18 +242,18 @@ let z2 = get_test_z(4);

let (w1, x1) = r1cs.split_z(&z1); let (w2, x2) = r1cs.split_z(&z2); - let w1 = Witness::<C>::new(w1.clone(), r1cs.A.n_rows); - let w2 = Witness::<C>::new(w2.clone(), r1cs.A.n_rows); + let w1 = Witness::<C>::new::<false>(w1.clone(), r1cs.A.n_rows, test_rng()); + let w2 = Witness::<C>::new::<false>(w2.clone(), r1cs.A.n_rows, test_rng()); let mut rng = ark_std::test_rng(); let (pedersen_params, _) = Pedersen::<C>::setup(&mut rng, r1cs.A.n_cols).unwrap(); // compute committed instances let ci1 = w1 - .commit::<Pedersen<C>>(&pedersen_params, x1.clone()) + .commit::<Pedersen<C>, false>(&pedersen_params, x1.clone()) .unwrap(); let ci2 = w2 - .commit::<Pedersen<C>>(&pedersen_params, x2.clone()) + .commit::<Pedersen<C>, false>(&pedersen_params, x2.clone()) .unwrap(); // NIFS.P

@@ -304,9 +305,9 @@ let mut rng = ark_std::test_rng();

let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, r1cs.A.n_cols).unwrap(); // dummy instance, witness and public inputs zeroes - let w_dummy = Witness::<Projective>::new(vec![Fr::zero(); w1.len()], r1cs.A.n_rows); + let w_dummy = Witness::<Projective>::dummy(w1.len(), r1cs.A.n_rows); let mut u_dummy = w_dummy - .commit::<Pedersen<Projective>>(&pedersen_params, vec![Fr::zero(); x1.len()]) + .commit::<Pedersen<Projective>, false>(&pedersen_params, vec![Fr::zero(); x1.len()]) .unwrap(); u_dummy.u = Fr::zero();

@@ -353,7 +354,7 @@

// check that folded commitments from folded instance (ci) are equal to folding the // use folded rE, rW to commit w3 let ci3_expected = w3 - .commit::<Pedersen<Projective>>(&pedersen_params, ci3.x.clone()) + .commit::<Pedersen<Projective>, false>(&pedersen_params, ci3.x.clone()) .unwrap(); assert_eq!(ci3_expected.cmE, ci3.cmE); assert_eq!(ci3_expected.cmW, ci3.cmW);

@@ -417,9 +418,10 @@ let mut rng = ark_std::test_rng();

let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, r1cs.A.n_cols).unwrap(); // prepare the running instance - let mut running_instance_w = Witness::<Projective>::new(w.clone(), r1cs.A.n_rows); + let mut running_instance_w = + Witness::<Projective>::new::<false>(w.clone(), r1cs.A.n_rows, test_rng()); let mut running_committed_instance = running_instance_w - .commit::<Pedersen<Projective>>(&pedersen_params, x) + .commit::<Pedersen<Projective>, false>(&pedersen_params, x) .unwrap(); r1cs.check_relaxed_instance_relation(&running_instance_w, &running_committed_instance)

@@ -430,9 +432,10 @@ for i in 0..num_iters {

// prepare the incoming instance let incoming_instance_z = get_test_z(i + 4); let (w, x) = r1cs.split_z(&incoming_instance_z); - let incoming_instance_w = Witness::<Projective>::new(w.clone(), r1cs.A.n_rows); + let incoming_instance_w = + Witness::<Projective>::new::<false>(w.clone(), r1cs.A.n_rows, test_rng()); let incoming_committed_instance = incoming_instance_w - .commit::<Pedersen<Projective>>(&pedersen_params, x) + .commit::<Pedersen<Projective>, false>(&pedersen_params, x) .unwrap(); r1cs.check_relaxed_instance_relation( &incoming_instance_w,
M folding-schemes/src/folding/nova/serialize.rs → folding-schemes/src/folding/nova/serialize.rs

@@ -21,13 +21,14 @@ arith::r1cs::extract_r1cs, commitment::CommitmentScheme, folding::circuits::CF1,

frontend::FCircuit, }; -impl<C1, GC1, C2, GC2, FC, CS1, CS2> CanonicalSerialize for Nova<C1, GC1, C2, GC2, FC, CS1, CS2> +impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> CanonicalSerialize + for Nova<C1, GC1, C2, GC2, FC, CS1, CS2, H> where C1: CurveGroup, C2: CurveGroup, FC: FCircuit<C1::ScalarField>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, <C1 as CurveGroup>::BaseField: PrimeField, <C2 as CurveGroup>::BaseField: PrimeField, <C1 as Group>::ScalarField: Absorb,

@@ -94,13 +95,13 @@ }

// Note that we can't derive or implement `CanonicalDeserialize` directly. // This is because `CurveVar` notably does not implement the `Sync` trait. -impl<C1, GC1, C2, GC2, FC, CS1, CS2> Nova<C1, GC1, C2, GC2, FC, CS1, CS2> +impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> Nova<C1, GC1, C2, GC2, FC, CS1, CS2, H> where C1: CurveGroup, C2: CurveGroup, FC: FCircuit<CF1<C1>, Params = ()>, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, <C1 as CurveGroup>::BaseField: PrimeField, <C2 as CurveGroup>::BaseField: PrimeField, <C1 as Group>::ScalarField: Absorb,

@@ -117,7 +118,7 @@ pub fn deserialize_nova<R: std::io::prelude::Read>(

mut reader: R, compress: ark_serialize::Compress, validate: ark_serialize::Validate, - prover_params: ProverParams<C1, C2, CS1, CS2>, + prover_params: ProverParams<C1, C2, CS1, CS2, H>, poseidon_config: PoseidonConfig<C1::ScalarField>, ) -> Result<Self, ark_serialize::SerializationError> { let pp_hash = C1::ScalarField::deserialize_with_mode(&mut reader, compress, validate)?;

@@ -152,7 +153,6 @@ .map_err(|_| SerializationError::InvalidData)?;

cs2.finalize(); let cs2 = cs2.into_inner().ok_or(SerializationError::InvalidData)?; let cf_r1cs = extract_r1cs::<C1::BaseField>(&cs2); - Ok(Nova { _gc1: PhantomData, _c2: PhantomData,

@@ -207,6 +207,7 @@ GVar2,

CubicFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<Projective2>, + false, >; let prep_param = PreprocessorParam::new(poseidon_config.clone(), F_circuit); let nova_params = N::preprocess(&mut rng, &prep_param).unwrap();

@@ -242,6 +243,7 @@ GVar2,

CubicFCircuit<Fr>, KZG<Bn254>, Pedersen<Projective2>, + false, >::deserialize_nova( bytes.as_slice(), Compress::No,
M folding-schemes/src/folding/nova/traits.rs → folding-schemes/src/folding/nova/traits.rs

@@ -1,6 +1,6 @@

use ark_crypto_primitives::sponge::Absorb; use ark_ec::{CurveGroup, Group}; -use ark_std::{One, Zero}; +use ark_std::One; use super::{CommittedInstance, Witness}; use crate::arith::{r1cs::R1CS, Arith};

@@ -34,7 +34,7 @@ <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,

{ fn dummy_instance(&self) -> (Witness<C>, CommittedInstance<C>) { let w_len = self.A.n_cols - 1 - self.l; - let w_dummy = Witness::<C>::new(vec![C::ScalarField::zero(); w_len], self.A.n_rows); + let w_dummy = Witness::<C>::dummy(w_len, self.A.n_rows); let u_dummy = CommittedInstance::<C>::dummy(self.l); (w_dummy, u_dummy) }
M folding-schemes/src/lib.rs → folding-schemes/src/lib.rs

@@ -77,6 +77,8 @@ #[error("Pedersen parameters length is not sufficient (generators.len={0} < vector.len={1} unsatisfied)")]

PedersenParamsLen(usize, usize), #[error("Blinding factor not 0 for Commitment without hiding")] BlindingNotZero, + #[error("Blinding factors incorrect, blinding is set to {0} but blinding values are {1}")] + IncorrectBlinding(bool, String), #[error("Commitment verification failed")] CommitmentVerificationFail,
M folding-schemes/src/utils/mod.rs → folding-schemes/src/utils/mod.rs

@@ -41,7 +41,7 @@ vec![*cm_x, *cm_y]

} /// returns the hash of the given public parameters of the Folding Scheme -pub fn pp_hash<C1, C2, CS1, CS2>( +pub fn pp_hash<C1, C2, CS1, CS2, const H: bool>( arith: &impl Arith<C1::ScalarField>, cf_arith: &impl Arith<C2::ScalarField>, cs_vp: &CS1::VerifierParams,

@@ -51,8 +51,8 @@ ) -> Result<C1::ScalarField, Error>

where C1: CurveGroup, C2: CurveGroup, - CS1: CommitmentScheme<C1>, - CS2: CommitmentScheme<C2>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, { let mut hasher = Sha3_256::new();
M solidity-verifiers/src/verifiers/nova_cyclefold.rs → solidity-verifiers/src/verifiers/nova_cyclefold.rs

@@ -166,7 +166,7 @@ verifiers::nova_cyclefold::get_decider_template_for_cyclefold_decider,

NovaCycleFoldVerifierKey, ProtocolVerifierKey, }; - type NOVA<FC> = Nova<G1, GVar, G2, GVar2, FC, KZG<'static, Bn254>, Pedersen<G2>>; + type NOVA<FC> = Nova<G1, GVar, G2, GVar2, FC, KZG<'static, Bn254>, Pedersen<G2>, false>; type DECIDER<FC> = DeciderEth< G1, GVar,

@@ -318,10 +318,11 @@ let mut rng = rand::rngs::OsRng;

let poseidon_config = poseidon_canonical_config::<Fr>(); let f_circuit = FC::new(()).unwrap(); - let prep_param = PreprocessorParam::<G1, G2, FC, KZG<'static, Bn254>, Pedersen<G2>>::new( - poseidon_config, - f_circuit.clone(), - ); + let prep_param = + PreprocessorParam::<G1, G2, FC, KZG<'static, Bn254>, Pedersen<G2>, false>::new( + poseidon_config, + f_circuit.clone(), + ); let nova_params = NOVA::preprocess(&mut rng, &prep_param).unwrap(); let nova = NOVA::init( &nova_params,