Browse Source

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.
update-nifs-interface
arnaucube 7 months ago
committed by GitHub
parent
commit
fe9a488f63
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
22 changed files with 1287 additions and 670 deletions
  1. +7
    -2
      Cargo.toml
  2. +2
    -4
      folding-schemes/Cargo.toml
  3. +7
    -7
      folding-schemes/examples/utils.rs
  4. +133
    -110
      folding-schemes/src/commitment/ipa.rs
  5. +113
    -82
      folding-schemes/src/commitment/kzg.rs
  6. +106
    -77
      folding-schemes/src/commitment/mod.rs
  7. +95
    -46
      folding-schemes/src/commitment/pedersen.rs
  8. +3
    -3
      folding-schemes/src/folding/hypernova/cccs.rs
  9. +7
    -4
      folding-schemes/src/folding/hypernova/circuit.rs
  10. +7
    -5
      folding-schemes/src/folding/hypernova/lcccs.rs
  11. +11
    -6
      folding-schemes/src/folding/hypernova/nimfs.rs
  12. +5
    -3
      folding-schemes/src/folding/hypernova/utils.rs
  13. +1
    -1
      folding-schemes/src/folding/nova/circuits.rs
  14. +34
    -14
      folding-schemes/src/folding/nova/cyclefold.rs
  15. +210
    -64
      folding-schemes/src/folding/nova/decider_eth.rs
  16. +327
    -71
      folding-schemes/src/folding/nova/decider_eth_circuit.rs
  17. +89
    -65
      folding-schemes/src/folding/nova/mod.rs
  18. +61
    -64
      folding-schemes/src/folding/nova/nifs.rs
  19. +10
    -7
      folding-schemes/src/folding/protogalaxy/folding.rs
  20. +38
    -22
      folding-schemes/src/lib.rs
  21. +10
    -0
      folding-schemes/src/utils/vec.rs
  22. +11
    -13
      solidity-verifiers/src/verifiers/mod.rs

+ 7
- 2
Cargo.toml

@ -6,10 +6,15 @@ members = [
]
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"}

+ 2
- 4
folding-schemes/Cargo.toml

@ -26,9 +26,8 @@ espresso_subroutines = {git="https://github.com/EspressoSystems/hyperplonk", pac
[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 @@ parallel = [
"ark-crypto-primitives/parallel",
"ark-r1cs-std/parallel",
]

+ 7
- 7
folding-schemes/examples/utils.rs

@ -6,7 +6,7 @@
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 @@ pub(crate) fn test_nova_setup>(
// 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(),

+ 133
- 110
folding-schemes/src/commitment/ipa.rs

@ -18,15 +18,11 @@ use ark_r1cs_std::{
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 @@ use crate::utils::{
};
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 @@ pub struct Proof {
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 @@ impl CommitmentProver for IPA {
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,16 +89,19 @@ impl CommitmentProver for IPA {
}
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 @@ impl CommitmentProver for IPA {
}
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 @@ impl CommitmentProver for IPA {
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
))
}
}
impl<C: CurveGroup, const H: bool> IPA<C, H> {
#[allow(clippy::too_many_arguments)]
pub fn verify(
params: &PedersenParams<C>,
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()))
}
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 @@ impl IPA {
transcript.absorb_point(&p.R[i])?;
u[i] = transcript.get_challenge();
}
let challenge = (x, U, u);
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);
let P = *P + U.mul(v); // where v=p(x)
let mut q_0 = P;
let mut r = r;
@ -262,7 +302,10 @@ impl IPA {
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 @@ where
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 @@ where
}
// 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 @@ mod tests {
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 @@ mod tests {
// 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 @@ mod tests {
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 @@ mod tests {
.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 @@ mod tests {
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,

+ 113
- 82
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 @@ pub struct ProverKey<'a, C: CurveGroup> {
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 @@ where
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 @@ where
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 @@ where
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 @@ where
// 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 })
}
}
/// 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())
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()));
}
// 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 @@ fn convert_to_bigints(p: &[F]) -> Vec {
#[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 @@ mod tests {
#[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();
}
}

+ 106
- 77
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 @@ mod tests {
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,
);
// test with KZG
test_homomorphic_property_using_Commitment_trait_opt::<G1, KZG<Bn254>>(
&poseidon_config,
&kzg_pk,
&kzg_vk,
r,
&v_1,
&v_2,
);
}
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();
// 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();
// 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();
// 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();
// verify Pedersen
let transcript_v = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
Pedersen::<G1>::verify(&pedersen_params, transcript_v, pedersen_cm, pedersen_proof)
.unwrap();
// 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,
},
)
.unwrap());
// verify the opening proof
let transcript_v = &mut PoseidonTranscript::<C>::new(poseidon_config);
CS::verify(verifier_params, transcript_v, &cm_3, &proof).unwrap();
}
}

