mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-08 15:01:30 +01:00
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.
This commit is contained in:
@@ -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"}
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
@@ -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<FC: FCircuit<Fr>>(
|
||||
// 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(),
|
||||
|
||||
@@ -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<C: CurveGroup> {
|
||||
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())
|
||||
.collect();
|
||||
PedersenParams::<C> {
|
||||
h: C::rand(rng),
|
||||
generators,
|
||||
}
|
||||
}
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
// 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>;
|
||||
/// 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();
|
||||
let p = PedersenParams::<C> {
|
||||
h: C::rand(&mut rng),
|
||||
generators,
|
||||
};
|
||||
Ok((p.clone(), p))
|
||||
}
|
||||
|
||||
fn commit(
|
||||
params: &PedersenParams<C>,
|
||||
a: &[C::ScalarField],
|
||||
@@ -74,6 +76,10 @@ impl<C: CurveGroup, const H: bool> CommitmentProver<C, H> for IPA<C, H> {
|
||||
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<C: CurveGroup, const H: bool> CommitmentProver<C, H> for IPA<C, H> {
|
||||
}
|
||||
|
||||
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<C: CurveGroup, const H: bool> CommitmentProver<C, H> for IPA<C, H> {
|
||||
}
|
||||
|
||||
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<C: CurveGroup, const H: bool> CommitmentProver<C, H> for IPA<C, H> {
|
||||
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<C: CurveGroup, const H: bool> IPA<C, H> {
|
||||
transcript.absorb_point(&p.R[i])?;
|
||||
u[i] = transcript.get_challenge();
|
||||
}
|
||||
let challenge = (x, U, u);
|
||||
|
||||
let P = P + U.mul(v);
|
||||
Self::verify_with_challenge(params, challenge, P, proof)
|
||||
}
|
||||
|
||||
fn verify_with_challenge(
|
||||
params: &Self::VerifierParams,
|
||||
challenge: Self::Challenge,
|
||||
P: &C, // commitment
|
||||
proof: &Self::Proof,
|
||||
) -> Result<(), Error> {
|
||||
let (p, v, r) = (proof.0.clone(), proof.1, proof.2);
|
||||
let (x, U, u) = challenge;
|
||||
|
||||
let k = p.L.len();
|
||||
if p.R.len() != k {
|
||||
return Err(Error::CommitmentVerificationFail);
|
||||
}
|
||||
if !H && (!r.is_zero()) {
|
||||
return Err(Error::BlindingNotZero);
|
||||
}
|
||||
if !H && (!p.l.is_empty() || !p.r.is_empty()) {
|
||||
return Err(Error::CommitmentVerificationFail);
|
||||
}
|
||||
if H && (p.l.len() != k || p.r.len() != k) {
|
||||
return Err(Error::CommitmentVerificationFail);
|
||||
}
|
||||
|
||||
let P = *P + U.mul(v); // where v=p(x)
|
||||
|
||||
let mut q_0 = P;
|
||||
let mut r = r;
|
||||
@@ -262,7 +302,10 @@ impl<C: CurveGroup, const H: bool> IPA<C, H> {
|
||||
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(¶ms, &a, &r_blind).unwrap();
|
||||
|
||||
// evaluation point
|
||||
let x = Fr::rand(&mut rng);
|
||||
|
||||
let proof = IPA::<Projective, hiding>::prove(
|
||||
¶ms,
|
||||
&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(
|
||||
¶ms,
|
||||
&mut transcript_v,
|
||||
x,
|
||||
v,
|
||||
cm,
|
||||
proof,
|
||||
r_blind,
|
||||
k,
|
||||
)
|
||||
.unwrap());
|
||||
IPA::<Projective, hiding>::verify(¶ms, &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(¶ms, &a, &r_blind).unwrap();
|
||||
|
||||
// evaluation point
|
||||
let x = Fr::rand(&mut rng);
|
||||
|
||||
let proof = IPA::<Projective, hiding>::prove(
|
||||
¶ms,
|
||||
&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(
|
||||
¶ms,
|
||||
&mut transcript_v,
|
||||
x,
|
||||
v,
|
||||
cm,
|
||||
proof.clone(),
|
||||
r_blind,
|
||||
k,
|
||||
)
|
||||
.unwrap());
|
||||
IPA::<Projective, hiding>::verify(¶ms, &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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
impl<'a, P> KZGSetup<P>
|
||||
|
||||
/// 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, 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(
|
||||
¶ms.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(
|
||||
¶ms.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<F: PrimeField>(p: &[F]) -> Vec<F::BigInt> {
|
||||
#[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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: CurveGroup, const H: bool = false> {
|
||||
_c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: CurveGroup, const H: bool> Pedersen<C, H> {
|
||||
pub fn new_params<R: Rng>(rng: &mut R, max: usize) -> Params<C> {
|
||||
let generators: Vec<C::Affine> = std::iter::repeat_with(|| C::Affine::rand(rng))
|
||||
.take(max.next_power_of_two())
|
||||
.collect();
|
||||
Params::<C> {
|
||||
h: C::rand(rng),
|
||||
generators,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// implement the CommitmentProver trait for Pedersen
|
||||
impl<C: CurveGroup, const H: bool> CommitmentProver<C, H> for Pedersen<C, H> {
|
||||
type Params = Params<C>;
|
||||
/// 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();
|
||||
let p = Params::<C> {
|
||||
h: C::rand(&mut rng),
|
||||
generators,
|
||||
};
|
||||
Ok((p.clone(), p))
|
||||
}
|
||||
|
||||
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<C: CurveGroup, const H: bool> CommitmentProver<C, H> for Pedersen<C, H> {
|
||||
}
|
||||
|
||||
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<C: CurveGroup, const H: bool> CommitmentProver<C, H> for Pedersen<C, H> {
|
||||
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<C: CurveGroup, const H: bool> CommitmentProver<C, H> for Pedersen<C, H> {
|
||||
|
||||
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<C: CurveGroup, const H: bool> Pedersen<C, H> {
|
||||
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(¶ms, &v, &r).unwrap();
|
||||
// blinding factor
|
||||
let r: Fr = if hiding {
|
||||
Fr::rand(&mut rng)
|
||||
} else {
|
||||
Fr::zero()
|
||||
};
|
||||
let cm = Pedersen::<Projective, hiding>::commit(¶ms, &v, &r).unwrap();
|
||||
let proof =
|
||||
Pedersen::<Projective>::prove(¶ms, &mut transcript_p, &cm, &v, &r, None).unwrap();
|
||||
Pedersen::<Projective>::verify(¶ms, &mut transcript_v, cm, proof).unwrap();
|
||||
Pedersen::<Projective, hiding>::prove(¶ms, &mut transcript_p, &cm, &v, &r, None)
|
||||
.unwrap();
|
||||
Pedersen::<Projective, hiding>::verify(¶ms, &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(¶ms, &v, &r).unwrap();
|
||||
// blinding factor
|
||||
let r: Fr = if hiding {
|
||||
Fr::rand(&mut rng)
|
||||
} else {
|
||||
Fr::zero()
|
||||
};
|
||||
let cm = Pedersen::<Projective, hiding>::commit(¶ms, &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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<C: CurveGroup> CCS<C> {
|
||||
) -> 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<C: CurveGroup> CCCS<C> {
|
||||
) -> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<C: CurveGroup> CCS<C> {
|
||||
) -> 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<C: CurveGroup> LCCCS<C> {
|
||||
) -> 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 prover_params =
|
||||
ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> {
|
||||
poseidon_config: poseidon_config.clone(),
|
||||
cm_params: pedersen_params,
|
||||
cf_cm_params: cf_pedersen_params,
|
||||
};
|
||||
println!("generating pedersen params, {:?}", start.elapsed());
|
||||
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, KZG<Bn254>, Pedersen<Projective2>> {
|
||||
poseidon_config: poseidon_config.clone(),
|
||||
cs_params: kzg_pk.clone(),
|
||||
cf_cs_params: cf_pedersen_params,
|
||||
};
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<C1: CurveGroup, C2: CurveGroup> {
|
||||
/// 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<C: CurveGroup>(cm: &C) -> Vec<C::BaseField> {
|
||||
#[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 (kzg_pk, _): (KZGProverKey<Projective>, KZGVerifierKey<Bn254>) =
|
||||
KZG::<Bn254>::setup(&mut rng, cs_len).unwrap();
|
||||
let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, cs_len).unwrap();
|
||||
let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cs_len).unwrap();
|
||||
|
||||
let prover_params =
|
||||
ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> {
|
||||
poseidon_config: poseidon_config.clone(),
|
||||
cm_params: pedersen_params,
|
||||
cf_cm_params: cf_pedersen_params,
|
||||
};
|
||||
// run the test using Pedersen commitments on both sides of the curve cycle
|
||||
test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>>(
|
||||
poseidon_config.clone(),
|
||||
pedersen_params,
|
||||
cf_pedersen_params.clone(),
|
||||
F_circuit,
|
||||
);
|
||||
// run the test using KZG for the commitments on the main curve, and Pedersen for the
|
||||
// commitments on the secondary curve
|
||||
test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>>(
|
||||
poseidon_config,
|
||||
kzg_pk,
|
||||
cf_pedersen_params,
|
||||
F_circuit,
|
||||
);
|
||||
}
|
||||
|
||||
// test_ivc allowing to choose the CommitmentSchemes
|
||||
fn test_ivc_opt<CS1: CommitmentScheme<Projective>, CS2: CommitmentScheme<Projective2>>(
|
||||
poseidon_config: PoseidonConfig<Fr>,
|
||||
cs_params: CS1::ProverParams,
|
||||
cf_cs_params: CS2::ProverParams,
|
||||
F_circuit: CubicFCircuit<Fr>,
|
||||
) {
|
||||
type NOVA<CS1, CS2> =
|
||||
Nova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2>;
|
||||
|
||||
let prover_params = ProverParams::<Projective, Projective2, CS1, CS2> {
|
||||
poseidon_config: poseidon_config.clone(),
|
||||
cs_params,
|
||||
cf_cs_params,
|
||||
};
|
||||
|
||||
let z_0 = vec![Fr::from(3_u32)];
|
||||
let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap();
|
||||
|
||||
let num_steps: usize = 3;
|
||||
@@ -782,7 +806,7 @@ 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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<F: PrimeField>(a: &[F], b: &[F]) -> Result<Vec<F>, 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::*;
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user