// 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");
}
}