arnaucube
arnaucube/sonobePublic Self-hosted repository
Compute Decider's CM challenges in Groth16 circuit, link G16 & KZG proofs in Onchain Decider, refactor CommitmentScheme trait (#79)

* Compute Decider's CM challenges in Groth16 circuit, link G16 & KZG proofs in Onchain Decider, refactor CommitmentScheme trait

- Refactor commitment package
  - Refactor `Commitment` trait and the kzg, ipa, pedersen impls
  - Add methods to prove & verify given challenges (not computing them in-method)
- Add KZG challenges computation in decider_eth_circuit
- Add cmE & cmW KZG proving & verification in DeciderEth
- Link Decider's Groth16 proof & KZG proofs data
- Fix point to bytes arkworks inconsistency
- Patch ark_curves to use a cherry-picked version with bn254::constraints & grumpkin for v0.4.0 (once arkworks v0.5.0 is released this will no longer be needed)

* DeciderEthCircuit: Add check eval=p(c) for E & W

The check is temporary disabled due
https://github.com/privacy-scaling-explorations/folding-schemes/issues/80,
but the public inputs and logic are there, to be able to continue the
other parts development while issue #80 is solved.
arnaucube root@arnaucube.com
Tue, 26 Mar 2024 10:54:13 +0100
commit

fe9a488f63fd8a131119cd3e40ec4d80dcf4690a

parent

1072b66e92aa90e1d2b34aa8c44977e7bafa8e72

M Cargo.toml → Cargo.toml

@@ -6,10 +6,15 @@ "cli"

] resolver = "2" +[patch.crates-io] # The following patch is to use a version of ark-r1cs-std compatible with # v0.4.0 but that includes a cherry-picked commit from after v0.4.0 which fixes # the in-circuit scalar multiplication of the zero point. The commit is from # https://github.com/arkworks-rs/r1cs-std/pull/124, without including other # changes done between v0.4.0 and this fix which would break compatibility. -[patch.crates-io] -ark-r1cs-std = { git = "https://github.com/arnaucube/ark-r1cs-std-cherry-picked/" }+ark-r1cs-std = { git = "https://github.com/arnaucube/ark-r1cs-std-cherry-picked/" } +# patch ark_curves to use a cherry-picked version which contains +# bn254::constraints & grumpkin for v0.4.0 (once arkworks v0.5.0 is released +# this will no longer be needed) +ark-bn254 = { git = "https://github.com/arnaucube/ark-curves-cherry-picked", branch="cherry-pick"} +ark-grumpkin = { git = "https://github.com/arnaucube/ark-curves-cherry-picked", branch="cherry-pick"}
M folding-schemes/Cargo.toml → folding-schemes/Cargo.toml

@@ -26,9 +26,8 @@

[dev-dependencies] ark-pallas = {version="0.4.0", features=["r1cs"]} ark-vesta = {version="0.4.0", features=["r1cs"]} -ark-bn254 = "0.4.0" -ark-mnt4-298 = {version="0.4.0", features=["r1cs"]} -ark-mnt6-298 = {version="0.4.0", features=["r1cs"]} +ark-bn254 = {version="0.4.0", features=["r1cs"]} +ark-grumpkin = {version="0.4.0", features=["r1cs"]} ark-groth16 = { version = "^0.4.0" } rand = "0.8.5" tracing = { version = "0.1", default-features = false, features = [ "attributes" ] }

@@ -45,4 +44,3 @@ "ark-poly/parallel",

"ark-crypto-primitives/parallel", "ark-r1cs-std/parallel", ] -
M folding-schemes/examples/utils.rs → folding-schemes/examples/utils.rs

@@ -6,7 +6,7 @@ #![allow(dead_code)]

use ark_pallas::{constraints::GVar, Fr, Projective}; use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2}; -use folding_schemes::commitment::pedersen::Pedersen; +use folding_schemes::commitment::{pedersen::Pedersen, CommitmentScheme}; use folding_schemes::folding::nova::{get_r1cs, ProverParams, VerifierParams}; use folding_schemes::frontend::FCircuit; use folding_schemes::transcript::poseidon::poseidon_test_config;

@@ -27,17 +27,17 @@

// get the CM & CF_CM len let (r1cs, cf_r1cs) = get_r1cs::<Projective, GVar, Projective2, GVar2, FC>(&poseidon_config, F_circuit).unwrap(); - let cm_len = r1cs.A.n_rows; - let cf_cm_len = cf_r1cs.A.n_rows; + let cf_len = r1cs.A.n_rows; + let cf_cf_len = cf_r1cs.A.n_rows; - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, cm_len); - let cf_pedersen_params = Pedersen::<Projective2>::new_params(&mut rng, cf_cm_len); + let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, cf_len).unwrap(); + let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cf_len).unwrap(); let prover_params = ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> { poseidon_config: poseidon_config.clone(), - cm_params: pedersen_params, - cf_cm_params: cf_pedersen_params, + cs_params: pedersen_params, + cf_cs_params: cf_pedersen_params, }; let verifier_params = VerifierParams::<Projective, Projective2> { poseidon_config: poseidon_config.clone(),
M folding-schemes/src/commitment/ipa.rs → folding-schemes/src/commitment/ipa.rs

@@ -18,15 +18,11 @@ prelude::CurveVar,

ToBitsGadget, }; use ark_relations::r1cs::{Namespace, SynthesisError}; -use ark_std::{ - cfg_iter, - rand::{Rng, RngCore}, - UniformRand, Zero, -}; +use ark_std::{cfg_iter, rand::RngCore, UniformRand, Zero}; use core::{borrow::Borrow, marker::PhantomData}; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; -use super::{pedersen::Params as PedersenParams, CommitmentProver}; +use super::{pedersen::Params as PedersenParams, CommitmentScheme}; use crate::transcript::Transcript; use crate::utils::{ powers_of,

@@ -34,13 +30,6 @@ vec::{vec_add, vec_scalar_mul},

}; use crate::Error; -/// IPA implements the Inner Product Argument protocol. The `H` parameter indicates if to use the -/// commitment in hiding mode or not. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct IPA<C: CurveGroup, const H: bool = false> { - _c: PhantomData<C>, -} - #[derive(Debug, Clone, Eq, PartialEq)] pub struct Proof<C: CurveGroup> { a: C::ScalarField,

@@ -50,22 +39,35 @@ L: Vec<C>,

R: Vec<C>, } -impl<C: CurveGroup, const H: bool> IPA<C, H> { - pub fn new_params<R: Rng>(rng: &mut R, max: usize) -> PedersenParams<C> { - let generators: Vec<C::Affine> = std::iter::repeat_with(|| C::Affine::rand(rng)) - .take(max.next_power_of_two()) +/// IPA implements the Inner Product Argument protocol following the CommitmentScheme trait. The +/// `H` parameter indicates if to use the commitment in hiding mode or not. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct IPA<C: CurveGroup, const H: bool = false> { + _c: PhantomData<C>, +} + +/// Implements the CommitmentScheme trait for IPA +impl<C: CurveGroup, const H: bool> CommitmentScheme<C, H> for IPA<C, H> { + type ProverParams = PedersenParams<C>; + type VerifierParams = PedersenParams<C>; + type Proof = (Proof<C>, C::ScalarField, C::ScalarField); // (proof, v=p(x), r=blinding factor) + type ProverChallenge = (C::ScalarField, C, Vec<C::ScalarField>); + type Challenge = (C::ScalarField, C, Vec<C::ScalarField>); + + fn setup( + mut rng: impl RngCore, + len: usize, + ) -> Result<(Self::ProverParams, Self::VerifierParams), Error> { + let generators: Vec<C::Affine> = std::iter::repeat_with(|| C::Affine::rand(&mut rng)) + .take(len.next_power_of_two()) .collect(); - PedersenParams::<C> { - h: C::rand(rng), + let p = PedersenParams::<C> { + h: C::rand(&mut rng), generators, - } + }; + Ok((p.clone(), p)) } -} -// Implement the CommitmentProver trait for IPA -impl<C: CurveGroup, const H: bool> CommitmentProver<C, H> for IPA<C, H> { - type Params = PedersenParams<C>; - type Proof = Proof<C>; fn commit( params: &PedersenParams<C>, a: &[C::ScalarField],

@@ -74,6 +76,10 @@ ) -> Result<C, Error> {

if params.generators.len() < a.len() { return Err(Error::PedersenParamsLen(params.generators.len(), a.len())); } + if !H && (!r.is_zero()) { + return Err(Error::BlindingNotZero); + } + // h⋅r + <g, a> // use msm_unchecked because we already ensured at the if that lengths match if !H {

@@ -83,15 +89,18 @@ Ok(params.h.mul(r) + C::msm_unchecked(&params.generators[..a.len()], a))

} fn prove( - params: &Self::Params, + params: &Self::ProverParams, transcript: &mut impl Transcript<C>, - P: &C, // commitment - a: &[C::ScalarField], - x: &C::ScalarField, + P: &C, // commitment + a: &[C::ScalarField], // vector + blind: &C::ScalarField, rng: Option<&mut dyn RngCore>, ) -> Result<Self::Proof, Error> { if !a.len().is_power_of_two() { return Err(Error::NotPowerOfTwo("a".to_string(), a.len())); + } + if !H && (!blind.is_zero()) { + return Err(Error::BlindingNotZero); } let d = a.len(); let k = (f64::from(d as u32).log2()) as usize;

@@ -116,11 +125,14 @@ r = vec![];

} transcript.absorb_point(P)?; + let x = transcript.get_challenge(); // challenge value at which we evaluate let s = transcript.get_challenge(); let U = C::generator().mul(s); let mut a = a.to_owned(); - let mut b = powers_of(*x, d); + let mut b = powers_of(x, d); + let v = inner_prod(&a, &b)?; + let mut G = params.generators.clone(); let mut L: Vec<C> = vec![C::zero(); k];

@@ -180,40 +192,41 @@ if G.len() != 1 {

return Err(Error::NotExpectedLength(G.len(), 1)); } - Ok(Self::Proof { - a: a[0], - l: l.clone(), - r: r.clone(), - L, - R, - }) + Ok(( + Proof { + a: a[0], + l: l.clone(), + r: r.clone(), + L, + R, + }, + v, // evaluation at challenge, v=p(x) + *blind, // blind factor + )) + } + + fn prove_with_challenge( + _params: &Self::ProverParams, + _challenge: Self::ProverChallenge, + _a: &[C::ScalarField], // vector + _blind: &C::ScalarField, + _rng: Option<&mut dyn RngCore>, + ) -> Result<Self::Proof, Error> { + // not supported because the prover logic computes challenges as it advances on the logic + Err(Error::NotSupported("IPA::prove_with_challenge".to_string())) } -} -impl<C: CurveGroup, const H: bool> IPA<C, H> { - #[allow(clippy::too_many_arguments)] - pub fn verify( - params: &PedersenParams<C>, + fn verify( + params: &Self::VerifierParams, transcript: &mut impl Transcript<C>, - x: C::ScalarField, // evaluation point - v: C::ScalarField, // value at evaluation point - P: C, // commitment - p: Proof<C>, - r: C::ScalarField, // blinding factor - k: usize, // k = log2(d), where d is the degree of the committed polynomial - ) -> Result<bool, Error> { - if !H && (!p.l.is_empty() || !p.r.is_empty()) { - return Err(Error::CommitmentVerificationFail); - } - if H && (p.l.len() != k || p.r.len() != k) { - return Err(Error::CommitmentVerificationFail); - } - if p.L.len() != k || p.R.len() != k { - return Err(Error::CommitmentVerificationFail); - } + P: &C, // commitment + proof: &Self::Proof, + ) -> Result<(), Error> { + let (p, _r) = (proof.0.clone(), proof.1); + let k = p.L.len(); - // absorbs & get challenges - transcript.absorb_point(&P)?; + transcript.absorb_point(P)?; + let x = transcript.get_challenge(); // challenge value at which we evaluate let s = transcript.get_challenge(); let U = C::generator().mul(s); let mut u: Vec<C::ScalarField> = vec![C::ScalarField::zero(); k];

@@ -222,8 +235,35 @@ transcript.absorb_point(&p.L[i])?;

transcript.absorb_point(&p.R[i])?; u[i] = transcript.get_challenge(); } + let challenge = (x, U, u); - let P = P + U.mul(v); + Self::verify_with_challenge(params, challenge, P, proof) + } + + fn verify_with_challenge( + params: &Self::VerifierParams, + challenge: Self::Challenge, + P: &C, // commitment + proof: &Self::Proof, + ) -> Result<(), Error> { + let (p, v, r) = (proof.0.clone(), proof.1, proof.2); + let (x, U, u) = challenge; + + let k = p.L.len(); + if p.R.len() != k { + return Err(Error::CommitmentVerificationFail); + } + if !H && (!r.is_zero()) { + return Err(Error::BlindingNotZero); + } + if !H && (!p.l.is_empty() || !p.r.is_empty()) { + return Err(Error::CommitmentVerificationFail); + } + if H && (p.l.len() != k || p.r.len() != k) { + return Err(Error::CommitmentVerificationFail); + } + + let P = *P + U.mul(v); // where v=p(x) let mut q_0 = P; let mut r = r;

@@ -262,7 +302,10 @@ } else {

G.mul(p.a) + U.mul(p.a * b) }; - Ok(q_0 == q_1) + if q_0 != q_1 { + return Err(Error::CommitmentVerificationFail); + } + Ok(()) } }

@@ -452,7 +495,7 @@ #[allow(clippy::too_many_arguments)]

pub fn verify<const K: usize>( g: &Vec<GC>, // params.generators h: &GC, // params.h - x: &NonNativeFieldVar<C::ScalarField, CF<C>>, // evaluation point + x: &NonNativeFieldVar<C::ScalarField, CF<C>>, // evaluation point, challenge v: &NonNativeFieldVar<C::ScalarField, CF<C>>, // value at evaluation point P: &GC, // commitment p: &ProofVar<C, GC>,

@@ -475,7 +518,6 @@ u_invs[j] = u_j.inverse()?;

} // compute b & G from s - // let d: usize = 2_u64.pow(K as u32) as usize; let s = build_s_gadget(u, &u_invs, K)?; // b = <s, b_vec> = <s, [1, x, x^2, ..., x^K-1]> let b = s_b_inner_gadget(u, x)?;

@@ -539,7 +581,7 @@ const k: usize = 4;

const d: usize = 2_u64.pow(k as u32) as usize; // setup params - let params = IPA::<Projective, hiding>::new_params(&mut rng, d); + let (params, _) = IPA::<Projective, hiding>::setup(&mut rng, d).unwrap(); let poseidon_config = poseidon_test_config::<Fr>(); // init Prover's transcript

@@ -547,38 +589,28 @@ let mut transcript_p = PoseidonTranscript::<Projective>::new(&poseidon_config);

// init Verifier's transcript let mut transcript_v = PoseidonTranscript::<Projective>::new(&poseidon_config); + // a is the vector that we're committing let a: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng)) .take(d) .collect(); - let r_blind: Fr = Fr::rand(&mut rng); + let r_blind: Fr = if hiding { + Fr::rand(&mut rng) + } else { + Fr::zero() + }; let cm = IPA::<Projective, hiding>::commit(&params, &a, &r_blind).unwrap(); - - // evaluation point - let x = Fr::rand(&mut rng); let proof = IPA::<Projective, hiding>::prove( &params, &mut transcript_p, &cm, &a, - &x, + &r_blind, Some(&mut rng), ) .unwrap(); - let b = powers_of(x, d); - let v = inner_prod(&a, &b).unwrap(); - assert!(IPA::<Projective, hiding>::verify( - &params, - &mut transcript_v, - x, - v, - cm, - proof, - r_blind, - k, - ) - .unwrap()); + IPA::<Projective, hiding>::verify(&params, &mut transcript_v, &cm, &proof).unwrap(); } #[test]

@@ -589,11 +621,11 @@ }

fn test_ipa_gadget_opt<const hiding: bool>() { let mut rng = ark_std::test_rng(); - const k: usize = 4; + const k: usize = 3; const d: usize = 2_u64.pow(k as u32) as usize; // setup params - let params = IPA::<Projective, hiding>::new_params(&mut rng, d); + let (params, _) = IPA::<Projective, hiding>::setup(&mut rng, d).unwrap(); let poseidon_config = poseidon_test_config::<Fr>(); // init Prover's transcript

@@ -605,57 +637,49 @@ let mut a: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))

