hyperplonk/pcs/src/multilinear_kzg/mod.rs
pcs/src/multilinear_kzg/mod.rsRaw
1// Copyright (c) 2022 Espresso Systems (espressosys.com)
2// This file is part of the Jellyfish library.
3
4// You should have received a copy of the MIT License
5// along with the Jellyfish library. If not, see <https://mit-license.org/>.
6
7//! Main module for multilinear KZG commitment scheme
8
9mod batching;
10pub(crate) mod srs;
11pub(crate) mod util;
12
13use self::batching::{
14 batch_verify_internal, batch_verify_same_poly_internal, multi_open_internal,
15 multi_open_same_poly_internal,
16};
17use crate::{
18 prelude::{
19 Commitment, UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam,
20 },
21 univariate_kzg::UnivariateKzgProof,
22 PCSError, PolynomialCommitmentScheme, StructuredReferenceString,
23};
24use arithmetic::{evaluate_opt, merge_polynomials};
25use ark_ec::{
26 msm::{FixedBaseMSM, VariableBaseMSM},
27 AffineCurve, PairingEngine, ProjectiveCurve,
28};
29use ark_ff::PrimeField;
30use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
31use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write};
32use ark_std::{
33 borrow::Borrow,
34 end_timer, format,
35 marker::PhantomData,
36 rand::{CryptoRng, RngCore},
37 rc::Rc,
38 start_timer,
39 string::ToString,
40 vec,
41 vec::Vec,
42 One, Zero,
43};
44// use batching::{batch_verify_internal, multi_open_internal};
45use srs::{MultilinearProverParam, MultilinearUniversalParams, MultilinearVerifierParam};
46
47/// KZG Polynomial Commitment Scheme on multilinear polynomials.
48pub struct MultilinearKzgPCS<E: PairingEngine> {
49 #[doc(hidden)]
50 phantom: PhantomData<E>,
51}
52
53#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq, Eq)]
54/// proof of opening
55pub struct MultilinearKzgProof<E: PairingEngine> {
56 /// Evaluation of quotients
57 pub proofs: Vec<E::G1Affine>,
58}
59
60#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq, Eq)]
61/// proof of batch opening
62pub struct MultilinearKzgBatchProof<E: PairingEngine> {
63 /// The actual proof
64 pub proof: MultilinearKzgProof<E>,
65 /// Commitment to q(x):= w(l(x)) where
66 /// - `w` is the merged MLE
67 /// - `l` is the list of univariate polys that goes through all points
68 pub q_x_commit: Commitment<E>,
69 /// openings of q(x) at 1, omega, ..., and r
70 pub q_x_opens: Vec<UnivariateKzgProof<E>>,
71}
72
73impl<E: PairingEngine> PolynomialCommitmentScheme<E> for MultilinearKzgPCS<E> {
74 // Parameters
75 type ProverParam = (
76 MultilinearProverParam<E>,
77 UnivariateProverParam<E::G1Affine>,
78 );
79 type VerifierParam = (MultilinearVerifierParam<E>, UnivariateVerifierParam<E>);
80 type SRS = (MultilinearUniversalParams<E>, UnivariateUniversalParams<E>);
81 // Polynomial and its associated types
82 type Polynomial = Rc<DenseMultilinearExtension<E::Fr>>;
83 type Point = Vec<E::Fr>;
84 type Evaluation = E::Fr;
85 // Commitments and proofs
86 type Commitment = Commitment<E>;
87 type BatchCommitment = Commitment<E>;
88 type Proof = MultilinearKzgProof<E>;
89 type BatchProof = MultilinearKzgBatchProof<E>;
90
91 /// Build SRS for testing.
92 ///
93 /// - For univariate polynomials, `log_size` is the log of maximum degree.
94 /// - For multilinear polynomials, `log_size` is the number of variables.
95 ///
96 /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY.
97 /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION.
98 fn gen_srs_for_testing<R: RngCore + CryptoRng>(
99 rng: &mut R,
100 log_size: usize,
101 ) -> Result<Self::SRS, PCSError> {
102 Ok((
103 MultilinearUniversalParams::<E>::gen_srs_for_testing(rng, log_size)?,
104 UnivariateUniversalParams::<E>::gen_srs_for_testing(rng, 1 << log_size)?,
105 ))
106 }
107
108 /// Trim the universal parameters to specialize the public parameters.
109 /// Input both `supported_log_degree` for univariate and
110 /// `supported_num_vars` for multilinear.
111 fn trim(
112 srs: impl Borrow<Self::SRS>,
113 supported_degree: usize,
114 supported_num_vars: Option<usize>,
115 ) -> Result<(Self::ProverParam, Self::VerifierParam), PCSError> {
116 let supported_num_vars = match supported_num_vars {
117 Some(p) => p,
118 None => {
119 return Err(PCSError::InvalidParameters(
120 "multilinear should receive a num_var param".to_string(),
121 ))
122 },
123 };
124 let (uni_ck, uni_vk) = srs.borrow().1.trim(supported_degree)?;
125 let (ml_ck, ml_vk) = srs.borrow().0.trim(supported_num_vars)?;
126
127 Ok(((ml_ck, uni_ck), (ml_vk, uni_vk)))
128 }
129
130 /// Generate a commitment for a polynomial.
131 ///
132 /// This function takes `2^num_vars` number of scalar multiplications over
133 /// G1.
134 fn commit(
135 prover_param: impl Borrow<Self::ProverParam>,
136 poly: &Self::Polynomial,
137 ) -> Result<Self::Commitment, PCSError> {
138 let prover_param = prover_param.borrow();
139 let commit_timer = start_timer!(|| "commit");
140 if prover_param.0.num_vars < poly.num_vars {
141 return Err(PCSError::InvalidParameters(format!(
142 "MlE length ({}) exceeds param limit ({})",
143 poly.num_vars, prover_param.0.num_vars
144 )));
145 }
146 let ignored = prover_param.0.num_vars - poly.num_vars;
147 let scalars: Vec<_> = poly
148 .to_evaluations()
149 .into_iter()
150 .map(|x| x.into_repr())
151 .collect();
152 let commitment = VariableBaseMSM::multi_scalar_mul(
153 &prover_param.0.powers_of_g[ignored].evals,
154 scalars.as_slice(),
155 )
156 .into_affine();
157
158 end_timer!(commit_timer);
159 Ok(Commitment(commitment))
160 }
161
162 /// Generate a commitment for a list of polynomials.
163 ///
164 /// This function takes `2^(num_vars + log(polys.len())` number of scalar
165 /// multiplications over G1.
166 fn multi_commit(
167 prover_param: impl Borrow<Self::ProverParam>,
168 polys: &[Self::Polynomial],
169 ) -> Result<Self::Commitment, PCSError> {
170 let prover_param = prover_param.borrow();
171 let commit_timer = start_timer!(|| "multi commit");
172 let poly = merge_polynomials(polys)?;
173
174 let scalars: Vec<_> = poly
175 .to_evaluations()
176 .iter()
177 .map(|x| x.into_repr())
178 .collect();
179
180 let commitment = VariableBaseMSM::multi_scalar_mul(
181 &prover_param.0.powers_of_g[0].evals,
182 scalars.as_slice(),
183 )
184 .into_affine();
185
186 end_timer!(commit_timer);
187 Ok(Commitment(commitment))
188 }
189
190 /// On input a polynomial `p` and a point `point`, outputs a proof for the
191 /// same. This function does not need to take the evaluation value as an
192 /// input.
193 ///
194 /// This function takes 2^{num_var +1} number of scalar multiplications over
195 /// G1:
196 /// - it prodceeds with `num_var` number of rounds,
197 /// - at round i, we compute an MSM for `2^{num_var - i + 1}` number of G2
198 /// elements.
199 fn open(
200 prover_param: impl Borrow<Self::ProverParam>,
201 polynomial: &Self::Polynomial,
202 point: &Self::Point,
203 ) -> Result<(Self::Proof, Self::Evaluation), PCSError> {
204 open_internal(&prover_param.borrow().0, polynomial, point)
205 }
206
207 /// Input
208 /// - the prover parameters for univariate KZG,
209 /// - the prover parameters for multilinear KZG,
210 /// - a list of multilinear extensions (MLEs),
211 /// - a commitment to all multilinear extensions,
212 /// - and a same number of points,
213 /// compute a multi-opening for all the polynomials.
214 ///
215 /// For simplicity, this API requires each MLE to have only one point. If
216 /// the caller wish to use more than one points per MLE, it should be
217 /// handled at the caller layer.
218 ///
219 /// Returns an error if the lengths do not match.
220 ///
221 /// Returns the proof, consists of
222 /// - the multilinear KZG opening
223 /// - the univariate KZG commitment to q(x)
224 /// - the openings and evaluations of q(x) at omega^i and r
225 ///
226 /// Steps:
227 /// 1. build `l(points)` which is a list of univariate polynomials that goes
228 /// through the points
229 /// 2. build MLE `w` which is the merge of all MLEs.
230 /// 3. build `q(x)` which is a univariate polynomial `W circ l`
231 /// 4. commit to q(x) and sample r from transcript
232 /// transcript contains: w commitment, points, q(x)'s commitment
233 /// 5. build q(omega^i) and their openings
234 /// 6. build q(r) and its opening
235 /// 7. get a point `p := l(r)`
236 /// 8. output an opening of `w` over point `p`
237 /// 9. output `w(p)`
238 fn multi_open(
239 prover_param: impl Borrow<Self::ProverParam>,
240 multi_commitment: &Self::BatchCommitment,
241 polynomials: &[Self::Polynomial],
242 points: &[Self::Point],
243 ) -> Result<(Self::BatchProof, Vec<Self::Evaluation>), PCSError> {
244 multi_open_internal::<E>(
245 &prover_param.borrow().1,
246 &prover_param.borrow().0,
247 polynomials,
248 multi_commitment,
249 points,
250 )
251 }
252
253 /// Input a multilinear extension, and a number of points, and
254 /// a transcript, compute a multi-opening for all the polynomials.
255 fn multi_open_single_poly(
256 prover_param: impl Borrow<Self::ProverParam>,
257 commitment: &Self::Commitment,
258 polynomial: &Self::Polynomial,
259 points: &[Self::Point],
260 ) -> Result<(Self::BatchProof, Vec<Self::Evaluation>), PCSError> {
261 multi_open_same_poly_internal::<E>(
262 &prover_param.borrow().1,
263 &prover_param.borrow().0,
264 polynomial,
265 commitment,
266 points,
267 )
268 }
269
270 /// Verifies that `value` is the evaluation at `x` of the polynomial
271 /// committed inside `comm`.
272 ///
273 /// This function takes
274 /// - num_var number of pairing product.
275 /// - num_var number of MSM
276 fn verify(
277 verifier_param: &Self::VerifierParam,
278 commitment: &Self::Commitment,
279 point: &Self::Point,
280 value: &E::Fr,
281 proof: &Self::Proof,
282 ) -> Result<bool, PCSError> {
283 verify_internal(&verifier_param.0, commitment, point, value, proof)
284 }
285
286 /// Verifies that `value` is the evaluation at `x_i` of the polynomial
287 /// `poly_i` committed inside `comm`.
288 /// steps:
289 ///
290 /// 1. put `q(x)`'s evaluations over `(1, omega,...)` into transcript
291 /// 2. sample `r` from transcript
292 /// 3. check `q(r) == value`
293 /// 4. build `l(points)` which is a list of univariate polynomials that goes
294 /// through the points
295 /// 5. get a point `p := l(r)`
296 /// 6. verifies `p` is verifies against proof
297 fn batch_verify<R: RngCore + CryptoRng>(
298 verifier_param: &Self::VerifierParam,
299 multi_commitment: &Self::BatchCommitment,
300 points: &[Self::Point],
301 values: &[E::Fr],
302 batch_proof: &Self::BatchProof,
303 _rng: &mut R,
304 ) -> Result<bool, PCSError> {
305 batch_verify_internal(
306 &verifier_param.1,
307 &verifier_param.0,
308 multi_commitment,
309 points,
310 values,
311 batch_proof,
312 )
313 }
314
315 /// Verifies that `value_i` is the evaluation at `x_i` of the polynomial
316 /// `poly` committed inside `comm`.
317 fn batch_verify_single_poly(
318 verifier_param: &Self::VerifierParam,
319 commitment: &Self::Commitment,
320 points: &[Self::Point],
321 values: &[E::Fr],
322 batch_proof: &Self::BatchProof,
323 ) -> Result<bool, PCSError> {
324 batch_verify_same_poly_internal(
325 &verifier_param.1,
326 &verifier_param.0,
327 commitment,
328 points,
329 values,
330 batch_proof,
331 )
332 }
333}
334
335/// On input a polynomial `p` and a point `point`, outputs a proof for the
336/// same. This function does not need to take the evaluation value as an
337/// input.
338///
339/// This function takes 2^{num_var +1} number of scalar multiplications over
340/// G1:
341/// - it proceeds with `num_var` number of rounds,
342/// - at round i, we compute an MSM for `2^{num_var - i + 1}` number of G2
343/// elements.
344fn open_internal<E: PairingEngine>(
345 prover_param: &MultilinearProverParam<E>,
346 polynomial: &DenseMultilinearExtension<E::Fr>,
347 point: &[E::Fr],
348) -> Result<(MultilinearKzgProof<E>, E::Fr), PCSError> {
349 let open_timer = start_timer!(|| format!("open mle with {} variable", polynomial.num_vars));
350
351 if polynomial.num_vars() > prover_param.num_vars {
352 return Err(PCSError::InvalidParameters(format!(
353 "Polynomial num_vars {} exceed the limit {}",
354 polynomial.num_vars, prover_param.num_vars
355 )));
356 }
357
358 if polynomial.num_vars() != point.len() {
359 return Err(PCSError::InvalidParameters(format!(
360 "Polynomial num_vars {} does not match point len {}",
361 polynomial.num_vars,
362 point.len()
363 )));
364 }
365
366 let nv = polynomial.num_vars();
367 let ignored = prover_param.num_vars - nv;
368 let mut r: Vec<Vec<E::Fr>> = (0..nv + 1).map(|_| Vec::new()).collect();
369 let mut q: Vec<Vec<E::Fr>> = (0..nv + 1).map(|_| Vec::new()).collect();
370
371 r[nv] = polynomial.to_evaluations();
372
373 let mut proofs = Vec::new();
374
375 for (i, (&point_at_k, gi)) in point
376 .iter()
377 .zip(prover_param.powers_of_g[ignored..].iter())
378 .take(nv)
379 .enumerate()
380 {
381 let ith_round = start_timer!(|| format!("{}-th round", i));
382
383 let k = nv - i;
384 let cur_dim = 1 << (k - 1);
385 let mut cur_q = vec![E::Fr::zero(); cur_dim];
386 let mut cur_r = vec![E::Fr::zero(); cur_dim];
387 let one_minus_point_at_k = E::Fr::one() - point_at_k;
388
389 let ith_round_eval = start_timer!(|| format!("{}-th round eval", i));
390 for b in 0..(1 << (k - 1)) {
391 // q_b = pre_r [2^b + 1] - pre_r [2^b]
392 cur_q[b] = r[k][(b << 1) + 1] - r[k][b << 1];
393
394 // r_b = pre_r [2^b]*(1-p) + pre_r [2^b + 1] * p
395 cur_r[b] = r[k][b << 1] * one_minus_point_at_k + (r[k][(b << 1) + 1] * point_at_k);
396 }
397 end_timer!(ith_round_eval);
398 let scalars: Vec<_> = (0..(1 << k)).map(|x| cur_q[x >> 1].into_repr()).collect();
399
400 q[k] = cur_q;
401 r[k - 1] = cur_r;
402
403 // this is a MSM over G1 and is likely to be the bottleneck
404 proofs.push(VariableBaseMSM::multi_scalar_mul(&gi.evals, &scalars).into_affine());
405 end_timer!(ith_round);
406 }
407 let eval = evaluate_opt(polynomial, point);
408 end_timer!(open_timer);
409 Ok((MultilinearKzgProof { proofs }, eval))
410}
411
412/// Verifies that `value` is the evaluation at `x` of the polynomial
413/// committed inside `comm`.
414///
415/// This function takes
416/// - num_var number of pairing product.
417/// - num_var number of MSM
418fn verify_internal<E: PairingEngine>(
419 verifier_param: &MultilinearVerifierParam<E>,
420 commitment: &Commitment<E>,
421 point: &[E::Fr],
422 value: &E::Fr,
423 proof: &MultilinearKzgProof<E>,
424) -> Result<bool, PCSError> {
425 let verify_timer = start_timer!(|| "verify");
426 let num_var = point.len();
427
428 if num_var > verifier_param.num_vars {
429 return Err(PCSError::InvalidParameters(format!(
430 "point length ({}) exceeds param limit ({})",
431 num_var, verifier_param.num_vars
432 )));
433 }
434
435 let ignored = verifier_param.num_vars - num_var;
436 let prepare_inputs_timer = start_timer!(|| "prepare pairing inputs");
437
438 let scalar_size = E::Fr::size_in_bits();
439 let window_size = FixedBaseMSM::get_mul_window_size(num_var);
440
441 let h_table = FixedBaseMSM::get_window_table(
442 scalar_size,
443 window_size,
444 verifier_param.h.into_projective(),
445 );
446 let h_mul: Vec<E::G2Projective> =
447 FixedBaseMSM::multi_scalar_mul(scalar_size, window_size, &h_table, point);
448
449 let h_vec: Vec<_> = (0..num_var)
450 .map(|i| verifier_param.h_mask[ignored + i].into_projective() - h_mul[i])
451 .collect();
452 let h_vec: Vec<E::G2Affine> = E::G2Projective::batch_normalization_into_affine(&h_vec);
453 end_timer!(prepare_inputs_timer);
454
455 let pairing_product_timer = start_timer!(|| "pairing product");
456
457 let mut pairings: Vec<_> = proof
458 .proofs
459 .iter()
460 .map(|&x| E::G1Prepared::from(x))
461 .zip(h_vec.into_iter().take(num_var).map(E::G2Prepared::from))
462 .collect();
463
464 pairings.push((
465 E::G1Prepared::from(
466 (verifier_param.g.mul(*value) - commitment.0.into_projective()).into_affine(),
467 ),
468 E::G2Prepared::from(verifier_param.h),
469 ));
470
471 let res = E::product_of_pairings(pairings.iter()) == E::Fqk::one();
472
473 end_timer!(pairing_product_timer);
474 end_timer!(verify_timer);
475 Ok(res)
476}
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481 use crate::multilinear_kzg::util::compute_qx_degree;
482 use arithmetic::get_batched_nv;
483 use ark_bls12_381::Bls12_381;
484 use ark_ec::PairingEngine;
485 use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
486 use ark_std::{log2, rand::RngCore, test_rng, vec::Vec, UniformRand};
487
488 type E = Bls12_381;
489 type Fr = <E as PairingEngine>::Fr;
490
491 fn test_single_helper<R: RngCore + CryptoRng>(
492 params: &(MultilinearUniversalParams<E>, UnivariateUniversalParams<E>),
493 poly: &Rc<DenseMultilinearExtension<Fr>>,
494 rng: &mut R,
495 ) -> Result<(), PCSError> {
496 let nv = poly.num_vars();
497 assert_ne!(nv, 0);
498 let uni_degree = 1;
499 let (ck, vk) = MultilinearKzgPCS::trim(params, uni_degree, Some(nv + 1))?;
500 let point: Vec<_> = (0..nv).map(|_| Fr::rand(rng)).collect();
501 let com = MultilinearKzgPCS::commit(&ck, poly)?;
502 let (proof, value) = MultilinearKzgPCS::open(&ck, poly, &point)?;
503
504 assert!(MultilinearKzgPCS::verify(
505 &vk, &com, &point, &value, &proof
506 )?);
507
508 let value = Fr::rand(rng);
509 assert!(!MultilinearKzgPCS::verify(
510 &vk, &com, &point, &value, &proof
511 )?);
512
513 Ok(())
514 }
515
516 #[test]
517 fn test_single_commit() -> Result<(), PCSError> {
518 let mut rng = test_rng();
519
520 let params = MultilinearKzgPCS::<E>::gen_srs_for_testing(&mut rng, 10)?;
521
522 // normal polynomials
523 let poly1 = Rc::new(DenseMultilinearExtension::rand(8, &mut rng));
524 test_single_helper(¶ms, &poly1, &mut rng)?;
525
526 // single-variate polynomials
527 let poly2 = Rc::new(DenseMultilinearExtension::rand(1, &mut rng));
528 test_single_helper(¶ms, &poly2, &mut rng)?;
529
530 Ok(())
531 }
532
533 #[test]
534 fn setup_commit_verify_constant_polynomial() {
535 let mut rng = test_rng();
536
537 // normal polynomials
538 assert!(MultilinearKzgPCS::<E>::gen_srs_for_testing(&mut rng, 0).is_err());
539 }
540
541 fn test_multi_open_single_poly_helper<R: RngCore + CryptoRng>(
542 params: &(MultilinearUniversalParams<E>, UnivariateUniversalParams<E>),
543 poly: Rc<DenseMultilinearExtension<Fr>>,
544 num_open: usize,
545 rng: &mut R,
546 ) -> Result<(), PCSError> {
547 let nv = poly.num_vars();
548 assert_ne!(nv, 0);
549 let uni_degree = 1024;
550 let (ck, vk) = MultilinearKzgPCS::trim(params, uni_degree, Some(nv + 1))?;
551 let mut points = vec![];
552 for _ in 0..num_open {
553 let point: Vec<_> = (0..nv).map(|_| Fr::rand(rng)).collect();
554 points.push(point)
555 }
556 let com = MultilinearKzgPCS::commit(&ck, &poly)?;
557 let (proof, mut values) =
558 MultilinearKzgPCS::multi_open_single_poly(&ck, &com, &poly, &points)?;
559 for (a, b) in values.iter().zip(points.iter()) {
560 let p = poly.evaluate(&b).unwrap();
561 assert_eq!(*a, p);
562 }
563
564 assert!(MultilinearKzgPCS::batch_verify_single_poly(
565 &vk, &com, &points, &values, &proof
566 )?);
567
568 values[0] = Fr::rand(rng);
569 assert!(!MultilinearKzgPCS::batch_verify_single_poly(
570 &vk, &com, &points, &values, &proof
571 )?);
572 Ok(())
573 }
574
575 #[test]
576 fn test_multi_open_single_poly() -> Result<(), PCSError> {
577 let mut rng = test_rng();
578
579 let params = MultilinearKzgPCS::<E>::gen_srs_for_testing(&mut rng, 15)?;
580
581 for nv in 1..10 {
582 for num_open in 2..10 {
583 let poly1 = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng));
584 test_multi_open_single_poly_helper(¶ms, poly1, num_open, &mut rng)?;
585 }
586 }
587
588 Ok(())
589 }
590
591 fn test_multi_open_helper<R: RngCore + CryptoRng>(
592 params: &(MultilinearUniversalParams<E>, UnivariateUniversalParams<E>),
593 polys: &[Rc<DenseMultilinearExtension<Fr>>],
594 num_open: usize,
595 rng: &mut R,
596 ) -> Result<(), PCSError> {
597 let nv = polys[0].num_vars();
598 assert_ne!(nv, 0);
599 let merged_nv = get_batched_nv(nv, polys.len());
600 let qx_degree = compute_qx_degree(merged_nv, polys.len());
601 let padded_qx_degree = 1usize << log2(qx_degree);
602
603 let (ck, vk) = MultilinearKzgPCS::trim(params, padded_qx_degree, Some(merged_nv))?;
604 let mut points = vec![];
605 for _ in 0..num_open {
606 let point: Vec<_> = (0..nv).map(|_| Fr::rand(rng)).collect();
607 points.push(point)
608 }
609 let com = MultilinearKzgPCS::multi_commit(&ck, &polys)?;
610 let (proof, mut values) = MultilinearKzgPCS::multi_open(&ck, &com, polys, &points)?;
611
612 assert!(MultilinearKzgPCS::batch_verify(
613 &vk, &com, &points, &values, &proof, rng
614 )?);
615
616 values[0] = Fr::rand(rng);
617 assert!(!MultilinearKzgPCS::batch_verify_single_poly(
618 &vk, &com, &points, &values, &proof
619 )?);
620
621 Ok(())
622 }
623
624 #[test]
625 fn test_multi_open() -> Result<(), PCSError> {
626 let mut rng = test_rng();
627
628 let params = MultilinearKzgPCS::<E>::gen_srs_for_testing(&mut rng, 15)?;
629
630 // normal polynomials
631 for nv in 1..10 {
632 for num_open in 1..4 {
633 let mut polys = vec![];
634 for _ in 0..num_open {
635 let poly = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng));
636 polys.push(poly)
637 }
638
639 test_multi_open_helper(¶ms, &polys, num_open, &mut rng)?;
640 }
641 }
642 Ok(())
643 }
644}