1use crate::{
2 utils::inner_product, BTreeMap, BTreeSet, BatchLCProof, DenseUVPolynomial, Error, Evaluations,
3 LabeledCommitment, LabeledPolynomial, LinearCombination, PCCommitmentState, PCCommitterKey,
4 PCUniversalParams, PolynomialCommitment, QuerySet, CHALLENGE_SIZE,
5};
6use ark_crypto_primitives::sponge::CryptographicSponge;
7use ark_ec::{AffineRepr, CurveGroup, VariableBaseMSM};
8use ark_ff::{Field, One, PrimeField, UniformRand, Zero};
9use ark_serialize::CanonicalSerialize;
10use ark_std::{convert::TryInto, format, marker::PhantomData, ops::Mul, rand::RngCore};
11#[cfg(not(feature = "std"))]
12use ark_std::{
13 string::{String, ToString},
14 vec::Vec,
15};
16use digest::Digest;
17#[cfg(feature = "parallel")]
18use rayon::prelude::*;
19
20mod data_structures;
21pub use data_structures::*;
22
23pub struct InnerProductArgPC<G: AffineRepr, D: Digest, P: DenseUVPolynomial<G::ScalarField>> {
37 _projective: PhantomData<G>,
38 _digest: PhantomData<D>,
39 _poly: PhantomData<P>,
40}
41
42impl<G, D, P> InnerProductArgPC<G, D, P>
43where
44 G: AffineRepr,
45 G::Group: VariableBaseMSM<MulBase = G>,
46 D: Digest,
47 P: DenseUVPolynomial<G::ScalarField>,
48{
49 pub const PROTOCOL_NAME: &'static [u8] = b"PC-DL-2020";
51
52 fn cm_commit(
55 comm_key: &[G],
56 scalars: &[G::ScalarField],
57 hiding_generator: Option<G>,
58 randomizer: Option<G::ScalarField>,
59 ) -> G::Group {
60 let scalars_bigint = ark_std::cfg_iter!(scalars)
61 .map(|s| s.into_bigint())
62 .collect::<Vec<_>>();
63
64 let mut comm = <G::Group as VariableBaseMSM>::msm_bigint(comm_key, &scalars_bigint);
65
66 if randomizer.is_some() {
67 assert!(hiding_generator.is_some());
68 comm += &hiding_generator.unwrap().mul(randomizer.unwrap());
69 }
70
71 comm
72 }
73
74 fn compute_random_oracle_challenge(bytes: &[u8]) -> G::ScalarField {
75 let mut i = 0u64;
76 let mut challenge = None;
77 while challenge.is_none() {
78 let mut hash_input = bytes.to_vec();
79 hash_input.extend(i.to_le_bytes());
80 let hash = D::digest(&hash_input.as_slice());
81 challenge = <G::ScalarField as Field>::from_random_bytes(&hash);
82
83 i += 1;
84 }
85
86 challenge.unwrap()
87 }
88
89 fn succinct_check<'a>(
92 vk: &VerifierKey<G>,
93 commitments: impl IntoIterator<Item = &'a LabeledCommitment<Commitment<G>>>,
94 point: G::ScalarField,
95 values: impl IntoIterator<Item = G::ScalarField>,
96 proof: &Proof<G>,
97 sponge: &mut impl CryptographicSponge,
98 ) -> Option<SuccinctCheckPolynomial<G::ScalarField>> {
99 let check_time = start_timer!(|| "Succinct checking");
100
101 let d = vk.supported_degree();
102
103 let log_d = ark_std::log2(d + 1) as usize;
105
106 let mut combined_commitment_proj = G::Group::zero();
107 let mut combined_v = G::ScalarField::zero();
108
109 let mut cur_challenge: G::ScalarField =
110 sponge.squeeze_field_elements_with_sizes(&[CHALLENGE_SIZE])[0];
111
112 let labeled_commitments = commitments.into_iter();
113 let values = values.into_iter();
114
115 for (labeled_commitment, value) in labeled_commitments.zip(values) {
116 let commitment = labeled_commitment.commitment();
117 combined_v += &(cur_challenge * &value);
118 combined_commitment_proj += &labeled_commitment.commitment().comm.mul(cur_challenge);
119 cur_challenge = sponge.squeeze_field_elements_with_sizes(&[CHALLENGE_SIZE])[0];
120
121 let degree_bound = labeled_commitment.degree_bound();
122 assert_eq!(degree_bound.is_some(), commitment.shifted_comm.is_some());
123
124 if let Some(degree_bound) = degree_bound {
125 let shift = point.pow([(vk.supported_degree() - degree_bound) as u64]);
126 combined_v += &(cur_challenge * &value * &shift);
127 combined_commitment_proj += &commitment.shifted_comm.unwrap().mul(cur_challenge);
128 }
129
130 cur_challenge = sponge.squeeze_field_elements_with_sizes(&[CHALLENGE_SIZE])[0];
131 }
132
133 let mut combined_commitment = combined_commitment_proj.into_affine();
134
135 assert_eq!(proof.hiding_comm.is_some(), proof.rand.is_some());
136 if proof.hiding_comm.is_some() {
137 let hiding_comm = proof.hiding_comm.unwrap();
138 let rand = proof.rand.unwrap();
139 let mut byte_vec = Vec::new();
140 combined_commitment
141 .serialize_uncompressed(&mut byte_vec)
142 .unwrap();
143 point.serialize_uncompressed(&mut byte_vec).unwrap();
144 combined_v.serialize_uncompressed(&mut byte_vec).unwrap();
145 hiding_comm.serialize_uncompressed(&mut byte_vec).unwrap();
146 let bytes = byte_vec.as_slice();
147 let hiding_challenge = Self::compute_random_oracle_challenge(bytes);
148 combined_commitment_proj += &(hiding_comm.mul(hiding_challenge) - &vk.s.mul(rand));
149 combined_commitment = combined_commitment_proj.into_affine();
150 }
151
152 let mut round_challenges = Vec::with_capacity(log_d);
154 let mut byte_vec = Vec::new();
155 combined_commitment
156 .serialize_uncompressed(&mut byte_vec)
157 .unwrap();
158 point.serialize_uncompressed(&mut byte_vec).unwrap();
159 combined_v.serialize_uncompressed(&mut byte_vec).unwrap();
160 let bytes = byte_vec.as_slice();
161 let mut round_challenge = Self::compute_random_oracle_challenge(bytes);
162
163 let h_prime = vk.h.mul(round_challenge);
164
165 let mut round_commitment_proj = combined_commitment_proj + &h_prime.mul(&combined_v);
166
167 let l_iter = proof.l_vec.iter();
168 let r_iter = proof.r_vec.iter();
169
170 for (l, r) in l_iter.zip(r_iter) {
171 let mut byte_vec = Vec::new();
172 round_challenge
173 .serialize_uncompressed(&mut byte_vec)
174 .unwrap();
175 l.serialize_uncompressed(&mut byte_vec).unwrap();
176 r.serialize_uncompressed(&mut byte_vec).unwrap();
177 let bytes = byte_vec.as_slice();
178
179 round_challenge = Self::compute_random_oracle_challenge(bytes);
180 round_challenges.push(round_challenge);
181 round_commitment_proj +=
182 &(l.mul(round_challenge.inverse().unwrap()) + &r.mul(round_challenge));
183 }
184
185 let check_poly = SuccinctCheckPolynomial::<G::ScalarField>(round_challenges);
186 let v_prime = check_poly.evaluate(point) * &proof.c;
187 let h_prime = h_prime.into_affine();
188
189 let check_commitment_elem: G::Group = Self::cm_commit(
190 &[proof.final_comm_key.clone(), h_prime],
191 &[proof.c.clone(), v_prime],
192 None,
193 None,
194 );
195
196 if !(round_commitment_proj - &check_commitment_elem).is_zero() {
197 end_timer!(check_time);
198 return None;
199 }
200
201 end_timer!(check_time);
202 Some(check_poly)
203 }
204
205 fn check_degrees_and_bounds(
206 supported_degree: usize,
207 p: &LabeledPolynomial<G::ScalarField, P>,
208 ) -> Result<(), Error> {
209 if p.degree() > supported_degree {
210 return Err(Error::TooManyCoefficients {
211 num_coefficients: p.degree() + 1,
212 num_powers: supported_degree + 1,
213 });
214 }
215
216 if let Some(bound) = p.degree_bound() {
217 if bound < p.degree() || bound > supported_degree {
218 return Err(Error::IncorrectDegreeBound {
219 poly_degree: p.degree(),
220 degree_bound: bound,
221 supported_degree,
222 label: p.label().to_string(),
223 });
224 }
225 }
226
227 Ok(())
228 }
229
230 fn shift_polynomial(ck: &CommitterKey<G>, p: &P, degree_bound: usize) -> P {
231 if p.is_zero() {
232 P::zero()
233 } else {
234 let mut shifted_polynomial_coeffs =
235 vec![G::ScalarField::zero(); ck.supported_degree() - degree_bound];
236 shifted_polynomial_coeffs.extend_from_slice(&p.coeffs());
237 P::from_coefficients_vec(shifted_polynomial_coeffs)
238 }
239 }
240
241 fn combine_shifted_rand(
242 combined_rand: Option<G::ScalarField>,
243 new_rand: Option<G::ScalarField>,
244 coeff: G::ScalarField,
245 ) -> Option<G::ScalarField> {
246 if let Some(new_rand) = new_rand {
247 let coeff_new_rand = new_rand * &coeff;
248 return Some(combined_rand.map_or(coeff_new_rand, |r| r + &coeff_new_rand));
249 };
250
251 combined_rand
252 }
253
254 fn combine_shifted_comm(
255 combined_comm: Option<G::Group>,
256 new_comm: Option<G>,
257 coeff: G::ScalarField,
258 ) -> Option<G::Group> {
259 if let Some(new_comm) = new_comm {
260 let coeff_new_comm = new_comm.mul(coeff);
261 return Some(combined_comm.map_or(coeff_new_comm, |c| c + &coeff_new_comm));
262 };
263
264 combined_comm
265 }
266
267 fn construct_labeled_commitments(
268 lc_info: &[(String, Option<usize>)],
269 elements: &[G::Group],
270 ) -> Vec<LabeledCommitment<Commitment<G>>> {
271 let comms = G::Group::normalize_batch(elements);
272 let mut commitments = Vec::new();
273
274 let mut i = 0;
275 for info in lc_info.into_iter() {
276 let commitment;
277 let label = info.0.clone();
278 let degree_bound = info.1;
279
280 if degree_bound.is_some() {
281 commitment = Commitment {
282 comm: comms[i].clone(),
283 shifted_comm: Some(comms[i + 1].clone()),
284 };
285
286 i += 2;
287 } else {
288 commitment = Commitment {
289 comm: comms[i].clone(),
290 shifted_comm: None,
291 };
292
293 i += 1;
294 }
295
296 commitments.push(LabeledCommitment::new(label, commitment, degree_bound));
297 }
298
299 return commitments;
300 }
301
302 fn sample_generators(num_generators: usize) -> Vec<G> {
303 let generators: Vec<_> = ark_std::cfg_into_iter!(0..num_generators)
304 .map(|i| {
305 let i = i as u64;
306 let mut hash =
307 D::digest([Self::PROTOCOL_NAME, &i.to_le_bytes()].concat().as_slice());
308 let mut g = G::from_random_bytes(&hash);
309 let mut j = 0u64;
310 while g.is_none() {
311 let mut bytes = Self::PROTOCOL_NAME.to_vec();
313 bytes.extend(i.to_le_bytes());
314 bytes.extend(j.to_le_bytes());
315 hash = D::digest(bytes.as_slice());
316 g = G::from_random_bytes(&hash);
317 j += 1;
318 }
319 let generator = g.unwrap();
320 generator.mul_by_cofactor_to_group()
321 })
322 .collect();
323
324 G::Group::normalize_batch(&generators)
325 }
326}
327
328impl<G, D, P> PolynomialCommitment<G::ScalarField, P> for InnerProductArgPC<G, D, P>
329where
330 G: AffineRepr,
331 G::Group: VariableBaseMSM<MulBase = G>,
332 D: Digest,
333 P: DenseUVPolynomial<G::ScalarField, Point = G::ScalarField>,
334{
335 type UniversalParams = UniversalParams<G>;
336 type CommitterKey = CommitterKey<G>;
337 type VerifierKey = VerifierKey<G>;
338 type Commitment = Commitment<G>;
339 type CommitmentState = Randomness<G>;
340 type Proof = Proof<G>;
341 type BatchProof = Vec<Self::Proof>;
342 type Error = Error;
343
344 fn setup<R: RngCore>(
345 max_degree: usize,
346 _: Option<usize>,
347 _rng: &mut R,
348 ) -> Result<Self::UniversalParams, Self::Error> {
349 let max_degree = (max_degree + 1).next_power_of_two() - 1;
351
352 let setup_time = start_timer!(|| format!("Sampling {} generators", max_degree + 3));
353 let mut generators = Self::sample_generators(max_degree + 3);
354 end_timer!(setup_time);
355
356 let h = generators.pop().unwrap();
357 let s = generators.pop().unwrap();
358
359 let pp = UniversalParams {
360 comm_key: generators,
361 h,
362 s,
363 };
364
365 Ok(pp)
366 }
367
368 fn trim(
369 pp: &Self::UniversalParams,
370 supported_degree: usize,
371 _supported_hiding_bound: usize,
372 _enforced_degree_bounds: Option<&[usize]>,
373 ) -> Result<(Self::CommitterKey, Self::VerifierKey), Self::Error> {
374 let supported_degree = (supported_degree + 1).next_power_of_two() - 1;
376 if supported_degree > pp.max_degree() {
377 return Err(Error::TrimmingDegreeTooLarge);
378 }
379
380 let trim_time =
381 start_timer!(|| format!("Trimming to supported degree of {}", supported_degree));
382
383 let ck = CommitterKey {
384 comm_key: pp.comm_key[0..(supported_degree + 1)].to_vec(),
385 h: pp.h.clone(),
386 s: pp.s.clone(),
387 max_degree: pp.max_degree(),
388 };
389
390 let vk = VerifierKey {
391 comm_key: pp.comm_key[0..(supported_degree + 1)].to_vec(),
392 h: pp.h.clone(),
393 s: pp.s.clone(),
394 max_degree: pp.max_degree(),
395 };
396
397 end_timer!(trim_time);
398
399 Ok((ck, vk))
400 }
401
402 fn commit<'a>(
404 ck: &Self::CommitterKey,
405 polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<G::ScalarField, P>>,
406 rng: Option<&mut dyn RngCore>,
407 ) -> Result<
408 (
409 Vec<LabeledCommitment<Self::Commitment>>,
410 Vec<Self::CommitmentState>,
411 ),
412 Self::Error,
413 >
414 where
415 P: 'a,
416 {
417 let rng = &mut crate::optional_rng::OptionalRng(rng);
418 let mut comms = Vec::new();
419 let mut states = Vec::new();
420
421 let commit_time = start_timer!(|| "Committing to polynomials");
422 for labeled_polynomial in polynomials {
423 Self::check_degrees_and_bounds(ck.supported_degree(), labeled_polynomial)?;
424
425 let polynomial: &P = labeled_polynomial.polynomial();
426 let label = labeled_polynomial.label();
427 let hiding_bound = labeled_polynomial.hiding_bound();
428 let degree_bound = labeled_polynomial.degree_bound();
429
430 let commit_time = start_timer!(|| format!(
431 "Polynomial {} of degree {}, degree bound {:?}, and hiding bound {:?}",
432 label,
433 polynomial.degree(),
434 degree_bound,
435 hiding_bound,
436 ));
437
438 let state = if let Some(h) = hiding_bound {
439 Randomness::rand(h, degree_bound.is_some(), None, rng)
440 } else {
441 Randomness::empty()
442 };
443
444 let comm = Self::cm_commit(
445 &ck.comm_key[..(polynomial.degree() + 1)],
446 &polynomial.coeffs(),
447 Some(ck.s),
448 Some(state.rand),
449 )
450 .into();
451
452 let shifted_comm = degree_bound.map(|d| {
453 Self::cm_commit(
454 &ck.comm_key[(ck.supported_degree() - d)..],
455 &polynomial.coeffs(),
456 Some(ck.s),
457 state.shifted_rand,
458 )
459 .into()
460 });
461
462 let commitment = Commitment { comm, shifted_comm };
463 let labeled_comm = LabeledCommitment::new(label.to_string(), commitment, degree_bound);
464
465 comms.push(labeled_comm);
466 states.push(state);
467
468 end_timer!(commit_time);
469 }
470
471 end_timer!(commit_time);
472 Ok((comms, states))
473 }
474
475 fn open<'a>(
476 ck: &Self::CommitterKey,
477 labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<G::ScalarField, P>>,
478 commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
479 point: &'a P::Point,
480 sponge: &mut impl CryptographicSponge,
481 states: impl IntoIterator<Item = &'a Self::CommitmentState>,
482 rng: Option<&mut dyn RngCore>,
483 ) -> Result<Self::Proof, Self::Error>
484 where
485 Self::Commitment: 'a,
486 Self::CommitmentState: 'a,
487 P: 'a,
488 {
489 let mut combined_polynomial = P::zero();
490 let mut combined_rand = G::ScalarField::zero();
491 let mut combined_commitment_proj = G::Group::zero();
492
493 let mut has_hiding = false;
494
495 let polys_iter = labeled_polynomials.into_iter();
496 let states_iter = states.into_iter();
497 let comms_iter = commitments.into_iter();
498
499 let combine_time = start_timer!(|| "Combining polynomials, randomness, and commitments.");
500
501 let mut cur_challenge = sponge.squeeze_field_elements_with_sizes(&[CHALLENGE_SIZE])[0];
502
503 for (labeled_polynomial, (labeled_commitment, state)) in
504 polys_iter.zip(comms_iter.zip(states_iter))
505 {
506 let label = labeled_polynomial.label();
507 assert_eq!(labeled_polynomial.label(), labeled_commitment.label());
508 Self::check_degrees_and_bounds(ck.supported_degree(), labeled_polynomial)?;
509
510 let polynomial = labeled_polynomial.polynomial();
511 let degree_bound = labeled_polynomial.degree_bound();
512 let hiding_bound = labeled_polynomial.hiding_bound();
513 let commitment = labeled_commitment.commitment();
514
515 combined_polynomial += (cur_challenge, polynomial);
516 combined_commitment_proj += &commitment.comm.mul(cur_challenge);
517
518 if hiding_bound.is_some() {
519 has_hiding = true;
520 combined_rand += &(cur_challenge * &state.rand);
521 }
522
523 cur_challenge = sponge.squeeze_field_elements_with_sizes(&[CHALLENGE_SIZE])[0];
524
525 let has_degree_bound = degree_bound.is_some();
526
527 assert_eq!(
528 has_degree_bound,
529 commitment.shifted_comm.is_some(),
530 "shifted_comm mismatch for {}",
531 label
532 );
533
534 assert_eq!(
535 degree_bound,
536 labeled_commitment.degree_bound(),
537 "labeled_comm degree bound mismatch for {}",
538 label
539 );
540 if let Some(degree_bound) = degree_bound {
541 let shifted_polynomial = Self::shift_polynomial(ck, polynomial, degree_bound);
542 combined_polynomial += (cur_challenge, &shifted_polynomial);
543 combined_commitment_proj += &commitment.shifted_comm.unwrap().mul(cur_challenge);
544
545 if hiding_bound.is_some() {
546 let shifted_rand = state.shifted_rand;
547 assert!(
548 shifted_rand.is_some(),
549 "shifted_rand.is_none() for {}",
550 label
551 );
552 combined_rand += &(cur_challenge * &shifted_rand.unwrap());
553 }
554 }
555
556 cur_challenge = sponge.squeeze_field_elements_with_sizes(&[CHALLENGE_SIZE])[0];
557 }
558
559 end_timer!(combine_time);
560
561 let combined_v = combined_polynomial.evaluate(point);
562
563 let d = ck.supported_degree();
565
566 let log_d = ark_std::log2(d + 1) as usize;
568
569 let mut combined_commitment;
570 let mut hiding_commitment = None;
571
572 if has_hiding {
573 let mut rng = rng.expect("hiding commitments require randomness");
574 let hiding_time = start_timer!(|| "Applying hiding.");
575 let mut hiding_polynomial = P::rand(d, &mut rng);
576 hiding_polynomial -= &P::from_coefficients_slice(&[hiding_polynomial.evaluate(point)]);
577 let hiding_rand = G::ScalarField::rand(&mut rng);
578 let hiding_commitment_proj = Self::cm_commit(
579 ck.comm_key.as_slice(),
580 hiding_polynomial.coeffs(),
581 Some(ck.s),
582 Some(hiding_rand),
583 );
584
585 let mut batch =
586 G::Group::normalize_batch(&[combined_commitment_proj, hiding_commitment_proj]);
587 hiding_commitment = Some(batch.pop().unwrap());
588 combined_commitment = batch.pop().unwrap();
589
590 let mut byte_vec = Vec::new();
591 combined_commitment
592 .serialize_uncompressed(&mut byte_vec)
593 .unwrap();
594 point.serialize_uncompressed(&mut byte_vec).unwrap();
595 combined_v.serialize_uncompressed(&mut byte_vec).unwrap();
596 hiding_commitment
597 .unwrap()
598 .serialize_uncompressed(&mut byte_vec)
599 .unwrap();
600 let bytes = byte_vec.as_slice();
601 let hiding_challenge = Self::compute_random_oracle_challenge(bytes);
602 combined_polynomial += (hiding_challenge, &hiding_polynomial);
603 combined_rand += &(hiding_challenge * &hiding_rand);
604 combined_commitment_proj +=
605 &(hiding_commitment.unwrap().mul(hiding_challenge) - &ck.s.mul(combined_rand));
606
607 end_timer!(hiding_time);
608 }
609
610 let combined_rand = if has_hiding {
611 Some(combined_rand)
612 } else {
613 None
614 };
615
616 let proof_time =
617 start_timer!(|| format!("Generating proof for degree {} combined polynomial", d + 1));
618
619 combined_commitment = combined_commitment_proj.into_affine();
620
621 let mut byte_vec = Vec::new();
623 combined_commitment
624 .serialize_uncompressed(&mut byte_vec)
625 .unwrap();
626 point.serialize_uncompressed(&mut byte_vec).unwrap();
627 combined_v.serialize_uncompressed(&mut byte_vec).unwrap();
628 let bytes = byte_vec.as_slice();
629 let mut round_challenge = Self::compute_random_oracle_challenge(bytes);
630
631 let h_prime = ck.h.mul(round_challenge).into_affine();
632
633 let mut coeffs = combined_polynomial.coeffs().to_vec();
635 if coeffs.len() < d + 1 {
636 for _ in coeffs.len()..(d + 1) {
637 coeffs.push(G::ScalarField::zero());
638 }
639 }
640 let mut coeffs = coeffs.as_mut_slice();
641
642 let mut z: Vec<G::ScalarField> = Vec::with_capacity(d + 1);
644 let mut cur_z: G::ScalarField = G::ScalarField::one();
645 for _ in 0..(d + 1) {
646 z.push(cur_z);
647 cur_z *= point;
648 }
649 let mut z = z.as_mut_slice();
650
651 let mut key_proj: Vec<G::Group> = ck.comm_key.iter().map(|x| (*x).into()).collect();
653 let mut key_proj = key_proj.as_mut_slice();
654
655 let mut temp;
656
657 let mut comm_key = &ck.comm_key;
660
661 let mut l_vec = Vec::with_capacity(log_d);
662 let mut r_vec = Vec::with_capacity(log_d);
663
664 let mut n = d + 1;
665 while n > 1 {
666 let (coeffs_l, coeffs_r) = coeffs.split_at_mut(n / 2);
667 let (z_l, z_r) = z.split_at_mut(n / 2);
668 let (key_l, key_r) = comm_key.split_at(n / 2);
669 let (key_proj_l, _) = key_proj.split_at_mut(n / 2);
670
671 let l = Self::cm_commit(key_l, coeffs_r, None, None)
672 + &h_prime.mul(inner_product(coeffs_r, z_l));
673
674 let r = Self::cm_commit(key_r, coeffs_l, None, None)
675 + &h_prime.mul(inner_product(coeffs_l, z_r));
676
677 let lr = G::Group::normalize_batch(&[l, r]);
678 l_vec.push(lr[0]);
679 r_vec.push(lr[1]);
680
681 let mut byte_vec = Vec::new();
682 round_challenge
683 .serialize_uncompressed(&mut byte_vec)
684 .unwrap();
685 lr[0].serialize_uncompressed(&mut byte_vec).unwrap();
686 lr[1].serialize_uncompressed(&mut byte_vec).unwrap();
687 let bytes = byte_vec.as_slice();
688 round_challenge = Self::compute_random_oracle_challenge(bytes);
689 let round_challenge_inv = round_challenge.inverse().unwrap();
690
691 ark_std::cfg_iter_mut!(coeffs_l)
692 .zip(coeffs_r)
693 .for_each(|(c_l, c_r)| *c_l += &(round_challenge_inv * &*c_r));
694
695 ark_std::cfg_iter_mut!(z_l)
696 .zip(z_r)
697 .for_each(|(z_l, z_r)| *z_l += &(round_challenge * &*z_r));
698
699 ark_std::cfg_iter_mut!(key_proj_l)
700 .zip(key_r)
701 .for_each(|(k_l, k_r)| *k_l += &(k_r.mul(round_challenge)));
702
703 coeffs = coeffs_l;
704 z = z_l;
705
706 key_proj = key_proj_l;
707 temp = G::Group::normalize_batch(key_proj);
708 comm_key = &temp;
709
710 n /= 2;
711 }
712
713 end_timer!(proof_time);
714
715 Ok(Proof {
716 l_vec,
717 r_vec,
718 final_comm_key: comm_key[0],
719 c: coeffs[0],
720 hiding_comm: hiding_commitment,
721 rand: combined_rand,
722 })
723 }
724
725 fn check<'a>(
726 vk: &Self::VerifierKey,
727 commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
728 point: &'a P::Point,
729 values: impl IntoIterator<Item = G::ScalarField>,
730 proof: &Self::Proof,
731 sponge: &mut impl CryptographicSponge,
732 _rng: Option<&mut dyn RngCore>,
733 ) -> Result<bool, Self::Error>
734 where
735 Self::Commitment: 'a,
736 {
737 let check_time = start_timer!(|| "Checking evaluations");
738 let d = vk.supported_degree();
739
740 let log_d = ark_std::log2(d + 1) as usize;
742
743 if proof.l_vec.len() != proof.r_vec.len() || proof.l_vec.len() != log_d {
744 return Err(Error::IncorrectInputLength(
745 format!(
746 "Expected proof vectors to be {:}. Instead, l_vec size is {:} and r_vec size is {:}",
747 log_d,
748 proof.l_vec.len(),
749 proof.r_vec.len()
750 )
751 ));
752 }
753
754 let check_poly = Self::succinct_check(vk, commitments, *point, values, proof, sponge);
755
756 if check_poly.is_none() {
757 return Ok(false);
758 }
759
760 let check_poly_coeffs = check_poly.unwrap().compute_coeffs();
761 let final_key = Self::cm_commit(
762 vk.comm_key.as_slice(),
763 check_poly_coeffs.as_slice(),
764 None,
765 None,
766 );
767 if !(final_key - &proof.final_comm_key.into()).is_zero() {
768 return Ok(false);
769 }
770
771 end_timer!(check_time);
772 Ok(true)
773 }
774
775 fn batch_check<'a, R: RngCore>(
776 vk: &Self::VerifierKey,
777 commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
778 query_set: &QuerySet<P::Point>,
779 values: &Evaluations<G::ScalarField, P::Point>,
780 proof: &Self::BatchProof,
781 sponge: &mut impl CryptographicSponge,
782 rng: &mut R,
783 ) -> Result<bool, Self::Error>
784 where
785 Self::Commitment: 'a,
786 {
787 let commitments: BTreeMap<_, _> = commitments.into_iter().map(|c| (c.label(), c)).collect();
788 let mut query_to_labels_map = BTreeMap::new();
789
790 for (label, (point_label, point)) in query_set.iter() {
791 let labels = query_to_labels_map
792 .entry(point_label)
793 .or_insert((point, BTreeSet::new()));
794 labels.1.insert(label);
795 }
796
797 assert_eq!(proof.len(), query_to_labels_map.len());
798
799 let mut randomizer = G::ScalarField::one();
800
801 let mut combined_check_poly = P::zero();
802 let mut combined_final_key = G::Group::zero();
803
804 for ((_point_label, (point, labels)), p) in query_to_labels_map.into_iter().zip(proof) {
805 let lc_time =
806 start_timer!(|| format!("Randomly combining {} commitments", labels.len()));
807 let mut comms: Vec<&'_ LabeledCommitment<_>> = Vec::new();
808 let mut vals = Vec::new();
809 for label in labels.into_iter() {
810 let commitment = commitments.get(label).ok_or(Error::MissingPolynomial {
811 label: label.to_string(),
812 })?;
813
814 let v_i = values
815 .get(&(label.clone(), *point))
816 .ok_or(Error::MissingEvaluation {
817 label: label.to_string(),
818 })?;
819
820 comms.push(commitment);
821 vals.push(*v_i);
822 }
823
824 let check_poly =
825 Self::succinct_check(vk, comms.into_iter(), *point, vals.into_iter(), p, sponge);
826
827 if check_poly.is_none() {
828 return Ok(false);
829 }
830
831 let check_poly = P::from_coefficients_vec(check_poly.unwrap().compute_coeffs());
832 combined_check_poly += (randomizer, &check_poly);
833 combined_final_key += &p.final_comm_key.mul(randomizer);
834
835 randomizer = u128::rand(rng).into();
836 end_timer!(lc_time);
837 }
838
839 let proof_time = start_timer!(|| "Checking batched proof");
840 let final_key = Self::cm_commit(
841 vk.comm_key.as_slice(),
842 combined_check_poly.coeffs(),
843 None,
844 None,
845 );
846 if !(final_key - &combined_final_key).is_zero() {
847 return Ok(false);
848 }
849
850 end_timer!(proof_time);
851
852 Ok(true)
853 }
854
855 fn open_combinations<'a>(
856 ck: &Self::CommitterKey,
857 linear_combinations: impl IntoIterator<Item = &'a LinearCombination<G::ScalarField>>,
858 polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<G::ScalarField, P>>,
859 commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
860 query_set: &QuerySet<P::Point>,
861 sponge: &mut impl CryptographicSponge,
862 states: impl IntoIterator<Item = &'a Self::CommitmentState>,
863 rng: Option<&mut dyn RngCore>,
864 ) -> Result<BatchLCProof<G::ScalarField, Self::BatchProof>, Self::Error>
865 where
866 Self::CommitmentState: 'a,
867 Self::Commitment: 'a,
868 P: 'a,
869 {
870 let label_poly_map = polynomials
871 .into_iter()
872 .zip(states)
873 .zip(commitments)
874 .map(|((p, s), c)| (p.label(), (p, s, c)))
875 .collect::<BTreeMap<_, _>>();
876
877 let mut lc_polynomials = Vec::new();
878 let mut lc_states = Vec::new();
879 let mut lc_commitments = Vec::new();
880 let mut lc_info = Vec::new();
881
882 for lc in linear_combinations {
883 let lc_label = lc.label().clone();
884 let mut poly = P::zero();
885 let mut degree_bound = None;
886 let mut hiding_bound = None;
887
888 let mut combined_comm = G::Group::zero();
889 let mut combined_shifted_comm: Option<G::Group> = None;
890
891 let mut combined_rand = G::ScalarField::zero();
892 let mut combined_shifted_rand: Option<G::ScalarField> = None;
893
894 let num_polys = lc.len();
895 for (coeff, label) in lc.iter().filter(|(_, l)| !l.is_one()) {
896 let label: &String = label.try_into().expect("cannot be one!");
897 let &(cur_poly, cur_rand, cur_comm) =
898 label_poly_map.get(label).ok_or(Error::MissingPolynomial {
899 label: label.to_string(),
900 })?;
901
902 if num_polys == 1 && cur_poly.degree_bound().is_some() {
903 assert!(
904 coeff.is_one(),
905 "Coefficient must be one for degree-bounded equations"
906 );
907 degree_bound = cur_poly.degree_bound();
908 } else if cur_poly.degree_bound().is_some() {
909 eprintln!("Degree bound when number of equations is non-zero");
910 return Err(Self::Error::EquationHasDegreeBounds(lc_label));
911 }
912
913 hiding_bound = core::cmp::max(hiding_bound, cur_poly.hiding_bound());
915 poly += (*coeff, cur_poly.polynomial());
916
917 combined_rand += &(cur_rand.rand * coeff);
918 combined_shifted_rand = Self::combine_shifted_rand(
919 combined_shifted_rand,
920 cur_rand.shifted_rand,
921 *coeff,
922 );
923
924 let commitment = cur_comm.commitment();
925 combined_comm += &commitment.comm.mul(*coeff);
926 combined_shifted_comm = Self::combine_shifted_comm(
927 combined_shifted_comm,
928 commitment.shifted_comm,
929 *coeff,
930 );
931 }
932
933 let lc_poly =
934 LabeledPolynomial::new(lc_label.clone(), poly, degree_bound, hiding_bound);
935 lc_polynomials.push(lc_poly);
936 lc_states.push(Randomness {
937 rand: combined_rand,
938 shifted_rand: combined_shifted_rand,
939 });
940
941 lc_commitments.push(combined_comm);
942 if let Some(combined_shifted_comm) = combined_shifted_comm {
943 lc_commitments.push(combined_shifted_comm);
944 }
945
946 lc_info.push((lc_label, degree_bound));
947 }
948
949 let lc_commitments = Self::construct_labeled_commitments(&lc_info, &lc_commitments);
950
951 let proof = Self::batch_open(
952 ck,
953 lc_polynomials.iter(),
954 lc_commitments.iter(),
955 &query_set,
956 sponge,
957 lc_states.iter(),
958 rng,
959 )?;
960 Ok(BatchLCProof { proof, evals: None })
961 }
962
963 fn check_combinations<'a, R: RngCore>(
966 vk: &Self::VerifierKey,
967 linear_combinations: impl IntoIterator<Item = &'a LinearCombination<G::ScalarField>>,
968 commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
969 eqn_query_set: &QuerySet<P::Point>,
970 eqn_evaluations: &Evaluations<P::Point, G::ScalarField>,
971 proof: &BatchLCProof<G::ScalarField, Self::BatchProof>,
972 sponge: &mut impl CryptographicSponge,
973 rng: &mut R,
974 ) -> Result<bool, Self::Error>
975 where
976 Self::Commitment: 'a,
977 {
978 let BatchLCProof { proof, .. } = proof;
979 let label_comm_map = commitments
980 .into_iter()
981 .map(|c| (c.label(), c))
982 .collect::<BTreeMap<_, _>>();
983
984 let mut lc_commitments = Vec::new();
985 let mut lc_info = Vec::new();
986 let mut evaluations = eqn_evaluations.clone();
987 for lc in linear_combinations {
988 let lc_label = lc.label().clone();
989 let num_polys = lc.len();
990
991 let mut degree_bound = None;
992 let mut combined_comm = G::Group::zero();
993 let mut combined_shifted_comm: Option<G::Group> = None;
994
995 for (coeff, label) in lc.iter() {
996 if label.is_one() {
997 for (&(ref label, _), ref mut eval) in evaluations.iter_mut() {
998 if label == &lc_label {
999 **eval -= coeff;
1000 }
1001 }
1002 } else {
1003 let label: &String = label.try_into().unwrap();
1004 let &cur_comm = label_comm_map.get(label).ok_or(Error::MissingPolynomial {
1005 label: label.to_string(),
1006 })?;
1007
1008 if num_polys == 1 && cur_comm.degree_bound().is_some() {
1009 assert!(
1010 coeff.is_one(),
1011 "Coefficient must be one for degree-bounded equations"
1012 );
1013 degree_bound = cur_comm.degree_bound();
1014 } else if cur_comm.degree_bound().is_some() {
1015 return Err(Self::Error::EquationHasDegreeBounds(lc_label));
1016 }
1017
1018 let commitment = cur_comm.commitment();
1019 combined_comm += &commitment.comm.mul(*coeff);
1020 combined_shifted_comm = Self::combine_shifted_comm(
1021 combined_shifted_comm,
1022 commitment.shifted_comm,
1023 *coeff,
1024 );
1025 }
1026 }
1027
1028 lc_commitments.push(combined_comm);
1029
1030 if let Some(combined_shifted_comm) = combined_shifted_comm {
1031 lc_commitments.push(combined_shifted_comm);
1032 }
1033
1034 lc_info.push((lc_label, degree_bound));
1035 }
1036
1037 let lc_commitments = Self::construct_labeled_commitments(&lc_info, &lc_commitments);
1038
1039 Self::batch_check(
1040 vk,
1041 &lc_commitments,
1042 &eqn_query_set,
1043 &evaluations,
1044 proof,
1045 sponge,
1046 rng,
1047 )
1048 }
1049}
1050
1051#[cfg(test)]
1052mod tests {
1053 #![allow(non_camel_case_types)]
1054
1055 use super::InnerProductArgPC;
1056 use ark_ed_on_bls12_381::{EdwardsAffine, Fr};
1057 use ark_ff::PrimeField;
1058 use ark_poly::{univariate::DensePolynomial as DensePoly, DenseUVPolynomial};
1059 use blake2::Blake2s256;
1060 use rand_chacha::ChaCha20Rng;
1061
1062 type UniPoly = DensePoly<Fr>;
1063 type PC<E, D, P> = InnerProductArgPC<E, D, P>;
1064 type PC_JJB2S = PC<EdwardsAffine, Blake2s256, UniPoly>;
1065
1066 fn rand_poly<F: PrimeField>(
1067 degree: usize,
1068 _: Option<usize>,
1069 rng: &mut ChaCha20Rng,
1070 ) -> DensePoly<F> {
1071 DensePoly::rand(degree, rng)
1072 }
1073
1074 fn constant_poly<F: PrimeField>(
1075 _: usize,
1076 _: Option<usize>,
1077 rng: &mut ChaCha20Rng,
1078 ) -> DensePoly<F> {
1079 DensePoly::from_coefficients_slice(&[F::rand(rng)])
1080 }
1081
1082 fn rand_point<F: PrimeField>(_: Option<usize>, rng: &mut ChaCha20Rng) -> F {
1083 F::rand(rng)
1084 }
1085
1086 #[test]
1087 fn single_poly_test() {
1088 use crate::tests::*;
1089 single_poly_test::<_, _, PC_JJB2S, _>(
1090 None,
1091 rand_poly::<Fr>,
1092 rand_point::<Fr>,
1093 poseidon_sponge_for_test::<Fr>,
1094 )
1095 .expect("test failed for ed_on_bls12_381-blake2s");
1096 }
1097
1098 #[test]
1099 fn constant_poly_test() {
1100 use crate::tests::*;
1101 single_poly_test::<_, _, PC_JJB2S, _>(
1102 None,
1103 constant_poly::<Fr>,
1104 rand_point::<Fr>,
1105 poseidon_sponge_for_test::<Fr>,
1106 )
1107 .expect("test failed for ed_on_bls12_381-blake2s");
1108 }
1109
1110 #[test]
1111 fn quadratic_poly_degree_bound_multiple_queries_test() {
1112 use crate::tests::*;
1113 quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_JJB2S, _>(
1114 rand_poly::<Fr>,
1115 rand_point::<Fr>,
1116 poseidon_sponge_for_test::<Fr>,
1117 )
1118 .expect("test failed for ed_on_bls12_381-blake2s");
1119 }
1120
1121 #[test]
1122 fn linear_poly_degree_bound_test() {
1123 use crate::tests::*;
1124 linear_poly_degree_bound_test::<_, _, PC_JJB2S, _>(
1125 rand_poly::<Fr>,
1126 rand_point::<Fr>,
1127 poseidon_sponge_for_test::<Fr>,
1128 )
1129 .expect("test failed for ed_on_bls12_381-blake2s");
1130 }
1131
1132 #[test]
1133 fn single_poly_degree_bound_test() {
1134 use crate::tests::*;
1135 single_poly_degree_bound_test::<_, _, PC_JJB2S, _>(
1136 rand_poly::<Fr>,
1137 rand_point::<Fr>,
1138 poseidon_sponge_for_test::<Fr>,
1139 )
1140 .expect("test failed for ed_on_bls12_381-blake2s");
1141 }
1142
1143 #[test]
1144 fn single_poly_degree_bound_multiple_queries_test() {
1145 use crate::tests::*;
1146 single_poly_degree_bound_multiple_queries_test::<_, _, PC_JJB2S, _>(
1147 rand_poly::<Fr>,
1148 rand_point::<Fr>,
1149 poseidon_sponge_for_test::<Fr>,
1150 )
1151 .expect("test failed for ed_on_bls12_381-blake2s");
1152 }
1153
1154 #[test]
1155 fn two_polys_degree_bound_single_query_test() {
1156 use crate::tests::*;
1157 two_polys_degree_bound_single_query_test::<_, _, PC_JJB2S, _>(
1158 rand_poly::<Fr>,
1159 rand_point::<Fr>,
1160 poseidon_sponge_for_test::<Fr>,
1161 )
1162 .expect("test failed for ed_on_bls12_381-blake2s");
1163 }
1164
1165 #[test]
1166 fn full_end_to_end_test() {
1167 use crate::tests::*;
1168 full_end_to_end_test::<_, _, PC_JJB2S, _>(
1169 None,
1170 rand_poly::<Fr>,
1171 rand_point::<Fr>,
1172 poseidon_sponge_for_test::<Fr>,
1173 )
1174 .expect("test failed for ed_on_bls12_381-blake2s");
1175 println!("Finished ed_on_bls12_381-blake2s");
1176 }
1177
1178 #[test]
1179 fn single_equation_test() {
1180 use crate::tests::*;
1181 single_equation_test::<_, _, PC_JJB2S, _>(
1182 None,
1183 rand_poly::<Fr>,
1184 rand_point::<Fr>,
1185 poseidon_sponge_for_test::<Fr>,
1186 )
1187 .expect("test failed for ed_on_bls12_381-blake2s");
1188 println!("Finished ed_on_bls12_381-blake2s");
1189 }
1190
1191 #[test]
1192 fn two_equation_test() {
1193 use crate::tests::*;
1194 two_equation_test::<_, _, PC_JJB2S, _>(
1195 None,
1196 rand_poly::<Fr>,
1197 rand_point::<Fr>,
1198 poseidon_sponge_for_test::<Fr>,
1199 )
1200 .expect("test failed for ed_on_bls12_381-blake2s");
1201 println!("Finished ed_on_bls12_381-blake2s");
1202 }
1203
1204 #[test]
1205 fn two_equation_degree_bound_test() {
1206 use crate::tests::*;
1207 two_equation_degree_bound_test::<_, _, PC_JJB2S, _>(
1208 rand_poly::<Fr>,
1209 rand_point::<Fr>,
1210 poseidon_sponge_for_test::<Fr>,
1211 )
1212 .expect("test failed for ed_on_bls12_381-blake2s");
1213 println!("Finished ed_on_bls12_381-blake2s");
1214 }
1215
1216 #[test]
1217 fn full_end_to_end_equation_test() {
1218 use crate::tests::*;
1219 full_end_to_end_equation_test::<_, _, PC_JJB2S, _>(
1220 None,
1221 rand_poly::<Fr>,
1222 rand_point::<Fr>,
1223 poseidon_sponge_for_test::<Fr>,
1224 )
1225 .expect("test failed for ed_on_bls12_381-blake2s");
1226 println!("Finished ed_on_bls12_381-blake2s");
1227 }
1228
1229 #[test]
1230 #[should_panic]
1231 fn bad_degree_bound_test() {
1232 use crate::tests::*;
1233 bad_degree_bound_test::<_, _, PC_JJB2S, _>(
1234 rand_poly::<Fr>,
1235 rand_point::<Fr>,
1236 poseidon_sponge_for_test::<Fr>,
1237 )
1238 .expect("test failed for ed_on_bls12_381-blake2s");
1239 println!("Finished ed_on_bls12_381-blake2s");
1240 }
1241}