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}