1use crate::{
15 commitment::{
16 combine_commitments, BatchEvaluationProof, BlindedCommitment, CommitmentCurve, Evaluation,
17 PolyComm,
18 },
19 ipa::SRS,
20 utils::combine_polys,
21 CommitmentError, PolynomialsToCombine, SRS as SRSTrait,
22};
23
24use ark_ec::{pairing::Pairing, AffineRepr, VariableBaseMSM};
25use ark_ff::{One, PrimeField, Zero};
26use ark_poly::{
27 univariate::{DenseOrSparsePolynomial, DensePolynomial},
28 DenseUVPolynomial, EvaluationDomain, Evaluations, Polynomial, Radix2EvaluationDomain as D,
29};
30use mina_poseidon::FqSponge;
31use rand::thread_rng;
32use rand_core::{CryptoRng, RngCore};
33use serde::{Deserialize, Serialize};
34use serde_with::serde_as;
35use std::ops::Neg;
36
37pub fn combine_evaluations<G: CommitmentCurve>(
62 evaluations: &[Evaluation<G>],
63 polyscale: G::ScalarField,
64) -> Vec<G::ScalarField> {
65 let mut polyscale_i = G::ScalarField::one();
66 let mut acc = {
67 let num_evals = evaluations.first().map_or(0, |e| e.evaluations.len());
68 vec![G::ScalarField::zero(); num_evals]
69 };
70
71 for Evaluation { evaluations, .. } in evaluations.iter().filter(|x| !x.commitment.is_empty()) {
72 for chunk_idx in 0..evaluations[0].len() {
78 for eval_pt_idx in 0..evaluations.len() {
80 acc[eval_pt_idx] += evaluations[eval_pt_idx][chunk_idx] * polyscale_i;
81 }
82 polyscale_i *= polyscale;
83 }
84 }
85
86 acc
87}
88
89#[serde_as]
90#[derive(Debug, Serialize, Deserialize)]
91#[serde(
92 bound = "Pair::G1Affine: ark_serialize::CanonicalDeserialize + ark_serialize::CanonicalSerialize"
93)]
94pub struct KZGProof<Pair: Pairing> {
95 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
96 pub quotient: Pair::G1Affine,
97 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
98 pub blinding: <Pair::G1Affine as AffineRepr>::ScalarField,
100}
101
102impl<Pair: Pairing> Default for KZGProof<Pair> {
103 fn default() -> Self {
104 Self {
105 quotient: Pair::G1Affine::generator(),
106 blinding: <Pair::G1Affine as AffineRepr>::ScalarField::zero(),
107 }
108 }
109}
110
111impl<Pair: Pairing> Clone for KZGProof<Pair> {
112 fn clone(&self) -> Self {
113 Self {
114 quotient: self.quotient,
115 blinding: self.blinding,
116 }
117 }
118}
119
120#[allow(clippy::unsafe_derive_deserialize)]
128#[derive(Debug, PartialEq, Serialize, Deserialize)]
129pub struct PairingSRS<Pair: Pairing> {
130 pub full_srs: SRS<Pair::G1Affine>,
133 pub verifier_srs: SRS<Pair::G2Affine>,
136}
137
138impl<
139 F: PrimeField,
140 G: CommitmentCurve<ScalarField = F>,
141 G2: CommitmentCurve<ScalarField = F>,
142 Pair: Pairing<G1Affine = G, G2Affine = G2>,
143 > PairingSRS<Pair>
144{
145 pub fn create_trusted_setup_with_toxic_waste(toxic_waste: F, depth: usize) -> Self {
152 let full_srs = SRS::create_trusted_setup_with_toxic_waste(toxic_waste, depth);
153 let verifier_srs = SRS::create_trusted_setup_with_toxic_waste(toxic_waste, 3);
154 Self {
155 full_srs,
156 verifier_srs,
157 }
158 }
159}
160
161impl<Pair: Pairing> Default for PairingSRS<Pair> {
162 fn default() -> Self {
163 Self {
164 full_srs: SRS::default(),
165 verifier_srs: SRS::default(),
166 }
167 }
168}
169
170impl<Pair: Pairing> Clone for PairingSRS<Pair> {
171 fn clone(&self) -> Self {
172 Self {
173 full_srs: self.full_srs.clone(),
174 verifier_srs: self.verifier_srs.clone(),
175 }
176 }
177}
178
179impl<
180 F: PrimeField,
181 G: CommitmentCurve<ScalarField = F>,
182 G2: CommitmentCurve<ScalarField = F>,
183 Pair: Pairing<G1Affine = G, G2Affine = G2>,
184 const FULL_ROUNDS: usize,
185 > crate::OpenProof<G, FULL_ROUNDS> for KZGProof<Pair>
186{
187 type SRS = PairingSRS<Pair>;
188
189 fn open<EFqSponge, RNG, D: EvaluationDomain<F>>(
198 srs: &Self::SRS,
199 _group_map: &<G as CommitmentCurve>::Map,
200 plnms: PolynomialsToCombine<G, D>,
201 elm: &[<G as AffineRepr>::ScalarField],
202 polyscale: <G as AffineRepr>::ScalarField,
203 _evalscale: <G as AffineRepr>::ScalarField,
204 _sponge: EFqSponge,
205 _rng: &mut RNG,
206 ) -> Self
207 where
208 EFqSponge: Clone + FqSponge<<G as AffineRepr>::BaseField, G, F, FULL_ROUNDS>,
209 RNG: RngCore + CryptoRng,
210 {
211 Self::create(srs, plnms, elm, polyscale).unwrap()
212 }
213
214 fn verify<EFqSponge, RNG>(
215 srs: &Self::SRS,
216 _group_map: &G::Map,
217 batch: &mut [BatchEvaluationProof<G, EFqSponge, Self, FULL_ROUNDS>],
218 _rng: &mut RNG,
219 ) -> bool
220 where
221 EFqSponge: FqSponge<G::BaseField, G, F, FULL_ROUNDS>,
222 RNG: RngCore + CryptoRng,
223 {
224 for BatchEvaluationProof {
225 sponge: _,
226 evaluations,
227 evaluation_points,
228 polyscale,
229 evalscale: _,
230 opening,
231 combined_inner_product: _,
232 } in batch.iter()
233 {
234 if !opening.verify(srs, evaluations, *polyscale, evaluation_points) {
235 return false;
236 }
237 }
238 true
239 }
240}
241
242impl<
243 F: PrimeField,
244 G: CommitmentCurve<ScalarField = F>,
245 G2: CommitmentCurve<ScalarField = F>,
246 Pair: Pairing<G1Affine = G, G2Affine = G2>,
247 > SRSTrait<G> for PairingSRS<Pair>
248{
249 fn max_poly_size(&self) -> usize {
250 self.full_srs.max_poly_size()
251 }
252
253 fn get_lagrange_basis(&self, domain: D<G::ScalarField>) -> &Vec<PolyComm<G>> {
254 self.full_srs.get_lagrange_basis(domain)
255 }
256
257 fn get_lagrange_basis_from_domain_size(&self, domain_size: usize) -> &Vec<PolyComm<G>> {
258 self.full_srs
259 .get_lagrange_basis_from_domain_size(domain_size)
260 }
261
262 fn blinding_commitment(&self) -> G {
263 self.full_srs.blinding_commitment()
264 }
265
266 fn mask_custom(
267 &self,
268 com: PolyComm<G>,
269 blinders: &PolyComm<G::ScalarField>,
270 ) -> Result<BlindedCommitment<G>, CommitmentError> {
271 self.full_srs.mask_custom(com, blinders)
272 }
273
274 fn mask(
275 &self,
276 comm: PolyComm<G>,
277 rng: &mut (impl RngCore + CryptoRng),
278 ) -> BlindedCommitment<G> {
279 self.full_srs.mask(comm, rng)
280 }
281
282 fn commit(
283 &self,
284 plnm: &DensePolynomial<F>,
285 num_chunks: usize,
286 rng: &mut (impl RngCore + CryptoRng),
287 ) -> BlindedCommitment<G> {
288 self.full_srs.commit(plnm, num_chunks, rng)
289 }
290
291 fn commit_non_hiding(
292 &self,
293 plnm: &DensePolynomial<G::ScalarField>,
294 num_chunks: usize,
295 ) -> PolyComm<G> {
296 self.full_srs.commit_non_hiding(plnm, num_chunks)
297 }
298
299 fn commit_custom(
300 &self,
301 plnm: &DensePolynomial<<G>::ScalarField>,
302 num_chunks: usize,
303 blinders: &PolyComm<<G>::ScalarField>,
304 ) -> Result<BlindedCommitment<G>, CommitmentError> {
305 self.full_srs.commit_custom(plnm, num_chunks, blinders)
306 }
307
308 fn commit_evaluations_non_hiding(
309 &self,
310 domain: D<G::ScalarField>,
311 plnm: &Evaluations<G::ScalarField, D<G::ScalarField>>,
312 ) -> PolyComm<G> {
313 self.full_srs.commit_evaluations_non_hiding(domain, plnm)
314 }
315
316 fn commit_evaluations(
317 &self,
318 domain: D<G::ScalarField>,
319 plnm: &Evaluations<G::ScalarField, D<G::ScalarField>>,
320 rng: &mut (impl RngCore + CryptoRng),
321 ) -> BlindedCommitment<G> {
322 self.full_srs.commit_evaluations(domain, plnm, rng)
323 }
324
325 fn commit_evaluations_custom(
326 &self,
327 domain: D<<G>::ScalarField>,
328 plnm: &Evaluations<<G>::ScalarField, D<<G>::ScalarField>>,
329 blinders: &PolyComm<<G>::ScalarField>,
330 ) -> Result<BlindedCommitment<G>, CommitmentError> {
331 self.full_srs
332 .commit_evaluations_custom(domain, plnm, blinders)
333 }
334
335 fn create(depth: usize) -> Self {
336 let mut rng = thread_rng();
337 let mut toxic_waste = G::ScalarField::rand(&mut rng);
338 let result = Self::create_trusted_setup_with_toxic_waste(toxic_waste, depth);
339 toxic_waste.zeroize();
340 result
341 }
342
343 fn size(&self) -> usize {
344 self.full_srs.g.len()
345 }
346}
347
348fn eval_polynomial<F: PrimeField>(elm: &[F], evals: &[F]) -> DensePolynomial<F> {
353 assert_eq!(elm.len(), evals.len());
354 let (zeta, zeta_omega) = if elm.len() == 2 {
355 (elm[0], elm[1])
356 } else {
357 todo!()
358 };
359 let (eval_zeta, eval_zeta_omega) = if evals.len() == 2 {
360 (evals[0], evals[1])
361 } else {
362 todo!()
363 };
364
365 let b = (eval_zeta_omega - eval_zeta) / (zeta_omega - zeta);
378 let a = eval_zeta - b * zeta;
379 DensePolynomial::from_coefficients_slice(&[a, b])
380}
381
382fn divisor_polynomial<F: PrimeField>(elm: &[F]) -> DensePolynomial<F> {
384 elm.iter()
385 .map(|value| DensePolynomial::from_coefficients_slice(&[-(*value), F::one()]))
386 .reduce(|poly1, poly2| &poly1 * &poly2)
387 .unwrap()
388}
389
390impl<
391 F: PrimeField,
392 G: CommitmentCurve<ScalarField = F>,
393 G2: CommitmentCurve<ScalarField = F>,
394 Pair: Pairing<G1Affine = G, G2Affine = G2>,
395 > KZGProof<Pair>
396{
397 pub fn create<D: EvaluationDomain<F>>(
408 srs: &PairingSRS<Pair>,
409 plnms: PolynomialsToCombine<G, D>,
410 elm: &[F],
411 polyscale: F,
412 ) -> Option<Self> {
413 let (p, blinding_factor) = combine_polys::<G, D>(plnms, polyscale, srs.full_srs.g.len());
414 let evals: Vec<_> = elm.iter().map(|pt| p.evaluate(pt)).collect();
415
416 let quotient_poly = {
417 let eval_polynomial = eval_polynomial(elm, &evals);
419 let divisor_polynomial = divisor_polynomial(elm);
420 let numerator_polynomial = &p - &eval_polynomial;
421 let (quotient, remainder) = DenseOrSparsePolynomial::divide_with_q_and_r(
422 &numerator_polynomial.into(),
423 &divisor_polynomial.into(),
424 )?;
425 if !remainder.is_zero() {
426 return None;
427 }
428 quotient
429 };
430
431 let quotient = srs
432 .full_srs
433 .commit_non_hiding("ient_poly, 1)
434 .get_first_chunk();
435
436 Some(Self {
437 quotient,
438 blinding: blinding_factor,
439 })
440 }
441
442 pub fn verify(
446 &self,
447 srs: &PairingSRS<Pair>,
448 evaluations: &[Evaluation<G>], polyscale: F, elm: &[F], ) -> bool {
452 let poly_commitment: G::Group = {
453 let mut scalars: Vec<F> = Vec::new();
454 let mut points = Vec::new();
455 combine_commitments(
456 evaluations,
457 &mut scalars,
458 &mut points,
459 polyscale,
460 F::one(), );
462 let scalars: Vec<_> = scalars.iter().map(|x| x.into_bigint()).collect();
463
464 G::Group::msm_bigint(&points, &scalars)
465 };
466
467 let evals = combine_evaluations(evaluations, polyscale);
470 let blinding_commitment = srs.full_srs.h.mul(self.blinding);
471 let divisor_commitment = srs
474 .verifier_srs
475 .commit_non_hiding(&divisor_polynomial(elm), 1)
476 .get_first_chunk();
477 let eval_commitment = srs
480 .full_srs
481 .commit_non_hiding(&eval_polynomial(elm, &evals), 1)
482 .get_first_chunk()
483 .into_group();
484 let numerator_commitment = { poly_commitment - eval_commitment - blinding_commitment };
485 let to_loop_left = [
488 ark_ec::pairing::prepare_g1::<Pair>(numerator_commitment),
489 ark_ec::pairing::prepare_g1::<Pair>(self.quotient.into_group().neg()),
492 ];
493 let to_loop_right = [
494 ark_ec::pairing::prepare_g2::<Pair>(Pair::G2Affine::generator()),
495 ark_ec::pairing::prepare_g2::<Pair>(divisor_commitment),
496 ];
497 let res = Pair::multi_pairing(to_loop_left, to_loop_right);
502
503 res.is_zero()
504 }
505}