.take(d / 2) .collect(); a.extend(vec![Fr::zero(); d / 2]); - let r_blind: Fr = Fr::rand(&mut rng); + let r_blind: Fr = if hiding { + Fr::rand(&mut rng) + } else { + Fr::zero() + }; let cm = IPA::<Projective, hiding>::commit(&params, &a, &r_blind).unwrap(); - - // evaluation point - let x = Fr::rand(&mut rng); let proof = IPA::<Projective, hiding>::prove( &params, &mut transcript_p, &cm, &a, - &x, + &r_blind, Some(&mut rng), ) .unwrap(); - let b = powers_of(x, d); - let v = inner_prod(&a, &b).unwrap(); - assert!(IPA::<Projective, hiding>::verify( - &params, - &mut transcript_v, - x, - v, - cm, - proof.clone(), - r_blind, - k, - ) - .unwrap()); + IPA::<Projective, hiding>::verify(&params, &mut transcript_v, &cm, &proof).unwrap(); // circuit let cs = ConstraintSystem::<Fq>::new_ref(); let mut transcript_v = PoseidonTranscript::<Projective>::new(&poseidon_config); transcript_v.absorb_point(&cm).unwrap(); + let challenge = transcript_v.get_challenge(); // challenge value at which we evaluate let s = transcript_v.get_challenge(); let U = Projective::generator().mul(s); let mut u: Vec<Fr> = vec![Fr::zero(); k]; for i in (0..k).rev() { - transcript_v.absorb_point(&proof.L[i]).unwrap(); - transcript_v.absorb_point(&proof.R[i]).unwrap(); + transcript_v.absorb_point(&proof.0.L[i]).unwrap(); + transcript_v.absorb_point(&proof.0.R[i]).unwrap(); u[i] = transcript_v.get_challenge(); } // prepare inputs let gVar = Vec::<GVar>::new_constant(cs.clone(), params.generators).unwrap(); let hVar = GVar::new_constant(cs.clone(), params.h).unwrap(); - let xVar = NonNativeFieldVar::<Fr, Fq>::new_witness(cs.clone(), || Ok(x)).unwrap(); - let vVar = NonNativeFieldVar::<Fr, Fq>::new_witness(cs.clone(), || Ok(v)).unwrap(); + let challengeVar = + NonNativeFieldVar::<Fr, Fq>::new_witness(cs.clone(), || Ok(challenge)).unwrap(); + let vVar = NonNativeFieldVar::<Fr, Fq>::new_witness(cs.clone(), || Ok(proof.1)).unwrap(); let cmVar = GVar::new_witness(cs.clone(), || Ok(cm)).unwrap(); - let proofVar = ProofVar::<Projective, GVar>::new_witness(cs.clone(), || Ok(proof)).unwrap(); + let proofVar = + ProofVar::<Projective, GVar>::new_witness(cs.clone(), || Ok(proof.0)).unwrap(); let r_blindVar = NonNativeFieldVar::<Fr, Fq>::new_witness(cs.clone(), || Ok(r_blind)).unwrap(); let uVar_vec = Vec::<NonNativeFieldVar<Fr, Fq>>::new_witness(cs.clone(), || Ok(u)).unwrap();

@@ -663,10 +687,9 @@ let uVar: [NonNativeFieldVar<Fr, Fq>; k] = uVar_vec.try_into().unwrap();

