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
jump to
@@ -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,
@@ -181,6 +181,7 @@ GVar2,
ExternalInputsCircuit<Fr>, KZG<'static, Bn254>, Pedersen<Projective2>, + false, >; let mut rng = rand::rngs::OsRng;
@@ -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,
@@ -137,6 +137,7 @@ GVar2,
MultiInputsFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<Projective2>, + false, >; println!("Prepare Nova ProverParams & VerifierParams");
@@ -119,6 +119,7 @@ GVar2,
Sha256FCircuit<Fr>, KZG<'static, Bn254>, Pedersen<Projective2>, + false, >; let poseidon_config = poseidon_canonical_config::<Fr>();
@@ -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))
@@ -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,
@@ -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();
@@ -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);
@@ -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,
@@ -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);
@@ -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
@@ -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,
@@ -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();
@@ -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,
@@ -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,
@@ -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,
@@ -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) }
@@ -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,
@@ -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();
@@ -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,