1use crate::{
17 AlgebraicSponge,
18 fft::DensePolynomial,
19 msm::variable_base::VariableBase,
20 polycommit::{PCError, kzg10, optional_rng::OptionalRng},
21 srs::{UniversalProver, UniversalVerifier},
22};
23use hashbrown::HashMap;
24use itertools::Itertools;
25use snarkvm_curves::traits::{AffineCurve, PairingCurve, PairingEngine, ProjectiveCurve};
26use snarkvm_fields::{One, Zero};
27
28use anyhow::{Result, bail, ensure};
29use core::{convert::TryInto, marker::PhantomData, ops::Mul};
30use rand_core::{RngCore, SeedableRng};
31use std::{
32 borrow::Borrow,
33 collections::{BTreeMap, BTreeSet},
34};
35
36mod data_structures;
37pub use data_structures::*;
38
39mod polynomial;
40pub use polynomial::*;
41
42#[derive(Clone, Debug)]
53pub struct SonicKZG10<E: PairingEngine, S: AlgebraicSponge<E::Fq, 2>> {
54 _engine: PhantomData<(E, S)>,
55}
56
57impl<E: PairingEngine, S: AlgebraicSponge<E::Fq, 2>> SonicKZG10<E, S> {
58 pub fn load_srs(max_degree: usize) -> Result<UniversalParams<E>, PCError> {
59 kzg10::KZG10::load_srs(max_degree).map_err(Into::into)
60 }
61
62 pub fn trim(
63 pp: &UniversalParams<E>,
64 supported_degree: usize,
65 supported_lagrange_sizes: impl IntoIterator<Item = usize>,
66 supported_hiding_bound: usize,
67 enforced_degree_bounds: Option<&[usize]>,
68 ) -> Result<(CommitterKey<E>, UniversalVerifier<E>)> {
69 let trim_time = start_timer!(|| "Trimming public parameters");
70 let max_degree = pp.max_degree();
71
72 let enforced_degree_bounds = enforced_degree_bounds.map(|bounds| {
73 let mut v = bounds.to_vec();
74 v.sort_unstable();
75 v.dedup();
76 v
77 });
78
79 let (shifted_powers_of_beta_g, shifted_powers_of_beta_times_gamma_g) = if let Some(enforced_degree_bounds) =
80 enforced_degree_bounds.as_ref()
81 {
82 if enforced_degree_bounds.is_empty() {
83 (None, None)
84 } else {
85 let highest_enforced_degree_bound = *enforced_degree_bounds.last().unwrap();
86 if highest_enforced_degree_bound > supported_degree {
87 bail!(
88 "The highest enforced degree bound {highest_enforced_degree_bound} is larger than the supported degree {supported_degree}"
89 );
90 }
91
92 let lowest_shift_degree = max_degree - highest_enforced_degree_bound;
93
94 let shifted_ck_time = start_timer!(|| format!(
95 "Constructing `shifted_powers_of_beta_g` of size {}",
96 max_degree - lowest_shift_degree + 1
97 ));
98
99 let shifted_powers_of_beta_g = pp.powers_of_beta_g(lowest_shift_degree, pp.max_degree() + 1)?;
100 let mut shifted_powers_of_beta_times_gamma_g = BTreeMap::new();
101 for degree_bound in enforced_degree_bounds {
103 let shift_degree = max_degree - degree_bound;
104 let powers_for_degree_bound = pp
107 .powers_of_beta_times_gamma_g()
108 .range(shift_degree..max_degree.min(shift_degree + supported_hiding_bound) + 2)
109 .map(|(_k, v)| *v)
110 .collect();
111 shifted_powers_of_beta_times_gamma_g.insert(*degree_bound, powers_for_degree_bound);
112 }
113
114 end_timer!(shifted_ck_time);
115
116 (Some(shifted_powers_of_beta_g), Some(shifted_powers_of_beta_times_gamma_g))
117 }
118 } else {
119 (None, None)
120 };
121
122 let powers_of_beta_g = pp.powers_of_beta_g(0, supported_degree + 1)?;
123 let powers_of_beta_times_gamma_g = pp
124 .powers_of_beta_times_gamma_g()
125 .range(0..=(supported_hiding_bound + 1))
126 .map(|(_k, v)| *v)
127 .collect::<Vec<_>>();
128 if powers_of_beta_times_gamma_g.len() != supported_hiding_bound + 2 {
129 return Err(
130 PCError::HidingBoundToolarge { hiding_poly_degree: supported_hiding_bound, num_powers: 0 }.into()
131 );
132 }
133
134 let mut lagrange_bases_at_beta_g = BTreeMap::new();
135 for size in supported_lagrange_sizes {
136 let lagrange_time = start_timer!(|| format!("Constructing `lagrange_bases` of size {size}"));
137 if !size.is_power_of_two() {
138 bail!("The Lagrange basis size ({size}) is not a power of two")
139 }
140 if size > pp.max_degree() + 1 {
141 bail!("The Lagrange basis size ({size}) is larger than the supported degree ({})", pp.max_degree() + 1)
142 }
143 let domain = crate::fft::EvaluationDomain::new(size).unwrap();
144 let lagrange_basis_at_beta_g = pp.lagrange_basis(domain)?;
145 assert!(lagrange_basis_at_beta_g.len().is_power_of_two());
146 lagrange_bases_at_beta_g.insert(domain.size(), lagrange_basis_at_beta_g);
147 end_timer!(lagrange_time);
148 }
149
150 let ck = CommitterKey {
151 powers_of_beta_g,
152 lagrange_bases_at_beta_g,
153 powers_of_beta_times_gamma_g,
154 shifted_powers_of_beta_g,
155 shifted_powers_of_beta_times_gamma_g,
156 enforced_degree_bounds,
157 };
158
159 let vk = pp.to_universal_verifier()?;
160
161 end_timer!(trim_time);
162 Ok((ck, vk))
163 }
164
165 #[allow(clippy::format_push_string)]
179 pub fn commit<'b>(
180 universal_prover: &UniversalProver<E>,
181 ck: &CommitterUnionKey<E>,
182 polynomials: impl IntoIterator<Item = LabeledPolynomialWithBasis<'b, E::Fr>>,
183 rng: Option<&mut dyn RngCore>,
184 ) -> Result<(Vec<LabeledCommitment<Commitment<E>>>, Vec<Randomness<E>>), PCError> {
185 let rng = &mut OptionalRng(rng);
186 let commit_time = start_timer!(|| "Committing to polynomials");
187
188 let mut pool = snarkvm_utilities::ExecutionPool::<Result<_, _>>::new();
189 for p in polynomials {
190 let seed = rng.0.as_mut().map(|r| {
191 let mut seed = [0u8; 32];
192 r.fill_bytes(&mut seed);
193 seed
194 });
195
196 kzg10::KZG10::<E>::check_degrees_and_bounds(
197 universal_prover.max_degree,
198 ck.enforced_degree_bounds.as_deref(),
199 p.clone(),
200 )?;
201 let degree_bound = p.degree_bound();
202 let hiding_bound = p.hiding_bound();
203 let label = p.label().to_string();
204
205 pool.add_job(move || {
206 let mut rng = seed.map(rand::rngs::StdRng::from_seed);
207 add_to_trace!(|| "PC::Commit", || format!(
208 "Polynomial {} of degree {}, degree bound {:?}, and hiding bound {:?}",
209 label,
210 p.degree(),
211 degree_bound,
212 hiding_bound,
213 ));
214
215 let (comm, rand) = {
216 let rng_ref = rng.as_mut().map(|s| s as _);
217 match p.polynomial {
218 PolynomialWithBasis::Lagrange { evaluations } => {
219 let domain = crate::fft::EvaluationDomain::new(evaluations.evaluations.len()).unwrap();
220 let lagrange_basis = ck
221 .lagrange_basis(domain)
222 .ok_or(PCError::UnsupportedLagrangeBasisSize(domain.size()))?;
223 assert!(domain.size().is_power_of_two());
224 assert!(lagrange_basis.size().is_power_of_two());
225 kzg10::KZG10::commit_lagrange(
226 &lagrange_basis,
227 &evaluations.evaluations,
228 hiding_bound,
229 rng_ref,
230 )?
231 }
232 PolynomialWithBasis::Monomial { polynomial, degree_bound } => {
233 let powers = if let Some(degree_bound) = degree_bound {
234 ck.shifted_powers_of_beta_g(degree_bound).unwrap()
235 } else {
236 ck.powers()
237 };
238
239 kzg10::KZG10::commit(&powers, &polynomial, hiding_bound, rng_ref)?
240 }
241 }
242 };
243
244 Ok((LabeledCommitment::new(label.to_string(), comm, degree_bound), rand))
245 });
246 }
247 let results: Vec<Result<_, PCError>> = pool.execute_all();
248
249 let mut labeled_comms = Vec::with_capacity(results.len());
250 let mut randomness = Vec::with_capacity(results.len());
251 for result in results {
252 let (comm, rand) = result?;
253 labeled_comms.push(comm);
254 randomness.push(rand);
255 }
256
257 end_timer!(commit_time);
258 Ok((labeled_comms, randomness))
259 }
260
261 pub fn combine_for_open<'a>(
262 universal_prover: &UniversalProver<E>,
263 ck: &CommitterUnionKey<E>,
264 labeled_polynomials: impl ExactSizeIterator<Item = &'a LabeledPolynomial<E::Fr>>,
265 rands: impl ExactSizeIterator<Item = &'a Randomness<E>>,
266 fs_rng: &mut S,
267 ) -> Result<(DensePolynomial<E::Fr>, Randomness<E>)>
268 where
269 Randomness<E>: 'a,
270 Commitment<E>: 'a,
271 {
272 ensure!(labeled_polynomials.len() == rands.len());
273 let mut to_combine = Vec::with_capacity(labeled_polynomials.len());
274
275 for (p, r) in labeled_polynomials.zip_eq(rands) {
276 let enforced_degree_bounds: Option<&[usize]> = ck.enforced_degree_bounds.as_deref();
277
278 kzg10::KZG10::<E>::check_degrees_and_bounds(universal_prover.max_degree, enforced_degree_bounds, p)?;
279 let challenge = fs_rng.squeeze_short_nonnative_field_element::<E::Fr>();
280 to_combine.push((challenge, p.polynomial().to_dense(), r));
281 }
282
283 Ok(Self::combine_polynomials(to_combine))
284 }
285
286 pub fn batch_open<'a>(
290 universal_prover: &UniversalProver<E>,
291 ck: &CommitterUnionKey<E>,
292 labeled_polynomials: impl ExactSizeIterator<Item = &'a LabeledPolynomial<E::Fr>>,
293 query_set: &QuerySet<E::Fr>,
294 rands: impl ExactSizeIterator<Item = &'a Randomness<E>>,
295 fs_rng: &mut S,
296 ) -> Result<BatchProof<E>>
297 where
298 Randomness<E>: 'a,
299 Commitment<E>: 'a,
300 {
301 ensure!(labeled_polynomials.len() == rands.len());
302 let poly_rand: HashMap<_, _> =
303 labeled_polynomials.into_iter().zip_eq(rands).map(|(poly, r)| (poly.label(), (poly, r))).collect();
304
305 let open_time = start_timer!(|| format!(
306 "Opening {} polynomials at query set of size {}",
307 poly_rand.len(),
308 query_set.len(),
309 ));
310
311 let mut query_to_labels_map = BTreeMap::new();
312
313 for (label, (point_name, point)) in query_set.iter() {
314 let labels = query_to_labels_map.entry(point_name).or_insert((point, BTreeSet::new()));
315 labels.1.insert(label);
316 }
317
318 let mut pool = snarkvm_utilities::ExecutionPool::<_>::with_capacity(query_to_labels_map.len());
319 for (_point_name, (&query, labels)) in query_to_labels_map.into_iter() {
320 let mut query_polys = Vec::with_capacity(labels.len());
321 let mut query_rands = Vec::with_capacity(labels.len());
322
323 for label in labels {
324 let (polynomial, rand) =
325 poly_rand.get(label as &str).ok_or(PCError::MissingPolynomial { label: label.to_string() })?;
326
327 query_polys.push(*polynomial);
328 query_rands.push(*rand);
329 }
330 let (polynomial, rand) =
331 Self::combine_for_open(universal_prover, ck, query_polys.into_iter(), query_rands.into_iter(), fs_rng)?;
332 let _randomizer = fs_rng.squeeze_short_nonnative_field_element::<E::Fr>();
333
334 pool.add_job(move || {
335 let proof_time = start_timer!(|| "Creating proof");
336 let proof = kzg10::KZG10::open(&ck.powers(), &polynomial, query, &rand);
337 end_timer!(proof_time);
338 proof
339 });
340 }
341 let batch_proof = pool.execute_all().into_iter().collect::<Result<_, _>>().map(BatchProof).map_err(Into::into);
342 end_timer!(open_time);
343
344 batch_proof
345 }
346
347 pub fn batch_check<'a>(
348 vk: &UniversalVerifier<E>,
349 commitments: impl IntoIterator<Item = &'a LabeledCommitment<Commitment<E>>>,
350 query_set: &QuerySet<E::Fr>,
351 values: &Evaluations<E::Fr>,
352 proof: &BatchProof<E>,
353 fs_rng: &mut S,
354 ) -> Result<bool>
355 where
356 Commitment<E>: 'a,
357 {
358 let commitments: BTreeMap<_, _> = commitments.into_iter().map(|c| (c.label().to_owned(), c)).collect();
359 let batch_check_time = start_timer!(|| format!(
360 "Checking {} commitments at query set of size {}",
361 commitments.len(),
362 query_set.len(),
363 ));
364 let mut query_to_labels_map = BTreeMap::new();
365
366 for (label, (point_name, point)) in query_set.iter() {
367 let labels = query_to_labels_map.entry(point_name).or_insert((point, BTreeSet::new()));
368 labels.1.insert(label);
369 }
370
371 assert_eq!(proof.0.len(), query_to_labels_map.len());
372
373 let mut randomizer = E::Fr::one();
374
375 let mut combined_comms = BTreeMap::new();
376 let mut combined_witness = E::G1Projective::zero();
377 let mut combined_adjusted_witness = E::G1Projective::zero();
378
379 ensure!(query_to_labels_map.len() == proof.0.len());
380 for ((_query_name, (query, labels)), p) in query_to_labels_map.into_iter().zip_eq(&proof.0) {
381 let mut comms_to_combine: Vec<&'_ LabeledCommitment<_>> = Vec::new();
382 let mut values_to_combine = Vec::new();
383 for label in labels.into_iter() {
384 let commitment =
385 commitments.get(label).ok_or(PCError::MissingPolynomial { label: label.to_string() })?;
386
387 let v_i = values
388 .get(&(label.clone(), *query))
389 .ok_or(PCError::MissingEvaluation { label: label.to_string() })?;
390
391 comms_to_combine.push(commitment);
392 values_to_combine.push(*v_i);
393 }
394
395 Self::accumulate_elems(
396 &mut combined_comms,
397 &mut combined_witness,
398 &mut combined_adjusted_witness,
399 vk,
400 comms_to_combine.into_iter(),
401 *query,
402 values_to_combine.into_iter(),
403 p,
404 Some(randomizer),
405 fs_rng,
406 )?;
407
408 randomizer = fs_rng.squeeze_short_nonnative_field_element::<E::Fr>();
409 }
410
411 let result = Self::check_elems(vk, combined_comms, combined_witness, combined_adjusted_witness);
412 end_timer!(batch_check_time);
413 result.map_err(Into::into)
414 }
415
416 pub fn open_combinations<'a>(
417 universal_prover: &UniversalProver<E>,
418 ck: &CommitterUnionKey<E>,
419 linear_combinations: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
420 polynomials: impl IntoIterator<Item = LabeledPolynomial<E::Fr>>,
421 rands: impl IntoIterator<Item = &'a Randomness<E>>,
422 query_set: &QuerySet<E::Fr>,
423 fs_rng: &mut S,
424 ) -> Result<BatchLCProof<E>>
425 where
426 Randomness<E>: 'a,
427 Commitment<E>: 'a,
428 {
429 let label_map =
430 polynomials.into_iter().zip_eq(rands).map(|(p, r)| (p.to_label(), (p, r))).collect::<BTreeMap<_, _>>();
431
432 let mut lc_polynomials = Vec::new();
433 let mut lc_randomness = Vec::new();
434 let mut lc_info = Vec::new();
435
436 for lc in linear_combinations {
437 let lc_label = lc.label().to_string();
438 let mut poly = DensePolynomial::zero();
439 let mut randomness = Randomness::empty();
440 let mut degree_bound = None;
441 let mut hiding_bound = None;
442
443 let num_polys = lc.len();
444 for (coeff, label) in lc.iter().filter(|(_, l)| !l.is_one()) {
447 let label: &String = label.try_into().expect("cannot be one!");
448 let (cur_poly, cur_rand) =
449 label_map.get(label as &str).ok_or(PCError::MissingPolynomial { label: label.to_string() })?;
450 if let Some(cur_degree_bound) = cur_poly.degree_bound() {
451 if num_polys != 1 {
452 bail!(PCError::EquationHasDegreeBounds(lc_label));
453 }
454 assert!(coeff.is_one(), "Coefficient must be one for degree-bounded equations");
455 if let Some(old_degree_bound) = degree_bound {
456 assert_eq!(old_degree_bound, cur_degree_bound)
457 } else {
458 degree_bound = cur_poly.degree_bound();
459 }
460 }
461 hiding_bound = core::cmp::max(hiding_bound, cur_poly.hiding_bound());
463 poly += (*coeff, cur_poly.polynomial());
464 randomness += (*coeff, *cur_rand);
465 }
466
467 let lc_poly = LabeledPolynomial::new(lc_label.clone(), poly, degree_bound, hiding_bound);
468 lc_polynomials.push(lc_poly);
469 lc_randomness.push(randomness);
470 lc_info.push((lc_label, degree_bound));
471 }
472
473 let proof =
474 Self::batch_open(universal_prover, ck, lc_polynomials.iter(), query_set, lc_randomness.iter(), fs_rng)?;
475
476 Ok(BatchLCProof { proof })
477 }
478
479 pub fn check_combinations<'a>(
482 vk: &UniversalVerifier<E>,
483 linear_combinations: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
484 commitments: impl IntoIterator<Item = &'a LabeledCommitment<Commitment<E>>>,
485 query_set: &QuerySet<E::Fr>,
486 evaluations: &Evaluations<E::Fr>,
487 proof: &BatchLCProof<E>,
488 fs_rng: &mut S,
489 ) -> Result<bool>
490 where
491 Commitment<E>: 'a,
492 {
493 let BatchLCProof { proof } = proof;
494 let label_comm_map = commitments.into_iter().map(|c| (c.label(), c)).collect::<BTreeMap<_, _>>();
495
496 let mut lc_commitments = Vec::new();
497 let mut lc_info = Vec::new();
498 let mut evaluations = evaluations.clone();
499
500 let lc_processing_time = start_timer!(|| "Combining commitments");
501 for lc in linear_combinations {
502 let lc_label = lc.label().to_string();
503 let num_polys = lc.len();
504
505 let mut degree_bound = None;
506 let mut coeffs_and_comms = Vec::new();
507
508 for (coeff, label) in lc.iter() {
509 if label.is_one() {
510 for ((label, _), ref mut eval) in evaluations.iter_mut() {
511 if label == &lc_label {
512 **eval -= coeff;
513 }
514 }
515 } else {
516 let label: &String = label.try_into().unwrap();
517 let &cur_comm = label_comm_map
518 .get(label as &str)
519 .ok_or(PCError::MissingPolynomial { label: label.to_string() })?;
520
521 if cur_comm.degree_bound().is_some() {
522 if num_polys != 1 || !coeff.is_one() {
523 bail!(PCError::EquationHasDegreeBounds(lc_label));
524 }
525 degree_bound = cur_comm.degree_bound();
526 }
527 coeffs_and_comms.push((*coeff, cur_comm.commitment()));
528 }
529 }
530 let lc_time = start_timer!(|| format!("Combining {num_polys} commitments for {lc_label}"));
531 lc_commitments.push(Self::combine_commitments(coeffs_and_comms));
532 end_timer!(lc_time);
533 lc_info.push((lc_label, degree_bound));
534 }
535 end_timer!(lc_processing_time);
536
537 let combined_comms_norm_time = start_timer!(|| "Normalizing commitments");
538 let comms = Self::normalize_commitments(lc_commitments);
539 ensure!(lc_info.len() == comms.len());
540 let lc_commitments = lc_info
541 .into_iter()
542 .zip_eq(comms)
543 .map(|((label, d), c)| LabeledCommitment::new(label, c, d))
544 .collect::<Vec<_>>();
545 end_timer!(combined_comms_norm_time);
546
547 Self::batch_check(vk, &lc_commitments, query_set, &evaluations, proof, fs_rng)
548 }
549}
550
551impl<E: PairingEngine, S: AlgebraicSponge<E::Fq, 2>> SonicKZG10<E, S> {
552 fn combine_polynomials<'a, B: Borrow<DensePolynomial<E::Fr>>>(
553 coeffs_polys_rands: impl IntoIterator<Item = (E::Fr, B, &'a Randomness<E>)>,
554 ) -> (DensePolynomial<E::Fr>, Randomness<E>) {
555 let mut combined_poly = DensePolynomial::zero();
556 let mut combined_rand = Randomness::empty();
557 for (coeff, poly, rand) in coeffs_polys_rands {
558 let poly = poly.borrow();
559 if coeff.is_one() {
560 combined_poly += poly;
561 combined_rand += rand;
562 } else {
563 combined_poly += (coeff, poly);
564 combined_rand += (coeff, rand);
565 }
566 }
567 (combined_poly, combined_rand)
568 }
569
570 fn combine_commitments<'a>(
572 coeffs_and_comms: impl IntoIterator<Item = (E::Fr, &'a Commitment<E>)>,
573 ) -> E::G1Projective {
574 let (scalars, bases): (Vec<_>, Vec<_>) = coeffs_and_comms.into_iter().map(|(f, c)| (f.into(), c.0)).unzip();
575 VariableBase::msm(&bases, &scalars)
576 }
577
578 fn normalize_commitments(commitments: Vec<E::G1Projective>) -> impl ExactSizeIterator<Item = Commitment<E>> {
579 let comms = E::G1Projective::batch_normalization_into_affine(commitments);
580 comms.into_iter().map(|c| kzg10::KZGCommitment(c))
581 }
582}
583
584impl<E: PairingEngine, S: AlgebraicSponge<E::Fq, 2>> SonicKZG10<E, S> {
585 #[allow(clippy::too_many_arguments)]
586 fn accumulate_elems<'a>(
587 combined_comms: &mut BTreeMap<Option<usize>, E::G1Projective>,
588 combined_witness: &mut E::G1Projective,
589 combined_adjusted_witness: &mut E::G1Projective,
590 vk: &UniversalVerifier<E>,
591 commitments: impl ExactSizeIterator<Item = &'a LabeledCommitment<Commitment<E>>>,
592 point: E::Fr,
593 values: impl ExactSizeIterator<Item = E::Fr>,
594 proof: &kzg10::KZGProof<E>,
595 randomizer: Option<E::Fr>,
596 fs_rng: &mut S,
597 ) -> Result<()> {
598 let acc_time = start_timer!(|| "Accumulating elements");
599 let mut combined_values = E::Fr::zero();
601
602 ensure!(commitments.len() == values.len());
605 for (labeled_comm, value) in commitments.into_iter().zip_eq(values) {
606 let acc_timer = start_timer!(|| format!("Accumulating {}", labeled_comm.label()));
607 let curr_challenge = fs_rng.squeeze_short_nonnative_field_element::<E::Fr>();
608
609 combined_values += &(value * curr_challenge);
610
611 let comm = labeled_comm.commitment();
612 let degree_bound = labeled_comm.degree_bound();
613
614 let coeff = randomizer.unwrap_or_else(E::Fr::one) * curr_challenge;
616 let comm_with_challenge: E::G1Projective = comm.0.mul(coeff);
617
618 *combined_comms.entry(degree_bound).or_insert_with(E::G1Projective::zero) += &comm_with_challenge;
620 end_timer!(acc_timer);
621 }
622
623 let mut bases = vec![vk.vk.g, -proof.w];
626 let mut coeffs = vec![combined_values, point];
627 if let Some(random_v) = proof.random_v {
628 bases.push(vk.vk.gamma_g);
629 coeffs.push(random_v);
630 }
631 *combined_witness += if let Some(randomizer) = randomizer {
632 coeffs.iter_mut().for_each(|c| *c *= randomizer);
633 proof.w.mul(randomizer)
634 } else {
635 proof.w.to_projective()
636 };
637 let coeffs = coeffs.into_iter().map(|c| c.into()).collect::<Vec<_>>();
638 *combined_adjusted_witness += VariableBase::msm(&bases, &coeffs);
639 end_timer!(acc_time);
640 Ok(())
641 }
642
643 fn check_elems(
644 vk: &UniversalVerifier<E>,
645 combined_comms: BTreeMap<Option<usize>, E::G1Projective>,
646 combined_witness: E::G1Projective,
647 combined_adjusted_witness: E::G1Projective,
648 ) -> Result<bool> {
649 let check_time = start_timer!(|| "Checking elems");
650 let mut g1_projective_elems = Vec::with_capacity(combined_comms.len() + 2);
651 let mut g2_prepared_elems = Vec::with_capacity(combined_comms.len() + 2);
652
653 for (degree_bound, comm) in combined_comms.into_iter() {
654 let shift_power = if let Some(degree_bound) = degree_bound {
655 vk.prepared_negative_powers_of_beta_h
657 .get(°ree_bound)
658 .cloned()
659 .ok_or(PCError::UnsupportedDegreeBound(degree_bound))?
660 } else {
661 vk.vk.prepared_h.clone()
662 };
663
664 g1_projective_elems.push(comm);
665 g2_prepared_elems.push(shift_power);
666 }
667
668 g1_projective_elems.push(-combined_adjusted_witness);
669 g2_prepared_elems.push(vk.vk.prepared_h.clone());
670
671 g1_projective_elems.push(-combined_witness);
672 g2_prepared_elems.push(vk.vk.prepared_beta_h.clone());
673
674 let g1_prepared_elems_iter = E::G1Projective::batch_normalization_into_affine(g1_projective_elems)
675 .into_iter()
676 .map(|a| a.prepare())
677 .collect::<Vec<_>>();
678
679 ensure!(g1_prepared_elems_iter.len() == g2_prepared_elems.len());
680 let g1_g2_prepared = g1_prepared_elems_iter.iter().zip_eq(g2_prepared_elems.iter());
681 let is_one: bool = E::product_of_pairings(g1_g2_prepared).is_one();
682 end_timer!(check_time);
683 Ok(is_one)
684 }
685}
686
687#[cfg(test)]
688mod tests {
689 #![allow(non_camel_case_types)]
690
691 use super::{CommitterKey, SonicKZG10};
692 use crate::{crypto_hash::PoseidonSponge, polycommit::test_templates::*};
693 use snarkvm_curves::bls12_377::{Bls12_377, Fq};
694 use snarkvm_utilities::{FromBytes, ToBytes, rand::TestRng};
695
696 use rand::distributions::Distribution;
697
698 type Sponge = PoseidonSponge<Fq, 2, 1>;
699 type PC_Bls12_377 = SonicKZG10<Bls12_377, Sponge>;
700
701 #[test]
702 fn test_committer_key_serialization() {
703 let rng = &mut TestRng::default();
704 let max_degree = rand::distributions::Uniform::from(8..=64).sample(rng);
705 let supported_degree = rand::distributions::Uniform::from(1..=max_degree).sample(rng);
706
707 let lagrange_size = |d: usize| if d.is_power_of_two() { d } else { d.next_power_of_two() >> 1 };
708
709 let pp = PC_Bls12_377::load_srs(max_degree).unwrap();
710
711 let (ck, _vk) = PC_Bls12_377::trim(&pp, supported_degree, [lagrange_size(supported_degree)], 0, None).unwrap();
712
713 let ck_bytes = ck.to_bytes_le().unwrap();
714 let ck_recovered: CommitterKey<Bls12_377> = FromBytes::read_le(&ck_bytes[..]).unwrap();
715 let ck_recovered_bytes = ck_recovered.to_bytes_le().unwrap();
716
717 assert_eq!(&ck_bytes, &ck_recovered_bytes);
718 }
719
720 #[test]
721 fn test_single_poly() {
722 single_poly_test::<Bls12_377, Sponge>().expect("test failed for bls12-377");
723 }
724
725 #[test]
726 fn test_quadratic_poly_degree_bound_multiple_queries() {
727 quadratic_poly_degree_bound_multiple_queries_test::<Bls12_377, Sponge>().expect("test failed for bls12-377");
728 }
729
730 #[test]
731 fn test_linear_poly_degree_bound() {
732 linear_poly_degree_bound_test::<Bls12_377, Sponge>().expect("test failed for bls12-377");
733 }
734
735 #[test]
736 fn test_single_poly_degree_bound() {
737 single_poly_degree_bound_test::<Bls12_377, Sponge>().expect("test failed for bls12-377");
738 }
739
740 #[test]
741 fn test_single_poly_degree_bound_multiple_queries() {
742 single_poly_degree_bound_multiple_queries_test::<Bls12_377, Sponge>().expect("test failed for bls12-377");
743 }
744
745 #[test]
746 fn test_two_polys_degree_bound_single_query() {
747 two_polys_degree_bound_single_query_test::<Bls12_377, Sponge>().expect("test failed for bls12-377");
748 }
749
750 #[test]
751 fn test_full_end_to_end() {
752 full_end_to_end_test::<Bls12_377, Sponge>().expect("test failed for bls12-377");
753 println!("Finished bls12-377");
754 }
755
756 #[test]
757 fn test_single_equation() {
758 single_equation_test::<Bls12_377, Sponge>().expect("test failed for bls12-377");
759 println!("Finished bls12-377");
760 }
761
762 #[test]
763 fn test_two_equation() {
764 two_equation_test::<Bls12_377, Sponge>().expect("test failed for bls12-377");
765 println!("Finished bls12-377");
766 }
767
768 #[test]
769 fn test_two_equation_degree_bound() {
770 two_equation_degree_bound_test::<Bls12_377, Sponge>().expect("test failed for bls12-377");
771 println!("Finished bls12-377");
772 }
773
774 #[test]
775 fn test_full_end_to_end_equation() {
776 full_end_to_end_equation_test::<Bls12_377, Sponge>().expect("test failed for bls12-377");
777 println!("Finished bls12-377");
778 }
779
780 #[test]
781 #[should_panic]
782 fn test_bad_degree_bound() {
783 bad_degree_bound_test::<Bls12_377, Sponge>().expect("test failed for bls12-377");
784 println!("Finished bls12-377");
785 }
786
787 #[test]
788 fn test_lagrange_commitment() {
789 crate::polycommit::test_templates::lagrange_test_template::<Bls12_377, Sponge>()
790 .expect("test failed for bls12-377");
791 println!("Finished bls12-377");
792 }
793}