Skip to content

Commit 594bb10

Browse files
committed
refactor: use Horner in UV evaluation
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.
1 parent eb2688c commit 594bb10

File tree

2 files changed

+45
-44
lines changed

2 files changed

+45
-44
lines changed

src/provider/mlkzg.rs

+42-38
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};
2929
deserialize = "E::G1Affine: Deserialize<'de>, E::Fr: Deserialize<'de>"
3030
))]
3131
pub struct EvaluationArgument<E: Engine> {
32-
com: Vec<E::G1Affine>,
33-
w: Vec<E::G1Affine>,
34-
v: Vec<Vec<E::Fr>>,
32+
evals_r: Vec<E::G1Affine>,
33+
evals_neg_r: Vec<E::G1Affine>,
34+
evals_r_squared: Vec<Vec<E::Fr>>,
3535
}
3636

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

43+
// This impl block defines helper functions that are not a part of
44+
// EvaluationEngineTrait, but that we will use to implement the trait methods.
4345
impl<E, NE> EvaluationEngine<E, NE>
4446
where
4547
E: Engine,
@@ -48,8 +50,6 @@ where
4850
E::Fr: TranscriptReprTrait<E::G1>,
4951
E::G1Affine: TranscriptReprTrait<E::G1>, // TODO: this bound on DlogGroup is really unusable!
5052
{
51-
// This impl block defines helper functions that are not a part of
52-
// EvaluationEngineTrait, but that we will use to implement the trait methods.
5353
fn compute_challenge(
5454
C: &E::G1Affine,
5555
y: &E::Fr,
@@ -87,11 +87,9 @@ where
8787

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

9795
fn verifier_second_challenge(
@@ -179,22 +177,21 @@ where
179177
transcript: &mut <NE as NovaEngine>::TE|
180178
-> (Vec<E::G1Affine>, Vec<Vec<E::Fr>>) {
181179
let poly_eval = |f: &[E::Fr], u: E::Fr| -> E::Fr {
182-
let mut v = f[0];
183-
let mut u_power = E::Fr::ONE;
184-
185-
for fi in f.iter().skip(1) {
186-
u_power *= u;
187-
v += u_power * fi;
188-
}
189-
190-
v
180+
// Horner's scheme
181+
f.iter()
182+
.rev()
183+
.skip(1)
184+
.fold(f.last().cloned().unwrap_or_default(), |acc, &fi| {
185+
acc * u + fi
186+
})
191187
};
192188

193189
let scalar_vector_muladd = |a: &mut Vec<E::Fr>, v: &Vec<E::Fr>, s: E::Fr| {
194190
assert!(a.len() >= v.len());
195-
for i in 0..v.len() {
196-
a[i] += s * v[i];
197-
}
191+
#[allow(clippy::disallowed_methods)]
192+
a.par_iter_mut()
193+
.zip(v.par_iter())
194+
.for_each(|(c, v)| *c += s * v);
198195
};
199196

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

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

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

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

300-
Ok(EvaluationArgument { com, w, v })
293+
Ok(EvaluationArgument {
294+
evals_r: com,
295+
evals_neg_r: w,
296+
evals_r_squared: v,
297+
})
301298
}
302299

303300
/// A method to verify purported evaluations of a batch of polynomials
@@ -385,7 +382,7 @@ where
385382

386383
let ell = x.len();
387384

388-
let mut com = pi.com.clone();
385+
let mut com = pi.evals_r.clone();
389386

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

401398
// Setup vectors (Y, ypos, yneg) from pi.v
402-
let v = &pi.v;
399+
let v = &pi.evals_r_squared;
403400
if v.len() != 3 {
404401
return Err(NovaError::ProofVerifyError);
405402
}
@@ -428,7 +425,14 @@ where
428425
}
429426

430427
// Check commitments to (Y, ypos, yneg) are valid
431-
if !kzg_verify_batch(vk, &com, &pi.w, &u, &pi.v, transcript) {
428+
if !kzg_verify_batch(
429+
vk,
430+
&com,
431+
&pi.evals_neg_r,
432+
&u,
433+
&pi.evals_r_squared,
434+
transcript,
435+
) {
432436
return Err(NovaError::ProofVerifyError);
433437
}
434438

@@ -552,7 +556,7 @@ mod tests {
552556

553557
// Change the proof and expect verification to fail
554558
let mut bad_proof = proof.clone();
555-
bad_proof.com[0] = (bad_proof.com[0] + bad_proof.com[1]).to_affine();
559+
bad_proof.evals_r[0] = (bad_proof.evals_r[0] + bad_proof.evals_r[1]).to_affine();
556560
let mut verifier_transcript2 = Keccak256Transcript::<NE>::new(b"TestEval");
557561
assert!(EvaluationEngine::<E, NE>::verify(
558562
&vk,
@@ -605,7 +609,7 @@ mod tests {
605609

606610
// Change the proof and expect verification to fail
607611
let mut bad_proof = proof.clone();
608-
bad_proof.com[0] = (bad_proof.com[0] + bad_proof.com[1]).to_affine();
612+
bad_proof.evals_r[0] = (bad_proof.evals_r[0] + bad_proof.evals_r[1]).to_affine();
609613
let mut verifier_tr2 = Keccak256Transcript::<NE>::new(b"TestEval");
610614
assert!(EvaluationEngine::<E, NE>::verify(
611615
&vk,

src/spartan/mod.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,9 @@ use rayon::{iter::IntoParallelRefIterator, prelude::*};
3030
// Creates a vector of the first `n` powers of `s`.
3131
fn powers<E: Engine>(s: &E::Scalar, n: usize) -> Vec<E::Scalar> {
3232
assert!(n >= 1);
33-
let mut powers = Vec::with_capacity(n);
34-
powers.push(E::Scalar::ONE);
35-
for i in 1..n {
36-
powers.push(powers[i - 1] * s);
37-
}
38-
powers
33+
std::iter::successors(Some(E::Scalar::ONE), |&x| Some(x * s))
34+
.take(n)
35+
.collect()
3936
}
4037

4138
/// A type that holds a witness to a polynomial evaluation instance

0 commit comments

Comments
 (0)