let UVar = GVar::new_witness(cs.clone(), || Ok(U)).unwrap(); let v = IPAGadget::<Projective, GVar, hiding>::verify::<k>( - // &mut transcriptVar, &gVar, &hVar, - &xVar, + &challengeVar, &vVar, &cmVar, &proofVar,
M folding-schemes/src/commitment/kzg.rs → folding-schemes/src/commitment/kzg.rs

@@ -1,30 +1,28 @@

/// Adaptation of the prover methods and structs from arkworks/poly-commit's KZG10 implementation -/// into the CommitmentProver trait. +/// into the CommitmentScheme trait. /// /// The motivation to do so, is that we want to be able to use KZG / Pedersen for committing to /// vectors indistinctly, and the arkworks KZG10 implementation contains all the methods under the /// same trait, which requires the Pairing trait, where the prover does not need access to the /// Pairing but only to G1. -/// For our case, we want the folding schemes prover to be agnostic to pairings, since in the -/// non-ethereum cases we may use non-pairing-friendly curves with Pedersen commitments, so the -/// trait & types that we use should not depend on the Pairing type for the prover. Therefore, we -/// separate the CommitmentSchemeProver from the setup and verify phases, so the prover can be -/// defined without depending on pairings. use ark_ec::{pairing::Pairing, CurveGroup, VariableBaseMSM}; use ark_ff::PrimeField; use ark_poly::{ univariate::{DenseOrSparsePolynomial, DensePolynomial}, - DenseUVPolynomial, EvaluationDomain, Evaluations, GeneralEvaluationDomain, Polynomial, + DenseUVPolynomial, Polynomial, }; -use ark_poly_commit::kzg10::{VerifierKey, KZG10}; -use ark_std::rand::{Rng, RngCore}; +use ark_poly_commit::kzg10::{ + Commitment as KZG10Commitment, Proof as KZG10Proof, VerifierKey, KZG10, +}; +use ark_std::rand::RngCore; use ark_std::{borrow::Cow, fmt::Debug}; use ark_std::{One, Zero}; use core::marker::PhantomData; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; -use super::CommitmentProver; +use super::CommitmentScheme; use crate::transcript::Transcript; +use crate::utils::vec::poly_from_vec; use crate::Error; /// ProverKey defines a similar struct as in ark_poly_commit::kzg10::Powers, but instead of

@@ -35,22 +33,40 @@ /// Group elements of the form `β^i G`, for different values of `i`.

pub powers_of_g: Cow<'a, [C::Affine]>, } -#[derive(Debug)] -pub struct KZGSetup<P: Pairing> { - _p: PhantomData<P>, +#[derive(Debug, Clone, Default, Eq, PartialEq)] +pub struct Proof<C: CurveGroup> { + pub eval: C::ScalarField, + pub proof: C, +} + +/// KZG implements the CommitmentScheme trait for the KZG commitment scheme. +#[derive(Debug, Clone, Default, Eq, PartialEq)] +pub struct KZG<'a, E: Pairing, const H: bool = false> { + _a: PhantomData<&'a ()>, + _e: PhantomData<E>, } -impl<'a, P> KZGSetup<P> +impl<'a, E, const H: bool> CommitmentScheme<E::G1, H> for KZG<'a, E, H> where - P: Pairing, + E: Pairing, { + type ProverParams = ProverKey<'a, E::G1>; + type VerifierParams = VerifierKey<E>; + type Proof = Proof<E::G1>; + type ProverChallenge = E::ScalarField; + type Challenge = E::ScalarField; + /// setup returns the tuple (ProverKey, VerifierKey). For real world deployments the setup must /// be computed in the most trustless way possible, usually through a MPC ceremony. - pub fn setup<R: Rng>(rng: &mut R, len: usize) -> (ProverKey<'a, P::G1>, VerifierKey<P>) { + fn setup( + mut rng: impl RngCore, + len: usize, + ) -> Result<(Self::ProverParams, Self::VerifierParams), Error> { let len = len.next_power_of_two(); - let universal_params = KZG10::<P, DensePolynomial<P::ScalarField>>::setup(len, false, rng) - .expect("Setup failed"); + let universal_params = + KZG10::<E, DensePolynomial<E::ScalarField>>::setup(len, false, &mut rng) + .expect("Setup failed"); let powers_of_g = universal_params.powers_of_g[..=len].to_vec(); - let powers = ProverKey::<P::G1> { + let powers = ProverKey::<E::G1> { powers_of_g: ark_std::borrow::Cow::Owned(powers_of_g), }; let vk = VerifierKey {

@@ -61,33 +77,18 @@ beta_h: universal_params.beta_h,

prepared_h: universal_params.prepared_h.clone(), prepared_beta_h: universal_params.prepared_beta_h.clone(), }; - (powers, vk) + Ok((powers, vk)) } -} -/// KZGProver implements the CommitmentProver trait for the KZG commitment scheme. -#[derive(Debug, Clone, Default, Eq, PartialEq)] -pub struct KZGProver<'a, C: CurveGroup, const H: bool = false> { - _a: PhantomData<&'a ()>, - _c: PhantomData<C>, -} -impl<'a, C, const H: bool> CommitmentProver<C, H> for KZGProver<'a, C, H> -where - C: CurveGroup, -{ - type Params = ProverKey<'a, C>; - /// Proof is a tuple containing (evaluation, proof) - type Proof = (C::ScalarField, C); - - /// commit implements the CommitmentProver commit interface, adapting the implementation from + /// commit implements the CommitmentScheme commit interface, adapting the implementation from /// https://github.com/arkworks-rs/poly-commit/tree/c724fa666e935bbba8db5a1421603bab542e15ab/poly-commit/src/kzg10/mod.rs#L178 /// with the main difference being the removal of the blinding factors and the no-dependency to /// the Pairing trait. fn commit( - params: &Self::Params, - v: &[C::ScalarField], - _blind: &C::ScalarField, - ) -> Result<C, Error> { + params: &Self::ProverParams, + v: &[E::ScalarField], + _blind: &E::ScalarField, + ) -> Result<E::G1, Error> { if !_blind.is_zero() || H { return Err(Error::NotSupportedYet("hiding".to_string())); }

@@ -97,23 +98,35 @@ check_degree_is_too_large(polynomial.degree(), params.powers_of_g.len())?;

let (num_leading_zeros, plain_coeffs) = skip_first_zero_coeffs_and_convert_to_bigints(&polynomial); - let commitment = <C as VariableBaseMSM>::msm_bigint( + let commitment = <E::G1 as VariableBaseMSM>::msm_bigint( &params.powers_of_g[num_leading_zeros..], &plain_coeffs, ); Ok(commitment) } - /// prove implements the CommitmentProver prove interface, adapting the implementation from + /// prove implements the CommitmentScheme prove interface, adapting the implementation from /// https://github.com/arkworks-rs/poly-commit/tree/c724fa666e935bbba8db5a1421603bab542e15ab/poly-commit/src/kzg10/mod.rs#L307 /// with the main difference being the removal of the blinding factors and the no-dependency to /// the Pairing trait. fn prove( - params: &Self::Params, - transcript: &mut impl Transcript<C>, - cm: &C, - v: &[C::ScalarField], - _blind: &C::ScalarField, + params: &Self::ProverParams, + transcript: &mut impl Transcript<E::G1>, + cm: &E::G1, + v: &[E::ScalarField], + _blind: &E::ScalarField, + _rng: Option<&mut dyn RngCore>, + ) -> Result<Self::Proof, Error> { + transcript.absorb_point(cm)?; + let challenge = transcript.get_challenge(); + Self::prove_with_challenge(params, challenge, v, _blind, _rng) + } + + fn prove_with_challenge( + params: &Self::ProverParams, + challenge: Self::ProverChallenge, + v: &[E::ScalarField], + _blind: &E::ScalarField, _rng: Option<&mut dyn RngCore>, ) -> Result<Self::Proof, Error> { if !_blind.is_zero() || H {

@@ -123,15 +136,12 @@

let polynomial = poly_from_vec(v.to_vec())?; check_degree_is_too_large(polynomial.degree(), params.powers_of_g.len())?; - transcript.absorb_point(cm)?; - let challenge = transcript.get_challenge(); - // Compute q(x) = (p(x) - p(z)) / (x-z). Observe that this quotient does not change with z // because p(z) is the remainder term. We can therefore omit p(z) when computing the // quotient. - let divisor = DensePolynomial::<C::ScalarField>::from_coefficients_vec(vec![ + let divisor = DensePolynomial::<E::ScalarField>::from_coefficients_vec(vec![ -challenge, - C::ScalarField::one(), + E::ScalarField::one(), ]); let (witness_poly, remainder_poly) = DenseOrSparsePolynomial::from(&polynomial) .divide_with_q_and_r(&DenseOrSparsePolynomial::from(&divisor))

@@ -139,25 +149,61 @@ // the panic inside `divide_with_q_and_r` should never be reached, since the divisor

// polynomial is constructed right before and is set to not be zero. And the `.unwrap` // should not give an error. .unwrap(); - let evaluation = remainder_poly[0]; + + let eval = if remainder_poly.is_zero() { + E::ScalarField::zero() + } else { + remainder_poly[0] + }; check_degree_is_too_large(witness_poly.degree(), params.powers_of_g.len())?; let (num_leading_zeros, witness_coeffs) = skip_first_zero_coeffs_and_convert_to_bigints(&witness_poly); - let proof = <C as VariableBaseMSM>::msm_bigint( + let proof = <E::G1 as VariableBaseMSM>::msm_bigint( &params.powers_of_g[num_leading_zeros..], &witness_coeffs, ); - Ok((evaluation, proof)) + Ok(Proof { eval, proof }) } -} + + fn verify( + params: &Self::VerifierParams, + transcript: &mut impl Transcript<E::G1>, + cm: &E::G1, + proof: &Self::Proof, + ) -> Result<(), Error> { + transcript.absorb_point(cm)?; + let challenge = transcript.get_challenge(); + Self::verify_with_challenge(params, challenge, cm, proof) + } + + fn verify_with_challenge( + params: &Self::VerifierParams, + challenge: Self::Challenge, + cm: &E::G1, + proof: &Self::Proof, + ) -> Result<(), Error> { + if H { + return Err(Error::NotSupportedYet("hiding".to_string())); + } -/// returns the interpolated polynomial of degree=v.len().next_power_of_two(), which passes through all -/// the given elements of v. -fn poly_from_vec<F: PrimeField>(v: Vec<F>) -> Result<DensePolynomial<F>, Error> { - let D = GeneralEvaluationDomain::<F>::new(v.len()).ok_or(Error::NewDomainFail)?; - Ok(Evaluations::from_vec_and_domain(v, D).interpolate()) + // verify the KZG proof using arkworks method + let v = KZG10::<E, DensePolynomial<E::ScalarField>>::check( + params, // vk + &KZG10Commitment(cm.into_affine()), + challenge, + proof.eval, + &KZG10Proof::<E> { + w: proof.proof.into_affine(), + random_v: None, + }, + )?; + if !v { + return Err(Error::CommitmentVerificationFail); + } + Ok(()) + } } fn check_degree_is_too_large(

@@ -195,7 +241,6 @@

#[cfg(test)] mod tests { use ark_bn254::{Bn254, Fr, G1Projective as G1}; - use ark_poly_commit::kzg10::{Commitment as KZG10Commitment, Proof as KZG10Proof, KZG10}; use ark_std::{test_rng, UniformRand}; use super::*;

@@ -203,35 +248,21 @@ use crate::transcript::poseidon::{poseidon_test_config, PoseidonTranscript};

#[test] fn test_kzg_commitment_scheme() { - let rng = &mut test_rng(); + let mut rng = &mut test_rng(); let poseidon_config = poseidon_test_config::<Fr>(); let transcript_p = &mut PoseidonTranscript::<G1>::new(&poseidon_config); let transcript_v = &mut PoseidonTranscript::<G1>::new(&poseidon_config); let n = 10; - let (pk, vk): (ProverKey<G1>, VerifierKey<Bn254>) = KZGSetup::<Bn254>::setup(rng, n); + let (pk, vk): (ProverKey<G1>, VerifierKey<Bn254>) = + KZG::<Bn254>::setup(&mut rng, n).unwrap(); let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(rng)).take(n).collect(); - let cm = KZGProver::<G1>::commit(&pk, &v, &Fr::zero()).unwrap(); + let cm = KZG::<Bn254>::commit(&pk, &v, &Fr::zero()).unwrap(); - let (eval, proof) = - KZGProver::<G1>::prove(&pk, transcript_p, &cm, &v, &Fr::zero(), None).unwrap(); + let proof = KZG::<Bn254>::prove(&pk, transcript_p, &cm, &v, &Fr::zero(), None).unwrap(); // verify the proof: - // get evaluation challenge - transcript_v.absorb_point(&cm).unwrap(); - let challenge = transcript_v.get_challenge(); - // verify the KZG proof using arkworks method - assert!(KZG10::<Bn254, DensePolynomial<Fr>>::check( - &vk, - &KZG10Commitment(cm.into_affine()), - challenge, - eval, - &KZG10Proof::<Bn254> { - w: proof.into_affine(), - random_v: None, - }, - ) - .unwrap()); + KZG::<Bn254>::verify(&vk, transcript_v, &cm, &proof).unwrap(); } }
M folding-schemes/src/commitment/mod.rs → folding-schemes/src/commitment/mod.rs

@@ -9,25 +9,60 @@ pub mod ipa;

pub mod kzg; pub mod pedersen; -/// CommitmentProver defines the vector commitment scheme prover trait. Where `H` indicates if to -/// use the commitment in hiding mode or not. -pub trait CommitmentProver<C: CurveGroup, const H: bool = false>: Clone + Debug { - type Params: Clone + Debug; +/// CommitmentScheme defines the vector commitment scheme trait. Where `H` indicates if to use the +/// commitment in hiding mode or not. +pub trait CommitmentScheme<C: CurveGroup, const H: bool = false>: Clone + Debug { + type ProverParams: Clone + Debug; + type VerifierParams: Clone + Debug; type Proof: Clone + Debug; + type ProverChallenge: Clone + Debug; + type Challenge: Clone + Debug; + + fn setup( + rng: impl RngCore, + len: usize, + ) -> Result<(Self::ProverParams, Self::VerifierParams), Error>; fn commit( - params: &Self::Params, + params: &Self::ProverParams, v: &[C::ScalarField], blind: &C::ScalarField, ) -> Result<C, Error>; + fn prove( - params: &Self::Params, + params: &Self::ProverParams, transcript: &mut impl Transcript<C>, cm: &C, v: &[C::ScalarField], blind: &C::ScalarField, rng: Option<&mut dyn RngCore>, ) -> Result<Self::Proof, Error>; + + /// same as `prove` but instead of providing a Transcript to use, providing the already + /// computed challenge + fn prove_with_challenge( + params: &Self::ProverParams, + challenge: Self::ProverChallenge, + v: &[C::ScalarField], + blind: &C::ScalarField, + rng: Option<&mut dyn RngCore>, + ) -> Result<Self::Proof, Error>; + + fn verify( + params: &Self::VerifierParams, + transcript: &mut impl Transcript<C>, + cm: &C, + proof: &Self::Proof, + ) -> Result<(), Error>; + + /// same as `verify` but instead of providing a Transcript to use, providing the already + /// computed challenge + fn verify_with_challenge( + params: &Self::VerifierParams, + challenge: Self::Challenge, + cm: &C, + proof: &Self::Proof, + ) -> Result<(), Error>; } #[cfg(test)]

@@ -35,58 +70,23 @@ mod tests {

use super::*; use ark_bn254::{Bn254, Fr, G1Projective as G1}; use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; - use ark_poly::univariate::DensePolynomial; - use ark_poly_commit::kzg10::{ - Commitment as KZG10Commitment, Proof as KZG10Proof, VerifierKey, KZG10, - }; + use ark_poly_commit::kzg10::VerifierKey; use ark_std::Zero; use ark_std::{test_rng, UniformRand}; - use super::kzg::{KZGProver, KZGSetup, ProverKey}; + use super::ipa::IPA; + use super::kzg::{ProverKey, KZG}; use super::pedersen::Pedersen; use crate::transcript::{ poseidon::{poseidon_test_config, PoseidonTranscript}, Transcript, }; - // Computes the commitment of the two vectors using the given CommitmentProver, then computes - // their random linear combination, and returns it together with the proof of it. - fn commit_rlc_and_prove<C: CurveGroup, CP: CommitmentProver<C>>( - poseidon_config: &PoseidonConfig<C::ScalarField>, - params: &CP::Params, - r: C::ScalarField, - v_1: &[C::ScalarField], - v_2: &[C::ScalarField], - ) -> Result<(C, CP::Proof), Error> - where - <C as ark_ec::Group>::ScalarField: Absorb, - { - let cm_1 = CP::commit(params, v_1, &C::ScalarField::zero())?; - let cm_2 = CP::commit(params, v_2, &C::ScalarField::zero())?; - - // random linear combination of the commitment and the witness (vector v) - let cm_3 = cm_1 + cm_2.mul(r); - let v_3: Vec<C::ScalarField> = v_1.iter().zip(v_2).map(|(a, b)| *a + (r * b)).collect(); - - let transcript = &mut PoseidonTranscript::<C>::new(poseidon_config); - let proof = CP::prove( - params, - transcript, - &cm_3, - &v_3, - &C::ScalarField::zero(), - None, - ) - .unwrap(); - - Ok((cm_3, proof)) - } - #[test] - fn test_homomorphic_property_using_CommitmentProver_trait() { - let rng = &mut test_rng(); + fn test_homomorphic_property_using_Commitment_trait() { + let mut rng = &mut test_rng(); let poseidon_config = poseidon_test_config::<Fr>(); - let n: usize = 100; + let n: usize = 128; // set random vector for the test let v_1: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(rng)).take(n).collect();

@@ -95,45 +95,74 @@ // set a random challenge for the random linear combination

let r = Fr::rand(rng); // setup params for Pedersen & KZG - let pedersen_params = Pedersen::<G1>::new_params(rng, n); + let (pedersen_params, _) = Pedersen::<G1>::setup(&mut rng, n).unwrap(); let (kzg_pk, kzg_vk): (ProverKey<G1>, VerifierKey<Bn254>) = - KZGSetup::<Bn254>::setup(rng, n); + KZG::<Bn254>::setup(rng, n).unwrap(); - // Pedersen commit the two vectors and return their random linear combination and proof - let (pedersen_cm, pedersen_proof) = commit_rlc_and_prove::<G1, Pedersen<G1>>( + // test with Pedersen + test_homomorphic_property_using_Commitment_trait_opt::<G1, Pedersen<G1>>( + &poseidon_config, + &pedersen_params, + &pedersen_params, + r, + &v_1, + &v_2, + ); + // test with IPA + test_homomorphic_property_using_Commitment_trait_opt::<G1, IPA<G1>>( &poseidon_config, + &pedersen_params, &pedersen_params, r, &v_1, &v_2, - ) - .unwrap(); + ); + // test with KZG + test_homomorphic_property_using_Commitment_trait_opt::<G1, KZG<Bn254>>( + &poseidon_config, + &kzg_pk, + &kzg_vk, + r, + &v_1, + &v_2, + ); + } - // KZG commit the two vectors and return their random linear combination and proof - let (kzg_cm, kzg_proof) = - commit_rlc_and_prove::<G1, KZGProver<G1>>(&poseidon_config, &kzg_pk, r, &v_1, &v_2) - .unwrap(); + fn test_homomorphic_property_using_Commitment_trait_opt< + C: CurveGroup, + CS: CommitmentScheme<C>, + >( + poseidon_config: &PoseidonConfig<C::ScalarField>, + prover_params: &CS::ProverParams, + verifier_params: &CS::VerifierParams, + r: C::ScalarField, + v_1: &[C::ScalarField], + v_2: &[C::ScalarField], + ) where + <C as ark_ec::Group>::ScalarField: Absorb, + { + // compute the commitment of the two vectors using the given CommitmentScheme + let cm_1 = CS::commit(prover_params, v_1, &C::ScalarField::zero()).unwrap(); + let cm_2 = CS::commit(prover_params, v_2, &C::ScalarField::zero()).unwrap(); - // verify Pedersen - let transcript_v = &mut PoseidonTranscript::<G1>::new(&poseidon_config); - Pedersen::<G1>::verify(&pedersen_params, transcript_v, pedersen_cm, pedersen_proof) - .unwrap(); + // random linear combination of the commitments and their witnesses (vectors v_i) + let cm_3 = cm_1 + cm_2.mul(r); + let v_3: Vec<C::ScalarField> = v_1.iter().zip(v_2).map(|(a, b)| *a + (r * b)).collect(); - // verify KZG - let transcript_v = &mut PoseidonTranscript::<G1>::new(&poseidon_config); - transcript_v.absorb_point(&kzg_cm).unwrap(); - let challenge = transcript_v.get_challenge(); - // verify the KZG proof using arkworks method - assert!(KZG10::<Bn254, DensePolynomial<Fr>>::check( - &kzg_vk, - &KZG10Commitment(kzg_cm.into_affine()), - challenge, - kzg_proof.0, // eval - &KZG10Proof::<Bn254> { - w: kzg_proof.1.into_affine(), // proof - random_v: None, - }, + // compute the proof of the cm_3 + let transcript_p = &mut PoseidonTranscript::<C>::new(poseidon_config); + let proof = CS::prove( + prover_params, + transcript_p, + &cm_3, + &v_3, + &C::ScalarField::zero(), + None, ) - .unwrap()); + .unwrap(); + + // verify the opening proof + let transcript_v = &mut PoseidonTranscript::<C>::new(poseidon_config); + CS::verify(verifier_params, transcript_v, &cm_3, &proof).unwrap(); } }
M folding-schemes/src/commitment/pedersen.rs → folding-schemes/src/commitment/pedersen.rs

@@ -3,13 +3,10 @@ use ark_ff::Field;

use ark_r1cs_std::{boolean::Boolean, groups::GroupOpsBounds, prelude::CurveVar}; use ark_relations::r1cs::SynthesisError; use ark_std::Zero; -use ark_std::{ - rand::{Rng, RngCore}, - UniformRand, -}; +use ark_std::{rand::RngCore, UniformRand}; use core::marker::PhantomData; -use super::CommitmentProver; +use super::CommitmentScheme; use crate::transcript::Transcript; use crate::utils::vec::{vec_add, vec_scalar_mul}; use crate::Error;

@@ -18,7 +15,7 @@ #[derive(Debug, Clone, Eq, PartialEq)]

pub struct Proof<C: CurveGroup> { pub R: C, pub u: Vec<C::ScalarField>, - pub r_u: C::ScalarField, + pub r_u: C::ScalarField, // blind } #[derive(Debug, Clone, Eq, PartialEq)]

@@ -32,30 +29,40 @@ pub struct Pedersen<C: CurveGroup, const H: bool = false> {

_c: PhantomData<C>, } -impl<C: CurveGroup, const H: bool> Pedersen<C, H> { - pub fn new_params<R: Rng>(rng: &mut R, max: usize) -> Params<C> { - let generators: Vec<C::Affine> = std::iter::repeat_with(|| C::Affine::rand(rng)) - .take(max.next_power_of_two()) +/// Implements the CommitmentScheme trait for Pedersen commitments +impl<C: CurveGroup, const H: bool> CommitmentScheme<C, H> for Pedersen<C, H> { + type ProverParams = Params<C>; + type VerifierParams = Params<C>; + type Proof = Proof<C>; + type ProverChallenge = (C::ScalarField, Vec<C::ScalarField>, C, C::ScalarField); + type Challenge = C::ScalarField; + + fn setup( + mut rng: impl RngCore, + len: usize, + ) -> Result<(Self::ProverParams, Self::VerifierParams), Error> { + let generators: Vec<C::Affine> = std::iter::repeat_with(|| C::Affine::rand(&mut rng)) + .take(len.next_power_of_two()) .collect(); - Params::<C> { - h: C::rand(rng), + let p = Params::<C> { + h: C::rand(&mut rng), generators, - } + }; + Ok((p.clone(), p)) } -} -// implement the CommitmentProver trait for Pedersen -impl<C: CurveGroup, const H: bool> CommitmentProver<C, H> for Pedersen<C, H> { - type Params = Params<C>; - type Proof = Proof<C>; fn commit( - params: &Self::Params, + params: &Self::ProverParams, v: &[C::ScalarField], r: &C::ScalarField, // blinding factor ) -> Result<C, Error> { if params.generators.len() < v.len() { return Err(Error::PedersenParamsLen(params.generators.len(), v.len())); } + if !H && (!r.is_zero()) { + return Err(Error::BlindingNotZero); + } + // h⋅r + <g, v> // use msm_unchecked because we already ensured at the if that lengths match if !H {

@@ -65,17 +72,13 @@ Ok(params.h.mul(r) + C::msm_unchecked(&params.generators[..v.len()], v))

} fn prove( - params: &Params<C>, + params: &Self::ProverParams, transcript: &mut impl Transcript<C>, cm: &C, v: &[C::ScalarField], r: &C::ScalarField, // blinding factor _rng: Option<&mut dyn RngCore>, ) -> Result<Self::Proof, Error> { - if params.generators.len() < v.len() { - return Err(Error::PedersenParamsLen(params.generators.len(), v.len())); - } - transcript.absorb_point(cm)?; let r1 = transcript.get_challenge(); let d = transcript.get_challenges(v.len());

@@ -90,6 +93,25 @@

transcript.absorb_point(&R)?; let e = transcript.get_challenge(); + let challenge = (r1, d, R, e); + Self::prove_with_challenge(params, challenge, v, r, _rng) + } + + fn prove_with_challenge( + params: &Self::ProverParams, + challenge: Self::ProverChallenge, + v: &[C::ScalarField], // vector + r: &C::ScalarField, // blinding factor + _rng: Option<&mut dyn RngCore>, + ) -> Result<Self::Proof, Error> { + if params.generators.len() < v.len() { + return Err(Error::PedersenParamsLen(params.generators.len(), v.len())); + } + if !H && (!r.is_zero()) { + return Err(Error::BlindingNotZero); + } + let (r1, d, R, e): (C::ScalarField, Vec<C::ScalarField>, C, C::ScalarField) = challenge; + // u = d + v⋅e let u = vec_add(&vec_scalar_mul(v, &e), &d)?; // r_u = e⋅r + r_1

@@ -100,14 +122,26 @@ }

Ok(Self::Proof { R, u, r_u }) } -} -impl<C: CurveGroup, const H: bool> Pedersen<C, H> { - pub fn verify( - params: &Params<C>, + fn verify( + params: &Self::VerifierParams, transcript: &mut impl Transcript<C>, - cm: C, - proof: Proof<C>, + cm: &C, + proof: &Proof<C>, + ) -> Result<(), Error> { + transcript.absorb_point(cm)?; + transcript.get_challenge(); // r_1 + transcript.get_challenges(proof.u.len()); // d + transcript.absorb_point(&proof.R)?; + let e = transcript.get_challenge(); + Self::verify_with_challenge(params, e, cm, proof) + } + + fn verify_with_challenge( + params: &Self::VerifierParams, + challenge: Self::Challenge, + cm: &C, + proof: &Proof<C>, ) -> Result<(), Error> { if params.generators.len() < proof.u.len() { return Err(Error::PedersenParamsLen(

@@ -115,12 +149,11 @@ params.generators.len(),

proof.u.len(), )); } + if !H && (!proof.r_u.is_zero()) { + return Err(Error::BlindingNotZero); + } - transcript.absorb_point(&cm)?; - transcript.get_challenge(); // r_1 - transcript.get_challenges(proof.u.len()); // d - transcript.absorb_point(&proof.R)?; - let e = transcript.get_challenge(); + let e = challenge; // check that: R + cm⋅e == h⋅r_u + <g, u> let lhs = proof.R + cm.mul(e);

@@ -186,12 +219,16 @@ use super::*;

use crate::transcript::poseidon::{poseidon_test_config, PoseidonTranscript}; #[test] - fn test_pedersen_vector() { + fn test_pedersen() { + test_pedersen_opt::<false>(); + test_pedersen_opt::<true>(); + } + fn test_pedersen_opt<const hiding: bool>() { let mut rng = ark_std::test_rng(); let n: usize = 10; // setup params - let params = Pedersen::<Projective>::new_params(&mut rng, n); + let (params, _) = Pedersen::<Projective>::setup(&mut rng, n).unwrap(); let poseidon_config = poseidon_test_config::<Fr>(); // init Prover's transcript

@@ -202,11 +239,17 @@

let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng)) .take(n) .collect(); - let r: Fr = Fr::rand(&mut rng); - let cm = Pedersen::<Projective>::commit(&params, &v, &r).unwrap(); + // blinding factor + let r: Fr = if hiding { + Fr::rand(&mut rng) + } else { + Fr::zero() + }; + let cm = Pedersen::<Projective, hiding>::commit(&params, &v, &r).unwrap(); let proof = - Pedersen::<Projective>::prove(&params, &mut transcript_p, &cm, &v, &r, None).unwrap(); - Pedersen::<Projective>::verify(&params, &mut transcript_v, cm, proof).unwrap(); + Pedersen::<Projective, hiding>::prove(&params, &mut transcript_p, &cm, &v, &r, None) + .unwrap(); + Pedersen::<Projective, hiding>::verify(&params, &mut transcript_v, &cm, &proof).unwrap(); } #[test]

@@ -217,15 +260,20 @@ }

fn test_pedersen_circuit_opt<const hiding: bool>() { let mut rng = ark_std::test_rng(); - let n: usize = 16; + let n: usize = 8; // setup params - let params = Pedersen::<Projective>::new_params(&mut rng, n); + let (params, _) = Pedersen::<Projective, hiding>::setup(&mut rng, n).unwrap(); let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng)) .take(n) .collect(); - let r: Fr = Fr::rand(&mut rng); - let cm = Pedersen::<Projective>::commit(&params, &v, &r).unwrap(); + // blinding factor + let r: Fr = if hiding { + Fr::rand(&mut rng) + } else { + Fr::zero() + }; + let cm = Pedersen::<Projective, hiding>::commit(&params, &v, &r).unwrap(); let v_bits: Vec<Vec<bool>> = v.iter().map(|val| val.into_bigint().to_bits_le()).collect(); let r_bits: Vec<bool> = r.into_bigint().to_bits_le();

@@ -246,7 +294,8 @@ let hVar = GVar::new_witness(cs.clone(), || Ok(params.h)).unwrap();

let expected_cmVar = GVar::new_witness(cs.clone(), || Ok(cm)).unwrap(); // use the gadget - let cmVar = PedersenGadget::<Projective, GVar>::commit(hVar, gVar, vVar, rVar).unwrap(); + let cmVar = + PedersenGadget::<Projective, GVar, hiding>::commit(hVar, gVar, vVar, rVar).unwrap(); cmVar.enforce_equal(&expected_cmVar).unwrap(); } }
M folding-schemes/src/folding/hypernova/cccs.rs → folding-schemes/src/folding/hypernova/cccs.rs

@@ -11,7 +11,7 @@ use super::utils::compute_sum_Mz;

use crate::ccs::CCS; use crate::commitment::{ pedersen::{Params as PedersenParams, Pedersen}, - CommitmentProver, + CommitmentScheme, }; use crate::utils::hypercube::BooleanHypercube; use crate::utils::mle::matrix_to_mle;

@@ -44,7 +44,7 @@ z: &[C::ScalarField],

) -> Result<(CCCS<C>, Witness<C::ScalarField>), Error> { let w: Vec<C::ScalarField> = z[(1 + self.l)..].to_vec(); let r_w = C::ScalarField::rand(rng); - let C = Pedersen::<C>::commit(pedersen_params, &w, &r_w)?; + let C = Pedersen::<C, true>::commit(pedersen_params, &w, &r_w)?; Ok(( CCCS::<C> {

@@ -112,7 +112,7 @@ w: &Witness<C::ScalarField>,

) -> Result<(), Error> { // check that C is the commitment of w. Notice that this is not verifying a Pedersen // opening, but checking that the commitment comes from committing to the witness. - if self.C != Pedersen::<C>::commit(pedersen_params, &w.w, &w.r_w)? { + if self.C != Pedersen::<C, true>::commit(pedersen_params, &w.w, &w.r_w)? { return Err(Error::NotSatisfied); }
M folding-schemes/src/folding/hypernova/circuit.rs → folding-schemes/src/folding/hypernova/circuit.rs

@@ -157,7 +157,7 @@ ccs::{

tests::{get_test_ccs, get_test_z}, CCS, }, - commitment::pedersen::Pedersen, + commitment::{pedersen::Pedersen, CommitmentScheme}, folding::hypernova::utils::{ compute_c_from_sigmas_and_thetas, compute_sigmas_and_thetas, sum_ci_mul_prod_thetaj, sum_muls_gamma_pows_eq_sigma,

@@ -180,7 +180,8 @@ let gamma: Fr = Fr::rand(&mut rng);

let r_x_prime: Vec<Fr> = (0..ccs.s).map(|_| Fr::rand(&mut rng)).collect(); // Initialize a multifolding object - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, ccs.n - ccs.l - 1); + let (pedersen_params, _) = + Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); let (lcccs_instance, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap(); let sigmas_thetas = compute_sigmas_and_thetas(&ccs, &[z1.clone()], &[z2.clone()], &r_x_prime);

@@ -224,7 +225,8 @@

let r_x_prime: Vec<Fr> = (0..ccs.s).map(|_| Fr::rand(&mut rng)).collect(); // Initialize a multifolding object - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, ccs.n - ccs.l - 1); + let (pedersen_params, _) = + Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); let (lcccs_instance, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap(); let sigmas_thetas = compute_sigmas_and_thetas(&ccs, &[z1.clone()], &[z2.clone()], &r_x_prime);

@@ -267,7 +269,8 @@ let beta: Vec<Fr> = (0..ccs.s).map(|_| Fr::rand(&mut rng)).collect();

let r_x_prime: Vec<Fr> = (0..ccs.s).map(|_| Fr::rand(&mut rng)).collect(); // Initialize a multifolding object - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, ccs.n - ccs.l - 1); + let (pedersen_params, _) = + Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); let (lcccs_instance, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap(); let sigmas_thetas = compute_sigmas_and_thetas(&ccs, &[z1.clone()], &[z2.clone()], &r_x_prime);
M folding-schemes/src/folding/hypernova/lcccs.rs → folding-schemes/src/folding/hypernova/lcccs.rs

@@ -10,7 +10,7 @@ use super::utils::{compute_all_sum_Mz_evals, compute_sum_Mz};

use crate::ccs::CCS; use crate::commitment::{ pedersen::{Params as PedersenParams, Pedersen}, - CommitmentProver, + CommitmentScheme, }; use crate::utils::mle::{matrix_to_mle, vec_to_mle}; use crate::utils::virtual_polynomial::VirtualPolynomial;

@@ -46,7 +46,7 @@ z: &[C::ScalarField],

) -> Result<(LCCCS<C>, Witness<C::ScalarField>), Error> { let w: Vec<C::ScalarField> = z[(1 + self.l)..].to_vec(); let r_w = C::ScalarField::rand(rng); - let C = Pedersen::<C>::commit(pedersen_params, &w, &r_w)?; + let C = Pedersen::<C, true>::commit(pedersen_params, &w, &r_w)?; let r_x: Vec<C::ScalarField> = (0..self.s).map(|_| C::ScalarField::rand(rng)).collect(); let v = self.compute_v_j(z, &r_x);

@@ -97,7 +97,7 @@ w: &Witness<C::ScalarField>,

) -> Result<(), Error> { // check that C is the commitment of w. Notice that this is not verifying a Pedersen // opening, but checking that the Commitment comes from committing to the witness. - if self.C != Pedersen::<C>::commit(pedersen_params, &w.w, &w.r_w)? { + if self.C != Pedersen::<C, true>::commit(pedersen_params, &w.w, &w.r_w)? { return Err(Error::NotSatisfied); }

@@ -131,7 +131,8 @@ let ccs = get_test_ccs();

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

@@ -161,7 +162,8 @@ let mut bad_z = z.clone();

bad_z[3] = Fr::zero(); assert!(ccs.check_relation(&bad_z.clone()).is_err()); - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, ccs.n - ccs.l - 1); + 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(&mut rng, &pedersen_params, &z).unwrap(); // with our test vector coming from R1CS, v should have length 3
M folding-schemes/src/folding/hypernova/nimfs.rs → folding-schemes/src/folding/hypernova/nimfs.rs

@@ -378,7 +378,7 @@ use crate::transcript::poseidon::PoseidonTranscript;

use ark_std::test_rng; use ark_std::UniformRand; - use crate::commitment::pedersen::Pedersen; + use crate::commitment::{pedersen::Pedersen, CommitmentScheme}; use ark_pallas::{Fr, Projective}; #[test]

@@ -395,7 +395,8 @@

let sigmas_thetas = compute_sigmas_and_thetas(&ccs, &[z1.clone()], &[z2.clone()], &r_x_prime); - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, ccs.n - ccs.l - 1); + let (pedersen_params, _) = + Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); let (lcccs, w1) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap(); let (cccs, w2) = ccs.to_cccs(&mut rng, &pedersen_params, &z2).unwrap();

@@ -430,7 +431,8 @@ let mut rng = test_rng();

// Create a basic CCS circuit let ccs = get_test_ccs::<Projective>(); - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, ccs.n - ccs.l - 1); + let (pedersen_params, _) = + Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); // Generate a satisfying witness let z_1 = get_test_z(3);

@@ -489,7 +491,8 @@ let mut rng = test_rng();

let ccs = get_test_ccs::<Projective>(); - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, ccs.n - ccs.l - 1); + let (pedersen_params, _) = + Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); // LCCCS witness let z_1 = get_test_z(2);

@@ -557,7 +560,8 @@ let mut rng = test_rng();

// Create a basic CCS circuit let ccs = get_test_ccs::<Projective>(); - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, ccs.n - ccs.l - 1); + let (pedersen_params, _) = + Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); let mu = 10; let nu = 15;

@@ -639,7 +643,8 @@ let mut rng = test_rng();

// Create a basic CCS circuit let ccs = get_test_ccs::<Projective>(); - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, ccs.n - ccs.l - 1); + let (pedersen_params, _) = + Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); let poseidon_config = poseidon_test_config::<Fr>(); // Prover's transcript
M folding-schemes/src/folding/hypernova/utils.rs → folding-schemes/src/folding/hypernova/utils.rs

@@ -199,7 +199,7 @@ use ark_std::UniformRand;

use ark_std::Zero; use crate::ccs::tests::{get_test_ccs, get_test_z}; - use crate::commitment::pedersen::Pedersen; + use crate::commitment::{pedersen::Pedersen, CommitmentScheme}; use crate::utils::multilinear_polynomial::tests::fix_last_variables; use crate::utils::virtual_polynomial::eq_eval;

@@ -290,7 +290,8 @@ let beta: Vec<Fr> = (0..ccs.s).map(|_| Fr::rand(&mut rng)).collect();

let r_x_prime: Vec<Fr> = (0..ccs.s).map(|_| Fr::rand(&mut rng)).collect(); // Initialize a multifolding object - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, ccs.n - ccs.l - 1); + let (pedersen_params, _) = + Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); let (lcccs_instance, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap(); let sigmas_thetas =

@@ -333,7 +334,8 @@ let gamma: Fr = Fr::rand(&mut rng);

let beta: Vec<Fr> = (0..ccs.s).map(|_| Fr::rand(&mut rng)).collect(); // Initialize a multifolding object - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, ccs.n - ccs.l - 1); + let (pedersen_params, _) = + Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); let (lcccs_instance, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap(); let mut sum_v_j_gamma = Fr::zero();
M folding-schemes/src/folding/nova/circuits.rs → folding-schemes/src/folding/nova/circuits.rs

@@ -509,8 +509,8 @@

#[cfg(test)] pub mod tests { use super::*; + use ark_bn254::{Fr, G1Projective as Projective}; use ark_ff::BigInteger; - use ark_pallas::{Fr, Projective}; use ark_r1cs_std::{alloc::AllocVar, R1CSVar}; use ark_relations::r1cs::ConstraintSystem; use ark_std::UniformRand;
M folding-schemes/src/folding/nova/cyclefold.rs → folding-schemes/src/folding/nova/cyclefold.rs

@@ -175,7 +175,7 @@ .and(&fourth_check)

} } -/// ChallengeGadget computes the RO challenge used for the CycleFold instances NIFS, it contains a +/// CycleFoldChallengeGadget computes the RO challenge used for the CycleFold instances NIFS, it contains a /// rust-native and a in-circuit compatible versions. pub struct CycleFoldChallengeGadget<C: CurveGroup, GC: CurveVar<C, CF2<C>>> { _c: PhantomData<C>, // Nova's Curve2, the one used for the CycleFold circuit

