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