Skip to content

Commit

Permalink
refactor: use Horner in UV evaluation
Browse files Browse the repository at this point in the history
chore: move comment

fix: standardize power sequences computation
fix: parallelize several poly computations

refactor: Refactor `EvaluationArgument` struct in mlkzg.rs

- Renamed several fields in `EvaluationArgument` struct within `src/provider/mlkzg.rs` for increased clarity.
- Adjusted the `prove` and `verify` methods in `src/provider/mlkzg.rs` to reflect these name changes.
- Modified test code to align with the updates in the `EvaluationArgument` structure.
  • Loading branch information
huitseeker committed Dec 18, 2023
1 parent 8889cae commit 45e0f9b
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 44 deletions.
69 changes: 31 additions & 38 deletions src/provider/mlkzg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};
deserialize = "E::G1Affine: Deserialize<'de>, E::Fr: Deserialize<'de>"
))]
pub struct EvaluationArgument<E: Engine> {
com: Vec<E::G1Affine>,
w: Vec<E::G1Affine>,
v: Vec<Vec<E::Fr>>,
evals_r: Vec<E::G1Affine>,
evals_neg_r: Vec<E::G1Affine>,
evals_r_squared: Vec<Vec<E::Fr>>,
}

/// Provides an implementation of a polynomial evaluation engine using KZG
Expand All @@ -40,6 +40,8 @@ pub struct EvaluationEngine<E, NE> {
_p: PhantomData<(E, NE)>,
}

