// Copyright (c) 2023 Espresso Systems (espressosys.com) // This file is part of the HyperPlonk library. // You should have received a copy of the MIT License // along with the HyperPlonk library. If not, see . //! Main module for univariate KZG commitment scheme use crate::pcs::{ prelude::Commitment, PCSError, PolynomialCommitmentScheme, StructuredReferenceString, }; use ark_ec::{ pairing::Pairing, scalar_mul::variable_base::VariableBaseMSM, AffineRepr, CurveGroup, }; use ark_ff::PrimeField; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{ borrow::Borrow, end_timer, format, marker::PhantomData, rand::Rng, start_timer, string::ToString, vec, vec::Vec, One, }; use srs::{UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam}; use std::ops::Mul; pub(crate) mod srs; /// KZG Polynomial Commitment Scheme on univariate polynomial. pub struct UnivariateKzgPCS { #[doc(hidden)] phantom: PhantomData, } #[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq, Eq)] /// proof of opening pub struct UnivariateKzgProof { /// Evaluation of quotients pub proof: E::G1Affine, } /// batch proof pub type UnivariateKzgBatchProof = Vec>; impl PolynomialCommitmentScheme for UnivariateKzgPCS { // Parameters type ProverParam = UnivariateProverParam; type VerifierParam = UnivariateVerifierParam; type SRS = UnivariateUniversalParams; // Polynomial and its associated types type Polynomial = DensePolynomial; type Point = E::ScalarField; type Evaluation = E::ScalarField; // Polynomial and its associated types type Commitment = Commitment; type Proof = UnivariateKzgProof; // We do not implement batch univariate KZG at the current version. type BatchProof = (); /// Build SRS for testing. /// /// - For univariate polynomials, `supported_size` is the maximum degree. /// /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION. fn gen_srs_for_testing( rng: &mut R, supported_size: usize, ) -> Result { Self::SRS::gen_srs_for_testing(rng, supported_size) } /// Trim the universal parameters to specialize the public parameters. /// Input `max_degree` for univariate. /// `supported_num_vars` must be None or an error is returned. fn trim( srs: impl Borrow, supported_degree: Option, supported_num_vars: Option, ) -> Result<(Self::ProverParam, Self::VerifierParam), PCSError> { assert!(supported_num_vars.is_none()); if supported_num_vars.is_some() { return Err(PCSError::InvalidParameters( "univariate should not receive a num_var param".to_string(), )); } srs.borrow().trim(supported_degree.unwrap()) } /// Generate a commitment for a polynomial /// Note that the scheme is not hidding fn commit( prover_param: impl Borrow, poly: &Self::Polynomial, ) -> Result { let prover_param = prover_param.borrow(); let commit_time = start_timer!(|| format!("Committing to polynomial of degree {} ", poly.degree())); if poly.degree() >= prover_param.powers_of_g.len() { return Err(PCSError::InvalidParameters(format!( "uni poly degree {} is larger than allowed {}", poly.degree(), prover_param.powers_of_g.len() ))); } let (num_leading_zeros, plain_coeffs) = skip_leading_zeros(poly); let msm_time = start_timer!(|| "MSM to compute commitment to plaintext poly"); let commitment = E::G1::msm_unchecked(&prover_param.powers_of_g[num_leading_zeros..], plain_coeffs) .into_affine(); end_timer!(msm_time); end_timer!(commit_time); Ok(Commitment(commitment)) } /// On input a polynomial `p` and a point `point`, outputs a proof for the /// same. fn open( prover_param: impl Borrow, polynomial: &Self::Polynomial, point: &Self::Point, ) -> Result<(Self::Proof, Self::Evaluation), PCSError> { let open_time = start_timer!(|| format!("Opening polynomial of degree {}", polynomial.degree())); let divisor = Self::Polynomial::from_coefficients_vec(vec![-*point, E::ScalarField::one()]); let witness_time = start_timer!(|| "Computing witness polynomial"); let witness_polynomial = polynomial / &divisor; end_timer!(witness_time); let (num_leading_zeros, witness_coeffs) = skip_leading_zeros(&witness_polynomial); let proof = E::G1::msm_unchecked( &prover_param.borrow().powers_of_g[num_leading_zeros..], witness_coeffs, ) .into_affine(); let eval = polynomial.evaluate(point); end_timer!(open_time); Ok((Self::Proof { proof }, eval)) } /// Verifies that `value` is the evaluation at `x` of the polynomial /// committed inside `comm`. fn verify( verifier_param: &Self::VerifierParam, commitment: &Self::Commitment, point: &Self::Point, value: &E::ScalarField, proof: &Self::Proof, ) -> Result { let check_time = start_timer!(|| "Checking evaluation"); let pairing_inputs: Vec<(E::G1Prepared, E::G2Prepared)> = vec![ ( (verifier_param.g.mul(value) - proof.proof.mul(point) - commitment.0.into_group()) .into_affine() .into(), verifier_param.h.into(), ), (proof.proof.into(), verifier_param.beta_h.into()), ]; let p1 = pairing_inputs.iter().map(|(a, _)| a.clone()); let p2 = pairing_inputs.iter().map(|(_, a)| a.clone()); let res = E::multi_pairing(p1, p2).0.is_one(); end_timer!(check_time, || format!("Result: {}", res)); Ok(res) } } fn skip_leading_zeros>(p: &P) -> (usize, &[F]) { let mut num_leading_zeros = 0; while num_leading_zeros < p.coeffs().len() && p.coeffs()[num_leading_zeros].is_zero() { num_leading_zeros += 1; } (num_leading_zeros, &p.coeffs()[num_leading_zeros..]) } #[cfg(test)] mod tests { use super::*; use crate::StructuredReferenceString; use ark_bls12_381::Bls12_381; use ark_ec::pairing::Pairing; use ark_poly::univariate::DensePolynomial; use ark_std::{test_rng, UniformRand}; fn end_to_end_test_template() -> Result<(), PCSError> where E: Pairing, { let rng = &mut test_rng(); for _ in 0..100 { let mut degree = 0; while degree <= 1 { degree = usize::rand(rng) % 20; } let pp = UnivariateKzgPCS::::gen_srs_for_testing(rng, degree)?; let (ck, vk) = pp.trim(degree)?; let p = as DenseUVPolynomial>::rand( degree, rng, ); let comm = UnivariateKzgPCS::::commit(&ck, &p)?; let point = E::ScalarField::rand(rng); let (proof, value) = UnivariateKzgPCS::::open(&ck, &p, &point)?; assert!( UnivariateKzgPCS::::verify(&vk, &comm, &point, &value, &proof)?, "proof was incorrect for max_degree = {}, polynomial_degree = {}", degree, p.degree(), ); } Ok(()) } fn linear_polynomial_test_template() -> Result<(), PCSError> where E: Pairing, { let rng = &mut test_rng(); for _ in 0..100 { let degree = 50; let pp = UnivariateKzgPCS::::gen_srs_for_testing(rng, degree)?; let (ck, vk) = pp.trim(degree)?; let p = as DenseUVPolynomial>::rand( degree, rng, ); let comm = UnivariateKzgPCS::::commit(&ck, &p)?; let point = E::ScalarField::rand(rng); let (proof, value) = UnivariateKzgPCS::::open(&ck, &p, &point)?; assert!( UnivariateKzgPCS::::verify(&vk, &comm, &point, &value, &proof)?, "proof was incorrect for max_degree = {}, polynomial_degree = {}", degree, p.degree(), ); } Ok(()) } #[test] fn end_to_end_test() { end_to_end_test_template::().expect("test failed for bls12-381"); } #[test] fn linear_polynomial_test() { linear_polynomial_test_template::().expect("test failed for bls12-381"); } }