Skip to content

Commit

Permalink
DeciderEthCircuit: Add check eval=p(c) for E & W
Browse files Browse the repository at this point in the history
The check is temporary disabled due
#80,
but the public inputs and logic are there, to be able to continue the
other parts development while issue #80 is solved.
  • Loading branch information
arnaucube committed Mar 23, 2024
1 parent af5cb83 commit 8ff1838
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 20 deletions.
24 changes: 12 additions & 12 deletions folding-schemes/src/commitment/kzg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ 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::{
Commitment as KZG10Commitment, Proof as KZG10Proof, VerifierKey, KZG10,
Expand All @@ -22,6 +22,7 @@ use rayon::iter::{IntoParallelRefIterator, ParallelIterator};

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
Expand All @@ -32,6 +33,12 @@ pub struct ProverKey<'a, C: CurveGroup> {
pub powers_of_g: Cow<'a, [C::Affine]>,
}

#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub struct Proof<C: CurveGroup> {
pub evaluation: C::ScalarField,
pub proof: C,
}

/// KZG implements the CommitmentScheme trait for the KZG commitment scheme.
#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub struct KZG<'a, E: Pairing, const H: bool = false> {
Expand All @@ -45,7 +52,7 @@ where
type ProverParams = ProverKey<'a, E::G1>;
type VerifierParams = VerifierKey<E>;
/// Proof is a tuple containing (evaluation, proof)
type Proof = (E::ScalarField, E::G1);
type Proof = Proof<E::G1>;
type ProverChallenge = E::ScalarField;
type Challenge = E::ScalarField;

Expand Down Expand Up @@ -158,7 +165,7 @@ where
&witness_coeffs,
);

Ok((evaluation, proof))
Ok(Proof { evaluation, proof })
}

fn verify(
Expand Down Expand Up @@ -187,9 +194,9 @@ where
params, // vk
&KZG10Commitment(cm.into_affine()),
challenge,
proof.0, // eval
proof.evaluation,
&KZG10Proof::<E> {
w: proof.1.into_affine(),
w: proof.proof.into_affine(),
random_v: None,
},
)?;
Expand All @@ -200,13 +207,6 @@ where
}
}

/// 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 check_degree_is_too_large(
degree: usize,
num_powers: usize,
Expand Down
15 changes: 13 additions & 2 deletions folding-schemes/src/folding/nova/decider_eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ use super::{
nifs::NIFS,
CommittedInstance, Nova, Witness,
};
use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme};
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::frontend::FCircuit;
use crate::Error;
Expand Down Expand Up @@ -61,7 +63,12 @@ where
GC1: CurveVar<C1, CF2<C1>>,
GC2: CurveVar<C2, CF2<C2>>,
FC: FCircuit<C1::ScalarField>,
CS1: CommitmentScheme<C1, ProverChallenge = C1::ScalarField, Challenge = C1::ScalarField>, // KZG commitment, where challenge is C1::Fr elem
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>,
Expand Down Expand Up @@ -200,6 +207,10 @@ where
cmW_x,
cmW_y,
proof.kzg_challenges.to_vec(),
vec![
proof.kzg_proofs[0].evaluation, // eval_W
proof.kzg_proofs[1].evaluation, // eval_E
],
cmT_x,
cmT_y,
vec![proof.r],
Expand Down
97 changes: 91 additions & 6 deletions folding-schemes/src/folding/nova/decider_eth_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ use ark_crypto_primitives::crh::poseidon::constraints::CRHParametersVar;
use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb};
use ark_ec::{CurveGroup, Group};
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};
Expand All @@ -30,8 +32,9 @@ use crate::transcript::{
poseidon::{PoseidonTranscript, PoseidonTranscriptVar},
Transcript, TranscriptVar,
};
use crate::utils::gadgets::{
hadamard, mat_vec_mul_sparse, vec_add, vec_scalar_mul, SparseMatrixVar,
use crate::utils::{
gadgets::{hadamard, mat_vec_mul_sparse, vec_add, vec_scalar_mul, SparseMatrixVar},
vec::poly_from_vec,
};
use crate::Error;

Expand Down Expand Up @@ -234,6 +237,8 @@ where
/// 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, CS1, CS2> DeciderEthCircuit<C1, GC1, C2, GC2, CS1, CS2>
where
Expand Down Expand Up @@ -275,6 +280,22 @@ where
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,
Expand Down Expand Up @@ -304,6 +325,8 @@ where
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),
})
}
}
Expand Down Expand Up @@ -366,6 +389,12 @@ where
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(
cs.clone(),
Expand All @@ -377,7 +406,7 @@ where
[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_i1.E,
W_i1.E.clone(),
U_i1.u.clone(),
z_U1,
)?;
Expand Down Expand Up @@ -469,7 +498,16 @@ where
incircuit_c_W.enforce_equal(&kzg_c_W)?;
incircuit_c_E.enforce_equal(&kzg_c_E)?;

// 7. compute the NIFS.V challenge and check that matches the one from the public input (so we
// 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)))?;
Expand All @@ -481,7 +519,6 @@ where
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)))?;
Expand All @@ -491,6 +528,24 @@ where
}
}

/// 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> {
Expand Down Expand Up @@ -845,4 +900,34 @@ pub mod tests {
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());
}
}
10 changes: 10 additions & 0 deletions folding-schemes/src/utils/vec.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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::*;
Expand Down

0 comments on commit 8ff1838

Please sign in to comment.