|
1 | 1 | //! Non-hiding variant of KZG10 scheme for univariate polynomials.
|
| 2 | +use crate::zip_with_for_each; |
2 | 3 | use abomonation_derive::Abomonation;
|
3 | 4 | use ff::{Field, PrimeField, PrimeFieldBits};
|
4 | 5 | use group::{prime::PrimeCurveAffine, Curve, Group as _};
|
| 6 | +use itertools::Itertools as _; |
5 | 7 | use pairing::{Engine, MillerLoopResult, MultiMillerLoop};
|
6 | 8 | use rand_core::{CryptoRng, RngCore};
|
| 9 | +use rayon::iter::{IntoParallelIterator, ParallelIterator}; |
7 | 10 | use serde::{Deserialize, Serialize};
|
8 | 11 | use std::{borrow::Borrow, marker::PhantomData, ops::Mul};
|
9 | 12 |
|
@@ -278,6 +281,20 @@ where
|
278 | 281 | Ok(UVKZGCommitment(C.to_affine()))
|
279 | 282 | }
|
280 | 283 |
|
| 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 | + |
281 | 298 | /// On input a polynomial `p` and a point `point`, outputs a proof for the
|
282 | 299 | /// same.
|
283 | 300 | pub fn open(
|
@@ -307,6 +324,31 @@ where
|
307 | 324 | ))
|
308 | 325 | }
|
309 | 326 |
|
| 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 | + |
310 | 352 | /// Verifies that `value` is the evaluation at `x` of the polynomial
|
311 | 353 | /// committed inside `comm`.
|
312 | 354 | #[allow(dead_code, clippy::unnecessary_wraps)]
|
@@ -335,6 +377,61 @@ where
|
335 | 377 | let pairing_result = E::multi_miller_loop(pairing_input_refs.as_slice()).final_exponentiation();
|
336 | 378 | Ok(pairing_result.is_identity().into())
|
337 | 379 | }
|
| 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 | + } |
338 | 435 | }
|
339 | 436 |
|
340 | 437 | #[cfg(test)]
|
@@ -379,4 +476,47 @@ mod tests {
|
379 | 476 | fn end_to_end_test() {
|
380 | 477 | end_to_end_test_template::<halo2curves::bn256::Bn256>().expect("test failed for Bn256");
|
381 | 478 | }
|
| 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 | + } |
382 | 522 | }
|
0 commit comments