@@ -252,19 +252,15 @@ .flat_map(|e| e.to_bytes().unwrap_or(vec![]))

.collect::<Vec<UInt8<CF2<C>>>>(); let input: Vec<UInt8<CF2<C>>> = [ - U_i.cmE.to_bytes()?, + pointvar_to_bytes(U_i.cmE)?, U_i.u.to_bytes()?, - U_i.cmW.to_bytes()?, + pointvar_to_bytes(U_i.cmW)?, U_i_x_bytes, - u_i.cmE.to_bytes()?, + pointvar_to_bytes(u_i.cmE)?, u_i.u.to_bytes()?, - u_i.cmW.to_bytes()?, + pointvar_to_bytes(u_i.cmW)?, u_i_x_bytes, - cmT.to_bytes()?, - // TODO instead of bytes, use field elements, but needs x,y coordinates from - // u_i.{cmE,cmW}, U_i.{cmE,cmW}, cmT. Depends exposing x,y coordinates of GC. Issue to - // keep track of this: - // https://github.com/privacy-scaling-explorations/folding-schemes/issues/44 + pointvar_to_bytes(cmT)?, ] .concat(); sponge.absorb(&input)?;

@@ -273,17 +269,26 @@ Ok(bits)

} } -/// returns the bytes being compatible with the ark_r1cs_std `.to_bytes` approach +/// returns the bytes being compatible with the pointvar_to_bytes method. +/// These methods are temporary once arkworks has the fix to prevent different to_bytes behaviour +/// across different curves. Eg, in pasta and bn254: pasta returns 65 bytes both native and gadget, +/// whereas bn254 returns 64 bytes native and 65 in gadget, also the penultimate byte is different +/// natively than in gadget. fn point_to_bytes<C: CurveGroup>(p: C) -> Result<Vec<u8>, Error> { let l = p.uncompressed_size(); let mut b = Vec::new(); p.serialize_uncompressed(&mut b)?; - b[l - 1] = 0; if p.is_zero() { b[l / 2] = 1; b[l - 1] = 1; } - Ok(b) + Ok(b[..63].to_vec()) +} +fn pointvar_to_bytes<C: CurveGroup, GC: CurveVar<C, CF2<C>>>( + p: GC, +) -> Result<Vec<UInt8<CF2<C>>>, SynthesisError> { + let b = p.to_bytes()?; + Ok(b[..63].to_vec()) } /// CycleFoldCircuit contains the constraints that check the correct fold of the committed

@@ -350,8 +355,8 @@

#[cfg(test)] pub mod tests { use super::*; + use ark_bn254::{constraints::GVar, Fq, Fr, G1Projective as Projective}; use ark_ff::BigInteger; - use ark_pallas::{constraints::GVar, Fq, Fr, Projective}; use ark_r1cs_std::{alloc::AllocVar, R1CSVar}; use ark_relations::r1cs::ConstraintSystem; use ark_std::UniformRand;

@@ -466,6 +471,21 @@ )

.unwrap(); nifs_check.enforce_equal(&Boolean::<Fq>::TRUE).unwrap(); assert!(cs.is_satisfied().unwrap()); + } + + #[test] + fn test_point_bytes() { + let mut rng = ark_std::test_rng(); + + let p = Projective::rand(&mut rng); + let p_bytes = point_to_bytes(p).unwrap(); + + let cs = ConstraintSystem::<Fq>::new_ref(); + let pVar = GVar::new_witness(cs.clone(), || Ok(p)).unwrap(); + assert_eq!(pVar.value().unwrap(), p); + + let p_bytesVar = &pointvar_to_bytes(pVar).unwrap(); + assert_eq!(p_bytesVar.value().unwrap(), p_bytes); } #[test]
M folding-schemes/src/folding/nova/decider_eth.rs → folding-schemes/src/folding/nova/decider_eth.rs

@@ -1,47 +1,76 @@

/// This file implements the onchain (Ethereum's EVM) decider. -use ark_crypto_primitives::sponge::Absorb; +use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; use ark_ec::{CurveGroup, Group}; -use ark_ff::PrimeField; +use ark_ff::{BigInteger, PrimeField}; +use ark_r1cs_std::fields::nonnative::params::OptimizationType; use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar}; use ark_snark::SNARK; -use ark_std::rand::CryptoRng; -use ark_std::rand::RngCore; +use ark_std::rand::{CryptoRng, RngCore}; +use ark_std::Zero; use core::marker::PhantomData; -pub use super::decider_eth_circuit::DeciderEthCircuit; -use crate::commitment::{pedersen::Params as PedersenParams, CommitmentProver}; +pub use super::decider_eth_circuit::{DeciderEthCircuit, KZGChallengesGadget}; +use super::{ + circuits::{ChallengeGadget, CF2}, + nifs::NIFS, + CommittedInstance, Nova, Witness, +}; +use crate::commitment::{ + kzg::Proof as KZGProof, pedersen::Params as PedersenParams, CommitmentScheme, +}; use crate::folding::circuits::nonnative::point_to_nonnative_limbs_custom_opt; -use crate::folding::nova::{circuits::CF2, CommittedInstance, Nova}; use crate::frontend::FCircuit; use crate::Error; use crate::{Decider as DeciderTrait, FoldingScheme}; -use ark_r1cs_std::fields::nonnative::params::OptimizationType; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Proof<C1, CS1, S> +where + C1: CurveGroup, + CS1: CommitmentScheme<C1, ProverChallenge = C1::ScalarField, Challenge = C1::ScalarField>, + S: SNARK<C1::ScalarField>, +{ + snark_proof: S::Proof, + kzg_proofs: [CS1::Proof; 2], + // cmT and r are values for the last fold, U_{i+1}=NIFS.V(r, U_i, u_i, cmT), and they are + // checked in-circuit + cmT: C1, + r: C1::ScalarField, + // the KZG challenges are provided by the prover, but in-circuit they are checked to match + // the in-circuit computed computed ones. + kzg_challenges: [C1::ScalarField; 2], +} /// Onchain Decider, for ethereum use cases #[derive(Clone, Debug)] -pub struct Decider<C1, GC1, C2, GC2, FC, CP1, CP2, S, FS> { +pub struct Decider<C1, GC1, C2, GC2, FC, CS1, CS2, S, FS> { _c1: PhantomData<C1>, _gc1: PhantomData<GC1>, _c2: PhantomData<C2>, _gc2: PhantomData<GC2>, _fc: PhantomData<FC>, - _cp1: PhantomData<CP1>, - _cp2: PhantomData<CP2>, + _cs1: PhantomData<CS1>, + _cs2: PhantomData<CS2>, _s: PhantomData<S>, _fs: PhantomData<FS>, } -impl<C1, GC1, C2, GC2, FC, CP1, CP2, S, FS> DeciderTrait<C1, C2, FC, FS> - for Decider<C1, GC1, C2, GC2, FC, CP1, CP2, S, FS> +impl<C1, GC1, C2, GC2, FC, CS1, CS2, S, FS> DeciderTrait<C1, C2, FC, FS> + for Decider<C1, GC1, C2, GC2, FC, CS1, CS2, S, FS> where C1: CurveGroup, C2: CurveGroup, GC1: CurveVar<C1, CF2<C1>>, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<C1::ScalarField>, - CP1: CommitmentProver<C1>, - // enforce that the CP2 is Pedersen commitment, since we're at Ethereum's EVM decider - CP2: CommitmentProver<C2, Params = PedersenParams<C2>>, + CS1: CommitmentScheme< + C1, + ProverChallenge = C1::ScalarField, + Challenge = C1::ScalarField, + Proof = KZGProof<C1>, + >, // KZG commitment, where challenge is C1::Fr elem + // enforce that the CS2 is Pedersen commitment scheme, since we're at Ethereum's EVM decider + CS2: CommitmentScheme<C2, ProverParams = PedersenParams<C2>>, S: SNARK<C1::ScalarField>, FS: FoldingScheme<C1, C2, FC>, <C1 as CurveGroup>::BaseField: PrimeField,

