Skip to content

Commit 44b9352

Browse files
huitseekersamuelburnham
authored andcommitted
feat: Implement batch operations in non_hiding_kzg module (#269)
* feat: Implement batch operations in non_hiding_kzg module - Added a `batch_commit` method to generate multiple commitments from a list of polynomials. - Introduced a `batch_open` functionality for evaluating multiple polynomials at different points. - Implemented `batch_verify` function for validation of polynomial evaluations in a multi-commitment setup. - Verified the correctness of the batch operations with a new unit test `batch_check_test`. * fix: convert to zip_with syntax
1 parent d128c82 commit 44b9352

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed

src/provider/non_hiding_kzg.rs

+140
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
//! Non-hiding variant of KZG10 scheme for univariate polynomials.
2+
use crate::zip_with_for_each;
23
use abomonation_derive::Abomonation;
34
use ff::{Field, PrimeField, PrimeFieldBits};
45
use group::{prime::PrimeCurveAffine, Curve, Group as _};
6+
use itertools::Itertools as _;
57
use pairing::{Engine, MillerLoopResult, MultiMillerLoop};
68
use rand_core::{CryptoRng, RngCore};
9+
use rayon::iter::{IntoParallelIterator, ParallelIterator};
710
use serde::{Deserialize, Serialize};
811
use std::{borrow::Borrow, marker::PhantomData, ops::Mul};
912

@@ -278,6 +281,20 @@ where
278281
Ok(UVKZGCommitment(C.to_affine()))
279282
}
280283

284+
/// Generate a commitment for a list of polynomials
285+
#[allow(dead_code)]
286+
pub fn batch_commit(
287+
prover_param: impl Borrow<KZGProverKey<E>>,
288+
polys: &[UVKZGPoly<E::Fr>],
289+
) -> Result<Vec<UVKZGCommitment<E>>, NovaError> {
290+
let prover_param = prover_param.borrow();
291+
292+
polys
293+
.into_par_iter()
294+
.map(|poly| Self::commit(prover_param, poly))
295+
.collect::<Result<Vec<UVKZGCommitment<E>>, NovaError>>()
296+
}
297+
281298
/// On input a polynomial `p` and a point `point`, outputs a proof for the
282299
/// same.
283300
pub fn open(
@@ -307,6 +324,31 @@ where
307324
))
308325
}
309326

