ark_poly_commit/marlin/marlin_pst13_pc/
mod.rs

1use crate::{
2    kzg10,
3    marlin::{marlin_pc, Marlin},
4    BatchLCProof, Error, Evaluations, LabeledCommitment, LabeledPolynomial, LinearCombination,
5    PCCommitmentState, PCUniversalParams, PolynomialCommitment, QuerySet, CHALLENGE_SIZE,
6};
7use ark_crypto_primitives::sponge::CryptographicSponge;
8use ark_ec::{
9    pairing::Pairing,
10    scalar_mul::{BatchMulPreprocessing, ScalarMul},
11    AffineRepr, CurveGroup, VariableBaseMSM,
12};
13use ark_ff::{One, PrimeField, UniformRand, Zero};
14use ark_poly::{multivariate::Term, DenseMVPolynomial};
15use ark_std::{marker::PhantomData, ops::Index, ops::Mul, rand::RngCore};
16#[cfg(not(feature = "std"))]
17use ark_std::{string::ToString, vec::Vec};
18#[cfg(feature = "parallel")]
19use rayon::prelude::*;
20
21mod data_structures;
22pub use data_structures::*;
23
24mod combinations;
25use combinations::*;
26
27/// Multivariate polynomial commitment based on the construction in [[PST13]][pst]
28/// with batching and (optional) hiding property inspired by the univariate scheme
29/// in [[CHMMVW20, "Marlin"]][marlin]
30///
31/// [pst]: https://eprint.iacr.org/2011/587
32/// [marlin]: https://eprint.iacr.org/2019/1047
33pub struct MarlinPST13<E: Pairing, P: DenseMVPolynomial<E::ScalarField>> {
34    _engine: PhantomData<E>,
35    _poly: PhantomData<P>,
36}
37
38impl<E: Pairing, P: DenseMVPolynomial<E::ScalarField>> MarlinPST13<E, P> {
39    /// Given some point `z`, compute the quotients `w_i(X)` s.t
40    ///
41    /// `p(X) - p(z) = (X_1-z_1)*w_1(X) + (X_2-z_2)*w_2(X) + ... + (X_l-z_l)*w_l(X)`
42    ///
43    /// These quotients can always be found with no remainder.
44    fn divide_at_point(p: &P, point: &P::Point) -> Vec<P>
45    where
46        P::Point: Index<usize, Output = E::ScalarField>,
47    {
48        let num_vars = p.num_vars();
49        if p.is_zero() {
50            return vec![P::zero(); num_vars];
51        }
52        let mut quotients = Vec::with_capacity(num_vars);
53        // `cur` represents the current dividend
54        let mut cur = p.clone();
55        // Divide `cur` by `X_i - z_i`
56        for i in 0..num_vars {
57            let mut quotient_terms = Vec::new();
58            let mut remainder_terms = Vec::new();
59            for (mut coeff, term) in cur.terms() {
60                // Since the final remainder is guaranteed to be 0, all the constant terms
61                // cancel out so we don't need to keep track of them
62                if term.is_constant() {
63                    continue;
64                }
65                // If the current term contains `X_i` then divide appropiately,
66                // otherwise add it to the remainder
67                let mut term_vec = (&*term).to_vec();
68                match term_vec.binary_search_by(|(var, _)| var.cmp(&i)) {
69                    Ok(idx) => {
70                        // Repeatedly divide the term by `X_i - z_i` until the remainder
71                        // doesn't contain any `X_i`s
72                        while term_vec[idx].1 > 1 {
73                            // First divide by `X_i` and add the term to the quotient
74                            term_vec[idx] = (i, term_vec[idx].1 - 1);
75                            quotient_terms.push((coeff, P::Term::new(term_vec.clone())));
76                            // Then compute the remainder term in-place
77                            coeff *= &point[i];
78                        }
79                        // Since `X_i` is power 1, we can remove it entirely
80                        term_vec.remove(idx);
81                        quotient_terms.push((coeff, P::Term::new(term_vec.clone())));
82                        remainder_terms.push((point[i] * &coeff, P::Term::new(term_vec)));
83                    }
84                    Err(_) => remainder_terms.push((coeff, term.clone())),
85                }
86            }
87            quotients.push(P::from_coefficients_vec(num_vars, quotient_terms));
88            // Set the current dividend to be the remainder of this division
89            cur = P::from_coefficients_vec(num_vars, remainder_terms);
90        }
91        quotients
92    }
93
94    /// Check that the powers support the hiding bound
95    fn check_hiding_bound(hiding_poly_degree: usize, num_powers: usize) -> Result<(), Error> {
96        if hiding_poly_degree == 0 {
97            Err(Error::HidingBoundIsZero)
98        } else if hiding_poly_degree >= num_powers {
99            // The above check uses `>=` because committing to a hiding poly with
100            // degree `hiding_poly_degree` requires `hiding_poly_degree + 1`
101            // powers.
102            Err(Error::HidingBoundToolarge {
103                hiding_poly_degree,
104                num_powers,
105            })
106        } else {
107            Ok(())
108        }
109    }
110
111    /// Check that a given polynomial is supported by parameters
112    fn check_degrees_and_bounds<'a>(
113        supported_degree: usize,
114        p: &'a LabeledPolynomial<E::ScalarField, P>,
115    ) -> Result<(), Error>
116    where
117        P: 'a,
118    {
119        if p.degree() > supported_degree {
120            return Err(Error::PolynomialDegreeTooLarge {
121                poly_degree: p.degree(),
122                supported_degree,
123                label: p.label().to_string(),
124            });
125        } else {
126            Ok(())
127        }
128    }
129
130    /// Convert polynomial coefficients to `BigInt`
131    fn convert_to_bigints(p: &P) -> Vec<<E::ScalarField as PrimeField>::BigInt> {
132        let plain_coeffs = ark_std::cfg_into_iter!(p.terms())
133            .map(|(coeff, _)| coeff.into_bigint())
134            .collect();
135        plain_coeffs
136    }
137}
138
139impl<E, P> PolynomialCommitment<E::ScalarField, P> for MarlinPST13<E, P>
140where
141    E: Pairing,
142    P: DenseMVPolynomial<E::ScalarField> + Sync,
143    P::Point: Index<usize, Output = E::ScalarField>,
144{
145    type UniversalParams = UniversalParams<E, P>;
146    type CommitterKey = CommitterKey<E, P>;
147    type VerifierKey = VerifierKey<E>;
148    type Commitment = marlin_pc::Commitment<E>;
149    type CommitmentState = Randomness<E, P>;
150    type Proof = Proof<E>;
151    type BatchProof = Vec<Self::Proof>;
152    type Error = Error;
153
154    /// Constructs public parameters when given as input the maximum degree `max_degree`
155    /// and number of variables `num_vars` for the polynomial commitment scheme.
156    fn setup<R: RngCore>(
157        max_degree: usize,
158        num_vars: Option<usize>,
159        rng: &mut R,
160    ) -> Result<UniversalParams<E, P>, Error> {
161        let num_vars = num_vars.ok_or(Error::InvalidNumberOfVariables)?;
162        if num_vars < 1 {
163            return Err(Error::InvalidNumberOfVariables);
164        }
165        if max_degree < 1 {
166            return Err(Error::DegreeIsZero);
167        }
168        let setup_time = start_timer!(|| format!(
169            "MarlinPST13::Setup with {} variables and max degree {}",
170            num_vars, max_degree
171        ));
172        // Trapdoor evaluation points
173        let mut betas = Vec::with_capacity(num_vars);
174        for _ in 0..num_vars {
175            betas.push(E::ScalarField::rand(rng));
176        }
177        // Generators
178        let g = E::G1::rand(rng);
179        let gamma_g = E::G1::rand(rng);
180        let h = E::G2::rand(rng);
181
182        // A list of all variable numbers of multiplicity `max_degree`
183        let variable_set: Vec<_> = (0..num_vars)
184            .flat_map(|var| vec![var; max_degree])
185            .collect();
186        // Generate all possible monomials with `1 <= degree <= max_degree`
187        let (powers_of_beta, mut powers_of_beta_terms): (Vec<_>, Vec<_>) = (1..=max_degree)
188            .flat_map(|degree| {
189                // Sample all combinations of `degree` variables from `variable_set`
190                let terms: Vec<Vec<usize>> = if variable_set.len() == degree {
191                    vec![variable_set.clone()]
192                } else {
193                    Combinations::new(variable_set.clone(), degree).collect()
194                };
195                // For each multiset in `terms` evaluate the corresponding monomial at the
196                // trapdoor and generate a `P::Term` object to index it
197                ark_std::cfg_into_iter!(terms)
198                    .map(|term| {
199                        let value: E::ScalarField = term.iter().map(|e| betas[*e]).product();
200                        let term = (0..num_vars)
201                            .map(|var| (var, term.iter().filter(|e| **e == var).count()))
202                            .collect();
203                        (value, P::Term::new(term))
204                    })
205                    .collect::<Vec<_>>()
206            })
207            .unzip();
208
209        let g_time = start_timer!(|| "Generating powers of G");
210        let mut powers_of_g = g.batch_mul(&powers_of_beta);
211        powers_of_g.push(g.into_affine());
212        powers_of_beta_terms.push(P::Term::new(vec![]));
213        end_timer!(g_time);
214
215        let gamma_g_time = start_timer!(|| "Generating powers of gamma * G");
216        // Each element `i` of `powers_of_gamma_g` is a vector of length `max_degree+1`
217        // containing `betas[i]^j \gamma G` for `j` from 1 to `max_degree+1` to support
218        // up to `max_degree` queries
219        let mut powers_of_gamma_g = vec![Vec::new(); num_vars];
220        let gamma_g_table = BatchMulPreprocessing::new(gamma_g, max_degree + 1);
221
222        ark_std::cfg_iter_mut!(powers_of_gamma_g)
223            .enumerate()
224            .for_each(|(i, v)| {
225                let mut powers_of_beta = Vec::with_capacity(max_degree + 1);
226                let mut cur = E::ScalarField::one();
227                for _ in 0..=max_degree {
228                    cur *= &betas[i];
229                    powers_of_beta.push(cur);
230                }
231                *v = gamma_g_table.batch_mul(&powers_of_beta);
232            });
233        end_timer!(gamma_g_time);
234
235        let gamma_g = gamma_g.into_affine();
236        let beta_h: Vec<_> = betas.iter().map(|b| h.mul(b).into_affine()).collect();
237        let h = h.into_affine();
238        let prepared_h = h.into();
239        let prepared_beta_h = beta_h.iter().map(|bh| (*bh).into()).collect();
240
241        // Convert `powers_of_g` to a BTreeMap indexed by `powers_of_beta_terms`
242        let powers_of_g = powers_of_beta_terms
243            .into_iter()
244            .zip(powers_of_g.into_iter())
245            .collect();
246
247        let pp = UniversalParams {
248            num_vars,
249            max_degree,
250            powers_of_g,
251            gamma_g,
252            powers_of_gamma_g,
253            h,
254            beta_h,
255            prepared_h,
256            prepared_beta_h,
257        };
258        end_timer!(setup_time);
259        Ok(pp)
260    }
261
262    /// Specializes the public parameters for polynomials up to the given `supported_degree`
263    ///
264    /// TODO: Add the ability to trim the number of variables
265    /// TODO: Update for support_hiding_bound
266    fn trim(
267        pp: &Self::UniversalParams,
268        supported_degree: usize,
269        _supported_hiding_bound: usize,
270        _enforced_degree_bounds: Option<&[usize]>,
271    ) -> Result<(Self::CommitterKey, Self::VerifierKey), Self::Error> {
272        let max_degree = pp.max_degree();
273        if supported_degree > max_degree {
274            return Err(Error::TrimmingDegreeTooLarge);
275        }
276
277        let ck_time = start_timer!(|| format!(
278            "Constructing CommitterKey of size {} for unshifted polys",
279            supported_degree
280        ));
281        // We want to support making up to supported_degree queries to committed
282        // polynomials.
283        let powers_of_g = pp
284            .powers_of_g
285            .iter()
286            .filter(|(k, _)| k.degree() <= supported_degree)
287            .map(|(k, v)| (k.clone(), v.clone()))
288            .collect();
289        let powers_of_gamma_g = pp
290            .powers_of_gamma_g
291            .iter()
292            .map(|e| e[..=supported_degree].to_vec())
293            .collect();
294        end_timer!(ck_time);
295
296        let ck = CommitterKey {
297            powers_of_g,
298            gamma_g: pp.gamma_g,
299            powers_of_gamma_g,
300            num_vars: pp.num_vars,
301            supported_degree,
302            max_degree,
303        };
304
305        let vk = VerifierKey {
306            g: pp.powers_of_g[&P::Term::new(vec![])],
307            gamma_g: pp.gamma_g,
308            h: pp.h,
309            beta_h: pp.beta_h.clone(),
310            prepared_h: pp.prepared_h.clone(),
311            prepared_beta_h: pp.prepared_beta_h.clone(),
312            num_vars: pp.num_vars,
313            supported_degree,
314            max_degree,
315        };
316        Ok((ck, vk))
317    }
318
319    /// Outputs a commitments to `polynomials`.
320    fn commit<'a>(
321        ck: &Self::CommitterKey,
322        polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::ScalarField, P>>,
323        rng: Option<&mut dyn RngCore>,
324    ) -> Result<
325        (
326            Vec<LabeledCommitment<Self::Commitment>>,
327            Vec<Self::CommitmentState>,
328        ),
329        Self::Error,
330    >
331    where
332        P: 'a,
333    {
334        let rng = &mut crate::optional_rng::OptionalRng(rng);
335        let commit_time = start_timer!(|| "Committing to polynomials");
336        let mut commitments = Vec::new();
337        let mut randomness = Vec::new();
338        for p in polynomials {
339            let label = p.label();
340            let hiding_bound = p.hiding_bound();
341            let polynomial: &P = p.polynomial();
342            Self::check_degrees_and_bounds(ck.supported_degree, &p)?;
343
344            let commit_time = start_timer!(|| {
345                format!(
346                    "Polynomial {} with degree {} and hiding bound {:?}",
347                    label,
348                    polynomial.degree(),
349                    hiding_bound,
350                )
351            });
352            // Get the powers of `G` corresponding to the terms of `polynomial`
353            let powers_of_g = ark_std::cfg_iter!(polynomial.terms())
354                .map(|(_, term)| *ck.powers_of_g.get(term).unwrap())
355                .collect::<Vec<_>>();
356            // Convert coefficients of `polynomial` to BigInts
357            let to_bigint_time = start_timer!(|| "Converting polynomial coeffs to bigints");
358            let plain_ints = Self::convert_to_bigints(&polynomial);
359            end_timer!(to_bigint_time);
360
361            let msm_time = start_timer!(|| "MSM to compute commitment to plaintext poly");
362            let mut commitment = <E::G1 as VariableBaseMSM>::msm_bigint(&powers_of_g, &plain_ints);
363            end_timer!(msm_time);
364
365            // Sample random polynomial
366            let mut rand = Randomness::<E, P>::empty();
367            if let Some(hiding_degree) = hiding_bound {
368                let sample_random_poly_time = start_timer!(|| format!(
369                    "Sampling a random polynomial of degree {}",
370                    hiding_degree
371                ));
372                rand = Randomness::rand(hiding_degree, false, Some(ck.num_vars), rng);
373                Self::check_hiding_bound(hiding_degree, ck.supported_degree + 1)?;
374                end_timer!(sample_random_poly_time);
375            }
376
377            // Get the powers of `\gamma G` corresponding to the terms of `rand`
378            let powers_of_gamma_g = rand
379                .blinding_polynomial
380                .terms()
381                .iter()
382                .map(|(_, term)| {
383                    // Implicit Assumption: Each monomial in `rand` is univariate
384                    let vars = term.vars();
385                    match term.is_constant() {
386                        true => ck.gamma_g,
387                        false => ck.powers_of_gamma_g[vars[0]][term.degree() - 1],
388                    }
389                })
390                .collect::<Vec<_>>();
391            // Convert coefficients of `rand` to BigInt
392            let to_bigint_time = start_timer!(|| "Converting polynomial coeffs to bigints");
393            let random_ints = Self::convert_to_bigints(&rand.blinding_polynomial);
394            end_timer!(to_bigint_time);
395
396            let msm_time = start_timer!(|| "MSM to compute commitment to random poly");
397            let random_commitment =
398                <E::G1 as VariableBaseMSM>::msm_bigint(&powers_of_gamma_g, &random_ints)
399                    .into_affine();
400            end_timer!(msm_time);
401
402            // Mask commitment with random poly
403            commitment += &random_commitment;
404
405            let comm = Self::Commitment {
406                comm: kzg10::Commitment(commitment.into()),
407                shifted_comm: None,
408            };
409
410            commitments.push(LabeledCommitment::new(label.to_string(), comm, None));
411            randomness.push(rand);
412            end_timer!(commit_time);
413        }
414        end_timer!(commit_time);
415        Ok((commitments, randomness))
416    }
417
418    /// On input a polynomial `p` and a point `point`, outputs a proof for the same.
419    fn open<'a>(
420        ck: &Self::CommitterKey,
421        labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::ScalarField, P>>,
422        _commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
423        point: &P::Point,
424        sponge: &mut impl CryptographicSponge,
425        states: impl IntoIterator<Item = &'a Self::CommitmentState>,
426        _rng: Option<&mut dyn RngCore>,
427    ) -> Result<Self::Proof, Self::Error>
428    where
429        P: 'a,
430        Self::CommitmentState: 'a,
431        Self::Commitment: 'a,
432    {
433        // Compute random linear combinations of committed polynomials and randomness
434        let mut p = P::zero();
435        let mut r = Randomness::empty();
436        for (polynomial, state) in labeled_polynomials.into_iter().zip(states) {
437            Self::check_degrees_and_bounds(ck.supported_degree, &polynomial)?;
438
439            // compute challenge^j and challenge^{j+1}.
440            let challenge_j = sponge.squeeze_field_elements_with_sizes(&[CHALLENGE_SIZE])[0];
441
442            p += (challenge_j, polynomial.polynomial());
443            r += (challenge_j, state);
444        }
445
446        let open_time = start_timer!(|| format!("Opening polynomial of degree {}", p.degree()));
447        let witness_time = start_timer!(|| "Computing witness polynomials");
448        let witnesses = Self::divide_at_point(&p, point);
449        let hiding_witnesses = if r.is_hiding() {
450            Some(Self::divide_at_point(&r.blinding_polynomial, point))
451        } else {
452            None
453        };
454        end_timer!(witness_time);
455
456        let witness_comm_time = start_timer!(|| "Computing commitment to witness polynomials");
457        let mut w = witnesses
458            .iter()
459            .map(|w| {
460                // Get the powers of `G` corresponding to the witness poly
461                let powers_of_g = ark_std::cfg_iter!(w.terms())
462                    .map(|(_, term)| *ck.powers_of_g.get(term).unwrap())
463                    .collect::<Vec<_>>();
464                // Convert coefficients to BigInt
465                let witness_ints = Self::convert_to_bigints(&w);
466                // Compute MSM
467                <E::G1 as VariableBaseMSM>::msm_bigint(&powers_of_g, &witness_ints)
468            })
469            .collect::<Vec<_>>();
470        end_timer!(witness_comm_time);
471
472        // If the evaluation should be hiding, compute the MSM for `hiding_witnesses` and add
473        // to the `w`. Additionally, compute the evaluation of `r` at `point`.
474        let random_v = if let Some(hiding_witnesses) = hiding_witnesses {
475            let witness_comm_time =
476                start_timer!(|| "Computing commitment to hiding witness polynomials");
477            ark_std::cfg_iter_mut!(w)
478                .enumerate()
479                .for_each(|(i, witness)| {
480                    let hiding_witness = &hiding_witnesses[i];
481                    // Get the powers of `\gamma G` corresponding to the terms of `hiding_witness`
482                    let powers_of_gamma_g = hiding_witness
483                        .terms()
484                        .iter()
485                        .map(|(_, term)| {
486                            // Implicit Assumption: Each monomial in `hiding_witness` is univariate
487                            let vars = term.vars();
488                            match term.is_constant() {
489                                true => ck.gamma_g,
490                                false => ck.powers_of_gamma_g[vars[0]][term.degree() - 1],
491                            }
492                        })
493                        .collect::<Vec<_>>();
494                    // Convert coefficients to BigInt
495                    let hiding_witness_ints = Self::convert_to_bigints(hiding_witness);
496                    // Compute MSM and add result to witness
497                    *witness += &<E::G1 as VariableBaseMSM>::msm_bigint(
498                        &powers_of_gamma_g,
499                        &hiding_witness_ints,
500                    );
501                });
502            end_timer!(witness_comm_time);
503            Some(r.blinding_polynomial.evaluate(point))
504        } else {
505            None
506        };
507        end_timer!(open_time);
508        Ok(Proof {
509            w: w.into_iter().map(|w| w.into_affine()).collect(),
510            random_v,
511        })
512    }
513
514    /// Verifies that `value` is the evaluation at `x` of the polynomial
515    /// committed inside `comm`.
516    fn check<'a>(
517        vk: &Self::VerifierKey,
518        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
519        point: &'a P::Point,
520        values: impl IntoIterator<Item = E::ScalarField>,
521        proof: &Self::Proof,
522        sponge: &mut impl CryptographicSponge,
523        _rng: Option<&mut dyn RngCore>,
524    ) -> Result<bool, Self::Error>
525    where
526        Self::Commitment: 'a,
527    {
528        let check_time = start_timer!(|| "Checking evaluations");
529        // Accumulate commitments and values
530        let (combined_comm, combined_value) =
531            Marlin::<E, P, Self>::accumulate_commitments_and_values(
532                commitments,
533                values,
534                sponge,
535                None,
536            )?;
537        // Compute both sides of the pairing equation
538        let mut inner = combined_comm.into().into_group() - &vk.g.mul(combined_value);
539        if let Some(random_v) = proof.random_v {
540            inner -= &vk.gamma_g.mul(random_v);
541        }
542        let lhs = E::pairing(inner, vk.h);
543
544        // Create a list of elements corresponding to each pairing in the product on the rhs
545        let (rhs_product_g1, rhs_product_g2): (Vec<E::G1Prepared>, Vec<E::G2Prepared>) =
546            ark_std::cfg_iter!(proof.w)
547                .enumerate()
548                .map(|(j, w_j)| {
549                    let beta_minus_z: E::G2Affine =
550                        (vk.beta_h[j].into_group() - &vk.h.mul(point[j])).into();
551                    ((*w_j).into(), beta_minus_z.into())
552                })
553                .unzip();
554        let rhs = E::multi_pairing(rhs_product_g1, rhs_product_g2);
555        end_timer!(check_time);
556
557        Ok(lhs == rhs)
558    }
559
560    fn batch_check<'a, R: RngCore>(
561        vk: &Self::VerifierKey,
562        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
563        query_set: &QuerySet<P::Point>,
564        values: &Evaluations<P::Point, E::ScalarField>,
565        proof: &Self::BatchProof,
566        sponge: &mut impl CryptographicSponge,
567        rng: &mut R,
568    ) -> Result<bool, Self::Error>
569    where
570        Self::Commitment: 'a,
571    {
572        let (combined_comms, combined_queries, combined_evals) =
573            Marlin::<E, P, Self>::combine_and_normalize(
574                commitments,
575                query_set,
576                values,
577                sponge,
578                None,
579            )?;
580        let check_time =
581            start_timer!(|| format!("Checking {} evaluation proofs", combined_comms.len()));
582        let g = vk.g.into_group();
583        let gamma_g = vk.gamma_g.into_group();
584        let mut total_c = <E::G1>::zero();
585        let mut total_w = vec![<E::G1>::zero(); vk.num_vars];
586        let combination_time = start_timer!(|| "Combining commitments and proofs");
587        let mut randomizer = E::ScalarField::one();
588        // Instead of multiplying g and gamma_g in each turn, we simply accumulate
589        // their coefficients and perform a final multiplication at the end.
590        let mut g_multiplier = E::ScalarField::zero();
591        let mut gamma_g_multiplier = E::ScalarField::zero();
592        for (((c, z), v), proof) in combined_comms
593            .iter()
594            .zip(combined_queries)
595            .zip(combined_evals)
596            .zip(proof)
597        {
598            let w = &proof.w;
599            let mut temp: E::G1 = ark_std::cfg_iter!(w)
600                .enumerate()
601                .map(|(j, w_j)| w_j.mul(z[j]))
602                .sum();
603            temp += &c.0;
604            let c = temp;
605            g_multiplier += &(randomizer * &v);
606            if let Some(random_v) = proof.random_v {
607                gamma_g_multiplier += &(randomizer * &random_v);
608            }
609            total_c += &c.mul(&randomizer);
610            ark_std::cfg_iter_mut!(total_w)
611                .enumerate()
612                .for_each(|(i, w_i)| *w_i += &w[i].mul(randomizer));
613            // We don't need to sample randomizers from the full field,
614            // only from 128-bit strings.
615            randomizer = u128::rand(rng).into();
616        }
617        total_c -= &g.mul(&g_multiplier);
618        total_c -= &gamma_g.mul(&gamma_g_multiplier);
619        end_timer!(combination_time);
620
621        let to_affine_time = start_timer!(|| "Converting results to affine for pairing");
622        let (mut p1, mut p2): (Vec<E::G1Prepared>, Vec<E::G2Prepared>) = total_w
623            .into_iter()
624            .enumerate()
625            .map(|(j, w_j)| ((-w_j).into_affine().into(), vk.prepared_beta_h[j].clone()))
626            .unzip();
627        p1.push(total_c.into_affine().into());
628        p2.push(vk.prepared_h.clone());
629        end_timer!(to_affine_time);
630
631        let pairing_time = start_timer!(|| "Performing product of pairings");
632        let result = E::multi_pairing(p1, p2).0.is_one();
633        end_timer!(pairing_time);
634        end_timer!(check_time);
635        Ok(result)
636    }
637
638    fn open_combinations<'a>(
639        ck: &Self::CommitterKey,
640        linear_combinations: impl IntoIterator<Item = &'a LinearCombination<E::ScalarField>>,
641        polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::ScalarField, P>>,
642        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
643        query_set: &QuerySet<P::Point>,
644        sponge: &mut impl CryptographicSponge,
645        states: impl IntoIterator<Item = &'a Self::CommitmentState>,
646        rng: Option<&mut dyn RngCore>,
647    ) -> Result<BatchLCProof<E::ScalarField, Self::BatchProof>, Self::Error>
648    where
649        P: 'a,
650        Self::CommitmentState: 'a,
651        Self::Commitment: 'a,
652    {
653        Marlin::<E, P, Self>::open_combinations(
654            ck,
655            linear_combinations,
656            polynomials,
657            commitments,
658            query_set,
659            sponge,
660            states,
661            rng,
662        )
663    }
664
665    /// Checks that `values` are the true evaluations at `query_set` of the polynomials
666    /// committed in `labeled_commitments`.
667    fn check_combinations<'a, R: RngCore>(
668        vk: &Self::VerifierKey,
669        linear_combinations: impl IntoIterator<Item = &'a LinearCombination<E::ScalarField>>,
670        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
671        eqn_query_set: &QuerySet<P::Point>,
672        eqn_evaluations: &Evaluations<P::Point, E::ScalarField>,
673        proof: &BatchLCProof<E::ScalarField, Self::BatchProof>,
674        sponge: &mut impl CryptographicSponge,
675        rng: &mut R,
676    ) -> Result<bool, Self::Error>
677    where
678        Self::Commitment: 'a,
679    {
680        Marlin::<E, P, Self>::check_combinations(
681            vk,
682            linear_combinations,
683            commitments,
684            eqn_query_set,
685            eqn_evaluations,
686            proof,
687            sponge,
688            rng,
689        )
690    }
691}
692
693#[cfg(test)]
694mod tests {
695    #![allow(non_camel_case_types)]
696    use super::MarlinPST13;
697    use ark_bls12_377::Bls12_377;
698    use ark_bls12_381::Bls12_381;
699    use ark_ec::pairing::Pairing;
700    use ark_ff::UniformRand;
701    use ark_poly::{
702        multivariate::{SparsePolynomial as SparsePoly, SparseTerm},
703        DenseMVPolynomial,
704    };
705    #[cfg(not(feature = "std"))]
706    use ark_std::vec::Vec;
707    use rand_chacha::ChaCha20Rng;
708
709    type MVPoly_381 = SparsePoly<<Bls12_381 as Pairing>::ScalarField, SparseTerm>;
710    type MVPoly_377 = SparsePoly<<Bls12_377 as Pairing>::ScalarField, SparseTerm>;
711
712    type PC<E, P> = MarlinPST13<E, P>;
713
714    type PC_Bls12_381 = PC<Bls12_381, MVPoly_381>;
715    type PC_Bls12_377 = PC<Bls12_377, MVPoly_377>;
716
717    fn rand_poly<E: Pairing>(
718        degree: usize,
719        num_vars: Option<usize>,
720        rng: &mut ChaCha20Rng,
721    ) -> SparsePoly<E::ScalarField, SparseTerm> {
722        SparsePoly::<E::ScalarField, SparseTerm>::rand(degree, num_vars.unwrap(), rng)
723    }
724
725    fn rand_point<E: Pairing>(
726        num_vars: Option<usize>,
727        rng: &mut ChaCha20Rng,
728    ) -> Vec<E::ScalarField> {
729        let num_vars = num_vars.unwrap();
730        let mut point = Vec::with_capacity(num_vars);
731        for _ in 0..num_vars {
732            point.push(E::ScalarField::rand(rng));
733        }
734        point
735    }
736
737    #[test]
738    fn single_poly_test() {
739        use crate::tests::*;
740        let num_vars = Some(10);
741        single_poly_test::<_, _, PC_Bls12_377, _>(
742            num_vars,
743            rand_poly::<Bls12_377>,
744            rand_point::<Bls12_377>,
745            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
746        )
747        .expect("test failed for bls12-377");
748        single_poly_test::<_, _, PC_Bls12_381, _>(
749            num_vars,
750            rand_poly::<Bls12_381>,
751            rand_point::<Bls12_381>,
752            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
753        )
754        .expect("test failed for bls12-381");
755    }
756
757    #[test]
758    fn full_end_to_end_test() {
759        use crate::tests::*;
760        let num_vars = Some(10);
761        full_end_to_end_test::<_, _, PC_Bls12_377, _>(
762            num_vars,
763            rand_poly::<Bls12_377>,
764            rand_point::<Bls12_377>,
765            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
766        )
767        .expect("test failed for bls12-377");
768        println!("Finished bls12-377");
769        full_end_to_end_test::<_, _, PC_Bls12_381, _>(
770            num_vars,
771            rand_poly::<Bls12_381>,
772            rand_point::<Bls12_381>,
773            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
774        )
775        .expect("test failed for bls12-381");
776        println!("Finished bls12-381");
777    }
778
779    #[test]
780    fn single_equation_test() {
781        use crate::tests::*;
782        let num_vars = Some(10);
783        single_equation_test::<_, _, PC_Bls12_377, _>(
784            num_vars,
785            rand_poly::<Bls12_377>,
786            rand_point::<Bls12_377>,
787            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
788        )
789        .expect("test failed for bls12-377");
790        println!("Finished bls12-377");
791        single_equation_test::<_, _, PC_Bls12_381, _>(
792            num_vars,
793            rand_poly::<Bls12_381>,
794            rand_point::<Bls12_381>,
795            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
796        )
797        .expect("test failed for bls12-381");
798        println!("Finished bls12-381");
799    }
800
801    #[test]
802    fn two_equation_test() {
803        use crate::tests::*;
804        let num_vars = Some(10);
805        two_equation_test::<_, _, PC_Bls12_377, _>(
806            num_vars,
807            rand_poly::<Bls12_377>,
808            rand_point::<Bls12_377>,
809            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
810        )
811        .expect("test failed for bls12-377");
812        println!("Finished bls12-377");
813        two_equation_test::<_, _, PC_Bls12_381, _>(
814            num_vars,
815            rand_poly::<Bls12_381>,
816            rand_point::<Bls12_381>,
817            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
818        )
819        .expect("test failed for bls12-381");
820        println!("Finished bls12-381");
821    }
822
823    #[test]
824    fn full_end_to_end_equation_test() {
825        use crate::tests::*;
826        let num_vars = Some(10);
827        full_end_to_end_equation_test::<_, _, PC_Bls12_377, _>(
828            num_vars,
829            rand_poly::<Bls12_377>,
830            rand_point::<Bls12_377>,
831            poseidon_sponge_for_test::<<Bls12_377 as Pairing>::ScalarField>,
832        )
833        .expect("test failed for bls12-377");
834        println!("Finished bls12-377");
835        full_end_to_end_equation_test::<_, _, PC_Bls12_381, _>(
836            num_vars,
837            rand_poly::<Bls12_381>,
838            rand_point::<Bls12_381>,
839            poseidon_sponge_for_test::<<Bls12_381 as Pairing>::ScalarField>,
840        )
841        .expect("test failed for bls12-381");
842        println!("Finished bls12-381");
843    }
844}