ark_poly_commit/sonic_pc/
mod.rs

1use crate::{
2    kzg10, 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::{pairing::Pairing, AffineRepr, CurveGroup};
8use ark_ff::{One, UniformRand, Zero};
9use ark_std::{convert::TryInto, marker::PhantomData, ops::Div, ops::Mul, rand::RngCore};
10#[cfg(not(feature = "std"))]
11use ark_std::{
12    string::{String, ToString},
13    vec::Vec,
14};
15
16mod data_structures;
17pub use data_structures::*;
18
19/// Polynomial commitment based on [[KZG10]][kzg], with degree enforcement and
20/// batching taken from [[MBKM19, “Sonic”]][sonic] (more precisely, their
21/// counterparts in [[Gabizon19, “AuroraLight”]][al] that avoid negative G1 powers).
22/// The (optional) hiding property of the commitment scheme follows the approach
23/// described in [[CHMMVW20, “Marlin”]][marlin].
24///
25/// [kzg]: http://cacr.uwaterloo.ca/techreports/2010/cacr2010-10.pdf
26/// [sonic]: https://eprint.iacr.org/2019/099
27/// [al]: https://eprint.iacr.org/2019/601
28/// [marlin]: https://eprint.iacr.org/2019/1047
29pub struct SonicKZG10<E: Pairing, P: DenseUVPolynomial<E::ScalarField>> {
30    _engine: PhantomData<E>,
31    _poly: PhantomData<P>,
32}
33
34impl<E, P> SonicKZG10<E, P>
35where
36    E: Pairing,
37    P: DenseUVPolynomial<E::ScalarField>,
38{
39    fn accumulate_elems<'a>(
40        combined_comms: &mut BTreeMap<Option<usize>, E::G1>,
41        combined_witness: &mut E::G1,
42        combined_adjusted_witness: &mut E::G1,
43        vk: &VerifierKey<E>,
44        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Commitment<E>>>,
45        point: P::Point,
46        values: impl IntoIterator<Item = E::ScalarField>,
47        proof: &kzg10::Proof<E>,
48        sponge: &mut impl CryptographicSponge,
49        randomizer: Option<E::ScalarField>,
50    ) {
51        let acc_time = start_timer!(|| "Accumulating elements");
52
53        let mut curr_challenge = sponge.squeeze_field_elements_with_sizes(&[CHALLENGE_SIZE])[0];
54
55        // Keeps track of running combination of values
56        let mut combined_values = E::ScalarField::zero();
57
58        // Iterates through all of the commitments and accumulates common degree_bound elements in a BTreeMap
59        for (labeled_comm, value) in commitments.into_iter().zip(values) {
60            combined_values += &(value * &curr_challenge);
61
62            let comm = labeled_comm.commitment();
63            let degree_bound = labeled_comm.degree_bound();
64
65            // Applying opening challenge and randomness (used in batch_checking)
66            let mut comm_with_challenge: E::G1 = comm.0.mul(curr_challenge);
67
68            if let Some(randomizer) = randomizer {
69                comm_with_challenge = comm_with_challenge.mul(&randomizer);
70            }
71
72            // Accumulate values in the BTreeMap
73            *combined_comms.entry(degree_bound).or_insert(E::G1::zero()) += &comm_with_challenge;
74            curr_challenge = sponge.squeeze_field_elements_with_sizes(&[CHALLENGE_SIZE])[0];
75        }
76
77        // Push expected results into list of elems. Power will be the negative of the expected power
78        let mut witness: E::G1 = proof.w.into_group();
79        let mut adjusted_witness = vk.g.mul(combined_values) - &proof.w.mul(point);
80        if let Some(random_v) = proof.random_v {
81            adjusted_witness += &vk.gamma_g.mul(random_v);
82        }
83
84        if let Some(randomizer) = randomizer {
85            witness = proof.w.mul(randomizer);
86            adjusted_witness = adjusted_witness.mul(&randomizer);
87        }
88
89        *combined_witness += &witness;
90        *combined_adjusted_witness += &adjusted_witness;
91        end_timer!(acc_time);
92    }
93
94    fn check_elems(
95        combined_comms: BTreeMap<Option<usize>, E::G1>,
96        combined_witness: E::G1,
97        combined_adjusted_witness: E::G1,
98        vk: &VerifierKey<E>,
99    ) -> Result<bool, Error> {
100        let check_time = start_timer!(|| "Checking elems");
101        let mut g1_projective_elems: Vec<E::G1> = Vec::new();
102        let mut g2_prepared_elems: Vec<E::G2Prepared> = Vec::new();
103
104        for (degree_bound, comm) in combined_comms.into_iter() {
105            let shift_power = if let Some(degree_bound) = degree_bound {
106                vk.get_shift_power(degree_bound)
107                    .ok_or(Error::UnsupportedDegreeBound(degree_bound))?
108            } else {
109                vk.prepared_h.clone()
110            };
111
112            g1_projective_elems.push(comm);
113            g2_prepared_elems.push(shift_power);
114        }
115
116        g1_projective_elems.push(-combined_adjusted_witness);
117        g2_prepared_elems.push(vk.prepared_h.clone());
118
119        g1_projective_elems.push(-combined_witness);
120        g2_prepared_elems.push(vk.prepared_beta_h.clone());
121
122        let g1_prepared_elems_iter: Vec<E::G1Prepared> =
123            E::G1::normalize_batch(g1_projective_elems.as_slice())
124                .into_iter()
125                .map(|a| a.into())
126                .collect::<Vec<_>>();
127
128        let is_one: bool = E::multi_pairing(g1_prepared_elems_iter, g2_prepared_elems)
129            .0
130            .is_one();
131        end_timer!(check_time);
132        Ok(is_one)
133    }
134}
135
136impl<E, P> PolynomialCommitment<E::ScalarField, P> for SonicKZG10<E, P>
137where
138    E: Pairing,
139    P: DenseUVPolynomial<E::ScalarField, Point = E::ScalarField>,
140    for<'a, 'b> &'a P: Div<&'b P, Output = P>,
141{
142    type UniversalParams = UniversalParams<E>;
143    type CommitterKey = CommitterKey<E>;
144    type VerifierKey = VerifierKey<E>;
145    type Commitment = Commitment<E>;
146    type CommitmentState = Randomness<E::ScalarField, P>;
147    type Proof = kzg10::Proof<E>;
148    type BatchProof = Vec<Self::Proof>;
149    type Error = Error;
150
151    fn setup<R: RngCore>(
152        max_degree: usize,
153        _: Option<usize>,
154        rng: &mut R,
155    ) -> Result<Self::UniversalParams, Self::Error> {
156        kzg10::KZG10::<E, P>::setup(max_degree, true, rng).map_err(Into::into)
157    }
158
159    fn trim(
160        pp: &Self::UniversalParams,
161        supported_degree: usize,
162        supported_hiding_bound: usize,
163        enforced_degree_bounds: Option<&[usize]>,
164    ) -> Result<(Self::CommitterKey, Self::VerifierKey), Self::Error> {
165        let trim_time = start_timer!(|| "Trimming public parameters");
166        let neg_powers_of_h = &pp.neg_powers_of_h;
167        let max_degree = pp.max_degree();
168        if supported_degree > max_degree {
169            return Err(Error::TrimmingDegreeTooLarge);
170        }
171
172        let enforced_degree_bounds = enforced_degree_bounds.map(|bounds| {
173            let mut v = bounds.to_vec();
174            v.sort();
175            v.dedup();
176            v
177        });
178
179        let (shifted_powers_of_g, shifted_powers_of_gamma_g, degree_bounds_and_neg_powers_of_h) =
180            if let Some(enforced_degree_bounds) = enforced_degree_bounds.as_ref() {
181                if enforced_degree_bounds.is_empty() {
182                    (None, None, None)
183                } else {
184                    let highest_enforced_degree_bound = *enforced_degree_bounds.last().unwrap();
185                    if highest_enforced_degree_bound > supported_degree {
186                        return Err(Error::UnsupportedDegreeBound(highest_enforced_degree_bound));
187                    }
188
189                    let lowest_shift_degree = max_degree - highest_enforced_degree_bound;
190
191                    let shifted_ck_time = start_timer!(|| format!(
192                        "Constructing `shifted_powers` of size {}",
193                        max_degree - lowest_shift_degree + 1
194                    ));
195
196                    let shifted_powers_of_g = pp.powers_of_g[lowest_shift_degree..].to_vec();
197                    let mut shifted_powers_of_gamma_g = BTreeMap::new();
198                    // Also add degree 0.
199                    for degree_bound in enforced_degree_bounds {
200                        let shift_degree = max_degree - degree_bound;
201                        let mut powers_for_degree_bound = vec![];
202                        for i in 0..=(supported_hiding_bound + 1) {
203                            // We have an additional degree in `powers_of_gamma_g` beyond `powers_of_g`.
204                            if shift_degree + i < max_degree + 2 {
205                                powers_for_degree_bound
206                                    .push(pp.powers_of_gamma_g[&(shift_degree + i)]);
207                            }
208                        }
209                        shifted_powers_of_gamma_g.insert(*degree_bound, powers_for_degree_bound);
210                    }
211
212                    end_timer!(shifted_ck_time);
213
214                    let neg_powers_of_h_time = start_timer!(|| format!(
215                        "Constructing `neg_powers_of_h` of size {}",
216                        enforced_degree_bounds.len()
217                    ));
218
219                    let degree_bounds_and_neg_powers_of_h = enforced_degree_bounds
220                        .iter()
221                        .map(|bound| (*bound, neg_powers_of_h[&(max_degree - *bound)].clone()))
222                        .collect();
223
224                    end_timer!(neg_powers_of_h_time);
225
226                    (
227                        Some(shifted_powers_of_g),
228                        Some(shifted_powers_of_gamma_g),
229                        Some(degree_bounds_and_neg_powers_of_h),
230                    )
231                }
232            } else {
233                (None, None, None)
234            };
235
236        let powers_of_g = pp.powers_of_g[..=supported_degree].to_vec();
237        let powers_of_gamma_g = (0..=(supported_hiding_bound + 1))
238            .map(|i| pp.powers_of_gamma_g[&i])
239            .collect();
240
241        let ck = CommitterKey {
242            powers_of_g,
243            powers_of_gamma_g,
244            shifted_powers_of_g,
245            shifted_powers_of_gamma_g,
246            enforced_degree_bounds,
247            max_degree,
248        };
249
250        let g = pp.powers_of_g[0];
251        let h = pp.h;
252        let beta_h = pp.beta_h;
253        let gamma_g = pp.powers_of_gamma_g[&0];
254        let prepared_h = (&pp.prepared_h).clone();
255        let prepared_beta_h = (&pp.prepared_beta_h).clone();
256
257        let vk = VerifierKey {
258            g,
259            gamma_g,
260            h,
261            beta_h,
262            prepared_h,
263            prepared_beta_h,
264            degree_bounds_and_neg_powers_of_h,
265            supported_degree,
266            max_degree,
267        };
268
269        end_timer!(trim_time);
270        Ok((ck, vk))
271    }
272
273    /// Outputs a commitment to `polynomial`.
274    fn commit<'a>(
275        ck: &Self::CommitterKey,
276        polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::ScalarField, P>>,
277        rng: Option<&mut dyn RngCore>,
278    ) -> Result<
279        (
280            Vec<LabeledCommitment<Self::Commitment>>,
281            Vec<Self::CommitmentState>,
282        ),
283        Self::Error,
284    >
285    where
286        P: 'a,
287    {
288        let rng = &mut crate::optional_rng::OptionalRng(rng);
289        let commit_time = start_timer!(|| "Committing to polynomials");
290        let mut labeled_comms: Vec<LabeledCommitment<Self::Commitment>> = Vec::new();
291        let mut randomness: Vec<Self::CommitmentState> = Vec::new();
292
293        for labeled_polynomial in polynomials {
294            let enforced_degree_bounds: Option<&[usize]> = ck
295                .enforced_degree_bounds
296                .as_ref()
297                .map(|bounds| bounds.as_slice());
298
299            kzg10::KZG10::<E, P>::check_degrees_and_bounds(
300                ck.supported_degree(),
301                ck.max_degree,
302                enforced_degree_bounds,
303                &labeled_polynomial,
304            )?;
305
306            let polynomial: &P = labeled_polynomial.polynomial();
307            let degree_bound = labeled_polynomial.degree_bound();
308            let hiding_bound = labeled_polynomial.hiding_bound();
309            let label = labeled_polynomial.label();
310
311            let commit_time = start_timer!(|| format!(
312                "Polynomial {} of degree {}, degree bound {:?}, and hiding bound {:?}",
313                label,
314                polynomial.degree(),
315                degree_bound,
316                hiding_bound,
317            ));
318
319            let powers = if let Some(degree_bound) = degree_bound {
320                ck.shifted_powers(degree_bound).unwrap()
321            } else {
322                ck.powers()
323            };
324
325            let (comm, rand) = kzg10::KZG10::commit(&powers, polynomial, hiding_bound, Some(rng))?;
326
327            labeled_comms.push(LabeledCommitment::new(
328                label.to_string(),
329                comm,
330                degree_bound,
331            ));
332            randomness.push(rand);
333            end_timer!(commit_time);
334        }
335
336        end_timer!(commit_time);
337        Ok((labeled_comms, randomness))
338    }
339
340    fn open<'a>(
341        ck: &Self::CommitterKey,
342        labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::ScalarField, P>>,
343        _commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
344        point: &'a P::Point,
345        sponge: &mut impl CryptographicSponge,
346        states: impl IntoIterator<Item = &'a Self::CommitmentState>,
347        _rng: Option<&mut dyn RngCore>,
348    ) -> Result<Self::Proof, Self::Error>
349    where
350        Self::CommitmentState: 'a,
351        Self::Commitment: 'a,
352        P: 'a,
353    {
354        let mut combined_polynomial = P::zero();
355        let mut combined_rand = kzg10::Randomness::empty();
356
357        let mut curr_challenge = sponge.squeeze_field_elements_with_sizes(&[CHALLENGE_SIZE])[0];
358
359        for (polynomial, state) in labeled_polynomials.into_iter().zip(states) {
360            let enforced_degree_bounds: Option<&[usize]> = ck
361                .enforced_degree_bounds
362                .as_ref()
363                .map(|bounds| bounds.as_slice());
364
365            kzg10::KZG10::<E, P>::check_degrees_and_bounds(
366                ck.supported_degree(),
367                ck.max_degree,
368                enforced_degree_bounds,
369                &polynomial,
370            )?;
371
372            combined_polynomial += (curr_challenge, polynomial.polynomial());
373            combined_rand += (curr_challenge, state);
374            curr_challenge = sponge.squeeze_field_elements_with_sizes(&[CHALLENGE_SIZE])[0];
375        }
376
377        let proof_time = start_timer!(|| "Creating proof for polynomials");
378        let proof = kzg10::KZG10::open(&ck.powers(), &combined_polynomial, *point, &combined_rand)?;
379        end_timer!(proof_time);
380
381        Ok(proof)
382    }
383
384    fn check<'a>(
385        vk: &Self::VerifierKey,
386        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
387        point: &'a P::Point,
388        values: impl IntoIterator<Item = E::ScalarField>,
389        proof: &Self::Proof,
390        sponge: &mut impl CryptographicSponge,
391        _rng: Option<&mut dyn RngCore>,
392    ) -> Result<bool, Self::Error>
393    where
394        Self::Commitment: 'a,
395    {
396        let check_time = start_timer!(|| "Checking evaluations");
397        let mut combined_comms: BTreeMap<Option<usize>, E::G1> = BTreeMap::new();
398        let mut combined_witness: E::G1 = E::G1::zero();
399        let mut combined_adjusted_witness: E::G1 = E::G1::zero();
400
401        Self::accumulate_elems(
402            &mut combined_comms,
403            &mut combined_witness,
404            &mut combined_adjusted_witness,
405            vk,
406            commitments,
407            *point,
408            values,
409            proof,
410            sponge,
411            None,
412        );
413
414        let res = Self::check_elems(
415            combined_comms,
416            combined_witness,
417            combined_adjusted_witness,
418            vk,
419        );
420        end_timer!(check_time);
421        res
422    }
423
424    fn batch_check<'a, R: RngCore>(
425        vk: &Self::VerifierKey,
426        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
427        query_set: &QuerySet<P::Point>,
428        values: &Evaluations<E::ScalarField, P::Point>,
429        proof: &Self::BatchProof,
430        sponge: &mut impl CryptographicSponge,
431        rng: &mut R,
432    ) -> Result<bool, Self::Error>
433    where
434        Self::Commitment: 'a,
435    {
436        let commitments: BTreeMap<_, _> = commitments.into_iter().map(|c| (c.label(), c)).collect();
437        let mut query_to_labels_map = BTreeMap::new();
438
439        for (label, (point_label, point)) in query_set.iter() {
440            let labels = query_to_labels_map
441                .entry(point_label)
442                .or_insert((point, BTreeSet::new()));
443            labels.1.insert(label);
444        }
445
446        assert_eq!(proof.len(), query_to_labels_map.len());
447
448        let mut randomizer = E::ScalarField::one();
449
450        let mut combined_comms: BTreeMap<Option<usize>, E::G1> = BTreeMap::new();
451        let mut combined_witness: E::G1 = E::G1::zero();
452        let mut combined_adjusted_witness: E::G1 = E::G1::zero();
453
454        for ((_point_label, (point, labels)), p) in query_to_labels_map.into_iter().zip(proof) {
455            let mut comms_to_combine: Vec<&'_ LabeledCommitment<_>> = Vec::new();
456            let mut values_to_combine = Vec::new();
457            for label in labels.into_iter() {
458                let commitment = commitments.get(label).ok_or(Error::MissingPolynomial {
459                    label: label.to_string(),
460                })?;
461
462                let v_i = values
463                    .get(&(label.clone(), *point))
464                    .ok_or(Error::MissingEvaluation {
465                        label: label.to_string(),
466                    })?;
467
468                comms_to_combine.push(commitment);
469                values_to_combine.push(*v_i);
470            }
471
472            Self::accumulate_elems(
473                &mut combined_comms,
474                &mut combined_witness,
475                &mut combined_adjusted_witness,
476                vk,
477                comms_to_combine.into_iter(),
478                *point,
479                values_to_combine.into_iter(),
480                p,
481                sponge,
482                Some(randomizer),
483            );
484
485            randomizer = u128::rand(rng).into();
486        }
487
488        Self::check_elems(
489            combined_comms,
490            combined_witness,
491            combined_adjusted_witness,
492            vk,
493        )
494    }
495
496    fn open_combinations<'a>(
497        ck: &Self::CommitterKey,
498        linear_combinations: impl IntoIterator<Item = &'a LinearCombination<E::ScalarField>>,
499        polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::ScalarField, P>>,
500        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
501        query_set: &QuerySet<P::Point>,
502        sponge: &mut impl CryptographicSponge,
503        states: impl IntoIterator<Item = &'a Self::CommitmentState>,
504        rng: Option<&mut dyn RngCore>,
505    ) -> Result<BatchLCProof<E::ScalarField, Self::BatchProof>, Self::Error>
506    where
507        Self::CommitmentState: 'a,
508        Self::Commitment: 'a,
509        P: 'a,
510    {
511        let label_map = polynomials
512            .into_iter()
513            .zip(states)
514            .zip(commitments)
515            .map(|((p, s), c)| (p.label(), (p, s, c)))
516            .collect::<BTreeMap<_, _>>();
517
518        let mut lc_polynomials = Vec::new();
519        let mut lc_states = Vec::new();
520        let mut lc_commitments = Vec::new();
521        let mut lc_info = Vec::new();
522
523        for lc in linear_combinations {
524            let lc_label = lc.label().clone();
525            let mut poly = P::zero();
526            let mut degree_bound = None;
527            let mut hiding_bound = None;
528            let mut state = Self::CommitmentState::empty();
529            let mut comm = E::G1::zero();
530
531            let num_polys = lc.len();
532            for (coeff, label) in lc.iter().filter(|(_, l)| !l.is_one()) {
533                let label: &String = label.try_into().expect("cannot be one!");
534                let &(cur_poly, cur_state, curr_comm) =
535                    label_map.get(label).ok_or(Error::MissingPolynomial {
536                        label: label.to_string(),
537                    })?;
538
539                if num_polys == 1 && cur_poly.degree_bound().is_some() {
540                    assert!(
541                        coeff.is_one(),
542                        "Coefficient must be one for degree-bounded equations"
543                    );
544                    degree_bound = cur_poly.degree_bound();
545                } else if cur_poly.degree_bound().is_some() {
546                    eprintln!("Degree bound when number of equations is non-zero");
547                    return Err(Self::Error::EquationHasDegreeBounds(lc_label));
548                }
549
550                // Some(_) > None, always.
551                hiding_bound = core::cmp::max(hiding_bound, cur_poly.hiding_bound());
552                poly += (*coeff, cur_poly.polynomial());
553                state += (*coeff, cur_state);
554                comm += &curr_comm.commitment().0.mul(*coeff);
555            }
556
557            let lc_poly =
558                LabeledPolynomial::new(lc_label.clone(), poly, degree_bound, hiding_bound);
559            lc_polynomials.push(lc_poly);
560            lc_states.push(state);
561            lc_commitments.push(comm);
562            lc_info.push((lc_label, degree_bound));
563        }
564
565        let comms: Vec<Self::Commitment> = E::G1::normalize_batch(&lc_commitments)
566            .into_iter()
567            .map(|c| kzg10::Commitment::<E>(c))
568            .collect();
569
570        let lc_commitments = lc_info
571            .into_iter()
572            .zip(comms)
573            .map(|((label, d), c)| LabeledCommitment::new(label, c, d))
574            .collect::<Vec<_>>();
575
576        let proof = Self::batch_open(
577            ck,
578            lc_polynomials.iter(),
579            lc_commitments.iter(),
580            &query_set,
581            sponge,
582            lc_states.iter(),
583            rng,
584        )?;
585        Ok(BatchLCProof { proof, evals: None })
586    }
587
588    /// Checks that `values` are the true evaluations at `query_set` of the polynomials
589    /// committed in `labeled_commitments`.
590    fn check_combinations<'a, R: RngCore>(
591        vk: &Self::VerifierKey,
592        linear_combinations: impl IntoIterator<Item = &'a LinearCombination<E::ScalarField>>,
593        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
594        eqn_query_set: &QuerySet<P::Point>,
595        eqn_evaluations: &Evaluations<P::Point, E::ScalarField>,
596        proof: &BatchLCProof<E::ScalarField, Self::BatchProof>,
597        sponge: &mut impl CryptographicSponge,
598        rng: &mut R,
599    ) -> Result<bool, Self::Error>
600    where
601        Self::Commitment: 'a,
602    {
603        let BatchLCProof { proof, .. } = proof;
604        let label_comm_map = commitments
605            .into_iter()
606            .map(|c| (c.label(), c))
607            .collect::<BTreeMap<_, _>>();
608
609        let mut lc_commitments = Vec::new();
610        let mut lc_info = Vec::new();
611        let mut evaluations = eqn_evaluations.clone();
612        for lc in linear_combinations {
613            let lc_label = lc.label().clone();
614            let num_polys = lc.len();
615
616            let mut degree_bound = None;
617            let mut combined_comm = E::G1::zero();
618
619            for (coeff, label) in lc.iter() {
620                if label.is_one() {
621                    for (&(ref label, _), ref mut eval) in evaluations.iter_mut() {
622                        if label == &lc_label {
623                            **eval -= coeff;
624                        }
625                    }
626                } else {
627                    let label: &String = label.try_into().unwrap();
628                    let &cur_comm = label_comm_map.get(label).ok_or(Error::MissingPolynomial {
629                        label: label.to_string(),
630                    })?;
631
632                    if num_polys == 1 && cur_comm.degree_bound().is_some() {
633                        assert!(
634                            coeff.is_one(),
635                            "Coefficient must be one for degree-bounded equations"
636                        );
637                        degree_bound = cur_comm.degree_bound();
638                    } else if cur_comm.degree_bound().is_some() {
639                        return Err(Self::Error::EquationHasDegreeBounds(lc_label));
640                    }
641                    combined_comm += &cur_comm.commitment().0.mul(*coeff);
642                }
643            }
644
645            lc_commitments.push(combined_comm);
646            lc_info.push((lc_label, degree_bound));
647        }
648
649        let comms: Vec<Self::Commitment> = E::G1::normalize_batch(&lc_commitments)
650            .into_iter()
651            .map(|c| kzg10::Commitment(c))
652            .collect();
653
654        let lc_commitments = lc_info
655            .into_iter()
656            .zip(comms)
657            .map(|((label, d), c)| LabeledCommitment::new(label, c, d))
658            .collect::<Vec<_>>();
659
660        Self::batch_check(
661            vk,
662            &lc_commitments,
663            &eqn_query_set,
664            &evaluations,
665            proof,
666            sponge,
667            rng,
668        )
669    }
670}
671
672#[cfg(test)]
673mod tests {
674    #![allow(non_camel_case_types)]
675    use super::SonicKZG10;
676    use ark_bls12_377::Bls12_377;
677    use ark_bls12_381::Bls12_381;
678    use ark_ec::pairing::Pairing;
679    use ark_ff::UniformRand;
680    use ark_poly::{univariate::DensePolynomial as DensePoly, DenseUVPolynomial};
681    use rand_chacha::ChaCha20Rng;
682
683    type UniPoly_381 = DensePoly<<Bls12_381 as Pairing>::ScalarField>;
684    type UniPoly_377 = DensePoly<<Bls12_377 as Pairing>::ScalarField>;
685
686    type PC<E, P> = SonicKZG10<E, P>;
687    type PC_Bls12_377 = PC<Bls12_377, UniPoly_377>;
688    type PC_Bls12_381 = PC<Bls12_381, UniPoly_381>;
689
690    fn rand_poly<E: Pairing>(
691        degree: usize,
692        _: Option<usize>,
693        rng: &mut ChaCha20Rng,
694    ) -> DensePoly<E::ScalarField> {
695        DensePoly::<E::ScalarField>::rand(degree, rng)
696    }
697
698    fn rand_point<E: Pairing>(_: Option<usize>, rng: &mut ChaCha20Rng) -> E::ScalarField {
699        E::ScalarField::rand(rng)
700    }
701
702    #[test]
703    fn single_poly_test() {
704        use crate::tests::*;
705        single_poly_test::<_, _, PC_Bls12_377, _>(
706            None,
707            rand_poly::<Bls12_377>,
708            rand_point::<Bls12_377>,
709            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
710        )
711        .expect("test failed for bls12-377");
712        single_poly_test::<_, _, PC_Bls12_381, _>(
713            None,
714            rand_poly::<Bls12_381>,
715            rand_point::<Bls12_381>,
716            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
717        )
718        .expect("test failed for bls12-381");
719    }
720
721    #[test]
722    fn quadratic_poly_degree_bound_multiple_queries_test() {
723        use crate::tests::*;
724        quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_377, _>(
725            rand_poly::<Bls12_377>,
726            rand_point::<Bls12_377>,
727            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
728        )
729        .expect("test failed for bls12-377");
730        quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_381, _>(
731            rand_poly::<Bls12_381>,
732            rand_point::<Bls12_381>,
733            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
734        )
735        .expect("test failed for bls12-381");
736    }
737
738    #[test]
739    fn linear_poly_degree_bound_test() {
740        use crate::tests::*;
741        linear_poly_degree_bound_test::<_, _, PC_Bls12_377, _>(
742            rand_poly::<Bls12_377>,
743            rand_point::<Bls12_377>,
744            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
745        )
746        .expect("test failed for bls12-377");
747        linear_poly_degree_bound_test::<_, _, PC_Bls12_381, _>(
748            rand_poly::<Bls12_381>,
749            rand_point::<Bls12_381>,
750            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
751        )
752        .expect("test failed for bls12-381");
753    }
754
755    #[test]
756    fn single_poly_degree_bound_test() {
757        use crate::tests::*;
758        single_poly_degree_bound_test::<_, _, PC_Bls12_377, _>(
759            rand_poly::<Bls12_377>,
760            rand_point::<Bls12_377>,
761            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
762        )
763        .expect("test failed for bls12-377");
764        single_poly_degree_bound_test::<_, _, PC_Bls12_381, _>(
765            rand_poly::<Bls12_381>,
766            rand_point::<Bls12_381>,
767            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
768        )
769        .expect("test failed for bls12-381");
770    }
771
772    #[test]
773    fn single_poly_degree_bound_multiple_queries_test() {
774        use crate::tests::*;
775        single_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_377, _>(
776            rand_poly::<Bls12_377>,
777            rand_point::<Bls12_377>,
778            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
779        )
780        .expect("test failed for bls12-377");
781        single_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_381, _>(
782            rand_poly::<Bls12_381>,
783            rand_point::<Bls12_381>,
784            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
785        )
786        .expect("test failed for bls12-381");
787    }
788
789    #[test]
790    fn two_polys_degree_bound_single_query_test() {
791        use crate::tests::*;
792        two_polys_degree_bound_single_query_test::<_, _, PC_Bls12_377, _>(
793            rand_poly::<Bls12_377>,
794            rand_point::<Bls12_377>,
795            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
796        )
797        .expect("test failed for bls12-377");
798        two_polys_degree_bound_single_query_test::<_, _, PC_Bls12_381, _>(
799            rand_poly::<Bls12_381>,
800            rand_point::<Bls12_381>,
801            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
802        )
803        .expect("test failed for bls12-381");
804    }
805
806    #[test]
807    fn full_end_to_end_test() {
808        use crate::tests::*;
809        full_end_to_end_test::<_, _, PC_Bls12_377, _>(
810            None,
811            rand_poly::<Bls12_377>,
812            rand_point::<Bls12_377>,
813            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
814        )
815        .expect("test failed for bls12-377");
816        println!("Finished bls12-377");
817        full_end_to_end_test::<_, _, PC_Bls12_381, _>(
818            None,
819            rand_poly::<Bls12_381>,
820            rand_point::<Bls12_381>,
821            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
822        )
823        .expect("test failed for bls12-381");
824        println!("Finished bls12-381");
825    }
826
827    #[test]
828    fn single_equation_test() {
829        use crate::tests::*;
830        single_equation_test::<_, _, PC_Bls12_377, _>(
831            None,
832            rand_poly::<Bls12_377>,
833            rand_point::<Bls12_377>,
834            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
835        )
836        .expect("test failed for bls12-377");
837        println!("Finished bls12-377");
838        single_equation_test::<_, _, PC_Bls12_381, _>(
839            None,
840            rand_poly::<Bls12_381>,
841            rand_point::<Bls12_381>,
842            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
843        )
844        .expect("test failed for bls12-381");
845        println!("Finished bls12-381");
846    }
847
848    #[test]
849    fn two_equation_test() {
850        use crate::tests::*;
851        two_equation_test::<_, _, PC_Bls12_377, _>(
852            None,
853            rand_poly::<Bls12_377>,
854            rand_point::<Bls12_377>,
855            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
856        )
857        .expect("test failed for bls12-377");
858        println!("Finished bls12-377");
859        two_equation_test::<_, _, PC_Bls12_381, _>(
860            None,
861            rand_poly::<Bls12_381>,
862            rand_point::<Bls12_381>,
863            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
864        )
865        .expect("test failed for bls12-381");
866        println!("Finished bls12-381");
867    }
868
869    #[test]
870    fn two_equation_degree_bound_test() {
871        use crate::tests::*;
872        two_equation_degree_bound_test::<_, _, PC_Bls12_377, _>(
873            rand_poly::<Bls12_377>,
874            rand_point::<Bls12_377>,
875            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
876        )
877        .expect("test failed for bls12-377");
878        println!("Finished bls12-377");
879        two_equation_degree_bound_test::<_, _, PC_Bls12_381, _>(
880            rand_poly::<Bls12_381>,
881            rand_point::<Bls12_381>,
882            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
883        )
884        .expect("test failed for bls12-381");
885        println!("Finished bls12-381");
886    }
887
888    #[test]
889    fn full_end_to_end_equation_test() {
890        use crate::tests::*;
891        full_end_to_end_equation_test::<_, _, PC_Bls12_377, _>(
892            None,
893            rand_poly::<Bls12_377>,
894            rand_point::<Bls12_377>,
895            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
896        )
897        .expect("test failed for bls12-377");
898        println!("Finished bls12-377");
899        full_end_to_end_equation_test::<_, _, PC_Bls12_381, _>(
900            None,
901            rand_poly::<Bls12_381>,
902            rand_point::<Bls12_381>,
903            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
904        )
905        .expect("test failed for bls12-381");
906        println!("Finished bls12-381");
907    }
908
909    #[test]
910    #[should_panic]
911    fn bad_degree_bound_test() {
912        use crate::tests::*;
913        bad_degree_bound_test::<_, _, PC_Bls12_377, _>(
914            rand_poly::<Bls12_377>,
915            rand_point::<Bls12_377>,
916            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
917        )
918        .expect("test failed for bls12-377");
919        println!("Finished bls12-377");
920        bad_degree_bound_test::<_, _, PC_Bls12_381, _>(
921            rand_poly::<Bls12_381>,
922            rand_point::<Bls12_381>,
923            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
924        )
925        .expect("test failed for bls12-381");
926        println!("Finished bls12-381");
927    }
928}