@@ -51,84 +80,192 @@ <C2 as Group>::ScalarField: Absorb,

C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>, 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, CP1, CP2>: From<FS>, + Nova<C1, GC1, C2, GC2, FC, CS1, CS2>: From<FS>, { - type ProverParam = S::ProvingKey; - type Proof = S::Proof; - type VerifierParam = S::VerifyingKey; + type ProverParam = ( + PoseidonConfig<C1::ScalarField>, + S::ProvingKey, + CS1::ProverParams, + ); + type Proof = Proof<C1, CS1, S>; + type VerifierParam = (S::VerifyingKey, CS1::VerifierParams); type PublicInput = Vec<C1::ScalarField>; type CommittedInstanceWithWitness = (); type CommittedInstance = CommittedInstance<C1>; fn prove( - pp: &Self::ProverParam, + pp: Self::ProverParam, mut rng: impl RngCore + CryptoRng, folding_scheme: FS, ) -> Result<Self::Proof, Error> { - let circuit = - DeciderEthCircuit::<C1, GC1, C2, GC2, CP1, CP2>::from_nova::<FC>(folding_scheme.into()); + let (poseidon_config, snark_pk, cs_pk): ( + PoseidonConfig<C1::ScalarField>, + S::ProvingKey, + CS1::ProverParams, + ) = pp; - S::prove(pp, circuit.clone(), &mut rng).map_err(|e| Error::Other(e.to_string())) + let circuit = DeciderEthCircuit::<C1, GC1, C2, GC2, CS1, CS2>::from_nova::<FC>( + folding_scheme.into(), + )?; + + let snark_proof = S::prove(&snark_pk, circuit.clone(), &mut rng) + .map_err(|e| Error::Other(e.to_string()))?; + + let U_i = circuit + .U_i + .clone() + .ok_or(Error::MissingValue("U_i".to_string()))?; + let W_i = circuit + .W_i + .clone() + .ok_or(Error::MissingValue("W_i".to_string()))?; + let u_i = circuit + .u_i + .clone() + .ok_or(Error::MissingValue("u_i".to_string()))?; + let w_i = circuit + .w_i + .clone() + .ok_or(Error::MissingValue("w_i".to_string()))?; + + // compute NIFS.P((U_d, W_d), (u_d, w_d)) = (U_{d+1}, W_{d+1}, cmT) + let (T, cmT) = NIFS::<C1, CS1>::compute_cmT(&cs_pk, &circuit.r1cs, &w_i, &u_i, &W_i, &U_i)?; + let r_bits = ChallengeGadget::<C1>::get_challenge_native( + &poseidon_config, + U_i.clone(), + u_i.clone(), + cmT, + )?; + let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)) + .ok_or(Error::OutOfBounds)?; + let (W_i1, _): (Witness<C1>, CommittedInstance<C1>) = + NIFS::<C1, CS1>::fold_instances(r_Fr, &W_i, &U_i, &w_i, &u_i, &T, cmT)?; + + // get the challenges that have been already computed when preparing the circuit inputs in + // the above `from_nova` call + let challenge_W = circuit + .kzg_c_W + .ok_or(Error::MissingValue("kzg_c_W".to_string()))?; + let challenge_E = circuit + .kzg_c_E + .ok_or(Error::MissingValue("kzg_c_E".to_string()))?; + + // generate KZG proofs + let U_cmW_proof = CS1::prove_with_challenge( + &cs_pk, + challenge_W, + &W_i1.W, + &C1::ScalarField::zero(), + None, + )?; + let U_cmE_proof = CS1::prove_with_challenge( + &cs_pk, + challenge_E, + &W_i1.E, + &C1::ScalarField::zero(), + None, + )?; + + Ok(Self::Proof { + snark_proof, + kzg_proofs: [U_cmW_proof, U_cmE_proof], + cmT, + r: r_Fr, + kzg_challenges: [challenge_W, challenge_E], + }) } fn verify( - vp: &Self::VerifierParam, + vp: Self::VerifierParam, i: C1::ScalarField, z_0: Vec<C1::ScalarField>, z_i: Vec<C1::ScalarField>, running_instance: &Self::CommittedInstance, + incoming_instance: &Self::CommittedInstance, proof: Self::Proof, ) -> Result<bool, Error> { - let (cmE_x, cmE_y) = point_to_nonnative_limbs_custom_opt::<C1>( - running_instance.cmE, - OptimizationType::Constraints, - )?; - let (cmW_x, cmW_y) = point_to_nonnative_limbs_custom_opt::<C1>( - running_instance.cmW, - OptimizationType::Constraints, - )?; + let (snark_vk, cs_vk): (S::VerifyingKey, CS1::VerifierParams) = vp; + + // compute U = U_{d+1}= NIFS.V(U_d, u_d, cmT) + let U = NIFS::<C1, CS1>::verify(proof.r, running_instance, incoming_instance, &proof.cmT); + + let (cmE_x, cmE_y) = + point_to_nonnative_limbs_custom_opt::<C1>(U.cmE, OptimizationType::Constraints)?; + let (cmW_x, cmW_y) = + point_to_nonnative_limbs_custom_opt::<C1>(U.cmW, OptimizationType::Constraints)?; + let (cmT_x, cmT_y) = + point_to_nonnative_limbs_custom_opt::<C1>(proof.cmT, OptimizationType::Constraints)?; + let public_input: Vec<C1::ScalarField> = vec![ vec![i], z_0, z_i, - vec![running_instance.u], - running_instance.x.clone(), + vec![U.u], + U.x.clone(), cmE_x, cmE_y, cmW_x, cmW_y, + proof.kzg_challenges.to_vec(), + vec![ + proof.kzg_proofs[0].eval, // eval_W + proof.kzg_proofs[1].eval, // eval_E + ], + cmT_x, + cmT_y, + vec![proof.r], ] .concat(); - S::verify(vp, &public_input, &proof).map_err(|e| Error::Other(e.to_string())) + + let snark_v = S::verify(&snark_vk, &public_input, &proof.snark_proof) + .map_err(|e| Error::Other(e.to_string()))?; + if !snark_v { + return Err(Error::SNARKVerificationFail); + } + + // we're at the Ethereum EVM case, so the CS1 is KZG commitments + CS1::verify_with_challenge( + &cs_vk, + proof.kzg_challenges[0], + &U.cmW, + &proof.kzg_proofs[0], + )?; + CS1::verify_with_challenge( + &cs_vk, + proof.kzg_challenges[1], + &U.cmE, + &proof.kzg_proofs[1], + )?; + + Ok(true) } } #[cfg(test)] pub mod tests { use super::*; + use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; use ark_groth16::Groth16; - use ark_mnt4_298::{constraints::G1Var as GVar, Fr, G1Projective as Projective, MNT4_298}; - use ark_mnt6_298::{constraints::G1Var as GVar2, G1Projective as Projective2}; + use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; + use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey; use std::time::Instant; + use crate::commitment::kzg::{ProverKey as KZGProverKey, KZG}; use crate::commitment::pedersen::Pedersen; - use crate::folding::nova::{get_pedersen_params_len, ProverParams}; + use crate::folding::nova::{get_cs_params_len, ProverParams}; use crate::frontend::tests::CubicFCircuit; use crate::transcript::poseidon::poseidon_test_config; - // Note: since we're testing a big circuit, this test takes a bit more of computation and time, - // do not run in the normal CI. - // To run the test use `--ignored` flag, eg. `cargo test -- --ignored` #[test] - #[ignore] fn test_decider() { + // use Nova as FoldingScheme type NOVA = Nova< Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, - Pedersen<Projective>, + KZG<'static, Bn254>, Pedersen<Projective2>, >; type DECIDER = Decider<

@@ -137,10 +274,10 @@ GVar,

Projective2, GVar2, CubicFCircuit<Fr>, - Pedersen<Projective>, + KZG<'static, Bn254>, Pedersen<Projective2>, - Groth16<MNT4_298>, // here we define the Snark to use in the decider - NOVA, // here we define the FoldingScheme to use + Groth16<Bn254>, // here we define the Snark to use in the decider + NOVA, // here we define the FoldingScheme to use >; let mut rng = ark_std::test_rng();

@@ -149,31 +286,32 @@

let F_circuit = CubicFCircuit::<Fr>::new(()); let z_0 = vec![Fr::from(3_u32)]; - let (cm_len, cf_cm_len) = - get_pedersen_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>( + let (cs_len, cf_cs_len) = + get_cs_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>( &poseidon_config, F_circuit, ) .unwrap(); - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, cm_len); - let cf_pedersen_params = Pedersen::<Projective2>::new_params(&mut rng, cf_cm_len); - let start = Instant::now(); + let (kzg_pk, kzg_vk): (KZGProverKey<Projective>, KZGVerifierKey<Bn254>) = + KZG::<Bn254>::setup(&mut rng, cs_len).unwrap(); + let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cs_len).unwrap(); + println!("generated KZG params, {:?}", start.elapsed()); + let prover_params = - ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> { + ProverParams::<Projective, Projective2, KZG<Bn254>, Pedersen<Projective2>> { poseidon_config: poseidon_config.clone(), - cm_params: pedersen_params, - cf_cm_params: cf_pedersen_params, + cs_params: kzg_pk.clone(), + cf_cs_params: cf_pedersen_params, }; - println!("generating pedersen params, {:?}", start.elapsed()); - // use Nova as FoldingScheme let start = Instant::now(); let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap(); println!("Nova initialized, {:?}", start.elapsed()); let start = Instant::now(); nova.prove_step().unwrap(); println!("prove_step, {:?}", start.elapsed()); + nova.prove_step().unwrap(); // do a 2nd step // generate Groth16 setup let circuit = DeciderEthCircuit::<

@@ -181,23 +319,31 @@ Projective,

GVar, Projective2, GVar2, - Pedersen<Projective>, + KZG<Bn254>, Pedersen<Projective2>, - >::from_nova::<CubicFCircuit<Fr>>(nova.clone()); + >::from_nova::<CubicFCircuit<Fr>>(nova.clone()) + .unwrap(); let mut rng = rand::rngs::OsRng; let start = Instant::now(); - let (pk, vk) = - Groth16::<MNT4_298>::circuit_specific_setup(circuit.clone(), &mut rng).unwrap(); + let (g16_pk, g16_vk) = + Groth16::<Bn254>::circuit_specific_setup(circuit.clone(), &mut rng).unwrap(); println!("Groth16 setup, {:?}", start.elapsed()); // decider proof generation let start = Instant::now(); - let proof = DECIDER::prove(&pk, rng, nova.clone()).unwrap(); - println!("Decider Groth16 prove, {:?}", start.elapsed()); + let decider_pp = (poseidon_config.clone(), g16_pk, kzg_pk); + let proof = DECIDER::prove(decider_pp, rng, nova.clone()).unwrap(); + println!("Decider prove, {:?}", start.elapsed()); // decider proof verification - let verified = DECIDER::verify(&vk, nova.i, nova.z_0, nova.z_i, &nova.U_i, proof).unwrap(); + let start = Instant::now(); + let decider_vp = (g16_vk, kzg_vk); + let verified = DECIDER::verify( + decider_vp, nova.i, nova.z_0, nova.z_i, &nova.U_i, &nova.u_i, proof, + ) + .unwrap(); assert!(verified); + println!("Decider verify, {:?}", start.elapsed()); } }
M folding-schemes/src/folding/nova/decider_eth_circuit.rs → folding-schemes/src/folding/nova/decider_eth_circuit.rs

@@ -3,29 +3,40 @@ /// other more efficient approaches can be used.

use ark_crypto_primitives::crh::poseidon::constraints::CRHParametersVar; use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; use ark_ec::{CurveGroup, Group}; -use ark_ff::PrimeField; +use ark_ff::{BigInteger, PrimeField}; +use ark_poly::Polynomial; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, boolean::Boolean, eq::EqGadget, fields::{fp::FpVar, nonnative::NonNativeFieldVar, FieldVar}, groups::GroupOpsBounds, + poly::{domain::Radix2DomainVar, evaluations::univariate::EvaluationsVar}, prelude::CurveVar, + ToConstraintFieldGadget, }; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError}; -use ark_std::{One, Zero}; +use ark_std::{log2, One, Zero}; use core::{borrow::Borrow, marker::PhantomData}; +use super::{circuits::ChallengeGadget, nifs::NIFS}; use crate::ccs::r1cs::R1CS; -use crate::commitment::{pedersen::Params as PedersenParams, CommitmentProver}; +use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme}; +use crate::folding::circuits::nonnative::{point_to_nonnative_limbs, NonNativeAffineVar}; use crate::folding::nova::{ circuits::{CommittedInstanceVar, CF1, CF2}, CommittedInstance, Nova, Witness, }; use crate::frontend::FCircuit; -use crate::utils::gadgets::{ - hadamard, mat_vec_mul_sparse, vec_add, vec_scalar_mul, SparseMatrixVar, +use crate::transcript::{ + poseidon::{PoseidonTranscript, PoseidonTranscriptVar}, + Transcript, TranscriptVar, }; +use crate::utils::{ + gadgets::{hadamard, mat_vec_mul_sparse, vec_add, vec_scalar_mul, SparseMatrixVar}, + vec::poly_from_vec, +}; +use crate::Error; #[derive(Debug, Clone)] pub struct RelaxedR1CSGadget<F: PrimeField, CF: PrimeField, FV: FieldVar<F, CF>> {

@@ -178,21 +189,21 @@

/// 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, CP1, CP2> +pub struct DeciderEthCircuit<C1, GC1, C2, GC2, CS1, CS2> where C1: CurveGroup, GC1: CurveVar<C1, CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, - CP1: CommitmentProver<C1>, - CP2: CommitmentProver<C2>, + CS1: CommitmentScheme<C1>, + CS2: CommitmentScheme<C2>, { _c1: PhantomData<C1>, _gc1: PhantomData<GC1>, _c2: PhantomData<C2>, _gc2: PhantomData<GC2>, - _cp1: PhantomData<CP1>, - _cp2: PhantomData<CP2>, + _cs1: PhantomData<CS1>, + _cs2: PhantomData<CS2>, /// E vector's length of the Nova instance witness pub E_len: usize,

@@ -215,36 +226,89 @@ pub u_i: Option<CommittedInstance<C1>>,

pub w_i: Option<Witness<C1>>, pub U_i: Option<CommittedInstance<C1>>, pub W_i: Option<Witness<C1>>, + pub U_i1: Option<CommittedInstance<C1>>, + pub W_i1: Option<Witness<C1>>, + pub cmT: Option<C1>, + pub r: Option<C1::ScalarField>, /// CycleFold running instance pub cf_U_i: Option<CommittedInstance<C2>>, pub cf_W_i: Option<Witness<C2>>, + + /// KZG challenges + pub kzg_c_W: Option<C1::ScalarField>, + pub kzg_c_E: Option<C1::ScalarField>, + pub eval_W: Option<C1::ScalarField>, + pub eval_E: Option<C1::ScalarField>, } -impl<C1, GC1, C2, GC2, CP1, CP2> DeciderEthCircuit<C1, GC1, C2, GC2, CP1, CP2> +impl<C1, GC1, C2, GC2, CS1, CS2> DeciderEthCircuit<C1, GC1, C2, GC2, CS1, CS2> where C1: CurveGroup, C2: CurveGroup, GC1: CurveVar<C1, CF2<C1>>, GC2: CurveVar<C2, CF2<C2>>, - CP1: CommitmentProver<C1>, - // enforce that the CP2 is Pedersen commitment, since we're at Ethereum's EVM decider - CP2: CommitmentProver<C2, Params = PedersenParams<C2>>, + CS1: CommitmentScheme<C1>, + // enforce that the CS2 is Pedersen commitment scheme, since we're at Ethereum's EVM decider + CS2: CommitmentScheme<C2, 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, CP1, CP2>, - ) -> Self { - Self { + nova: Nova<C1, GC1, C2, GC2, FC, CS1, CS2>, + ) -> Result<Self, Error> { + // compute the U_{i+1}, W_{i+1} + let (T, cmT) = NIFS::<C1, CS1>::compute_cmT( + &nova.cs_params, + &nova.r1cs.clone(), + &nova.w_i.clone(), + &nova.u_i.clone(), + &nova.W_i.clone(), + &nova.U_i.clone(), + )?; + let r_bits = ChallengeGadget::<C1>::get_challenge_native( + &nova.poseidon_config, + nova.U_i.clone(), + nova.u_i.clone(), + 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( + r_Fr, &nova.W_i, &nova.U_i, &nova.w_i, &nova.u_i, &T, cmT, + )?; + + // compute the KZG challenges used as inputs in the circuit + let (kzg_challenge_W, kzg_challenge_E) = + KZGChallengesGadget::<C1>::get_challenges_native(&nova.poseidon_config, U_i1.clone())?; + + // get KZG evals + let mut W = W_i1.W.clone(); + W.extend( + std::iter::repeat(C1::ScalarField::zero()) + .take(W_i1.W.len().next_power_of_two() - W_i1.W.len()), + ); + let mut E = W_i1.E.clone(); + E.extend( + std::iter::repeat(C1::ScalarField::zero()) + .take(W_i1.E.len().next_power_of_two() - W_i1.E.len()), + ); + let p_W = poly_from_vec(W.to_vec())?; + let eval_W = p_W.evaluate(&kzg_challenge_W); + let p_E = poly_from_vec(E.to_vec())?; + let eval_E = p_E.evaluate(&kzg_challenge_E); + + Ok(Self { _c1: PhantomData, _gc1: PhantomData, _c2: PhantomData, _gc2: PhantomData, - _cp1: PhantomData, - _cp2: PhantomData, + _cs1: PhantomData, + _cs2: PhantomData, E_len: nova.W_i.E.len(), cf_E_len: nova.cf_W_i.E.len(), r1cs: nova.r1cs, cf_r1cs: nova.cf_r1cs, - cf_pedersen_params: nova.cf_cm_params, + cf_pedersen_params: nova.cf_cs_params, poseidon_config: nova.poseidon_config, i: Some(nova.i), z_0: Some(nova.z_0),

@@ -253,21 +317,29 @@ u_i: Some(nova.u_i),

w_i: Some(nova.w_i), U_i: Some(nova.U_i), W_i: Some(nova.W_i), + U_i1: Some(U_i1), + W_i1: Some(W_i1), + cmT: Some(cmT), + r: Some(r_Fr), cf_U_i: Some(nova.cf_U_i), cf_W_i: Some(nova.cf_W_i), - } + kzg_c_W: Some(kzg_challenge_W), + kzg_c_E: Some(kzg_challenge_E), + eval_W: Some(eval_W), + eval_E: Some(eval_E), + }) } } -impl<C1, GC1, C2, GC2, CP1, CP2> ConstraintSynthesizer<CF1<C1>> - for DeciderEthCircuit<C1, GC1, C2, GC2, CP1, CP2> +impl<C1, GC1, C2, GC2, CS1, CS2> ConstraintSynthesizer<CF1<C1>> + for DeciderEthCircuit<C1, GC1, C2, GC2, CS1, CS2> where C1: CurveGroup, C2: CurveGroup, GC1: CurveVar<C1, CF2<C1>>, GC2: CurveVar<C2, CF2<C2>>, - CP1: CommitmentProver<C1>, - CP2: CommitmentProver<C2>, + CS1: CommitmentScheme<C1>, + CS2: CommitmentScheme<C2>, <C1 as CurveGroup>::BaseField: PrimeField, <C2 as CurveGroup>::BaseField: PrimeField, <C1 as Group>::ScalarField: Absorb,

