hyperplonk/subroutines/src/pcs/multilinear_kzg/mod.rs
subroutines/src/pcs/multilinear_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 multilinear KZG commitment scheme
8
9pub(crate) mod batching;
10pub(crate) mod srs;
11pub(crate) mod util;
12
13use crate::{
14 pcs::{prelude::Commitment, PCSError, PolynomialCommitmentScheme, StructuredReferenceString},
15 BatchProof,
16};
17use arithmetic::evaluate_opt;
18use ark_ec::{
19 pairing::Pairing,
20 scalar_mul::{fixed_base::FixedBase, variable_base::VariableBaseMSM},
21 AffineRepr, CurveGroup,
22};
23use ark_ff::PrimeField;
24use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
25use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
26use ark_std::{
27 borrow::Borrow, end_timer, format, marker::PhantomData, rand::Rng, start_timer,
28 string::ToString, sync::Arc, vec, vec::Vec, One, Zero,
29};
30use std::ops::Mul;
31// use batching::{batch_verify_internal, multi_open_internal};
32use srs::{MultilinearProverParam, MultilinearUniversalParams, MultilinearVerifierParam};
33use transcript::IOPTranscript;
34
35use self::batching::{batch_verify_internal, multi_open_internal};
36
37/// KZG Polynomial Commitment Scheme on multilinear polynomials.
38pub struct MultilinearKzgPCS<E: Pairing> {
39 #[doc(hidden)]
40 phantom: PhantomData<E>,
41}
42
43#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq, Eq)]
44/// proof of opening
45pub struct MultilinearKzgProof<E: Pairing> {
46 /// Evaluation of quotients
47 pub proofs: Vec<E::G1Affine>,
48}
49
50impl<E: Pairing> PolynomialCommitmentScheme<E> for MultilinearKzgPCS<E> {
51 // Parameters
52 type ProverParam = MultilinearProverParam<E>;
53 type VerifierParam = MultilinearVerifierParam<E>;
54 type SRS = MultilinearUniversalParams<E>;
55 // Polynomial and its associated types
56 type Polynomial = Arc<DenseMultilinearExtension<E::ScalarField>>;
57 type Point = Vec<E::ScalarField>;
58 type Evaluation = E::ScalarField;
59 // Commitments and proofs
60 type Commitment = Commitment<E>;
61 type Proof = MultilinearKzgProof<E>;
62 type BatchProof = BatchProof<E, Self>;
63
64 /// Build SRS for testing.
65 ///
66 /// - For univariate polynomials, `log_size` is the log of maximum degree.
67 /// - For multilinear polynomials, `log_size` is the number of variables.
68 ///
69 /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY.
70 /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION.
71 fn gen_srs_for_testing<R: Rng>(rng: &mut R, log_size: usize) -> Result<Self::SRS, PCSError> {
72 MultilinearUniversalParams::<E>::gen_srs_for_testing(rng, log_size)
73 }
74
75 /// Trim the universal parameters to specialize the public parameters.
76 /// Input both `supported_log_degree` for univariate and
77 /// `supported_num_vars` for multilinear.
78 fn trim(
79 srs: impl Borrow<Self::SRS>,
80 supported_degree: Option<usize>,
81 supported_num_vars: Option<usize>,
82 ) -> Result<(Self::ProverParam, Self::VerifierParam), PCSError> {
83 assert!(supported_degree.is_none());
84
85 let supported_num_vars = match supported_num_vars {
86 Some(p) => p,
87 None => {
88 return Err(PCSError::InvalidParameters(
89 "multilinear should receive a num_var param".to_string(),
90 ))
91 },
92 };
93 let (ml_ck, ml_vk) = srs.borrow().trim(supported_num_vars)?;
94
95 Ok((ml_ck, ml_vk))
96 }
97
98 /// Generate a commitment for a polynomial.
99 ///
100 /// This function takes `2^num_vars` number of scalar multiplications over
101 /// G1.
102 fn commit(
103 prover_param: impl Borrow<Self::ProverParam>,
104 poly: &Self::Polynomial,
105 ) -> Result<Self::Commitment, PCSError> {
106 let prover_param = prover_param.borrow();
107 let commit_timer = start_timer!(|| "commit");
108 if prover_param.num_vars < poly.num_vars {
109 return Err(PCSError::InvalidParameters(format!(
110 "MlE length ({}) exceeds param limit ({})",
111 poly.num_vars, prover_param.num_vars
112 )));
113 }
114 let ignored = prover_param.num_vars - poly.num_vars;
115 let scalars: Vec<_> = poly.to_evaluations();
116 let msm_timer = start_timer!(|| format!(
117 "msm of size {}",
118 prover_param.powers_of_g[ignored].evals.len()
119 ));
120 let commitment =
121 E::G1::msm_unchecked(&prover_param.powers_of_g[ignored].evals, scalars.as_slice())
122 .into_affine();
123 end_timer!(msm_timer);
124
125 end_timer!(commit_timer);
126 Ok(Commitment(commitment))
127 }
128
129 /// On input a polynomial `p` and a point `point`, outputs a proof for the
130 /// same. This function does not need to take the evaluation value as an
131 /// input.
132 ///
133 /// This function takes 2^{num_var +1} number of scalar multiplications over
134 /// G1:
135 /// - it prodceeds with `num_var` number of rounds,
136 /// - at round i, we compute an MSM for `2^{num_var - i + 1}` number of G2
137 /// elements.
138 fn open(
139 prover_param: impl Borrow<Self::ProverParam>,
140 polynomial: &Self::Polynomial,
141 point: &Self::Point,
142 ) -> Result<(Self::Proof, Self::Evaluation), PCSError> {
143 open_internal(prover_param.borrow(), polynomial, point)
144 }
145
146 /// Input a list of multilinear extensions, and a same number of points, and
147 /// a transcript, compute a multi-opening for all the polynomials.
148 fn multi_open(
149 prover_param: impl Borrow<Self::ProverParam>,
150 polynomials: &[Self::Polynomial],
151 points: &[Self::Point],
152 evals: &[Self::Evaluation],
153 transcript: &mut IOPTranscript<E::ScalarField>,
154 ) -> Result<BatchProof<E, Self>, PCSError> {
155 multi_open_internal(
156 prover_param.borrow(),
157 polynomials,
158 points,
159 evals,
160 transcript,
161 )
162 }
163
164 /// Verifies that `value` is the evaluation at `x` of the polynomial
165 /// committed inside `comm`.
166 ///
167 /// This function takes
168 /// - num_var number of pairing product.
169 /// - num_var number of MSM
170 fn verify(
171 verifier_param: &Self::VerifierParam,
172 commitment: &Self::Commitment,
173 point: &Self::Point,
174 value: &E::ScalarField,
175 proof: &Self::Proof,
176 ) -> Result<bool, PCSError> {
177 verify_internal(verifier_param, commitment, point, value, proof)
178 }
179
180 /// Verifies that `value_i` is the evaluation at `x_i` of the polynomial
181 /// `poly_i` committed inside `comm`.
182 fn batch_verify(
183 verifier_param: &Self::VerifierParam,
184 commitments: &[Self::Commitment],
185 points: &[Self::Point],
186 batch_proof: &Self::BatchProof,
187 transcript: &mut IOPTranscript<E::ScalarField>,
188 ) -> Result<bool, PCSError> {
189 batch_verify_internal(verifier_param, commitments, points, batch_proof, transcript)
190 }
191}
192
193/// On input a polynomial `p` and a point `point`, outputs a proof for the
194/// same. This function does not need to take the evaluation value as an
195/// input.
196///
197/// This function takes 2^{num_var} number of scalar multiplications over
198/// G1:
199/// - it proceeds with `num_var` number of rounds,
200/// - at round i, we compute an MSM for `2^{num_var - i}` number of G1 elements.
201fn open_internal<E: Pairing>(
202 prover_param: &MultilinearProverParam<E>,
203 polynomial: &DenseMultilinearExtension<E::ScalarField>,
204 point: &[E::ScalarField],
205) -> Result<(MultilinearKzgProof<E>, E::ScalarField), PCSError> {
206 let open_timer = start_timer!(|| format!("open mle with {} variable", polynomial.num_vars));
207
208 if polynomial.num_vars() > prover_param.num_vars {
209 return Err(PCSError::InvalidParameters(format!(
210 "Polynomial num_vars {} exceed the limit {}",
211 polynomial.num_vars, prover_param.num_vars
212 )));
213 }
214
215 if polynomial.num_vars() != point.len() {
216 return Err(PCSError::InvalidParameters(format!(
217 "Polynomial num_vars {} does not match point len {}",
218 polynomial.num_vars,
219 point.len()
220 )));
221 }
222
223 let nv = polynomial.num_vars();
224 // the first `ignored` SRS vectors are unused for opening.
225 let ignored = prover_param.num_vars - nv + 1;
226 let mut f = polynomial.to_evaluations();
227
228 let mut proofs = Vec::new();
229
230 for (i, (&point_at_k, gi)) in point
231 .iter()
232 .zip(prover_param.powers_of_g[ignored..ignored + nv].iter())
233 .enumerate()
234 {
235 let ith_round = start_timer!(|| format!("{}-th round", i));
236
237 let k = nv - 1 - i;
238 let cur_dim = 1 << k;
239 let mut q = vec![E::ScalarField::zero(); cur_dim];
240 let mut r = vec![E::ScalarField::zero(); cur_dim];
241
242 let ith_round_eval = start_timer!(|| format!("{}-th round eval", i));
243 for b in 0..(1 << k) {
244 // q[b] = f[1, b] - f[0, b]
245 q[b] = f[(b << 1) + 1] - f[b << 1];
246
247 // r[b] = f[0, b] + q[b] * p
248 r[b] = f[b << 1] + (q[b] * point_at_k);
249 }
250 f = r;
251 end_timer!(ith_round_eval);
252
253 // this is a MSM over G1 and is likely to be the bottleneck
254 let msm_timer = start_timer!(|| format!("msm of size {} at round {}", gi.evals.len(), i));
255
256 proofs.push(E::G1::msm_unchecked(&gi.evals, &q).into_affine());
257 end_timer!(msm_timer);
258
259 end_timer!(ith_round);
260 }
261 let eval = evaluate_opt(polynomial, point);
262 end_timer!(open_timer);
263 Ok((MultilinearKzgProof { proofs }, eval))
264}
265
266/// Verifies that `value` is the evaluation at `x` of the polynomial
267/// committed inside `comm`.
268///
269/// This function takes
270/// - num_var number of pairing product.
271/// - num_var number of MSM
272fn verify_internal<E: Pairing>(
273 verifier_param: &MultilinearVerifierParam<E>,
274 commitment: &Commitment<E>,
275 point: &[E::ScalarField],
276 value: &E::ScalarField,
277 proof: &MultilinearKzgProof<E>,
278) -> Result<bool, PCSError> {
279 let verify_timer = start_timer!(|| "verify");
280 let num_var = point.len();
281
282 if num_var > verifier_param.num_vars {
283 return Err(PCSError::InvalidParameters(format!(
284 "point length ({}) exceeds param limit ({})",
285 num_var, verifier_param.num_vars
286 )));
287 }
288
289 let prepare_inputs_timer = start_timer!(|| "prepare pairing inputs");
290
291 let scalar_size = E::ScalarField::MODULUS_BIT_SIZE as usize;
292 let window_size = FixedBase::get_mul_window_size(num_var);
293
294 let h_table =
295 FixedBase::get_window_table(scalar_size, window_size, verifier_param.h.into_group());
296 let h_mul: Vec<E::G2> = FixedBase::msm(scalar_size, window_size, &h_table, point);
297
298 let ignored = verifier_param.num_vars - num_var;
299 let h_vec: Vec<_> = (0..num_var)
300 .map(|i| verifier_param.h_mask[ignored + i].into_group() - h_mul[i])
301 .collect();
302 let h_vec: Vec<E::G2Affine> = E::G2::normalize_batch(&h_vec);
303 end_timer!(prepare_inputs_timer);
304
305 let pairing_product_timer = start_timer!(|| "pairing product");
306
307 let mut pairings: Vec<_> = proof
308 .proofs
309 .iter()
310 .map(|&x| E::G1Prepared::from(x))
311 .zip(h_vec.into_iter().take(num_var).map(E::G2Prepared::from))
312 .collect();
313
314 pairings.push((
315 E::G1Prepared::from(
316 (verifier_param.g.mul(*value) - commitment.0.into_group()).into_affine(),
317 ),
318 E::G2Prepared::from(verifier_param.h),
319 ));
320
321 let ps = pairings.iter().map(|(p, _)| p.clone());
322 let hs = pairings.iter().map(|(_, h)| h.clone());
323
324 let res = E::multi_pairing(ps, hs) == ark_ec::pairing::PairingOutput(E::TargetField::one());
325
326 end_timer!(pairing_product_timer);
327 end_timer!(verify_timer);
328 Ok(res)
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334 use ark_bls12_381::Bls12_381;
335 use ark_ec::pairing::Pairing;
336 use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
337 use ark_std::{test_rng, vec::Vec, UniformRand};
338
339 type E = Bls12_381;
340 type Fr = <E as Pairing>::ScalarField;
341
342 fn test_single_helper<R: Rng>(
343 params: &MultilinearUniversalParams<E>,
344 poly: &Arc<DenseMultilinearExtension<Fr>>,
345 rng: &mut R,
346 ) -> Result<(), PCSError> {
347 let nv = poly.num_vars();
348 assert_ne!(nv, 0);
349 let (ck, vk) = MultilinearKzgPCS::trim(params, None, Some(nv))?;
350 let point: Vec<_> = (0..nv).map(|_| Fr::rand(rng)).collect();
351 let com = MultilinearKzgPCS::commit(&ck, poly)?;
352 let (proof, value) = MultilinearKzgPCS::open(&ck, poly, &point)?;
353
354 assert!(MultilinearKzgPCS::verify(
355 &vk, &com, &point, &value, &proof
356 )?);
357
358 let value = Fr::rand(rng);
359 assert!(!MultilinearKzgPCS::verify(
360 &vk, &com, &point, &value, &proof
361 )?);
362
363 Ok(())
364 }
365
366 #[test]
367 fn test_single_commit() -> Result<(), PCSError> {
368 let mut rng = test_rng();
369
370 let params = MultilinearKzgPCS::<E>::gen_srs_for_testing(&mut rng, 10)?;
371
372 // normal polynomials
373 let poly1 = Arc::new(DenseMultilinearExtension::rand(8, &mut rng));
374 test_single_helper(¶ms, &poly1, &mut rng)?;
375
376 // single-variate polynomials
377 let poly2 = Arc::new(DenseMultilinearExtension::rand(1, &mut rng));
378 test_single_helper(¶ms, &poly2, &mut rng)?;
379
380 Ok(())
381 }
382
383 #[test]
384 fn setup_commit_verify_constant_polynomial() {
385 let mut rng = test_rng();
386
387 // normal polynomials
388 assert!(MultilinearKzgPCS::<E>::gen_srs_for_testing(&mut rng, 0).is_err());
389 }
390}