Skip to content

Commit

Permalink
feat: add batch commit / open / prove
Browse files Browse the repository at this point in the history
  • Loading branch information
huitseeker committed Aug 15, 2023
1 parent e69fad9 commit 7e210a5
Showing 1 changed file with 138 additions and 0 deletions.
138 changes: 138 additions & 0 deletions src/provider/non_hiding_kzg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use std::{borrow::Borrow, marker::PhantomData, ops::Mul};
use ff::{Field, PrimeField};
use group::{prime::PrimeCurveAffine, Curve, Group as _};
use pairing::{Engine, MillerLoopResult, MultiMillerLoop};
use rand::Rng;
use rand_core::{CryptoRng, RngCore};
use rayon::prelude::{IntoParallelIterator, ParallelIterator};

use crate::{errors::NovaError, traits::Group};

Expand Down Expand Up @@ -220,6 +222,19 @@ where
Ok(UVKZGCommitment(C.to_affine()))
}

/// Generate a commitment for a list of polynomials
pub fn batch_commit(
prover_param: impl Borrow<UVKZGProverKey<E>>,
polys: &[UVKZGPoly<E::Fr>],
) -> Result<Vec<UVKZGCommitment<E>>, NovaError> {
let prover_param = prover_param.borrow();

polys
.into_par_iter()
.map(|poly| Self::commit(prover_param, poly))
.collect::<Result<Vec<UVKZGCommitment<E>>, NovaError>>()
}

/// On input a polynomial `p` and a point `point`, outputs a proof for the
/// same.
pub fn open(
Expand Down Expand Up @@ -250,6 +265,31 @@ where
))
}

/// Input a list of polynomials, and a same number of points,
/// compute a multi-opening for all the polynomials.
// This is a naive approach
// TODO: to implement a more efficient batch opening algorithm
// (e.g., the appendix C.4 in https://eprint.iacr.org/2020/1536.pdf)
pub fn batch_open(
prover_param: impl Borrow<UVKZGProverKey<E>>,
polynomials: &[UVKZGPoly<E::Fr>],
points: &[E::Fr],
) -> Result<(Vec<UVKZGProof<E>>, Vec<UVKZGEvaluation<E>>), NovaError> {
if polynomials.len() != points.len() {
// TODO: a better Error
return Err(NovaError::InvalidIPA);
}
let mut batch_proof = vec![];
let mut evals = vec![];
for (poly, point) in polynomials.iter().zip(points.iter()) {
let (proof, eval) = Self::open(prover_param.borrow(), poly, point)?;
batch_proof.push(proof);
evals.push(eval);
}

Ok((batch_proof, evals))
}

/// Verifies that `value` is the evaluation at `x` of the polynomial
/// committed inside `comm`.
pub fn verify(
Expand All @@ -276,6 +316,62 @@ where
let pairing_result = E::multi_miller_loop(pairing_input_refs.as_slice()).final_exponentiation();
Ok(pairing_result.is_identity().into())
}

/// Verifies that `value_i` is the evaluation at `x_i` of the polynomial
/// `poly_i` committed inside `comm`.
// This is a naive approach
// TODO: to implement the more efficient batch verification algorithm
// (e.g., the appendix C.4 in https://eprint.iacr.org/2020/1536.pdf)
pub fn batch_verify<R: RngCore + CryptoRng>(
verifier_params: impl Borrow<UVKZGVerifierKey<E>>,
multi_commitment: &[UVKZGCommitment<E>],
points: &[E::Fr],
values: &[UVKZGEvaluation<E>],
batch_proof: &[UVKZGProof<E>],
rng: &mut R,
) -> Result<bool, NovaError> {
let verifier_params = verifier_params.borrow();

let mut total_c = <E::G1>::identity();
let mut total_w = <E::G1>::identity();

let mut randomizer = E::Fr::ONE;
// Instead of multiplying g and gamma_g in each turn, we simply accumulate
// their coefficients and perform a final multiplication at the end.
let mut g_multiplier = E::Fr::ZERO;
for (((c, z), v), proof) in multi_commitment
.iter()
.zip(points)
.zip(values)
.zip(batch_proof)
{
let w = proof.proof;
let mut temp = w.mul(*z);
temp += &c.0;
let c = temp;
g_multiplier += &(randomizer * v.0);
total_c += &c.mul(randomizer);
total_w += &w.mul(randomizer);
// We don't need to sample randomizers from the full field,
// only from 128-bit strings.
randomizer = E::Fr::from_u128(rng.gen::<u128>());
}
total_c -= &verifier_params.g.mul(g_multiplier);

let mut affine_points = vec![E::G1Affine::identity(); 2];
E::G1::batch_normalize(&[-total_w, total_c], &mut affine_points);
let (total_w, total_c) = (affine_points[0], affine_points[1]);

let result = E::multi_miller_loop(&[
(&total_w, &verifier_params.beta_h.into()),
(&total_c, &verifier_params.h.into()),
])
.final_exponentiation()
.is_identity()
.into();

Ok(result)
}
}

#[cfg(test)]
Expand Down Expand Up @@ -309,8 +405,50 @@ mod tests {
Ok(())
}

fn batch_check_test_template<E>() -> Result<(), NovaError>
where
E: MultiMillerLoop,
E::G1: Group<PreprocessedGroupElement = E::G1Affine, Scalar = E::Fr>,
{
for _ in 0..10 {
let mut rng = &mut thread_rng();

let degree = rng.gen_range(2..20);

let pp = UVUniversalKZGParam::<E>::gen_srs_for_testing(&mut rng, degree);
let (ck, vk) = pp.trim(degree);

let mut comms = Vec::new();
let mut values = Vec::new();
let mut points = Vec::new();
let mut proofs = Vec::new();
for _ in 0..10 {
let mut rng = rng.clone();
let p = UVKZGPoly::random(degree, &mut rng);
let comm = UVKZGPCS::<E>::commit(&ck, &p)?;
let point = E::Fr::random(rng);
let (proof, value) = UVKZGPCS::<E>::open(&ck, &p, &point)?;

assert!(UVKZGPCS::<E>::verify(&vk, &comm, &point, &proof, &value)?);
comms.push(comm);
values.push(value);
points.push(point);
proofs.push(proof);
}
assert!(UVKZGPCS::<E>::batch_verify(
&vk, &comms, &points, &values, &proofs, &mut rng
)?);
}
Ok(())
}

#[test]
fn end_to_end_test() {
end_to_end_test_template::<halo2curves::bn256::Bn256>().expect("test failed for Bn256");
}

#[test]
fn batch_check_test() {
batch_check_test_template::<halo2curves::bn256::Bn256>().expect("test failed for Bn256");
}
}

0 comments on commit 7e210a5

Please sign in to comment.