@@ -299,14 +371,29 @@

let u_i = CommittedInstanceVar::<C1>::new_witness(cs.clone(), || { Ok(self.u_i.unwrap_or(u_dummy_native.clone())) })?; - let w_i = WitnessVar::<C1>::new_witness(cs.clone(), || { - Ok(self.w_i.unwrap_or(w_dummy_native.clone())) - })?; - let U_i = CommittedInstanceVar::<C1>::new_input(cs.clone(), || { + let U_i = CommittedInstanceVar::<C1>::new_witness(cs.clone(), || { Ok(self.U_i.unwrap_or(u_dummy_native.clone())) })?; - let W_i = WitnessVar::<C1>::new_witness(cs.clone(), || { - Ok(self.W_i.unwrap_or(w_dummy_native.clone())) + // here (U_i1, W_i1) = NIFS.P( (U_i,W_i), (u_i,w_i)) + let U_i1 = CommittedInstanceVar::<C1>::new_input(cs.clone(), || { + Ok(self.U_i1.unwrap_or(u_dummy_native.clone())) + })?; + let W_i1 = WitnessVar::<C1>::new_witness(cs.clone(), || { + Ok(self.W_i1.unwrap_or(w_dummy_native.clone())) + })?; + + // allocate the inputs for the check 6 + let kzg_c_W = FpVar::<CF1<C1>>::new_input(cs.clone(), || { + Ok(self.kzg_c_W.unwrap_or_else(CF1::<C1>::zero)) + })?; + let kzg_c_E = FpVar::<CF1<C1>>::new_input(cs.clone(), || { + Ok(self.kzg_c_E.unwrap_or_else(CF1::<C1>::zero)) + })?; + let _eval_W = FpVar::<CF1<C1>>::new_input(cs.clone(), || { + Ok(self.eval_W.unwrap_or_else(CF1::<C1>::zero)) + })?; + let _eval_E = FpVar::<CF1<C1>>::new_input(cs.clone(), || { + Ok(self.eval_E.unwrap_or_else(CF1::<C1>::zero)) })?; let crh_params = CRHParametersVar::<C1::ScalarField>::new_constant(

@@ -314,31 +401,17 @@ cs.clone(),

self.poseidon_config.clone(), )?; - // 1. check RelaxedR1CS of u_i - let z_u: Vec<FpVar<CF1<C1>>> = [ - vec![FpVar::<CF1<C1>>::one()], - u_i.x.to_vec(), - w_i.W.to_vec(), - ] - .concat(); - RelaxedR1CSGadget::<C1::ScalarField, CF1<C1>, FpVar<CF1<C1>>>::check( - r1cs.clone(), - w_i.E, - u_i.u.clone(), - z_u, - )?; - - // 2. check RelaxedR1CS of U_i - let z_U: Vec<FpVar<CF1<C1>>> = - [vec![U_i.u.clone()], U_i.x.to_vec(), W_i.W.to_vec()].concat(); + // 1. check RelaxedR1CS of U_{i+1} + let z_U1: Vec<FpVar<CF1<C1>>> = + [vec![U_i1.u.clone()], U_i1.x.to_vec(), W_i1.W.to_vec()].concat(); RelaxedR1CSGadget::<C1::ScalarField, CF1<C1>, FpVar<CF1<C1>>>::check( r1cs, - W_i.E, - U_i.u.clone(), - z_U, + W_i1.E.clone(), + U_i1.u.clone(), + z_U1, )?; - // 3. u_i.cmE==cm(0), u_i.u==1 + // 2. u_i.cmE==cm(0), u_i.u==1 // Here zero_x & zero_y are the x & y coordinates of the zero point affine representation. let zero_x = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant( cs.clone(),

@@ -348,19 +421,19 @@ let zero_y = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant(

cs.clone(), C1::BaseField::one(), )?; - (u_i.cmE.x.is_eq(&zero_x)?).enforce_equal(&Boolean::TRUE)?; - (u_i.cmE.y.is_eq(&zero_y)?).enforce_equal(&Boolean::TRUE)?; + u_i.cmE.x.enforce_equal(&zero_x)?; + u_i.cmE.y.enforce_equal(&zero_y)?; (u_i.u.is_one()?).enforce_equal(&Boolean::TRUE)?; - // 4. u_i.x == H(i, z_0, z_i, U_i) - let (u_i_x, _) = U_i - .clone() - .hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?; + // 3. u_i.x == H(i, z_0, z_i, U_i) + let (u_i_x, U_i_vec) = + U_i.clone() + .hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?; (u_i.x[0]).enforce_equal(&u_i_x)?; // The following two checks (and their respective allocations) are disabled for normal - // tests since they take ~24.5M constraints and would take several minutes (and RAM) to run - // the test + // tests since they take several millions of constraints and would take several minutes + // (and RAM) to run the test. #[cfg(not(test))] { // imports here instead of at the top of the file, so we avoid having multiple

@@ -381,7 +454,7 @@ let cf_W_i = CycleFoldWitnessVar::<C2>::new_witness(cs.clone(), || {

Ok(self.cf_W_i.unwrap_or(w_dummy_native.clone())) })?; - // 5. check Pedersen commitments of cf_U_i.{cmE, cmW} + // 4. check Pedersen commitments of cf_U_i.{cmE, cmW} let H = GC2::new_constant(cs.clone(), self.cf_pedersen_params.h)?; let G = Vec::<GC2>::new_constant(cs.clone(), self.cf_pedersen_params.generators)?; let cf_W_i_E_bits: Result<Vec<Vec<Boolean<CF1<C1>>>>, SynthesisError> =

@@ -406,7 +479,7 @@ CF1<C1>,

NonNativeFieldVar<C1::BaseField, CF1<C1>>, >::new_witness(cs.clone(), || Ok(self.cf_r1cs.clone()))?; - // 6. check RelaxedR1CS of cf_U_i + // 5. check RelaxedR1CS of cf_U_i let cf_z_U: Vec<NonNativeFieldVar<C2::ScalarField, CF1<C1>>> = [vec![cf_U_i.u.clone()], cf_U_i.x.to_vec(), cf_W_i.W.to_vec()].concat(); RelaxedR1CSGadget::<

@@ -416,10 +489,122 @@ NonNativeFieldVar<C2::ScalarField, CF1<C1>>,

>::check(cf_r1cs, cf_W_i.E, cf_U_i.u.clone(), cf_z_U)?; } + // 6. check KZG challenges + let (incircuit_c_W, incircuit_c_E) = KZGChallengesGadget::<C1>::get_challenges_gadget( + cs.clone(), + &self.poseidon_config, + U_i1.clone(), + )?; + incircuit_c_W.enforce_equal(&kzg_c_W)?; + incircuit_c_E.enforce_equal(&kzg_c_E)?; + + // Check 7 is temporary disabled due + // https://github.com/privacy-scaling-explorations/folding-schemes/issues/80 + // + // 7. check eval_W==p_W(c_W) and eval_E==p_E(c_E) + // let incircuit_eval_W = evaluate_gadget::<CF1<C1>>(W_i1.W, incircuit_c_W)?; + // let incircuit_eval_E = evaluate_gadget::<CF1<C1>>(W_i1.E, incircuit_c_E)?; + // incircuit_eval_W.enforce_equal(&eval_W)?; + // incircuit_eval_E.enforce_equal(&eval_E)?; + + // 8. compute the NIFS.V challenge and check that matches the one from the public input (so we + // avoid the verifier computing it) + let cmT = + NonNativeAffineVar::new_input(cs.clone(), || Ok(self.cmT.unwrap_or_else(C1::zero)))?; + let r_bits = ChallengeGadget::<C1>::get_challenge_gadget( + cs.clone(), + &self.poseidon_config, + U_i_vec, + u_i.clone(), + cmT.clone(), + )?; + let r_Fr = Boolean::le_bits_to_fp_var(&r_bits)?; + // check that the in-circuit computed r is equal to the inputted r + let r = + FpVar::<CF1<C1>>::new_input(cs.clone(), || Ok(self.r.unwrap_or_else(CF1::<C1>::zero)))?; + r_Fr.enforce_equal(&r)?; + Ok(()) } } +/// Interpolates the polynomial from the given vector, and then returns it's evaluation at the +/// given point. +#[allow(unused)] // unused while check 7 is disabled +fn evaluate_gadget<F: PrimeField>( + v: Vec<FpVar<F>>, + point: FpVar<F>, +) -> Result<FpVar<F>, SynthesisError> { + if !v.len().is_power_of_two() { + return Err(SynthesisError::Unsatisfiable); + } + let n = v.len() as u64; + let gen = F::get_root_of_unity(n).unwrap(); + let domain = Radix2DomainVar::new(gen, log2(v.len()) as u64, FpVar::one()).unwrap(); + + let evaluations_var = EvaluationsVar::from_vec_and_domain(v, domain, true); + evaluations_var.interpolate_and_evaluate(&point) +} + +/// Gadget that computes the KZG challenges, also offers the rust native implementation compatible +/// with the gadget. +pub struct KZGChallengesGadget<C: CurveGroup> { + _c: PhantomData<C>, +} +impl<C> KZGChallengesGadget<C> +where + C: CurveGroup, + C::ScalarField: PrimeField, + <C as CurveGroup>::BaseField: PrimeField, + C::ScalarField: Absorb, +{ + pub fn get_challenges_native( + poseidon_config: &PoseidonConfig<C::ScalarField>, + U_i: CommittedInstance<C>, + ) -> Result<(C::ScalarField, C::ScalarField), Error> { + let (cmE_x_limbs, cmE_y_limbs): (Vec<C::ScalarField>, Vec<C::ScalarField>) = + point_to_nonnative_limbs::<C>(U_i.cmE)?; + let (cmW_x_limbs, cmW_y_limbs): (Vec<C::ScalarField>, Vec<C::ScalarField>) = + point_to_nonnative_limbs::<C>(U_i.cmW)?; + + let transcript = &mut PoseidonTranscript::<C>::new(poseidon_config); + // compute the KZG challenges, which are computed in-circuit and checked that it matches + // the inputted one + transcript.absorb_vec(&cmW_x_limbs); + transcript.absorb_vec(&cmW_y_limbs); + let challenge_W = transcript.get_challenge(); + transcript.absorb_vec(&cmE_x_limbs); + transcript.absorb_vec(&cmE_y_limbs); + let challenge_E = transcript.get_challenge(); + + Ok((challenge_W, challenge_E)) + } + // compatible with the native get_challenges_native + #[allow(clippy::type_complexity)] + pub fn get_challenges_gadget( + cs: ConstraintSystemRef<C::ScalarField>, + poseidon_config: &PoseidonConfig<C::ScalarField>, + U_i: CommittedInstanceVar<C>, + ) -> Result<(FpVar<C::ScalarField>, FpVar<C::ScalarField>), SynthesisError> { + let mut transcript = + PoseidonTranscriptVar::<CF1<C>>::new(cs.clone(), &poseidon_config.clone()); + + let cmW_x_limbs = U_i.cmW.x.to_constraint_field()?; + let cmW_y_limbs = U_i.cmW.y.to_constraint_field()?; + transcript.absorb_vec(&cmW_x_limbs)?; + transcript.absorb_vec(&cmW_y_limbs)?; + let challenge_W = transcript.get_challenge()?; + + let cmE_x_limbs = U_i.cmE.x.to_constraint_field()?; + let cmE_y_limbs = U_i.cmE.y.to_constraint_field()?; + transcript.absorb_vec(&cmE_x_limbs)?; + transcript.absorb_vec(&cmE_y_limbs)?; + let challenge_E = transcript.get_challenge()?; + + Ok((challenge_W, challenge_E)) + } +} + #[cfg(test)] pub mod tests { use super::*;

@@ -439,10 +624,11 @@ eq::EqGadget,

fields::{fp::FpVar, nonnative::NonNativeFieldVar}, }; use ark_relations::r1cs::ConstraintSystem; + use ark_std::UniformRand; use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2}; use crate::commitment::pedersen::Pedersen; - use crate::folding::nova::{get_pedersen_params_len, ProverParams, VerifierParams}; + use crate::folding::nova::{get_cs_params_len, ProverParams, VerifierParams}; use crate::frontend::tests::{CubicFCircuit, CustomFCircuit, WrapperCircuit}; use crate::transcript::poseidon::poseidon_test_config; use crate::FoldingScheme;

@@ -610,21 +796,21 @@

let F_circuit = CubicFCircuit::<Fr>::new(()); let z_0 = vec![Fr::from(3_u32)]; - // get the CM & CF_CM len - let (cm_len, cf_cm_len) = - get_pedersen_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>( + // get the CS & CF_CS len + let (cs_len, cf_cs_len) = + get_cs_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>( &poseidon_config, F_circuit, ) .unwrap(); - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, cm_len); - let cf_pedersen_params = Pedersen::<Projective2>::new_params(&mut rng, cf_cm_len); + let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, cs_len).unwrap(); + let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cs_len).unwrap(); let prover_params = ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> { poseidon_config: poseidon_config.clone(), - cm_params: pedersen_params, - cf_cm_params: cf_pedersen_params, + cs_params: pedersen_params, + cf_cs_params: cf_pedersen_params, }; type NOVA = Nova<

@@ -666,12 +852,82 @@ Projective2,