327+
/// Input a list of polynomials, and a same number of points,
328+
/// compute a multi-opening for all the polynomials.
329+
// This is a naive approach
330+
// TODO: to implement a more efficient batch opening algorithm
331+
#[allow(dead_code)]
332+
pub fn batch_open(
333+
prover_param: impl Borrow<KZGProverKey<E>>,
334+
polynomials: &[UVKZGPoly<E::Fr>],
335+
points: &[E::Fr],
336+
) -> Result<(Vec<UVKZGProof<E>>, Vec<UVKZGEvaluation<E>>), NovaError> {
337+
if polynomials.len() != points.len() {
338+
// TODO: a better Error
339+
return Err(NovaError::PCSError(PCSError::LengthError));
340+
}
341+
let mut batch_proof = vec![];
342+
let mut evals = vec![];
343+
for (poly, point) in polynomials.iter().zip_eq(points.iter()) {
344+
let (proof, eval) = Self::open(prover_param.borrow(), poly, point)?;
345+
batch_proof.push(proof);
346+
evals.push(eval);
347+
}
348+
349+
Ok((batch_proof, evals))
350+
}
351+
310352
/// Verifies that `value` is the evaluation at `x` of the polynomial
311353
/// committed inside `comm`.
312354
#[allow(dead_code, clippy::unnecessary_wraps)]
@@ -335,6 +377,61 @@ where
335377
let pairing_result = E::multi_miller_loop(pairing_input_refs.as_slice()).final_exponentiation();
336378
Ok(pairing_result.is_identity().into())
337379
}
380+
381+
/// Verifies that `value_i` is the evaluation at `x_i` of the polynomial
382+
/// `poly_i` committed inside `comm`.
383+
// This is a naive approach
384+
// TODO: to implement the more efficient batch verification algorithm
385+
#[allow(dead_code, clippy::unnecessary_wraps)]
386+
pub fn batch_verify<R: RngCore + CryptoRng>(
387+
verifier_params: impl Borrow<KZGVerifierKey<E>>,
388+
multi_commitment: &[UVKZGCommitment<E>],
389+
points: &[E::Fr],
390+
values: &[UVKZGEvaluation<E>],
391+
batch_proof: &[UVKZGProof<E>],
392+
rng: &mut R,
393+
) -> Result<bool, NovaError> {
394+
let verifier_params = verifier_params.borrow();
395+
396+
let mut total_c = <E::G1>::identity();
397+
let mut total_w = <E::G1>::identity();
398+
399+
let mut randomizer = E::Fr::ONE;
400+
// Instead of multiplying g and gamma_g in each turn, we simply accumulate
401+
// their coefficients and perform a final multiplication at the end.
402+
let mut g_multiplier = E::Fr::ZERO;
403+
zip_with_for_each!(
404+
into_iter,
405+
(multi_commitment, points, values, batch_proof),
406+
|c, z, v, proof| {
407+
let w = proof.proof;
408+
let mut temp = w.mul(*z);
409+
temp += &c.0;
410+
let c = temp;
411+
g_multiplier += &(randomizer * v.0);
412+
total_c += &c.mul(randomizer);
413+
total_w += &w.mul(randomizer);
414+
// We don't need to sample randomizers from the full field,
415+
// only from 128-bit strings.
416+
randomizer = E::Fr::from_u128(rand::Rng::gen::<u128>(rng));
417+
}
418+
);
419+
total_c -= &verifier_params.g.mul(g_multiplier);
420+
421+
let mut affine_points = vec![E::G1Affine::identity(); 2];
422+
E::G1::batch_normalize(&[-total_w, total_c], &mut affine_points);
423+
let (total_w, total_c) = (affine_points[0], affine_points[1]);
424+
425+
let result = E::multi_miller_loop(&[
426+
(&total_w, &verifier_params.beta_h.into()),
427+
(&total_c, &verifier_params.h.into()),
428+
])
429+
.final_exponentiation()
430+
.is_identity()
431+
.into();
432+
433+
Ok(result)
434+
}
338435
}
339436

340437
#[cfg(test)]
@@ -379,4 +476,47 @@ mod tests {
379476
fn end_to_end_test() {
380477
end_to_end_test_template::<halo2curves::bn256::Bn256>().expect("test failed for Bn256");
381478
}
479+
480+
fn batch_check_test_template<E>() -> Result<(), NovaError>
481+
where
482+
E: MultiMillerLoop,
483+
E::Fr: PrimeFieldBits,
484+
E::G1: DlogGroup<ScalarExt = E::Fr, AffineExt = E::G1Affine>,
485+
{
486+
for _ in 0..10 {
487+
let mut rng = &mut thread_rng();
488+
489+
let degree = rng.gen_range(2..20);
490+
491+
let pp = UniversalKZGParam::<E>::gen_srs_for_testing(&mut rng, degree);
492+
let (ck, vk) = pp.trim(degree);
493+
494+
let mut comms = Vec::new();
495+
let mut values = Vec::new();
496+
let mut points = Vec::new();
497+
let mut proofs = Vec::new();
498+
for _ in 0..10 {
499+
let mut rng = rng.clone();
500+
let p = random(degree, &mut rng);
501+
let comm = UVKZGPCS::<E>::commit(&ck, &p)?;
502+
let point = E::Fr::random(rng);
503+
let (proof, value) = UVKZGPCS::<E>::open(&ck, &p, &point)?;
504+
505+
assert!(UVKZGPCS::<E>::verify(&vk, &comm, &point, &proof, &value)?);
506+
comms.push(comm);
507+
values.push(value);
508+
points.push(point);
509+
proofs.push(proof);
510+
}
511+
assert!(UVKZGPCS::<E>::batch_verify(
512+
&vk, &comms, &points, &values, &proofs, &mut rng
513+
)?);
514+
}
515+
Ok(())
516+
}
517+
518+
#[test]
519+
fn batch_check_test() {
520+
batch_check_test_template::<halo2curves::bn256::Bn256>().expect("test failed for Bn256");
521+
}
382522
}

0 commit comments

Comments
 (0)