ark_poly_commit/ipa_pc/
mod.rs

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
23/// A polynomial commitment scheme based on the hardness of the
24/// discrete logarithm problem in prime-order groups.
25/// The construction is described in detail in [[BCMS20]][pcdas].
26///
27/// Degree bound enforcement requires that (at least one of) the points at
28/// which a committed polynomial is evaluated are from a distribution that is
29/// random conditioned on the polynomial. This is because degree bound
30/// enforcement relies on checking a polynomial identity at this point.
31/// More formally, the points must be sampled from an admissible query sampler,
32/// as detailed in [[CHMMVW20]][marlin].
33///
34/// [pcdas]: https://eprint.iacr.org/2020/499
35/// [marlin]: https://eprint.iacr.org/2019/1047
36pub 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    /// `PROTOCOL_NAME` is used as a seed for the setup function.
50    pub const PROTOCOL_NAME: &'static [u8] = b"PC-DL-2020";
51
52    /// Create a Pedersen commitment to `scalars` using the commitment key `comm_key`.
53    /// Optionally, randomize the commitment using `hiding_generator` and `randomizer`.
54    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    /// The succinct portion of `PC::check`. This algorithm runs in time
90    /// O(log d), where d is the degree of the committed polynomials.
91    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        // `log_d` is ceil(log2 (d + 1)), which is the number of steps to compute all of the challenges
104        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        // Challenge for each round
153        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                    // PROTOCOL NAME, i, j
312                    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        // Ensure that max_degree + 1 is a power of 2
350        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        // Ensure that supported_degree + 1 is a power of two
375        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    /// Outputs a commitment to `polynomial`.
403    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        // Pad the coefficients to the appropriate vector size
564        let d = ck.supported_degree();
565
566        // `log_d` is ceil(log2 (d + 1)), which is the number of steps to compute all of the challenges
567        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        // ith challenge
622        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        // Pads the coefficients with zeroes to get the number of coeff to be d+1
634        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        // Powers of z
643        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        // This will be used for transforming the key in each step
652        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        // Key for MSM
658        // We initialize this to capacity 0 initially because we want to use the key slice first
659        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        // `log_d` is ceil(log2 (d + 1)), which is the number of steps to compute all of the challenges
741        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                // Some(_) > None, always.
914                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    /// Checks that `values` are the true evaluations at `query_set` of the polynomials
964    /// committed in `labeled_commitments`.
965    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}