// This impl block defines helper functions that are not a part of
// EvaluationEngineTrait, but that we will use to implement the trait methods.
impl<E, NE> EvaluationEngine<E, NE>
where
E: Engine,
Expand All @@ -48,8 +50,6 @@ where
E::Fr: TranscriptReprTrait<E::G1>,
E::G1Affine: TranscriptReprTrait<E::G1>, // TODO: this bound on DlogGroup is really unusable!
{
// This impl block defines helper functions that are not a part of
// EvaluationEngineTrait, but that we will use to implement the trait methods.
fn compute_challenge(
C: &E::G1Affine,
y: &E::Fr,
Expand Down Expand Up @@ -87,11 +87,9 @@ where

fn batch_challenge_powers(q: E::Fr, k: usize) -> Vec<E::Fr> {
// Compute powers of q : (1, q, q^2, ..., q^(k-1))
let mut q_powers = vec![E::Fr::ONE; k];
for i in 1..k {
q_powers[i] = q_powers[i - 1] * q;
}
q_powers
std::iter::successors(Some(E::Fr::ONE), |&x| Some(x * q))
.take(k)
.collect()
}

fn verifier_second_challenge(
Expand Down Expand Up @@ -179,22 +177,21 @@ where
transcript: &mut <NE as NovaEngine>::TE|
-> (Vec<E::G1Affine>, Vec<Vec<E::Fr>>) {
let poly_eval = |f: &[E::Fr], u: E::Fr| -> E::Fr {
let mut v = f[0];
let mut u_power = E::Fr::ONE;

for fi in f.iter().skip(1) {
u_power *= u;
v += u_power * fi;
}

v
// Horner's scheme
f.iter()
.rev()
.skip(1)
.fold(f.last().cloned().unwrap_or_default(), |acc, &fi| {
acc * u + fi
})
};

let scalar_vector_muladd = |a: &mut Vec<E::Fr>, v: &Vec<E::Fr>, s: E::Fr| {
assert!(a.len() >= v.len());
for i in 0..v.len() {
a[i] += s * v[i];
}
#[allow(clippy::disallowed_methods)]
a.par_iter_mut()
.zip(v.par_iter())
.for_each(|(c, v)| *c += s * v);
};

let kzg_compute_batch_polynomial = |f: &[Vec<E::Fr>], q: E::Fr| -> Vec<E::Fr> {
Expand All @@ -219,23 +216,19 @@ where
// The verifier needs f_i(u_j), so we compute them here
// (V will compute B(u_j) itself)
let mut v = vec![vec!(E::Fr::ZERO; k); t];
for i in 0..t {
v.par_iter_mut().enumerate().for_each(|(i, v_i)| {
// for each point u
for (j, f_j) in f.iter().enumerate().take(k) {
v_i.par_iter_mut().zip_eq(f).for_each(|(v_ij, f)| {
// for each poly f
v[i][j] = poly_eval(f_j, u[i]); // = f_j(u_i)
}
}
*v_ij = poly_eval(&f, u[i]);
});
});

let q = Self::get_batch_challenge(C, u, &v, transcript);
let B = kzg_compute_batch_polynomial(f, q);

// Now open B at u0, ..., u_{t-1}
let mut w = Vec::with_capacity(t);
for ui in u {
let wi = kzg_open(&B, *ui);
w.push(wi);
}
let w = u.par_iter().map(|ui| kzg_open(&B, *ui)).collect::<Vec<_>>();

// Compute the commitment to the batched polynomial B(X)
let q_powers = Self::batch_challenge_powers(q, k);
Expand Down Expand Up @@ -297,7 +290,7 @@ where
com_all.insert(0, C.comm.preprocessed());
let (w, v) = kzg_open_batch(&com_all, &polys, &u, transcript);

Ok(EvaluationArgument { com, w, v })
Ok(EvaluationArgument { evals_r: com, evals_neg_r: w, evals_r_squared: v })
}

/// A method to verify purported evaluations of a batch of polynomials
Expand Down Expand Up @@ -385,7 +378,7 @@ where

let ell = x.len();

let mut com = pi.com.clone();
let mut com = pi.evals_r.clone();

// we do not need to add x to the transcript, because in our context x was
// obtained from the transcript
Expand All @@ -399,7 +392,7 @@ where
let u = vec![r, -r, r * r];

// Setup vectors (Y, ypos, yneg) from pi.v
let v = &pi.v;
let v = &pi.evals_r_squared;
if v.len() != 3 {
return Err(NovaError::ProofVerifyError);
}
Expand Down Expand Up @@ -428,7 +421,7 @@ where
}

// Check commitments to (Y, ypos, yneg) are valid
if !kzg_verify_batch(vk, &com, &pi.w, &u, &pi.v, transcript) {
if !kzg_verify_batch(vk, &com, &pi.evals_neg_r, &u, &pi.evals_r_squared, transcript) {
return Err(NovaError::ProofVerifyError);
}

Expand Down Expand Up @@ -552,7 +545,7 @@ mod tests {

// Change the proof and expect verification to fail
let mut bad_proof = proof.clone();
bad_proof.com[0] = (bad_proof.com[0] + bad_proof.com[1]).to_affine();
bad_proof.evals_r[0] = (bad_proof.evals_r[0] + bad_proof.evals_r[1]).to_affine();
let mut verifier_transcript2 = Keccak256Transcript::<NE>::new(b"TestEval");
assert!(EvaluationEngine::<E, NE>::verify(
&vk,
Expand Down Expand Up @@ -605,7 +598,7 @@ mod tests {

// Change the proof and expect verification to fail
let mut bad_proof = proof.clone();
bad_proof.com[0] = (bad_proof.com[0] + bad_proof.com[1]).to_affine();
bad_proof.evals_r[0] = (bad_proof.evals_r[0] + bad_proof.evals_r[1]).to_affine();
let mut verifier_tr2 = Keccak256Transcript::<NE>::new(b"TestEval");
assert!(EvaluationEngine::<E, NE>::verify(
&vk,
Expand Down
9 changes: 3 additions & 6 deletions src/spartan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,9 @@ use rayon::{iter::IntoParallelRefIterator, prelude::*};
// Creates a vector of the first `n` powers of `s`.
fn powers<E: Engine>(s: &E::Scalar, n: usize) -> Vec<E::Scalar> {
assert!(n >= 1);
let mut powers = Vec::with_capacity(n);
powers.push(E::Scalar::ONE);
for i in 1..n {
powers.push(powers[i - 1] * s);
}
powers
std::iter::successors(Some(E::Scalar::ONE), |&x| Some(x * s))
.take(n)
.collect()
}

/// A type that holds a witness to a polynomial evaluation instance
Expand Down

0 comments on commit 45e0f9b

Please sign in to comment.