hyperplonk/subroutines/src/pcs/univariate_kzg/mod.rs
subroutines/src/pcs/univariate_kzg/mod.rsRaw
1// Copyright (c) 2023 Espresso Systems (espressosys.com)
2// This file is part of the HyperPlonk library.
3
4// You should have received a copy of the MIT License
5// along with the HyperPlonk library. If not, see <https://mit-license.org/>.
6
7//! Main module for univariate KZG commitment scheme
8
9use crate::pcs::{
10 prelude::Commitment, PCSError, PolynomialCommitmentScheme, StructuredReferenceString,
11};
12use ark_ec::{
13 pairing::Pairing, scalar_mul::variable_base::VariableBaseMSM, AffineRepr, CurveGroup,
14};
15use ark_ff::PrimeField;
16use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial};
17use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
18use ark_std::{
19 borrow::Borrow, end_timer, format, marker::PhantomData, rand::Rng, start_timer,
20 string::ToString, vec, vec::Vec, One,
21};
22use srs::{UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam};
23use std::ops::Mul;
24
25pub(crate) mod srs;
26
27/// KZG Polynomial Commitment Scheme on univariate polynomial.
28pub struct UnivariateKzgPCS<E: Pairing> {
29 #[doc(hidden)]
30 phantom: PhantomData<E>,
31}
32
33#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq, Eq)]
34/// proof of opening
35pub struct UnivariateKzgProof<E: Pairing> {
36 /// Evaluation of quotients
37 pub proof: E::G1Affine,
38}
39/// batch proof
40pub type UnivariateKzgBatchProof<E> = Vec<UnivariateKzgProof<E>>;
41
42impl<E: Pairing> PolynomialCommitmentScheme<E> for UnivariateKzgPCS<E> {
43 // Parameters
44 type ProverParam = UnivariateProverParam<E::G1Affine>;
45 type VerifierParam = UnivariateVerifierParam<E>;
46 type SRS = UnivariateUniversalParams<E>;
47 // Polynomial and its associated types
48 type Polynomial = DensePolynomial<E::ScalarField>;
49 type Point = E::ScalarField;
50 type Evaluation = E::ScalarField;
51 // Polynomial and its associated types
52 type Commitment = Commitment<E>;
53 type Proof = UnivariateKzgProof<E>;
54
55 // We do not implement batch univariate KZG at the current version.
56 type BatchProof = ();
57
58 /// Build SRS for testing.
59 ///
60 /// - For univariate polynomials, `supported_size` is the maximum degree.
61 ///
62 /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY.
63 /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION.
64 fn gen_srs_for_testing<R: Rng>(
65 rng: &mut R,
66 supported_size: usize,
67 ) -> Result<Self::SRS, PCSError> {
68 Self::SRS::gen_srs_for_testing(rng, supported_size)
69 }
70
71 /// Trim the universal parameters to specialize the public parameters.
72 /// Input `max_degree` for univariate.
73 /// `supported_num_vars` must be None or an error is returned.
74 fn trim(
75 srs: impl Borrow<Self::SRS>,
76 supported_degree: Option<usize>,
77 supported_num_vars: Option<usize>,
78 ) -> Result<(Self::ProverParam, Self::VerifierParam), PCSError> {
79 assert!(supported_num_vars.is_none());
80 if supported_num_vars.is_some() {
81 return Err(PCSError::InvalidParameters(
82 "univariate should not receive a num_var param".to_string(),
83 ));
84 }
85 srs.borrow().trim(supported_degree.unwrap())
86 }
87
88 /// Generate a commitment for a polynomial
89 /// Note that the scheme is not hidding
90 fn commit(
91 prover_param: impl Borrow<Self::ProverParam>,
92 poly: &Self::Polynomial,
93 ) -> Result<Self::Commitment, PCSError> {
94 let prover_param = prover_param.borrow();
95 let commit_time =
96 start_timer!(|| format!("Committing to polynomial of degree {} ", poly.degree()));
97
98 if poly.degree() >= prover_param.powers_of_g.len() {
99 return Err(PCSError::InvalidParameters(format!(
100 "uni poly degree {} is larger than allowed {}",
101 poly.degree(),
102 prover_param.powers_of_g.len()
103 )));
104 }
105
106 let (num_leading_zeros, plain_coeffs) = skip_leading_zeros(poly);
107
108 let msm_time = start_timer!(|| "MSM to compute commitment to plaintext poly");
109 let commitment = E::G1::msm_unchecked(
110 &prover_param.powers_of_g[num_leading_zeros..],
111 &plain_coeffs,
112 )
113 .into_affine();
114 end_timer!(msm_time);
115
116 end_timer!(commit_time);
117 Ok(Commitment(commitment))
118 }
119
120 /// On input a polynomial `p` and a point `point`, outputs a proof for the
121 /// same.
122 fn open(
123 prover_param: impl Borrow<Self::ProverParam>,
124 polynomial: &Self::Polynomial,
125 point: &Self::Point,
126 ) -> Result<(Self::Proof, Self::Evaluation), PCSError> {
127 let open_time =
128 start_timer!(|| format!("Opening polynomial of degree {}", polynomial.degree()));
129 let divisor = Self::Polynomial::from_coefficients_vec(vec![-*point, E::ScalarField::one()]);
130
131 let witness_time = start_timer!(|| "Computing witness polynomial");
132 let witness_polynomial = polynomial / &divisor;
133 end_timer!(witness_time);
134
135 let (num_leading_zeros, witness_coeffs) = skip_leading_zeros(&witness_polynomial);
136
137 let proof = E::G1::msm_unchecked(
138 &prover_param.borrow().powers_of_g[num_leading_zeros..],
139 &witness_coeffs,
140 )
141 .into_affine();
142
143 let eval = polynomial.evaluate(point);
144
145 end_timer!(open_time);
146 Ok((Self::Proof { proof }, eval))
147 }
148
149 /// Verifies that `value` is the evaluation at `x` of the polynomial
150 /// committed inside `comm`.
151 fn verify(
152 verifier_param: &Self::VerifierParam,
153 commitment: &Self::Commitment,
154 point: &Self::Point,
155 value: &E::ScalarField,
156 proof: &Self::Proof,
157 ) -> Result<bool, PCSError> {
158 let check_time = start_timer!(|| "Checking evaluation");
159 let pairing_inputs: Vec<(E::G1Prepared, E::G2Prepared)> = vec![
160 (
161 (verifier_param.g.mul(value) - proof.proof.mul(point) - commitment.0.into_group())
162 .into_affine()
163 .into(),
164 verifier_param.h.into(),
165 ),
166 (proof.proof.into(), verifier_param.beta_h.into()),
167 ];
168
169 let p1 = pairing_inputs.iter().map(|(a, _)| a.clone());
170 let p2 = pairing_inputs.iter().map(|(_, a)| a.clone());
171
172 let res = E::multi_pairing(p1, p2).0.is_one();
173
174 end_timer!(check_time, || format!("Result: {}", res));
175 Ok(res)
176 }
177}
178
179fn skip_leading_zeros<F: PrimeField, P: DenseUVPolynomial<F>>(p: &P) -> (usize, &[F]) {
180 let mut num_leading_zeros = 0;
181 while num_leading_zeros < p.coeffs().len() && p.coeffs()[num_leading_zeros].is_zero() {
182 num_leading_zeros += 1;
183 }
184 (num_leading_zeros, &p.coeffs()[num_leading_zeros..])
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190 use crate::StructuredReferenceString;
191 use ark_bls12_381::Bls12_381;
192 use ark_ec::pairing::Pairing;
193 use ark_poly::univariate::DensePolynomial;
194 use ark_std::{test_rng, UniformRand};
195
196 fn end_to_end_test_template<E>() -> Result<(), PCSError>
197 where
198 E: Pairing,
199 {
200 let rng = &mut test_rng();
201 for _ in 0..100 {
202 let mut degree = 0;
203 while degree <= 1 {
204 degree = usize::rand(rng) % 20;
205 }
206 let pp = UnivariateKzgPCS::<E>::gen_srs_for_testing(rng, degree)?;
207 let (ck, vk) = pp.trim(degree)?;
208 let p = <DensePolynomial<E::ScalarField> as DenseUVPolynomial<E::ScalarField>>::rand(
209 degree, rng,
210 );
211 let comm = UnivariateKzgPCS::<E>::commit(&ck, &p)?;
212 let point = E::ScalarField::rand(rng);
213 let (proof, value) = UnivariateKzgPCS::<E>::open(&ck, &p, &point)?;
214 assert!(
215 UnivariateKzgPCS::<E>::verify(&vk, &comm, &point, &value, &proof)?,
216 "proof was incorrect for max_degree = {}, polynomial_degree = {}",
217 degree,
218 p.degree(),
219 );
220 }
221 Ok(())
222 }
223
224 fn linear_polynomial_test_template<E>() -> Result<(), PCSError>
225 where
226 E: Pairing,
227 {
228 let rng = &mut test_rng();
229 for _ in 0..100 {
230 let degree = 50;
231
232 let pp = UnivariateKzgPCS::<E>::gen_srs_for_testing(rng, degree)?;
233 let (ck, vk) = pp.trim(degree)?;
234 let p = <DensePolynomial<E::ScalarField> as DenseUVPolynomial<E::ScalarField>>::rand(
235 degree, rng,
236 );
237 let comm = UnivariateKzgPCS::<E>::commit(&ck, &p)?;
238 let point = E::ScalarField::rand(rng);
239 let (proof, value) = UnivariateKzgPCS::<E>::open(&ck, &p, &point)?;
240 assert!(
241 UnivariateKzgPCS::<E>::verify(&vk, &comm, &point, &value, &proof)?,
242 "proof was incorrect for max_degree = {}, polynomial_degree = {}",
243 degree,
244 p.degree(),
245 );
246 }
247 Ok(())
248 }
249
250 #[test]
251 fn end_to_end_test() {
252 end_to_end_test_template::<Bls12_381>().expect("test failed for bls12-381");
253 }
254
255 #[test]
256 fn linear_polynomial_test() {
257 linear_polynomial_test_template::<Bls12_381>().expect("test failed for bls12-381");
258 }
259}