mod.rs
  1  // Copyright (c) 2025-2026 ACDC Network
  2  // This file is part of the alphavm library.
  3  //
  4  // Alpha Chain | Delta Chain Protocol
  5  // International Monetary Graphite.
  6  //
  7  // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com).
  8  // They built world-class ZK infrastructure. We installed the EASY button.
  9  // Their cryptography: elegant. Our modifications: bureaucracy-compatible.
 10  // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours.
 11  //
 12  // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0
 13  // All modifications and new work: CC0 1.0 Universal Public Domain Dedication.
 14  // No rights reserved. No permission required. No warranty. No refunds.
 15  //
 16  // https://creativecommons.org/publicdomain/zero/1.0/
 17  // SPDX-License-Identifier: CC0-1.0
 18  
 19  //! Here we construct a polynomial commitment that enables users to commit to a
 20  //! single polynomial `p`, and then later provide an evaluation proof that
 21  //! convinces verifiers that a claimed value `v` is the true evaluation of `p`
 22  //! at a chosen point `x`. Our construction follows the template of the
 23  //! construction proposed by Kate, Zaverucha, and Goldberg ([KZG11](http://cacr.uwaterloo.ca/techreports/2010/cacr2010-10.pdf)).
 24  //! This construction achieves extractability in the algebraic group model
 25  //! (AGM).
 26  
 27  use crate::{
 28      fft::{DensePolynomial, Polynomial},
 29      msm::VariableBase,
 30      polycommit::PCError,
 31  };
 32  use alphavm_curves::traits::{AffineCurve, PairingCurve, PairingEngine, ProjectiveCurve};
 33  use alphavm_fields::{One, PrimeField, Zero};
 34  use alphavm_utilities::{cfg_iter, cfg_iter_mut, rand::Uniform, BitIteratorBE};
 35  
 36  use anyhow::{anyhow, ensure, Result};
 37  use core::{marker::PhantomData, ops::Mul};
 38  use itertools::Itertools;
 39  use rand::RngCore;
 40  
 41  #[cfg(not(feature = "serial"))]
 42  use rayon::prelude::*;
 43  
 44  mod data_structures;
 45  pub use data_structures::*;
 46  
 47  use super::sonic_pc::LabeledPolynomialWithBasis;
 48  
 49  #[derive(Debug, PartialEq, Eq)]
 50  pub enum KZGDegreeBounds {
 51      All,
 52      Varuna,
 53      List(Vec<usize>),
 54      None,
 55  }
 56  
 57  impl KZGDegreeBounds {
 58      pub fn get_list<F: PrimeField>(&self, max_degree: usize) -> Vec<usize> {
 59          match self {
 60              KZGDegreeBounds::All => (0..max_degree).collect(),
 61              KZGDegreeBounds::Varuna => {
 62                  // In Varuna, the degree bounds are all of the forms `domain_size - 2`.
 63                  // Consider that we are using radix-2 FFT,
 64                  // there are only a few possible domain sizes and therefore degree bounds.
 65                  //
 66                  // We do not consider mixed-radix FFT for simplicity, as the curves that we
 67                  // are using have very high two-arity.
 68  
 69                  let mut radix_2_possible_domain_sizes = vec![];
 70  
 71                  let mut cur = 2usize;
 72                  while cur - 2 <= max_degree {
 73                      radix_2_possible_domain_sizes.push(cur - 2);
 74                      cur *= 2;
 75                  }
 76  
 77                  radix_2_possible_domain_sizes
 78              }
 79              KZGDegreeBounds::List(v) => v.clone(),
 80              KZGDegreeBounds::None => vec![],
 81          }
 82      }
 83  }
 84  
 85  /// `KZG10` is an implementation of the polynomial commitment scheme of
 86  /// [Kate, Zaverucha and Goldbgerg][kzg10]
 87  ///
 88  /// [kzg10]: http://cacr.uwaterloo.ca/techreports/2010/cacr2010-10.pdf
 89  #[derive(Clone, Debug)]
 90  pub struct KZG10<E: PairingEngine>(PhantomData<E>);
 91  
 92  impl<E: PairingEngine> KZG10<E> {
 93      /// Constructs public parameters when given as input the maximum degree
 94      /// `degree` for the polynomial commitment scheme.
 95      pub fn load_srs(max_degree: usize) -> Result<UniversalParams<E>, PCError> {
 96          let params = UniversalParams::load()?;
 97          params.download_powers_for(0..(max_degree + 1))?;
 98          Ok(params)
 99      }
100  
101      /// Outputs a commitment to `polynomial`.
102      pub fn commit(
103          powers: &Powers<E>,
104          polynomial: &Polynomial<'_, E::Fr>,
105          hiding_bound: Option<usize>,
106          rng: Option<&mut dyn RngCore>,
107      ) -> Result<(KZGCommitment<E>, KZGRandomness<E>), PCError> {
108          Self::check_degree_is_too_large(polynomial.degree(), powers.size())?;
109  
110          let commit_time = start_timer!(|| format!(
111              "Committing to polynomial of degree {} with hiding_bound: {:?}",
112              polynomial.degree(),
113              hiding_bound,
114          ));
115  
116          let mut commitment = match polynomial {
117              Polynomial::Dense(polynomial) => {
118                  let (num_leading_zeros, plain_coeffs) = skip_leading_zeros_and_convert_to_bigints(polynomial);
119  
120                  let bases = &powers.powers_of_beta_g[num_leading_zeros..(num_leading_zeros + plain_coeffs.len())];
121  
122                  let msm_time = start_timer!(|| "MSM to compute commitment to plaintext poly");
123                  let commitment = VariableBase::msm(bases, &plain_coeffs);
124                  end_timer!(msm_time);
125  
126                  commitment
127              }
128              Polynomial::Sparse(polynomial) => polynomial
129                  .coeffs()
130                  .map(|(i, coeff)| {
131                      powers.powers_of_beta_g[*i].mul_bits(BitIteratorBE::new_without_leading_zeros(coeff.to_bigint()))
132                  })
133                  .sum(),
134          };
135  
136          let mut randomness = KZGRandomness::empty();
137          if let Some(hiding_degree) = hiding_bound {
138              let mut rng = rng.ok_or(PCError::MissingRng)?;
139              let sample_random_poly_time =
140                  start_timer!(|| format!("Sampling a random polynomial of degree {hiding_degree}"));
141  
142              randomness = KZGRandomness::rand(hiding_degree, false, &mut rng);
143              Self::check_hiding_bound(
144                  randomness.blinding_polynomial.degree(),
145                  powers.powers_of_beta_times_gamma_g.len(),
146              )?;
147              end_timer!(sample_random_poly_time);
148          }
149  
150          let random_ints = convert_to_bigints(&randomness.blinding_polynomial.coeffs);
151          let msm_time = start_timer!(|| "MSM to compute commitment to random poly");
152          let random_commitment =
153              VariableBase::msm(&powers.powers_of_beta_times_gamma_g, random_ints.as_slice()).to_affine();
154          end_timer!(msm_time);
155  
156          commitment.add_assign_mixed(&random_commitment);
157  
158          end_timer!(commit_time);
159          Ok((KZGCommitment(commitment.into()), randomness))
160      }
161  
162      /// Outputs a commitment to `polynomial`.
163      pub fn commit_lagrange(
164          lagrange_basis: &LagrangeBasis<E>,
165          evaluations: &[E::Fr],
166          hiding_bound: Option<usize>,
167          rng: Option<&mut dyn RngCore>,
168      ) -> Result<(KZGCommitment<E>, KZGRandomness<E>), PCError> {
169          Self::check_degree_is_too_large(evaluations.len() - 1, lagrange_basis.size())?;
170          assert_eq!(
171              evaluations.len().checked_next_power_of_two().ok_or(PCError::LagrangeBasisSizeIsTooLarge)?,
172              lagrange_basis.size()
173          );
174  
175          let commit_time = start_timer!(|| format!(
176              "Committing to polynomial of degree {} with hiding_bound: {:?}",
177              evaluations.len() - 1,
178              hiding_bound,
179          ));
180  
181          let evaluations = evaluations.iter().map(|e| e.to_bigint()).collect::<Vec<_>>();
182          let msm_time = start_timer!(|| "MSM to compute commitment to plaintext poly");
183          let mut commitment = VariableBase::msm(&lagrange_basis.lagrange_basis_at_beta_g, &evaluations);
184          end_timer!(msm_time);
185  
186          let mut randomness = KZGRandomness::empty();
187          if let Some(hiding_degree) = hiding_bound {
188              let mut rng = rng.ok_or(PCError::MissingRng)?;
189              let sample_random_poly_time =
190                  start_timer!(|| format!("Sampling a random polynomial of degree {hiding_degree}"));
191  
192              randomness = KZGRandomness::rand(hiding_degree, false, &mut rng);
193              Self::check_hiding_bound(
194                  randomness.blinding_polynomial.degree(),
195                  lagrange_basis.powers_of_beta_times_gamma_g.len(),
196              )?;
197              end_timer!(sample_random_poly_time);
198          }
199  
200          let random_ints = convert_to_bigints(&randomness.blinding_polynomial.coeffs);
201          let msm_time = start_timer!(|| "MSM to compute commitment to random poly");
202          let random_commitment =
203              VariableBase::msm(&lagrange_basis.powers_of_beta_times_gamma_g, random_ints.as_slice()).to_affine();
204          end_timer!(msm_time);
205  
206          commitment.add_assign_mixed(&random_commitment);
207  
208          end_timer!(commit_time);
209          Ok((KZGCommitment(commitment.into()), randomness))
210      }
211  
212      /// Compute witness polynomial.
213      ///
214      /// The witness polynomial w(x) the quotient of the division (p(x) - p(z)) /
215      /// (x - z) Observe that this quotient does not change with z because
216      /// p(z) is the remainder term. We can therefore omit p(z) when computing
217      /// the quotient.
218      pub fn compute_witness_polynomial(
219          polynomial: &DensePolynomial<E::Fr>,
220          point: E::Fr,
221          randomness: &KZGRandomness<E>,
222      ) -> Result<(DensePolynomial<E::Fr>, Option<DensePolynomial<E::Fr>>), PCError> {
223          let divisor = DensePolynomial::from_coefficients_vec(vec![-point, E::Fr::one()]);
224  
225          let witness_time = start_timer!(|| "Computing witness polynomial");
226          let witness_polynomial = polynomial / &divisor;
227          end_timer!(witness_time);
228  
229          let random_witness_polynomial = if randomness.is_hiding() {
230              let random_p = &randomness.blinding_polynomial;
231  
232              let witness_time = start_timer!(|| "Computing random witness polynomial");
233              let random_witness_polynomial = random_p / &divisor;
234              end_timer!(witness_time);
235              Some(random_witness_polynomial)
236          } else {
237              None
238          };
239  
240          Ok((witness_polynomial, random_witness_polynomial))
241      }
242  
243      pub(crate) fn open_with_witness_polynomial(
244          powers: &Powers<E>,
245          point: E::Fr,
246          randomness: &KZGRandomness<E>,
247          witness_polynomial: &DensePolynomial<E::Fr>,
248          hiding_witness_polynomial: Option<&DensePolynomial<E::Fr>>,
249      ) -> Result<KZGProof<E>, PCError> {
250          Self::check_degree_is_too_large(witness_polynomial.degree(), powers.size())?;
251          let (num_leading_zeros, witness_coeffs) = skip_leading_zeros_and_convert_to_bigints(witness_polynomial);
252  
253          let bases = &powers.powers_of_beta_g[num_leading_zeros..(num_leading_zeros + witness_coeffs.len())];
254  
255          let witness_comm_time = start_timer!(|| "Computing commitment to witness polynomial");
256          let mut w = VariableBase::msm(bases, &witness_coeffs);
257          end_timer!(witness_comm_time);
258  
259          let random_v = if let Some(hiding_witness_polynomial) = hiding_witness_polynomial {
260              let blinding_p = &randomness.blinding_polynomial;
261              let blinding_eval_time = start_timer!(|| "Evaluating random polynomial");
262              let blinding_evaluation = blinding_p.evaluate(point);
263              end_timer!(blinding_eval_time);
264  
265              let random_witness_coeffs = convert_to_bigints(&hiding_witness_polynomial.coeffs);
266              let witness_comm_time = start_timer!(|| "Computing commitment to random witness polynomial");
267              w += &VariableBase::msm(&powers.powers_of_beta_times_gamma_g, &random_witness_coeffs);
268              end_timer!(witness_comm_time);
269              Some(blinding_evaluation)
270          } else {
271              None
272          };
273  
274          Ok(KZGProof { w: w.to_affine(), random_v })
275      }
276  
277      /// On input a polynomial `p` in Lagrange basis, and a point `point`,
278      /// outputs an evaluation proof for the same.
279      pub fn open_lagrange(
280          lagrange_basis: &LagrangeBasis<E>,
281          domain_elements: &[E::Fr],
282          evaluations: &[E::Fr],
283          point: E::Fr,
284          evaluation_at_point: E::Fr,
285      ) -> Result<KZGProof<E>> {
286          Self::check_degree_is_too_large(evaluations.len() - 1, lagrange_basis.size())?;
287          // Ensure that the point is not in the domain
288          if lagrange_basis.domain.evaluate_vanishing_polynomial(point).is_zero() {
289              Err(anyhow!("Point cannot be in the domain"))?;
290          }
291          if evaluations.len().checked_next_power_of_two().ok_or_else(|| anyhow!("Evaluations length is too large"))?
292              != lagrange_basis.size()
293          {
294              Err(anyhow!("`evaluations.len()` must equal `domain.size()`"))?;
295          }
296  
297          let mut divisor_evals = cfg_iter!(domain_elements).map(|&e| e - point).collect::<Vec<_>>();
298          alphavm_fields::batch_inversion(&mut divisor_evals);
299          ensure!(divisor_evals.len() == evaluations.len());
300          cfg_iter_mut!(divisor_evals).zip_eq(evaluations).for_each(|(divisor_eval, &eval)| {
301              *divisor_eval *= eval - evaluation_at_point;
302          });
303          let (witness_comm, _) = Self::commit_lagrange(lagrange_basis, &divisor_evals, None, None)?;
304  
305          Ok(KZGProof { w: witness_comm.0, random_v: None })
306      }
307  
308      /// On input a polynomial `p` and a point `point`, outputs a proof for the
309      /// same.
310      pub fn open(
311          powers: &Powers<E>,
312          polynomial: &DensePolynomial<E::Fr>,
313          point: E::Fr,
314          rand: &KZGRandomness<E>,
315      ) -> Result<KZGProof<E>, PCError> {
316          Self::check_degree_is_too_large(polynomial.degree(), powers.size())?;
317          let open_time = start_timer!(|| format!("Opening polynomial of degree {}", polynomial.degree()));
318  
319          let witness_time = start_timer!(|| "Computing witness polynomials");
320          let (witness_poly, hiding_witness_poly) = Self::compute_witness_polynomial(polynomial, point, rand)?;
321          end_timer!(witness_time);
322  
323          let proof =
324              Self::open_with_witness_polynomial(powers, point, rand, &witness_poly, hiding_witness_poly.as_ref());
325  
326          end_timer!(open_time);
327          proof
328      }
329  
330      /// Verifies that `value` is the evaluation at `point` of the polynomial
331      /// committed inside `commitment`.
332      pub fn check(
333          vk: &VerifierKey<E>,
334          commitment: &KZGCommitment<E>,
335          point: E::Fr,
336          value: E::Fr,
337          proof: &KZGProof<E>,
338      ) -> Result<bool, PCError> {
339          let check_time = start_timer!(|| "Checking evaluation");
340          let mut inner = commitment.0.to_projective() - vk.g.to_projective().mul(value);
341          if let Some(random_v) = proof.random_v {
342              inner -= &vk.gamma_g.mul(random_v);
343          }
344          let lhs = E::pairing(inner, vk.h);
345  
346          let inner = vk.beta_h.to_projective() - vk.h.mul(point);
347          let rhs = E::pairing(proof.w, inner);
348  
349          end_timer!(check_time, || format!("Result: {}", lhs == rhs));
350          Ok(lhs == rhs)
351      }
352  
353      /// Check that each `proof_i` in `proofs` is a valid proof of evaluation for
354      /// `commitment_i` at `point_i`.
355      pub fn batch_check<R: RngCore>(
356          vk: &VerifierKey<E>,
357          commitments: &[KZGCommitment<E>],
358          points: &[E::Fr],
359          values: &[E::Fr],
360          proofs: &[KZGProof<E>],
361          rng: &mut R,
362      ) -> Result<bool> {
363          let check_time = start_timer!(|| format!("Checking {} evaluation proofs", commitments.len()));
364          let g = vk.g.to_projective();
365          let gamma_g = vk.gamma_g.to_projective();
366  
367          let mut total_c = <E::G1Projective>::zero();
368          let mut total_w = <E::G1Projective>::zero();
369  
370          let combination_time = start_timer!(|| "Combining commitments and proofs");
371          let mut randomizer = E::Fr::one();
372          // Instead of multiplying g and gamma_g in each turn, we simply accumulate
373          // their coefficients and perform a final multiplication at the end.
374          let mut g_multiplier = E::Fr::zero();
375          let mut gamma_g_multiplier = E::Fr::zero();
376          ensure!(commitments.len() == points.len());
377          ensure!(commitments.len() == values.len());
378          ensure!(commitments.len() == proofs.len());
379          for (((c, z), v), proof) in commitments.iter().zip_eq(points).zip_eq(values).zip_eq(proofs) {
380              let w = proof.w;
381              let mut temp = w.mul(*z);
382              temp.add_assign_mixed(&c.0);
383              let c = temp;
384              g_multiplier += &(randomizer * v);
385              if let Some(random_v) = proof.random_v {
386                  gamma_g_multiplier += &(randomizer * random_v);
387              }
388              total_c += &c.mul(randomizer);
389              total_w += &w.mul(randomizer);
390              // We don't need to sample randomizers from the full field,
391              // only from 128-bit strings.
392              randomizer = u128::rand(rng).into();
393          }
394          total_c -= &g.mul(g_multiplier);
395          total_c -= &gamma_g.mul(gamma_g_multiplier);
396          end_timer!(combination_time);
397  
398          let to_affine_time = start_timer!(|| "Converting results to affine for pairing");
399          let affine_points = E::G1Projective::batch_normalization_into_affine(vec![-total_w, total_c]);
400          let (total_w, total_c) = (affine_points[0], affine_points[1]);
401          end_timer!(to_affine_time);
402  
403          let pairing_time = start_timer!(|| "Performing product of pairings");
404          let result = E::product_of_pairings(
405              [(&total_w.prepare(), &vk.prepared_beta_h), (&total_c.prepare(), &vk.prepared_h)].iter().copied(),
406          )
407          .is_one();
408          end_timer!(pairing_time);
409          end_timer!(check_time, || format!("Result: {result}"));
410          Ok(result)
411      }
412  
413      pub(crate) fn check_degree_is_too_large(degree: usize, num_powers: usize) -> Result<(), PCError> {
414          let num_coefficients = degree + 1;
415          if num_coefficients > num_powers {
416              Err(PCError::TooManyCoefficients { num_coefficients, num_powers })
417          } else {
418              Ok(())
419          }
420      }
421  
422      pub(crate) fn check_hiding_bound(hiding_poly_degree: usize, num_powers: usize) -> Result<(), PCError> {
423          if hiding_poly_degree == 0 {
424              Err(PCError::HidingBoundIsZero)
425          } else if hiding_poly_degree >= num_powers {
426              // The above check uses `>=` because committing to a hiding poly with
427              // degree `hiding_poly_degree` requires `hiding_poly_degree + 1` powers.
428              Err(PCError::HidingBoundToolarge { hiding_poly_degree, num_powers })
429          } else {
430              Ok(())
431          }
432      }
433  
434      pub(crate) fn check_degrees_and_bounds<'a>(
435          max_degree: usize,
436          enforced_degree_bounds: Option<&[usize]>,
437          p: impl Into<LabeledPolynomialWithBasis<'a, E::Fr>>,
438      ) -> Result<(), PCError> {
439          let p = p.into();
440          if let Some(bound) = p.degree_bound() {
441              let enforced_degree_bounds = enforced_degree_bounds.ok_or(PCError::UnsupportedDegreeBound(bound))?;
442  
443              if enforced_degree_bounds.binary_search(&bound).is_err() {
444                  Err(PCError::UnsupportedDegreeBound(bound))
445              } else if bound < p.degree() || bound > max_degree {
446                  Err(PCError::IncorrectDegreeBound {
447                      poly_degree: p.degree(),
448                      degree_bound: p.degree_bound().unwrap(),
449                      max_degree,
450                      label: p.label().to_string(),
451                  })
452              } else {
453                  Ok(())
454              }
455          } else {
456              Ok(())
457          }
458      }
459  }
460  
461  fn skip_leading_zeros_and_convert_to_bigints<F: PrimeField>(p: &DensePolynomial<F>) -> (usize, Vec<F::BigInteger>) {
462      if p.coeffs.is_empty() {
463          (0, vec![])
464      } else {
465          let num_leading_zeros = p.coeffs.iter().take_while(|c| c.is_zero()).count();
466          let coeffs = if num_leading_zeros == p.coeffs.len() {
467              vec![]
468          } else {
469              convert_to_bigints(&p.coeffs[num_leading_zeros..])
470          };
471          (num_leading_zeros, coeffs)
472      }
473  }
474  
475  fn convert_to_bigints<F: PrimeField>(p: &[F]) -> Vec<F::BigInteger> {
476      let to_bigint_time = start_timer!(|| "Converting polynomial coeffs to bigints");
477      let coeffs = cfg_iter!(p).map(|s| s.to_bigint()).collect::<Vec<_>>();
478      end_timer!(to_bigint_time);
479      coeffs
480  }
481  
482  #[cfg(test)]
483  mod tests {
484      #![allow(non_camel_case_types)]
485      #![allow(clippy::needless_borrow)]
486      use super::*;
487      use alphavm_curves::bls12_377::{Bls12_377, Fr};
488      use alphavm_utilities::{rand::TestRng, FromBytes, ToBytes};
489  
490      use std::borrow::Cow;
491  
492      type KZG_Bls12_377 = KZG10<Bls12_377>;
493  
494      impl<E: PairingEngine> KZG10<E> {
495          /// Specializes the public parameters for a given maximum degree `d` for
496          /// polynomials `d` should be less that `pp.max_degree()`.
497          pub(crate) fn trim(
498              pp: &UniversalParams<E>,
499              mut supported_degree: usize,
500              hiding_bound: Option<usize>,
501          ) -> (Powers<'_, E>, VerifierKey<E>) {
502              if supported_degree == 1 {
503                  supported_degree += 1;
504              }
505              let powers_of_beta_g = pp.powers_of_beta_g(0, supported_degree + 1).unwrap().to_vec();
506  
507              let powers_of_beta_times_gamma_g = if let Some(hiding_bound) = hiding_bound {
508                  (0..=(hiding_bound + 1)).map(|i| pp.powers_of_beta_times_gamma_g()[&i]).collect()
509              } else {
510                  vec![]
511              };
512  
513              let powers = Powers {
514                  powers_of_beta_g: Cow::Owned(powers_of_beta_g),
515                  powers_of_beta_times_gamma_g: Cow::Owned(powers_of_beta_times_gamma_g),
516              };
517              let vk = VerifierKey {
518                  g: pp.power_of_beta_g(0).unwrap(),
519                  gamma_g: pp.powers_of_beta_times_gamma_g()[&0],
520                  h: pp.h,
521                  beta_h: pp.beta_h(),
522                  prepared_h: pp.prepared_h.clone(),
523                  prepared_beta_h: pp.prepared_beta_h.clone(),
524              };
525              (powers, vk)
526          }
527      }
528  
529      #[test]
530      fn test_kzg10_universal_params_serialization() {
531          let degree = 4;
532          let pp = KZG_Bls12_377::load_srs(degree).unwrap();
533  
534          let pp_bytes = pp.to_bytes_le().unwrap();
535          let pp_recovered: UniversalParams<Bls12_377> = FromBytes::read_le(&pp_bytes[..]).unwrap();
536          let pp_recovered_bytes = pp_recovered.to_bytes_le().unwrap();
537  
538          assert_eq!(&pp_bytes, &pp_recovered_bytes);
539      }
540  
541      fn end_to_end_test_template<E: PairingEngine>() -> Result<(), PCError> {
542          let rng = &mut TestRng::default();
543          for _ in 0..100 {
544              let mut degree = 0;
545              while degree <= 1 {
546                  degree = usize::rand(rng) % 20;
547              }
548              let pp = KZG10::<E>::load_srs(degree)?;
549              let hiding_bound = Some(1);
550              let (ck, vk) = KZG10::trim(&pp, degree, hiding_bound);
551              let p = DensePolynomial::rand(degree, rng);
552              let (comm, rand) = KZG10::<E>::commit(&ck, &(&p).into(), hiding_bound, Some(rng))?;
553              let point = E::Fr::rand(rng);
554              let value = p.evaluate(point);
555              let proof = KZG10::<E>::open(&ck, &p, point, &rand)?;
556              assert!(
557                  KZG10::<E>::check(&vk, &comm, point, value, &proof)?,
558                  "proof was incorrect for max_degree = {}, polynomial_degree = {}, hiding_bound = {:?}",
559                  degree,
560                  p.degree(),
561                  hiding_bound,
562              );
563          }
564          Ok(())
565      }
566  
567      fn linear_polynomial_test_template<E: PairingEngine>() -> Result<(), PCError> {
568          let rng = &mut TestRng::default();
569          for _ in 0..100 {
570              let degree = 50;
571              let pp = KZG10::<E>::load_srs(degree)?;
572              let hiding_bound = Some(1);
573              let (ck, vk) = KZG10::trim(&pp, 2, hiding_bound);
574              let p = DensePolynomial::rand(1, rng);
575              let (comm, rand) = KZG10::<E>::commit(&ck, &(&p).into(), hiding_bound, Some(rng))?;
576              let point = E::Fr::rand(rng);
577              let value = p.evaluate(point);
578              let proof = KZG10::<E>::open(&ck, &p, point, &rand)?;
579              assert!(
580                  KZG10::<E>::check(&vk, &comm, point, value, &proof)?,
581                  "proof was incorrect for max_degree = {}, polynomial_degree = {}, hiding_bound = {:?}",
582                  degree,
583                  p.degree(),
584                  hiding_bound,
585              );
586          }
587          Ok(())
588      }
589  
590      fn batch_check_test_template<E: PairingEngine>() -> Result<(), PCError> {
591          let rng = &mut TestRng::default();
592          for _ in 0..10 {
593              let hiding_bound = Some(1);
594              let mut degree = 0;
595              while degree <= 1 {
596                  degree = usize::rand(rng) % 20;
597              }
598              let pp = KZG10::<E>::load_srs(degree)?;
599              let (ck, vk) = KZG10::trim(&pp, degree, hiding_bound);
600  
601              let mut comms = Vec::new();
602              let mut values = Vec::new();
603              let mut points = Vec::new();
604              let mut proofs = Vec::new();
605  
606              for _ in 0..10 {
607                  let p = DensePolynomial::rand(degree, rng);
608                  let (comm, rand) = KZG10::<E>::commit(&ck, &(&p).into(), hiding_bound, Some(rng))?;
609                  let point = E::Fr::rand(rng);
610                  let value = p.evaluate(point);
611                  let proof = KZG10::<E>::open(&ck, &p, point, &rand)?;
612  
613                  assert!(KZG10::<E>::check(&vk, &comm, point, value, &proof)?);
614                  comms.push(comm);
615                  values.push(value);
616                  points.push(point);
617                  proofs.push(proof);
618              }
619              assert!(KZG10::<E>::batch_check(&vk, &comms, &points, &values, &proofs, rng)?);
620          }
621          Ok(())
622      }
623  
624      #[test]
625      fn test_end_to_end() {
626          end_to_end_test_template::<Bls12_377>().expect("test failed for bls12-377");
627      }
628  
629      #[test]
630      fn test_linear_polynomial() {
631          linear_polynomial_test_template::<Bls12_377>().expect("test failed for bls12-377");
632      }
633  
634      #[test]
635      fn test_batch_check() {
636          batch_check_test_template::<Bls12_377>().expect("test failed for bls12-377");
637      }
638  
639      #[test]
640      fn test_degree_is_too_large() {
641          let rng = &mut TestRng::default();
642  
643          let max_degree = 123;
644          let pp = KZG_Bls12_377::load_srs(max_degree).unwrap();
645          let (powers, _) = KZG_Bls12_377::trim(&pp, max_degree, None);
646  
647          let p = DensePolynomial::<Fr>::rand(max_degree + 1, rng);
648          assert!(p.degree() > max_degree);
649          assert!(KZG_Bls12_377::check_degree_is_too_large(p.degree(), powers.size()).is_err());
650      }
651  }