GVar2, Pedersen<Projective>, Pedersen<Projective2>, - >::from_nova(nova); + >::from_nova(nova) + .unwrap(); let cs = ConstraintSystem::<Fr>::new_ref(); // generate the constraints and check that are satisfied by the inputs decider_circuit.generate_constraints(cs.clone()).unwrap(); + assert!(cs.is_satisfied().unwrap()); + dbg!(cs.num_constraints()); + } + + // checks that the gadget and native implementations of the challenge computation match + #[test] + fn test_kzg_challenge_gadget() { + let mut rng = ark_std::test_rng(); + let poseidon_config = poseidon_test_config::<Fr>(); + + let U_i = CommittedInstance::<Projective> { + cmE: Projective::rand(&mut rng), + u: Fr::rand(&mut rng), + cmW: Projective::rand(&mut rng), + x: vec![Fr::rand(&mut rng); 1], + }; + + // compute the challenge natively + let (challenge_W, challenge_E) = + KZGChallengesGadget::<Projective>::get_challenges_native(&poseidon_config, U_i.clone()) + .unwrap(); + + let cs = ConstraintSystem::<Fr>::new_ref(); + let U_iVar = + CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(U_i.clone())) + .unwrap(); + + let (challenge_W_Var, challenge_E_Var) = + KZGChallengesGadget::<Projective>::get_challenges_gadget( + cs.clone(), + &poseidon_config, + U_iVar, + ) + .unwrap(); + assert!(cs.is_satisfied().unwrap()); + + // check that the natively computed and in-circuit computed hashes match + use ark_r1cs_std::R1CSVar; + assert_eq!(challenge_W_Var.value().unwrap(), challenge_W); + assert_eq!(challenge_E_Var.value().unwrap(), challenge_E); + } + + // The test test_polynomial_interpolation is temporary disabled due + // https://github.com/privacy-scaling-explorations/folding-schemes/issues/80 + // for n<=11 it will work, but for n>11 it will fail with stack overflow. + #[ignore] + #[test] + fn test_polynomial_interpolation() { + let mut rng = ark_std::test_rng(); + let n = 12; + let l = 1 << n; + + let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng)) + .take(l) + .collect(); + let challenge = Fr::rand(&mut rng); + + use ark_poly::Polynomial; + let polynomial = poly_from_vec(v.to_vec()).unwrap(); + let eval = polynomial.evaluate(&challenge); + + let cs = ConstraintSystem::<Fr>::new_ref(); + let vVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(v)).unwrap(); + let challengeVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(challenge)).unwrap(); + + let evalVar = evaluate_gadget::<Fr>(vVar, challengeVar).unwrap(); + + use ark_r1cs_std::R1CSVar; + assert_eq!(evalVar.value().unwrap(), eval); assert!(cs.is_satisfied().unwrap()); } }
M folding-schemes/src/folding/nova/mod.rs → folding-schemes/src/folding/nova/mod.rs

@@ -14,7 +14,7 @@

use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; use crate::ccs::r1cs::{extract_r1cs, extract_w_x, R1CS}; -use crate::commitment::CommitmentProver; +use crate::commitment::CommitmentScheme; use crate::folding::circuits::nonnative::point_to_nonnative_limbs; use crate::frontend::FCircuit; use crate::utils::vec::is_zero_vec;

@@ -115,16 +115,16 @@ W: w,

rW: C::ScalarField::zero(), } } - pub fn commit<CP: CommitmentProver<C>>( + pub fn commit<CS: CommitmentScheme<C>>( &self, - params: &CP::Params, + params: &CS::ProverParams, x: Vec<C::ScalarField>, ) -> Result<CommittedInstance<C>, Error> { let mut cmE = C::zero(); if !is_zero_vec::<C::ScalarField>(&self.E) { - cmE = CP::commit(params, &self.E, &self.rE)?; + cmE = CS::commit(params, &self.E, &self.rE)?; } - let cmW = CP::commit(params, &self.W, &self.rW)?; + let cmW = CS::commit(params, &self.W, &self.rW)?; Ok(CommittedInstance { cmE, u: C::ScalarField::one(),

@@ -135,16 +135,16 @@ }

} #[derive(Debug, Clone)] -pub struct ProverParams<C1, C2, CP1, CP2> +pub struct ProverParams<C1, C2, CS1, CS2> where C1: CurveGroup, C2: CurveGroup, - CP1: CommitmentProver<C1>, - CP2: CommitmentProver<C2>, + CS1: CommitmentScheme<C1>, + CS2: CommitmentScheme<C2>, { pub poseidon_config: PoseidonConfig<C1::ScalarField>, - pub cm_params: CP1::Params, - pub cf_cm_params: CP2::Params, + pub cs_params: CS1::ProverParams, + pub cf_cs_params: CS2::ProverParams, } #[derive(Debug, Clone)]

@@ -157,15 +157,15 @@

/// 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 #[derive(Clone, Debug)] -pub struct Nova<C1, GC1, C2, GC2, FC, CP1, CP2> +pub struct Nova<C1, GC1, C2, GC2, FC, CS1, CS2> where C1: CurveGroup, GC1: CurveVar<C1, CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<C1::ScalarField>, - CP1: CommitmentProver<C1>, - CP2: CommitmentProver<C2>, + CS1: CommitmentScheme<C1>, + CS2: CommitmentScheme<C2>, { _gc1: PhantomData<GC1>, _c2: PhantomData<C2>,

@@ -175,10 +175,10 @@ pub r1cs: R1CS<C1::ScalarField>,

/// R1CS of the CycleFold circuit pub cf_r1cs: R1CS<C2::ScalarField>, pub poseidon_config: PoseidonConfig<C1::ScalarField>, - /// CommitmentProver::Params over C1 - pub cm_params: CP1::Params, - /// CycleFold CommitmentProver::Params, over C2 - pub cf_cm_params: CP2::Params, + /// CommitmentScheme::ProverParams over C1 + pub cs_params: CS1::ProverParams, + /// CycleFold CommitmentScheme::ProverParams, over C2 + pub cf_cs_params: CS2::ProverParams, /// F circuit, the circuit that is being folded pub F: FC, pub i: C1::ScalarField,

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

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

@@ -216,7 +216,7 @@ for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,

for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, { type PreprocessorParam = (Self::ProverParam, FC); - type ProverParam = ProverParams<C1, C2, CP1, CP2>; + type ProverParam = ProverParams<C1, C2, CS1, CS2>; type VerifierParam = VerifierParams<C1, C2>; type CommittedInstanceWithWitness = (CommittedInstance<C1>, Witness<C1>); type CFCommittedInstanceWithWitness = (CommittedInstance<C2>, Witness<C2>);

@@ -270,8 +270,8 @@ _gc2: PhantomData,

r1cs, cf_r1cs, poseidon_config: pp.poseidon_config.clone(), - cm_params: pp.cm_params.clone(), - cf_cm_params: pp.cf_cm_params.clone(), + cs_params: pp.cs_params.clone(), + cf_cs_params: pp.cf_cs_params.clone(), F, i: C1::ScalarField::zero(), z_0: z_0.clone(),

@@ -315,7 +315,7 @@ let r_Fr = C1::ScalarField::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, CP1>::fold_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, )?;

@@ -355,7 +355,7 @@ cf2_r_nonnat: None,

}; #[cfg(test)] - NIFS::<C1, CP1>::verify_folded_instance(r_Fr, &self.U_i, &self.u_i, &U_i1, &cmT)?; + NIFS::<C1, CS1>::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

@@ -444,6 +444,9 @@ let cs = ConstraintSystem::<C1::ScalarField>::new_ref();

augmented_F_circuit.generate_constraints(cs.clone())?; + #[cfg(test)] + assert!(cs.is_satisfied().unwrap()); + let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?; let (w_i1, x_i1) = extract_w_x::<C1::ScalarField>(&cs); if x_i1[0] != u_i1_x {

@@ -459,7 +462,7 @@ // set values for next iteration

self.i += C1::ScalarField::one(); self.z_i = z_i1.clone(); self.w_i = Witness::<C1>::new(w_i1, self.r1cs.A.n_rows); - self.u_i = self.w_i.commit::<CP1>(&self.cm_params, vec![u_i1_x])?; + self.u_i = self.w_i.commit::<CS1>(&self.cs_params, vec![u_i1_x])?; self.W_i = W_i1.clone(); self.U_i = U_i1.clone();

@@ -533,15 +536,15 @@ Ok(())

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

@@ -549,8 +552,8 @@ 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, CP1>::compute_cmT( - &self.cm_params, + NIFS::<C1, CS1>::compute_cmT( + &self.cs_params, &self.r1cs, &self.w_i, &self.u_i,

@@ -566,8 +569,8 @@ cf_u_i: &CommittedInstance<C2>,

cf_W_i: &Witness<C2>, cf_U_i: &CommittedInstance<C2>, ) -> Result<(Vec<C2::ScalarField>, C2), Error> { - NIFS::<C2, CP2>::compute_cyclefold_cmT( - &self.cf_cm_params, + NIFS::<C2, CS2>::compute_cyclefold_cmT( + &self.cf_cs_params, &self.cf_r1cs, cf_w_i, cf_u_i,

@@ -577,15 +580,15 @@ )

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

@@ -630,7 +633,7 @@

// fold cyclefold instances let cf_w_i = Witness::<C2>::new(cf_w_i.clone(), self.cf_r1cs.A.n_rows); let cf_u_i: CommittedInstance<C2> = - cf_w_i.commit::<CP2>(&self.cf_cm_params, cf_x_i.clone())?; + cf_w_i.commit::<CS2>(&self.cf_cs_params, cf_x_i.clone())?; // compute T* and cmT* for CycleFoldCircuit let (cf_T, cf_cmT) = self.compute_cf_cmT(&cf_w_i, &cf_u_i, &cf_W_i, &cf_U_i)?;

@@ -644,7 +647,7 @@ )?;

let cf_r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&cf_r_bits)) .ok_or(Error::OutOfBounds)?; - let (cf_W_i1, cf_U_i1) = NIFS::<C2, CP2>::fold_instances( + let (cf_W_i1, cf_U_i1) = NIFS::<C2, CS2>::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))

@@ -693,7 +696,7 @@ }

/// helper method to get the pedersen params length for both the AugmentedFCircuit and the /// CycleFold circuit -pub fn get_pedersen_params_len<C1, GC1, C2, GC2, FC>( +pub fn get_cs_params_len<C1, GC1, C2, GC2, FC>( poseidon_config: &PoseidonConfig<C1::ScalarField>, F_circuit: FC, ) -> Result<(usize, usize), Error>

@@ -715,6 +718,7 @@ let (r1cs, cf_r1cs) = get_r1cs::<C1, GC1, C2, GC2, FC>(poseidon_config, F_circuit)?;

Ok((r1cs.A.n_rows, cf_r1cs.A.n_rows)) } +/// returns the coordinates of a commitment point pub(crate) fn get_cm_coordinates<C: CurveGroup>(cm: &C) -> Vec<C::BaseField> { let zero = (&C::BaseField::zero(), &C::BaseField::one()); let cm = cm.into_affine();

@@ -725,8 +729,10 @@

#[cfg(test)] pub mod tests { use super::*; - use ark_pallas::{constraints::GVar, Fr, Projective}; - use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2}; + use crate::commitment::kzg::{ProverKey as KZGProverKey, KZG}; + use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; + use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; + use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey; use crate::commitment::pedersen::Pedersen; use crate::frontend::tests::CubicFCircuit;

@@ -736,38 +742,56 @@ /// This test tests the Nova+CycleFold IVC, and by consequence it is also testing the

/// AugmentedFCircuit #[test] fn test_ivc() { - type NOVA = Nova< - Projective, - GVar, - Projective2, - GVar2, - CubicFCircuit<Fr>, - Pedersen<Projective>, - Pedersen<Projective2>, - >; - let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_test_config::<Fr>(); let F_circuit = CubicFCircuit::<Fr>::new(()); - let z_0 = vec![Fr::from(3_u32)]; - let (cm_len, cf_cm_len) = - get_pedersen_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>( + let (cs_len, cf_cs_len) = + get_cs_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>( &poseidon_config, F_circuit, ) .unwrap(); - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, cm_len); - let cf_pedersen_params = Pedersen::<Projective2>::new_params(&mut rng, cf_cm_len); + let (kzg_pk, _): (KZGProverKey<Projective>, KZGVerifierKey<Bn254>) = + KZG::<Bn254>::setup(&mut rng, cs_len).unwrap(); + let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, cs_len).unwrap(); + let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cs_len).unwrap(); - let prover_params = - ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> { - poseidon_config: poseidon_config.clone(), - cm_params: pedersen_params, - cf_cm_params: cf_pedersen_params, - }; + // run the test using Pedersen commitments on both sides of the curve cycle + test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>>( + poseidon_config.clone(), + pedersen_params, + cf_pedersen_params.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, + kzg_pk, + cf_pedersen_params, + F_circuit, + ); + } + // test_ivc allowing to choose the CommitmentSchemes + fn test_ivc_opt<CS1: CommitmentScheme<Projective>, CS2: CommitmentScheme<Projective2>>( + poseidon_config: PoseidonConfig<Fr>, + cs_params: CS1::ProverParams, + cf_cs_params: CS2::ProverParams, + F_circuit: CubicFCircuit<Fr>, + ) { + type NOVA<CS1, CS2> = + Nova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2>; + + let prover_params = ProverParams::<Projective, Projective2, CS1, CS2> { + poseidon_config: poseidon_config.clone(), + cs_params, + cf_cs_params, + }; + + let z_0 = vec![Fr::from(3_u32)]; let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap(); let num_steps: usize = 3;

@@ -782,7 +806,7 @@ r1cs: nova.clone().r1cs,

