ark_poly_commit/streaming_kzg/
mod.rs

1//! The KZG (or Kate) polynomial commitment, space- and time-efficient.
2//!
3//! # Background
4//! [[KZG](https://www.iacr.org/archive/asiacrypt2010/6477178/6477178.pdf)]
5//! commitments are pretty simple:
6//! - A [`CommitterKey`](self::CommitterKey) is consists of a sequence \\(\vec G \defeq (G, \tau G, \dots, \tau^DG)\\).
7//! - A [`Commitment`](self::EvaluationProof) is a polynomial \\(f(x)\\) is \\(C \defeq \langle \vec f, \vec G \rangle \\).
8//! - An [`EvaluationProof`](self::EvaluationProof)
9//! for the polynomial \\(f\\)
10//! in the evaluation point \\(\alpha\\)
11//! is a commitment to the quotient of \\(f(x)\\) by \\((\tau - \alpha)\\).
12//! The remainder is the evaluation \\(f(\alpha)\\).
13//! When evaluation over points \\((\alpha_0, \dots, \alpha_m)\\),
14//! we can consider at once the quotient of \\(f(x)\\) by \\(Z\\) (the polynomial whose roots are \\(\alpha_i\\)).
15//! The remainder is a polynomial \\(r\\) such that \\(r(\alpha_i) = f(\alpha_i)\\).
16//! We refer to the proof as \\(\pi\\).
17//!
18//! To verify a proof \\(\pi\\) proving that \\(f(\alpha) = \mu\\), one considers the pairing equation:
19//! \\[
20//! e(C, \tau H - \mu H) = e(f - \mu G, H)
21//! \\]
22//! To verify a proof \\(\pi\\) over a set of points \\(f(\alpha_i) = \mu_i\\),
23//! consider the polynomial \\(\nu\\) such that \\(\nu(\alpha_i) = \mu_i \\), and check:
24//! \\[
25//! e(C, Z) = e(f - \nu, H).
26//! \\]
27//!
28//! It is also possible to open multiple polynomials \\(f_0, \dots, f_n\\)
29//!  _on the same set of evaluation points_
30//! by asking the verifier a random challenge \\(\eta\\), and opening instead
31//! \\(\sum_i \eta^i f_i \\).
32//!
33//! _Nota bene:_ despite it is also possible to open multiple polynomials
34//! over different points [[BDFG20](https://eprint.iacr.org/2020/081.pdf)],
35//! however this is not currently supported by our implementation.
36//!
37//!
38//! # Examples
39//!
40//! When creating a new SRS, one must specify a degree bound `max_degree`
41//! for the commitment polynomials, and a degree bound `max_evals` for
42//! the maximum number of opening points.
43//! From the SRS, it is possible to derive the verification key
44//! [`VerifierKey`](self::VerifierKey).
45//!
46//! ```
47//! use ark_poly_commit::streaming_kzg::CommitterKey;
48//! use ark_bls12_381::{Fr, Bls12_381};
49//!
50//! let rng = &mut ark_std::test_rng();
51//! let max_degree = 100;
52//! let max_evals = 10;
53//!
54//! let ck = CommitterKey::<Bls12_381>::new(max_degree, max_evals, rng);
55//! # // XXX. if you change the following lines,
56//! # // please note that documentation below might break.
57//! # let f = vec![Fr::from(1u64), Fr::from(2u64), Fr::from(4u64), Fr::from(8u64)];
58//! # let commitment  = ck.commit(&f);
59//! # let alpha = Fr::from(42u64);
60//! # let (evaluation, proof) = ck.open(&f, &alpha);
61//! # use ark_poly_commit::streaming_kzg::VerifierKey;
62//! # let vk = VerifierKey::from(&ck);
63//! # assert!(vk.verify(&commitment, &alpha, &evaluation, &proof).is_ok())
64//! ```
65//!
66//! Then to commit to a polynomial `f`:
67//! ```ignore
68//! let f = vec![Fr::from(1u64), Fr::from(2u64), Fr::from(4u64), Fr::from(8u64)];
69//! let commitment  = ck.commit(&f);
70//! ```
71//! To prove the evaluation of `f` in a point `alpha`:
72//!
73//! ```ignore
74//! let alpha = Fr::from(42u64);
75//! let (evaluation, proof) = ck.open(&f, &alpha);
76//! ```
77//! To veify that an opening is correct:
78//! ```ignore
79//! use gemini::kzg::VerifierKey;
80//!
81//! let vk = VerifierKey::from(&ck);
82//! assert!(vk.verify(&commitment, &alpha, &evaluation, &proof).is_ok())
83//! ```
84
85use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup, VariableBaseMSM};
86use ark_ff::{Field, One, PrimeField, Zero};
87use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial};
88use ark_serialize::{CanonicalSerialize, Compress};
89#[cfg(not(feature = "std"))]
90use ark_std::vec::Vec;
91use ark_std::{
92    borrow::Borrow,
93    fmt,
94    ops::{Add, Mul},
95};
96
97mod data_structures;
98mod space;
99mod time;
100pub use data_structures::*;
101pub use space::CommitterKeyStream;
102pub use time::CommitterKey;
103
104#[cfg(test)]
105#[allow(missing_docs)]
106pub mod tests;
107
108/// A Kate polynomial commitment over a bilinear group, represented as a single \\(\GG_1\\) element.
109#[derive(Debug, Copy, Clone, PartialEq, Eq)]
110pub struct Commitment<E: Pairing>(pub(crate) E::G1Affine);
111
112impl<E: Pairing> Commitment<E> {
113    /// Return the size of Commitment in bytes.
114    pub fn size_in_bytes(&self) -> usize {
115        E::G1Affine::zero().serialized_size(Compress::Yes)
116    }
117}
118
119#[inline]
120fn msm<E: Pairing>(bases: &[E::G1Affine], scalars: &[E::ScalarField]) -> E::G1Affine {
121    let scalars = scalars.iter().map(|x| x.into_bigint()).collect::<Vec<_>>();
122    let sp = <E::G1 as VariableBaseMSM>::msm_bigint(bases, &scalars);
123    sp.into_affine()
124}
125
126/// Polynomial evaluation proof, represented as a single \\(\GG_1\\) element.
127#[derive(Clone, Debug, PartialEq, Eq)]
128pub struct EvaluationProof<E: Pairing>(pub E::G1Affine);
129
130impl<E: Pairing> Add for EvaluationProof<E> {
131    type Output = Self;
132
133    fn add(self, rhs: Self) -> Self::Output {
134        EvaluationProof((self.0 + rhs.0).into_affine())
135    }
136}
137
138impl<E: Pairing> core::iter::Sum for EvaluationProof<E> {
139    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
140        let zero = EvaluationProof(E::G1Affine::zero());
141        iter.fold(zero, |x, y| x + y)
142    }
143}
144
145/// Error type denoting an incorrect evaluation proof.
146#[derive(Debug, Clone)]
147pub struct VerificationError;
148
149impl fmt::Display for VerificationError {
150    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151        write!(f, "Error in stream.")
152    }
153}
154
155pub(crate) type VerificationResult = Result<(), VerificationError>;
156
157// XXX.  add const generic argument for the size.
158/// The verification key for the polynomial commitment scheme.
159/// It also implements verification functions for the evaluation proof.
160#[derive(Debug, PartialEq, Eq)]
161pub struct VerifierKey<E: Pairing> {
162    /// The generator of  \\(\GG_1\\)
163    powers_of_g: Vec<E::G1Affine>,
164    /// The generator og \\(\GG_2\\), together with its multiplication by the trapdoor.
165    powers_of_g2: Vec<E::G2Affine>,
166}
167
168impl<E: Pairing> VerifierKey<E> {
169    /// The verification procedure for the EvaluationProof with a single polynomial evaluated at a single evaluation point.
170    /// The polynomial are evaluated at the point ``alpha`` and is committed as ``commitment``.
171    /// The evaluation proof can be obtained either in a space-efficient or a time-efficient flavour.
172    pub fn verify(
173        &self,
174        commitment: &Commitment<E>,
175        &alpha: &E::ScalarField,
176        evaluation: &E::ScalarField,
177        proof: &EvaluationProof<E>,
178    ) -> VerificationResult {
179        let scalars = [(-alpha).into_bigint(), E::ScalarField::one().into_bigint()];
180        let ep = <E::G2 as VariableBaseMSM>::msm_bigint(&self.powers_of_g2, &scalars);
181        let lhs = commitment.0.into_group() - self.powers_of_g[0].mul(evaluation);
182        let g2 = self.powers_of_g2[0];
183
184        if E::pairing(lhs, g2) == E::pairing(proof.0, ep) {
185            Ok(())
186        } else {
187            Err(VerificationError)
188        }
189    }
190
191    /// The verification procedure for the EvaluationProof with a set of polynomials evaluated at a set of evaluation points.
192    /// All the polynomials are evaluated at the set of points ``eval_points`` and are committed as ``commitments``.
193    /// ``evaluations`` contains evaluations of each polynomial at each point in ``eval_points``.
194    /// ``evaluations`` follows the same polynomial order as ``commitments`` and the same evaluation point order as ``eval_points``.
195    /// The evaluation proof can be obtained either in a space-efficient or a time-efficient flavour.
196    /// ``open_chal`` is a random challenge for batching evaluation proofs across different polynomials.
197    pub fn verify_multi_points(
198        &self,
199        commitments: &[Commitment<E>],
200        eval_points: &[E::ScalarField],
201        evaluations: &[Vec<E::ScalarField>],
202        proof: &EvaluationProof<E>,
203        open_chal: &E::ScalarField,
204    ) -> VerificationResult {
205        // Computing the vanishing polynomial over eval_points
206        let zeros = vanishing_polynomial(eval_points);
207        let zeros_repr = zeros.iter().map(|x| x.into_bigint()).collect::<Vec<_>>();
208        let zeros = <E::G2 as VariableBaseMSM>::msm_bigint(&self.powers_of_g2, &zeros_repr);
209
210        // Computing the inverse for the interpolation
211        let mut sca_inverse = Vec::new();
212        for (j, x_j) in eval_points.iter().enumerate() {
213            let mut sca = E::ScalarField::one();
214            for (k, x_k) in eval_points.iter().enumerate() {
215                if j == k {
216                    continue;
217                }
218                sca *= *x_j - x_k;
219            }
220            sca = sca.inverse().unwrap();
221            sca_inverse.push(sca);
222        }
223
224        // Computing the lagrange polynomial for the interpolation
225        let mut lang = Vec::new();
226        for (j, _x_j) in eval_points.iter().enumerate() {
227            let mut l_poly = DensePolynomial::from_coefficients_vec(vec![E::ScalarField::one()]);
228            for (k, x_k) in eval_points.iter().enumerate() {
229                if j == k {
230                    continue;
231                }
232                let tmp_poly =
233                    DensePolynomial::from_coefficients_vec(vec![-(*x_k), E::ScalarField::one()]);
234                l_poly = l_poly.mul(&tmp_poly);
235            }
236            lang.push(l_poly);
237        }
238
239        // Computing the commitment for the interpolated polynomials
240        let etas = powers(*open_chal, evaluations.len());
241        let interpolated_polynomials = evaluations
242            .iter()
243            .map(|e| interpolate_poly::<E>(eval_points, e, &sca_inverse, &lang).coeffs)
244            .collect::<Vec<_>>();
245        let i_poly = linear_combination(&interpolated_polynomials[..], &etas).unwrap();
246
247        let i_comm = msm::<E>(&self.powers_of_g, &i_poly);
248
249        // Gathering commitments
250        let comm_vec = commitments.iter().map(|x| x.0).collect::<Vec<_>>();
251        let etas_repr = etas.iter().map(|e| e.into_bigint()).collect::<Vec<_>>();
252        let f_comm = <E::G1 as VariableBaseMSM>::msm_bigint(&comm_vec, &etas_repr);
253
254        let g2 = self.powers_of_g2[0];
255
256        if E::pairing(f_comm - i_comm.into_group(), g2) == E::pairing(proof.0, zeros) {
257            Ok(())
258        } else {
259            Err(VerificationError)
260        }
261    }
262}
263
264fn interpolate_poly<E: Pairing>(
265    eval_points: &[E::ScalarField],
266    evals: &[E::ScalarField],
267    sca_inverse: &[E::ScalarField],
268    lang: &[DensePolynomial<E::ScalarField>],
269) -> DensePolynomial<E::ScalarField> {
270    let mut res = DensePolynomial::from_coefficients_vec(vec![E::ScalarField::zero()]);
271    for (j, (_x_j, y_j)) in eval_points.iter().zip(evals.iter()).enumerate() {
272        let l_poly = (&lang[j]).mul(sca_inverse[j] * y_j);
273        res = (&res).add(&l_poly);
274    }
275    res
276}
277
278/// The polynomial in \\(\FF\\) that vanishes in all the points `points`.
279pub(crate) fn vanishing_polynomial<F: Field>(points: &[F]) -> DensePolynomial<F> {
280    let one = DensePolynomial::from_coefficients_vec(vec![F::one()]);
281    points
282        .iter()
283        .map(|&point| DensePolynomial::from_coefficients_vec(vec![-point, F::one()]))
284        .fold(one, |x, y| x.naive_mul(&y))
285}
286
287/// Compute a linear combination of the polynomials `polynomials` with the given challenges.
288pub(crate) fn linear_combination<F: Field, PP>(
289    polynomials: &[PP],
290    challenges: &[F],
291) -> Option<Vec<F>>
292where
293    PP: Borrow<Vec<F>>,
294{
295    polynomials
296        .iter()
297        .zip(challenges.iter())
298        .map(|(p, &c)| &DensePolynomial::from_coefficients_vec(p.borrow().to_vec()) * c)
299        .reduce(|x, y| x + y)?
300        .coeffs
301        .into()
302}
303
304/// Return a vector of length `len` containing the consecutive powers of element.
305pub(crate) fn powers<F: Field>(element: F, len: usize) -> Vec<F> {
306    let mut powers = vec![F::one(); len];
307    for i in 1..len {
308        powers[i] = element * powers[i - 1];
309    }
310    powers
311}