1use super::{
8 open_internal,
9 srs::{MultilinearProverParam, MultilinearVerifierParam},
10 util::{build_l, compute_w_circ_l, merge_polynomials},
11 verify_internal, MultilinearKzgBatchProof, MLE,
12};
13use crate::{
14 multilinear_kzg::util::get_uni_domain,
15 prelude::{Commitment, UnivariateProverParam, UnivariateVerifierParam},
16 transcript::IOPTranscript,
17 univariate_kzg::UnivariateKzgPCS,
18 PCSError, PolynomialCommitmentScheme,
19};
20use ark_ec::pairing::Pairing;
21use ark_poly::{EvaluationDomain, MultilinearExtension, Polynomial};
22use ark_std::{end_timer, format, start_timer, string::ToString, vec, vec::Vec};
23
24pub(super) fn batch_open_internal<E: Pairing>(
58 uni_prover_param: &UnivariateProverParam<E>,
59 ml_prover_param: &MultilinearProverParam<E>,
60 polynomials: &[MLE<E::ScalarField>],
61 batch_commitment: &Commitment<E>,
62 points: &[Vec<E::ScalarField>],
63) -> Result<(MultilinearKzgBatchProof<E>, Vec<E::ScalarField>), PCSError> {
64 let open_timer = start_timer!(|| "batch open");
65
66 let points_len = points.len();
70 if points_len == 0 {
71 return Err(PCSError::InvalidParameters("points is empty".to_string()));
72 }
73
74 if points_len != polynomials.len() {
75 return Err(PCSError::InvalidParameters(
76 "polynomial length does not match point length".to_string(),
77 ));
78 }
79
80 let num_var = polynomials[0].num_vars();
81 for poly in polynomials.iter().skip(1) {
82 if poly.num_vars() != num_var {
83 return Err(PCSError::InvalidParameters(
84 "polynomials do not have same num_vars".to_string(),
85 ));
86 }
87 }
88 for point in points.iter() {
89 if point.len() != num_var {
90 return Err(PCSError::InvalidParameters(
91 "points do not have same num_vars".to_string(),
92 ));
93 }
94 }
95
96 let domain = get_uni_domain::<E::ScalarField>(points_len)?;
97
98 let uni_polys = build_l(num_var, points, &domain)?;
101
102 let merge_poly = merge_polynomials(polynomials)?;
104
105 let q_x = compute_w_circ_l(&merge_poly, &uni_polys)?;
107
108 let mut transcript = IOPTranscript::new(b"ml kzg");
111 transcript.append_serializable_element(b"w", batch_commitment)?;
112 for point in points {
113 transcript.append_serializable_element(b"w", point)?;
114 }
115
116 let q_x_commit = UnivariateKzgPCS::<E>::commit(uni_prover_param, &q_x)?;
117 transcript.append_serializable_element(b"q(x)", &q_x_commit)?;
118 let r = transcript.get_and_append_challenge(b"r")?;
119
120 let mut q_x_opens = vec![];
122 let mut q_x_evals = vec![];
123 for i in 0..points_len {
124 let (q_x_open, q_x_eval) =
125 UnivariateKzgPCS::<E>::open(uni_prover_param, &q_x, &domain.element(i))?;
126 q_x_opens.push(q_x_open);
127 q_x_evals.push(q_x_eval);
128
129 let point: Vec<E::ScalarField> = uni_polys
131 .iter()
132 .rev()
133 .map(|poly| poly.evaluate(&domain.element(i)))
134 .collect();
135 let mle_eval = merge_poly.evaluate(&point).unwrap();
136 if mle_eval != q_x_eval {
137 return Err(PCSError::InvalidProver(
138 "Q(omega) does not match W(l(omega))".to_string(),
139 ));
140 }
141 }
142
143 let (q_x_open, q_r_value) = UnivariateKzgPCS::<E>::open(uni_prover_param, &q_x, &r)?;
145 q_x_opens.push(q_x_open);
146 q_x_evals.push(q_r_value);
147
148 let point: Vec<E::ScalarField> = uni_polys
150 .iter()
151 .rev()
152 .map(|poly| poly.evaluate(&r))
153 .collect();
154
155 let (mle_opening, mle_eval) = open_internal(ml_prover_param, &merge_poly, &point)?;
157
158 if mle_eval != q_r_value {
160 return Err(PCSError::InvalidProver(
161 "Q(r) does not match W(l(r))".to_string(),
162 ));
163 }
164 end_timer!(open_timer);
165
166 Ok((
167 MultilinearKzgBatchProof {
168 proof: mle_opening,
169 q_x_commit,
170 q_x_opens,
171 },
172 q_x_evals,
173 ))
174}
175
176pub(super) fn batch_verify_internal<E: Pairing>(
191 uni_verifier_param: &UnivariateVerifierParam<E>,
192 ml_verifier_param: &MultilinearVerifierParam<E>,
193 batch_commitment: &Commitment<E>,
194 points: &[Vec<E::ScalarField>],
195 values: &[E::ScalarField],
196 batch_proof: &MultilinearKzgBatchProof<E>,
197) -> Result<bool, PCSError> {
198 let verify_timer = start_timer!(|| "batch verify");
199
200 let points_len = points.len();
204 if points_len == 0 {
205 return Err(PCSError::InvalidParameters("points is empty".to_string()));
206 }
207
208 if points_len + 1 != batch_proof.q_x_opens.len() {
210 return Err(PCSError::InvalidParameters(
211 "openings length does not match point length".to_string(),
212 ));
213 }
214
215 if points_len + 1 != values.len() {
216 return Err(PCSError::InvalidParameters(
217 "values length does not match point length".to_string(),
218 ));
219 }
220
221 let num_var = points[0].len();
222 for point in points.iter().skip(1) {
223 if point.len() != num_var {
224 return Err(PCSError::InvalidParameters(format!(
225 "points do not have same num_vars ({} vs {})",
226 point.len(),
227 num_var,
228 )));
229 }
230 }
231
232 let domain = get_uni_domain::<E::ScalarField>(points_len)?;
233
234 let mut transcript = IOPTranscript::new(b"ml kzg");
236 transcript.append_serializable_element(b"w", batch_commitment)?;
237 for point in points {
238 transcript.append_serializable_element(b"w", point)?;
239 }
240
241 transcript.append_serializable_element(b"q(x)", &batch_proof.q_x_commit)?;
242
243 let r = transcript.get_and_append_challenge(b"r")?;
245
246 for (i, value) in values.iter().enumerate().take(points_len) {
249 if !UnivariateKzgPCS::verify(
250 uni_verifier_param,
251 &batch_proof.q_x_commit,
252 &domain.element(i),
253 value,
254 &batch_proof.q_x_opens[i],
255 )? {
256 #[cfg(debug_assertion)]
257 println!("q(omega^{}) verification failed", i);
258 return Ok(false);
259 }
260 }
261
262 if !UnivariateKzgPCS::verify(
263 uni_verifier_param,
264 &batch_proof.q_x_commit,
265 &r,
266 &values[points_len],
267 &batch_proof.q_x_opens[points_len],
268 )? {
269 #[cfg(debug_assertion)]
270 println!("q(r) verification failed");
271 return Ok(false);
272 }
273
274 let uni_polys = build_l(num_var, points, &domain)?;
277
278 let point: Vec<E::ScalarField> = uni_polys.iter().rev().map(|x| x.evaluate(&r)).collect();
280
281 let res = verify_internal(
283 ml_verifier_param,
284 batch_commitment,
285 &point,
286 &values[points_len],
287 &batch_proof.proof,
288 )?;
289
290 #[cfg(debug_assertion)]
291 if !res {
292 println!("multilinear KZG verification failed");
293 }
294
295 end_timer!(verify_timer);
296
297 Ok(res)
298}
299
300#[cfg(test)]
301mod tests {
302 use super::{
303 super::{util::get_batched_nv, *},
304 *,
305 };
306 use crate::multilinear_kzg::util::{compute_qx_degree, generate_evaluations};
307 use ark_bls12_381::Bls12_381 as E;
308 use ark_std::{log2, UniformRand};
309 use jf_utils::test_rng;
310 type Fr = <E as Pairing>::ScalarField;
311
312 fn test_batch_commit_helper<R: RngCore + CryptoRng>(
313 uni_params: &UnivariateUniversalParams<E>,
314 ml_params: &MultilinearUniversalParams<E>,
315 polys: &[MLE<Fr>],
316 rng: &mut R,
317 ) -> Result<(), PCSError> {
318 let merged_nv = get_batched_nv(polys[0].num_vars(), polys.len());
319 let qx_degree = compute_qx_degree(merged_nv, polys.len());
320 let padded_qx_degree = 1usize << log2(qx_degree);
321
322 let (uni_ck, uni_vk) = uni_params.trim(padded_qx_degree)?;
323 let (ml_ck, ml_vk) = ml_params.trim(merged_nv)?;
324
325 let mut points = Vec::new();
326 for poly in polys.iter() {
327 let point = (0..poly.num_vars())
328 .map(|_| Fr::rand(rng))
329 .collect::<Vec<Fr>>();
330 points.push(point);
331 }
332
333 let evals = generate_evaluations(polys, &points)?;
334
335 let com = MultilinearKzgPCS::batch_commit((ml_ck.clone(), uni_ck.clone()), polys)?;
336 let (batch_proof, evaluations) =
337 batch_open_internal(&uni_ck, &ml_ck, polys, &com, &points)?;
338
339 for (a, b) in evals.iter().zip(evaluations.iter()) {
340 assert_eq!(a, b)
341 }
342
343 assert!(batch_verify_internal(
345 &uni_vk,
346 &ml_vk,
347 &com,
348 &points,
349 &evaluations,
350 &batch_proof,
351 )?);
352
353 assert!(!batch_verify_internal(
355 &uni_vk,
356 &ml_vk,
357 &Commitment(<E as Pairing>::G1Affine::default()),
358 &points,
359 &evaluations,
360 &batch_proof,
361 )?);
362
363 assert!(
365 batch_verify_internal(&uni_vk, &ml_vk, &com, &points[1..], &[], &batch_proof,).is_err()
366 );
367
368 assert!(batch_verify_internal(
370 &uni_vk,
371 &ml_vk,
372 &com,
373 &points,
374 &evaluations,
375 &MultilinearKzgBatchProof {
376 proof: MultilinearKzgProof { proofs: Vec::new() },
377 q_x_commit: Commitment(<E as Pairing>::G1Affine::default()),
378 q_x_opens: vec![],
379 },
380 )
381 .is_err());
382
383 let mut wrong_evals = evaluations.clone();
385 wrong_evals[0] = Fr::default();
386 assert!(!batch_verify_internal(
387 &uni_vk,
388 &ml_vk,
389 &com,
390 &points,
391 &wrong_evals,
392 &batch_proof
393 )?);
394
395 let mut wrong_proof = batch_proof;
397 wrong_proof.q_x_commit = Commitment(<E as Pairing>::G1Affine::default());
398 assert!(!batch_verify_internal(
399 &uni_vk,
400 &ml_vk,
401 &com,
402 &points,
403 &evaluations,
404 &wrong_proof,
405 )?);
406 Ok(())
407 }
408
409 #[test]
410 fn test_batch_commit_internal() -> Result<(), PCSError> {
411 let mut rng = test_rng();
412
413 let uni_params =
414 UnivariateUniversalParams::<E>::gen_srs_for_testing(&mut rng, 1usize << 15)?;
415 let ml_params = MultilinearUniversalParams::<E>::gen_srs_for_testing(&mut rng, 15)?;
416
417 let polys1: Vec<_> = (0..5)
419 .map(|_| MLE::from(DenseMultilinearExtension::rand(4, &mut rng)))
420 .collect();
421 test_batch_commit_helper(&uni_params, &ml_params, &polys1, &mut rng)?;
422
423 let polys1: Vec<_> = (0..5)
425 .map(|_| MLE::from(DenseMultilinearExtension::rand(1, &mut rng)))
426 .collect();
427 test_batch_commit_helper(&uni_params, &ml_params, &polys1, &mut rng)?;
428
429 Ok(())
430 }
431}