// Copyright (c) 2022 Espresso Systems (espressosys.com) // This file is part of the Jellyfish library. // You should have received a copy of the MIT License // along with the Jellyfish library. If not, see . //! Main module for univariate KZG commitment scheme use crate::pcs::{ prelude::Commitment, PCSError, PolynomialCommitmentScheme, StructuredReferenceString, }; use ark_ec::{msm::VariableBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; use ark_ff::PrimeField; use ark_poly::{univariate::DensePolynomial, Polynomial, UVPolynomial}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; use ark_std::{ borrow::Borrow, end_timer, format, marker::PhantomData, rand::{CryptoRng, RngCore}, start_timer, string::ToString, vec, vec::Vec, One, }; use srs::{UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam}; 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::Fr; type Evaluation = E::Fr; // 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_and_convert_to_bigints(poly); let msm_time = start_timer!(|| "MSM to compute commitment to plaintext poly"); let commitment = VariableBaseMSM::multi_scalar_mul( &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::Fr::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_and_convert_to_bigints(&witness_polynomial); let proof = VariableBaseMSM::multi_scalar_mul( &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::Fr, 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.into_repr()) - proof.proof.mul(point.into_repr()) - commitment.0.into_projective()) .into_affine() .into(), verifier_param.h.into(), ), (proof.proof.into(), verifier_param.beta_h.into()), ]; let res = E::product_of_pairings(pairing_inputs.iter()).is_one(); end_timer!(check_time, || format!("Result: {}", res)); Ok(res) } } fn skip_leading_zeros_and_convert_to_bigints>( p: &P, ) -> (usize, Vec) { let mut num_leading_zeros = 0; while num_leading_zeros < p.coeffs().len() && p.coeffs()[num_leading_zeros].is_zero() { num_leading_zeros += 1; } let coeffs = convert_to_bigints(&p.coeffs()[num_leading_zeros..]); (num_leading_zeros, coeffs) } fn convert_to_bigints(p: &[F]) -> Vec { let to_bigint_time = start_timer!(|| "Converting polynomial coeffs to bigints"); let coeffs = p.iter().map(|s| s.into_repr()).collect::>(); end_timer!(to_bigint_time); coeffs } #[cfg(test)] mod tests { use super::*; use crate::StructuredReferenceString; use ark_bls12_381::Bls12_381; use ark_ec::PairingEngine; use ark_poly::univariate::DensePolynomial; use ark_std::{test_rng, UniformRand}; fn end_to_end_test_template() -> Result<(), PCSError> where E: PairingEngine, { 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 UVPolynomial>::rand(degree, rng); let comm = UnivariateKzgPCS::::commit(&ck, &p)?; let point = E::Fr::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: PairingEngine, { 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 UVPolynomial>::rand(degree, rng); let comm = UnivariateKzgPCS::::commit(&ck, &p)?; let point = E::Fr::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"); } }