cf_r1cs: nova.clone().cf_r1cs, }; let (running_instance, incoming_instance, cyclefold_instance) = nova.instances(); - NOVA::verify( + NOVA::<CS1, CS2>::verify( verifier_params, z_0, nova.z_i,
M folding-schemes/src/folding/nova/nifs.rs → folding-schemes/src/folding/nova/nifs.rs

@@ -1,23 +1,23 @@

use ark_crypto_primitives::sponge::Absorb; use ark_ec::{CurveGroup, Group}; -use ark_std::One; +use ark_std::Zero; use std::marker::PhantomData; use super::{CommittedInstance, Witness}; use crate::ccs::r1cs::R1CS; -use crate::commitment::CommitmentProver; +use crate::commitment::CommitmentScheme; use crate::transcript::Transcript; use crate::utils::vec::*; 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, CP: CommitmentProver<C>> { +pub struct NIFS<C: CurveGroup, CS: CommitmentScheme<C>> { _c: PhantomData<C>, - _cp: PhantomData<CP>, + _cp: PhantomData<CS>, } -impl<C: CurveGroup, CP: CommitmentProver<C>> NIFS<C, CP> +impl<C: CurveGroup, CS: CommitmentScheme<C>> NIFS<C, CS> where <C as Group>::ScalarField: Absorb, {

@@ -90,7 +90,7 @@ /// NIFS.P is the consecutive combination of compute_cmT with fold_instances

/// compute_cmT is part of the NIFS.P logic pub fn compute_cmT( - cm_prover_params: &CP::Params, + cs_prover_params: &CS::ProverParams, r1cs: &R1CS<C::ScalarField>, w1: &Witness<C>, ci1: &CommittedInstance<C>,

@@ -102,12 +102,12 @@ let z2: Vec<C::ScalarField> = [vec![ci2.u], ci2.x.to_vec(), w2.W.to_vec()].concat();

// compute cross terms let T = Self::compute_T(r1cs, ci1.u, ci2.u, &z1, &z2)?; - // use r_T=1 since we don't need hiding property for cm(T) - let cmT = CP::commit(cm_prover_params, &T, &C::ScalarField::one())?; + // use r_T=0 since we don't need hiding property for cm(T) + let cmT = CS::commit(cs_prover_params, &T, &C::ScalarField::zero())?; Ok((T, cmT)) } pub fn compute_cyclefold_cmT( - cm_prover_params: &CP::Params, + cs_prover_params: &CS::ProverParams, r1cs: &R1CS<C::ScalarField>, // R1CS over C2.Fr=C1.Fq (here C=C2) w1: &Witness<C>, ci1: &CommittedInstance<C>,

@@ -122,8 +122,8 @@ let z2: Vec<C::ScalarField> = [vec![ci2.u], ci2.x.to_vec(), w2.W.to_vec()].concat();

// compute cross terms let T = Self::compute_T(r1cs, ci1.u, ci2.u, &z1, &z2)?; - // use r_T=1 since we don't need hiding property for cm(T) - let cmT = CP::commit(cm_prover_params, &T, &C::ScalarField::one())?; + // use r_T=0 since we don't need hiding property for cm(T) + let cmT = CS::commit(cs_prover_params, &T, &C::ScalarField::zero())?; Ok((T, cmT)) }

@@ -140,11 +140,11 @@ T: &[C::ScalarField],

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

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

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

@@ -184,15 +184,15 @@ }

pub fn prove_commitments( tr: &mut impl Transcript<C>, - cm_prover_params: &CP::Params, + cs_prover_params: &CS::ProverParams, w: &Witness<C>, ci: &CommittedInstance<C>, T: Vec<C::ScalarField>, cmT: &C, - ) -> Result<[CP::Proof; 3], Error> { - let cmE_proof = CP::prove(cm_prover_params, tr, &ci.cmE, &w.E, &w.rE, None)?; - let cmW_proof = CP::prove(cm_prover_params, tr, &ci.cmW, &w.W, &w.rW, None)?; - let cmT_proof = CP::prove(cm_prover_params, tr, cmT, &T, &C::ScalarField::one(), None)?; // cm(T) is committed with rT=1 + ) -> Result<[CS::Proof; 3], Error> { + let cmE_proof = CS::prove(cs_prover_params, tr, &ci.cmE, &w.E, &w.rE, None)?; + let cmW_proof = CS::prove(cs_prover_params, tr, &ci.cmW, &w.W, &w.rW, None)?; + let cmT_proof = CS::prove(cs_prover_params, tr, cmT, &T, &C::ScalarField::zero(), None)?; // cm(T) is committed with rT=0 Ok([cmE_proof, cmW_proof, cmT_proof]) } }

@@ -213,67 +213,64 @@ use crate::transcript::poseidon::{poseidon_test_config, PoseidonTranscript};

use crate::utils::vec::vec_scalar_mul; #[allow(clippy::type_complexity)] - pub(crate) fn prepare_simple_fold_inputs() -> ( - PedersenParams<Projective>, - PoseidonConfig<Fr>, - R1CS<Fr>, - Witness<Projective>, // w1 - CommittedInstance<Projective>, // ci1 - Witness<Projective>, // w2 - CommittedInstance<Projective>, // ci2 - Witness<Projective>, // w3 - CommittedInstance<Projective>, // ci3 - Vec<Fr>, // T - Projective, // cmT - Vec<bool>, // r_bits - Fr, // r_Fr - ) { + pub(crate) fn prepare_simple_fold_inputs<C>() -> ( + PedersenParams<C>, + PoseidonConfig<C::ScalarField>, + R1CS<C::ScalarField>, + Witness<C>, // w1 + CommittedInstance<C>, // ci1 + Witness<C>, // w2 + CommittedInstance<C>, // ci2 + Witness<C>, // w3 + CommittedInstance<C>, // ci3 + Vec<C::ScalarField>, // T + C, // cmT + Vec<bool>, // r_bits + C::ScalarField, // r_Fr + ) + where + C: CurveGroup, + <C as CurveGroup>::BaseField: PrimeField, + C::ScalarField: Absorb, + { let r1cs = get_test_r1cs(); let z1 = get_test_z(3); let z2 = get_test_z(4); let (w1, x1) = r1cs.split_z(&z1); let (w2, x2) = r1cs.split_z(&z2); - let w1 = Witness::<Projective>::new(w1.clone(), r1cs.A.n_rows); - let w2 = Witness::<Projective>::new(w2.clone(), r1cs.A.n_rows); + let w1 = Witness::<C>::new(w1.clone(), r1cs.A.n_rows); + let w2 = Witness::<C>::new(w2.clone(), r1cs.A.n_rows); let mut rng = ark_std::test_rng(); - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, r1cs.A.n_cols); + let (pedersen_params, _) = Pedersen::<C>::setup(&mut rng, r1cs.A.n_cols).unwrap(); // compute committed instances let ci1 = w1 - .commit::<Pedersen<Projective>>(&pedersen_params, x1.clone()) + .commit::<Pedersen<C>>(&pedersen_params, x1.clone()) .unwrap(); let ci2 = w2 - .commit::<Pedersen<Projective>>(&pedersen_params, x2.clone()) + .commit::<Pedersen<C>>(&pedersen_params, x2.clone()) .unwrap(); // NIFS.P - let (T, cmT) = NIFS::<Projective, Pedersen<Projective>>::compute_cmT( - &pedersen_params, - &r1cs, - &w1, - &ci1, - &w2, - &ci2, - ) - .unwrap(); + let (T, cmT) = + NIFS::<C, Pedersen<C>>::compute_cmT(&pedersen_params, &r1cs, &w1, &ci1, &w2, &ci2) + .unwrap(); - let poseidon_config = poseidon_test_config::<Fr>(); + let poseidon_config = poseidon_test_config::<C::ScalarField>(); - let r_bits = ChallengeGadget::<Projective>::get_challenge_native( + let r_bits = ChallengeGadget::<C>::get_challenge_native( &poseidon_config, ci1.clone(), ci2.clone(), cmT, ) .unwrap(); - let r_Fr = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap(); + let r_Fr = C::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap(); - let (w3, ci3) = NIFS::<Projective, Pedersen<Projective>>::fold_instances( - r_Fr, &w1, &ci1, &w2, &ci2, &T, cmT, - ) - .unwrap(); + let (w3, ci3) = + NIFS::<C, Pedersen<C>>::fold_instances(r_Fr, &w1, &ci1, &w2, &ci2, &T, cmT).unwrap(); ( pedersen_params,

@@ -300,7 +297,7 @@ let z1 = get_test_z(3);

let (w1, x1) = r1cs.split_z(&z1); let mut rng = ark_std::test_rng(); - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, r1cs.A.n_cols); + 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);

@@ -386,22 +383,22 @@ assert_eq!(cm_proofs.len(), 3);

Pedersen::<Projective>::verify( &pedersen_params, &mut transcript_v, - ci3.cmE, - cm_proofs[0].clone(), + &ci3.cmE, + &cm_proofs[0].clone(), ) .unwrap(); Pedersen::<Projective>::verify( &pedersen_params, &mut transcript_v, - ci3.cmW, - cm_proofs[1].clone(), + &ci3.cmW, + &cm_proofs[1].clone(), ) .unwrap(); Pedersen::<Projective>::verify( &pedersen_params, &mut transcript_v, - cmT, - cm_proofs[2].clone(), + &cmT, + &cm_proofs[2].clone(), ) .unwrap(); }

@@ -413,7 +410,7 @@ let z = get_test_z(3);

let (w, x) = r1cs.split_z(&z); let mut rng = ark_std::test_rng(); - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, r1cs.A.n_cols); + 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);
M folding-schemes/src/folding/protogalaxy/folding.rs → folding-schemes/src/folding/protogalaxy/folding.rs

@@ -384,7 +384,7 @@ use ark_pallas::{Fr, Projective};

use ark_std::UniformRand; use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z}; - use crate::commitment::{pedersen::Pedersen, CommitmentProver}; + use crate::commitment::{pedersen::Pedersen, CommitmentScheme}; use crate::transcript::poseidon::{poseidon_test_config, PoseidonTranscript}; pub(crate) fn check_instance<C: CurveGroup>(

@@ -452,7 +452,7 @@ Vec<Witness<Fr>>,

Vec<CommittedInstance<Projective>>, ) { let mut rng = ark_std::test_rng(); - let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, 100); // 100 is wip, will get it from actual vec + let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, 100).unwrap(); // 100 is wip, will get it from actual vec let z = get_test_z::<Fr>(3); let mut zs: Vec<Vec<Fr>> = Vec::new();

@@ -471,8 +471,8 @@ let witness = Witness::<Fr> {

w: z.clone(), r_w: Fr::rand(&mut rng), }; - let phi = - Pedersen::<Projective>::commit(&pedersen_params, &witness.w, &witness.r_w).unwrap(); + let phi = Pedersen::<Projective, true>::commit(&pedersen_params, &witness.w, &witness.r_w) + .unwrap(); let instance = CommittedInstance::<Projective> { phi, betas: betas.clone(),

@@ -487,9 +487,12 @@ let witness_i = Witness::<Fr> {

w: zs[i].clone(), r_w: Fr::rand(&mut rng), }; - let phi_i = - Pedersen::<Projective>::commit(&pedersen_params, &witness_i.w, &witness_i.r_w) - .unwrap(); + let phi_i = Pedersen::<Projective, true>::commit( + &pedersen_params, + &witness_i.w, + &witness_i.r_w, + ) + .unwrap(); let instance_i = CommittedInstance::<Projective> { phi: phi_i, betas: betas.clone(),
M folding-schemes/src/lib.rs → folding-schemes/src/lib.rs

@@ -21,6 +21,7 @@ pub mod utils;

#[derive(Debug, Error)] pub enum Error { + // Wrappers on top of other errors #[error("ark_relations::r1cs::SynthesisError")] SynthesisError(#[from] ark_relations::r1cs::SynthesisError), #[error("ark_serialize::SerializationError")]

@@ -29,27 +30,16 @@ #[error("ark_poly_commit::Error")]

PolyCommitError(#[from] ark_poly_commit::Error), #[error("crate::utils::espresso::virtual_polynomial::ArithErrors")] ArithError(#[from] utils::espresso::virtual_polynomial::ArithErrors), + #[error(transparent)] + ProtoGalaxy(folding::protogalaxy::ProtoGalaxyError), #[error("{0}")] Other(String), + // Relation errors #[error("Relation not satisfied")] NotSatisfied, - #[error("Not equal")] - NotEqual, - #[error("Vectors should have the same length ({0}: {1}, {2}: {3})")] - NotSameLength(String, usize, String, usize), - #[error("Vector's length ({0}) is not the expected ({1})")] - NotExpectedLength(usize, usize), - #[error("Vector ({0}) length ({1}) is not a power of two")] - NotPowerOfTwo(String, usize), - #[error("Can not be empty")] - Empty, - #[error("Pedersen parameters length is not sufficient (generators.len={0} < vector.len={1} unsatisfied)")] - PedersenParamsLen(usize, usize), - #[error("Randomness for blinding not found")] - MissingRandomness, - #[error("Commitment verification failed")] - CommitmentVerificationFail, + #[error("SNARK verification failed")] + SNARKVerificationFail, #[error("IVC verification failed")] IVCVerificationFail, #[error("R1CS instance is expected to not be relaxed")]

@@ -60,17 +50,42 @@ #[error("Sum-check prove failed: {0}")]

SumCheckProveError(String), #[error("Sum-check verify failed: {0}")] SumCheckVerifyError(String), + + // Comparators errors + #[error("Not equal")] + NotEqual, + #[error("Vectors should have the same length ({0}: {1}, {2}: {3})")] + NotSameLength(String, usize, String, usize), + #[error("Vector's length ({0}) is not the expected ({1})")] + NotExpectedLength(usize, usize), + #[error("Vector ({0}) length ({1}) is not a power of two")] + NotPowerOfTwo(String, usize), + #[error("Can not be empty")] + Empty, #[error("Value out of bounds")] OutOfBounds, #[error("Could not construct the Evaluation Domain")] NewDomainFail, + + // Commitment errors + #[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("Commitment verification failed")] + CommitmentVerificationFail, + + // Other + #[error("Randomness for blinding not found")] + MissingRandomness, + #[error("Missing value: {0}")] + MissingValue(String), #[error("Feature '{0}' not supported yet")] NotSupportedYet(String), + #[error("Feature '{0}' is not supported and it will not be")] + NotSupported(String), #[error("max i-th step reached (usize limit reached)")] MaxStep, - - #[error(transparent)] - ProtoGalaxy(folding::protogalaxy::ProtoGalaxyError), } /// FoldingScheme defines trait that is implemented by the diverse folding schemes. It is defined

@@ -138,24 +153,25 @@ C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,

C2::BaseField: PrimeField, { type ProverParam: Clone; - type Proof: Clone; + type Proof; type VerifierParam; type PublicInput: Debug; type CommittedInstanceWithWitness: Debug; type CommittedInstance: Clone + Debug; fn prove( - pp: &Self::ProverParam, + pp: Self::ProverParam, rng: impl RngCore + CryptoRng, folding_scheme: FS, ) -> Result<Self::Proof, Error>; fn verify( - vp: &Self::VerifierParam, + vp: Self::VerifierParam, i: C1::ScalarField, z_0: Vec<C1::ScalarField>, z_i: Vec<C1::ScalarField>, running_instance: &Self::CommittedInstance, + incoming_instance: &Self::CommittedInstance, proof: Self::Proof, // returns `Result<bool, Error>` to differentiate between an error occurred while performing // the verification steps, and the verification logic of the scheme not passing.
M folding-schemes/src/utils/vec.rs → folding-schemes/src/utils/vec.rs

@@ -1,4 +1,7 @@

use ark_ff::PrimeField; +use ark_poly::{ + univariate::DensePolynomial, EvaluationDomain, Evaluations, GeneralEvaluationDomain, +}; pub use ark_relations::r1cs::Matrix as R1CSMatrix; use ark_std::cfg_iter; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};

@@ -126,6 +129,13 @@ b.len(),

)); } Ok(cfg_iter!(a).zip(b).map(|(a, b)| *a * b).collect()) +} + +/// returns the interpolated polynomial of degree=v.len().next_power_of_two(), which passes through all +/// the given elements of v. +pub fn poly_from_vec<F: PrimeField>(v: Vec<F>) -> Result<DensePolynomial<F>, Error> { + let D = GeneralEvaluationDomain::<F>::new(v.len()).ok_or(Error::NewDomainFail)?; + Ok(Evaluations::from_vec_and_domain(v, D).interpolate()) } #[cfg(test)]
M solidity-verifiers/src/verifiers/mod.rs → solidity-verifiers/src/verifiers/mod.rs

@@ -68,8 +68,8 @@ use ark_std::{test_rng, UniformRand};

use askama::Template; use folding_schemes::{ commitment::{ - kzg::{KZGProver, KZGSetup, ProverKey}, - CommitmentProver, + kzg::{ProverKey, KZG}, + CommitmentScheme, }, transcript::{ poseidon::{poseidon_test_config, PoseidonTranscript},

@@ -131,7 +131,7 @@ };

let (g16_pk, g16_vk) = Groth16::<Bn254>::setup(circuit, &mut rng).unwrap(); let (kzg_pk, kzg_vk): (ProverKey<G1>, VerifierKey<Bn254>) = - KZGSetup::<Bn254>::setup(&mut rng, n); + KZG::<Bn254>::setup(&mut rng, n).unwrap(); (kzg_pk, kzg_vk, g16_pk, g16_vk, circuit) }

@@ -290,9 +290,8 @@

let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng)) .take(DEFAULT_SETUP_LEN) .collect(); - let cm = KZGProver::<G1>::commit(&kzg_pk, &v, &Fr::zero()).unwrap(); - let (eval, proof) = - KZGProver::<G1>::prove(&kzg_pk, transcript_p, &cm, &v, &Fr::zero(), None).unwrap(); + let cm = KZG::<Bn254>::commit(&kzg_pk, &v, &Fr::zero()).unwrap(); + let proof = KZG::<Bn254>::prove(&kzg_pk, transcript_p, &cm, &v, &Fr::zero(), None).unwrap(); let template = HeaderInclusion::<KZG10Verifier>::builder() .template(kzg_data) .build()

@@ -303,10 +302,10 @@ let kzg_verifier_bytecode = compile_solidity(template, "KZG10");

let mut evm = Evm::default(); let verifier_address = evm.create(kzg_verifier_bytecode); - let (cm_affine, proof_affine) = (cm.into_affine(), proof.into_affine()); + let (cm_affine, proof_affine) = (cm.into_affine(), proof.proof.into_affine()); let (x_comm, y_comm) = cm_affine.xy().unwrap(); let (x_proof, y_proof) = proof_affine.xy().unwrap(); - let y = eval.into_bigint().to_bytes_be(); + let y = proof.eval.into_bigint().to_bytes_be(); transcript_v.absorb_point(&cm).unwrap(); let x = transcript_v.get_challenge();

@@ -372,9 +371,8 @@

let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng)) .take(DEFAULT_SETUP_LEN) .collect(); - let cm = KZGProver::<G1>::commit(&kzg_pk, &v, &Fr::zero()).unwrap(); - let (eval, proof) = - KZGProver::<G1>::prove(&kzg_pk, transcript_p, &cm, &v, &Fr::zero(), None).unwrap(); + let cm = KZG::<Bn254>::commit(&kzg_pk, &v, &Fr::zero()).unwrap(); + let proof = KZG::<Bn254>::prove(&kzg_pk, transcript_p, &cm, &v, &Fr::zero(), None).unwrap(); let decider_template = HeaderInclusion::<NovaCyclefoldDecider>::builder() .template(nova_cyclefold_data)

@@ -387,10 +385,10 @@

let mut evm = Evm::default(); let verifier_address = evm.create(nova_cyclefold_verifier_bytecode); - let (cm_affine, proof_affine) = (cm.into_affine(), proof.into_affine()); + let (cm_affine, proof_affine) = (cm.into_affine(), proof.proof.into_affine()); let (x_comm, y_comm) = cm_affine.xy().unwrap(); let (x_proof, y_proof) = proof_affine.xy().unwrap(); - let y = eval.into_bigint().to_bytes_be(); + let y = proof.eval.into_bigint().to_bytes_be(); transcript_v.absorb_point(&cm).unwrap(); let x = transcript_v.get_challenge();