+ 95
- 46
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 @@ use crate::Error;
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: 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 @@ impl CommitmentProver for Pedersen {
}
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 @@ impl CommitmentProver for Pedersen {
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 @@ impl CommitmentProver for Pedersen {
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 @@ impl Pedersen {
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 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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();
}
}

+ 3
- 3
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 @@ impl CCS {
) -> 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 @@ impl CCCS {
) -> 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);
}

+ 7
- 4
folding-schemes/src/folding/hypernova/circuit.rs

@ -157,7 +157,7 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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);

+ 7
- 5
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 @@ impl CCS {
) -> 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 @@ impl LCCCS {
) -> 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 @@ pub mod tests {
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 @@ pub mod tests {
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

+ 11
- 6
folding-schemes/src/folding/hypernova/nimfs.rs

@ -378,7 +378,7 @@ pub mod tests {
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 @@ pub mod tests {
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 @@ pub mod tests {
// 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 @@ pub mod tests {
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 @@ pub mod tests {
// 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 @@ pub mod tests {
// 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

+ 5
- 3
folding-schemes/src/folding/hypernova/utils.rs

@ -199,7 +199,7 @@ pub mod tests {
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 @@ pub mod tests {
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 @@ pub mod tests {
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();

+ 1
- 1
folding-schemes/src/folding/nova/circuits.rs

@ -509,8 +509,8 @@ where
#[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;

+ 34
- 14
folding-schemes/src/folding/nova/cyclefold.rs

@ -175,7 +175,7 @@ where
}
}
/// 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 @@ where
.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 @@ where
}
}
/// 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 @@ where
#[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;
@ -468,6 +473,21 @@ pub mod tests {
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]
fn test_cyclefold_challenge_gadget() {
let mut rng = ark_std::test_rng();

+ 210
- 64
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 @@ where
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 @@ pub mod tests {
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 @@ pub mod tests {
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 @@ pub mod tests {
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());
}
}

+ 327
- 71
folding-schemes/src/folding/nova/decider_eth_circuit.rs

@ -3,29 +3,40 @@
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 @@ where
/// 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 @@ where
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 @@ where
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 @@ where
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 @@ where
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 @@ where
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 @@ where
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 @@ where
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 @@ where
>::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 @@ pub mod tests {
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 @@ pub mod tests {
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 @@ pub mod tests {
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());
}
}

+ 89
- 65
folding-schemes/src/folding/nova/mod.rs

@ -14,7 +14,7 @@ use core::marker::PhantomData;
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 @@ where
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 @@ where
}
#[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 @@ pub struct VerifierParams {
/// 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 @@ where
/// 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 @@ where
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 @@ where
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 @@ where
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 @@ where
.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 @@ where
};
#[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 @@ where
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 @@ where
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 @@ where
}
}
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 @@ where
{
// 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 @@ where
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 @@ where
}
}
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 @@ where
// 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 @@ where
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 @@ where
/// 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 @@ where
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 @@ pub(crate) fn get_cm_coordinates(cm: &C) -> Vec {
#[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 @@ pub mod tests {
/// 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 prover_params =
ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> {
poseidon_config: poseidon_config.clone(),
cm_params: pedersen_params,
cf_cm_params: cf_pedersen_params,
};
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();
// 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 @@ pub mod tests {
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,

+ 61
- 64
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 @@ where
/// 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 @@ where
// 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 @@ where
// 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 @@ where
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 @@ where
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 @@ where
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 @@ pub mod tests {
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 @@ pub mod tests {
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 @@ pub mod tests {
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 @@ pub mod tests {
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);

+ 10
- 7
folding-schemes/src/folding/protogalaxy/folding.rs

@ -384,7 +384,7 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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(),

+ 38
- 22
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 @@ pub enum 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 @@ pub enum Error {
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 @@ pub trait Decider<
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.

+ 10
- 0
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};
@ -128,6 +131,13 @@ pub fn hadamard(a: &[F], b: &[F]) -> Result, Error> {
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)]
pub mod tests {
use super::*;

+ 11
- 13
solidity-verifiers/src/verifiers/mod.rs

@ -68,8 +68,8 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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();

Loading…
Cancel
Save