Skip to main content

uzkge/poly_commit/
kzg_poly_commitment.rs

1use ark_bn254::{Bn254, Fq12Config, Fr, G1Projective};
2use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup, PrimeGroup, VariableBaseMSM};
3use ark_ff::{AdditiveGroup, Fp12, One, PrimeField};
4use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate};
5use ark_std::{
6    ops::*,
7    rand::{CryptoRng, RngCore},
8    UniformRand, Zero,
9};
10use serde::{Deserialize, Serialize};
11
12use crate::{
13    errors::UzkgeError,
14    poly_commit::{
15        field_polynomial::FpPolynomial,
16        pcs::{HomomorphicPolyComElem, PolyComScheme, ToBytes},
17    },
18    utils::serialization::{ark_deserialize, ark_serialize},
19};
20
21/// KZG commitment scheme over the `Group`.
22#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Default)]
23pub struct KZGCommitment<G: CanonicalSerialize + CanonicalDeserialize>(
24    #[serde(serialize_with = "ark_serialize", deserialize_with = "ark_deserialize")] pub G,
25);
26
27impl<G> ToBytes for G
28where
29    G: CurveGroup,
30{
31    fn to_bytes(&self) -> Vec<u8> {
32        let mut buf = Vec::new();
33        self.serialize_with_mode(&mut buf, Compress::Yes).unwrap();
34        buf
35    }
36
37    fn to_transcript_bytes(&self) -> Vec<u8> {
38        let aff_repr = self.into_affine();
39        let x: G::BaseField = aff_repr.x().unwrap_or(G::BaseField::ZERO);
40        let y: G::BaseField = aff_repr.y().unwrap_or(G::BaseField::ZERO);
41
42        let mut buf_x = vec![];
43        x.serialize_with_mode(&mut buf_x, Compress::Yes).unwrap();
44        buf_x.reverse();
45
46        let mut buf_y = vec![];
47        y.serialize_with_mode(&mut buf_y, Compress::Yes).unwrap();
48        buf_y.reverse();
49
50        buf_x.extend_from_slice(&buf_y);
51
52        buf_x
53    }
54}
55
56impl<G> ToBytes for KZGCommitment<G>
57where
58    G: CurveGroup,
59{
60    fn to_bytes(&self) -> Vec<u8> {
61        self.0.to_bytes()
62    }
63
64    fn to_transcript_bytes(&self) -> Vec<u8> {
65        self.0.to_transcript_bytes()
66    }
67}
68
69impl HomomorphicPolyComElem for KZGCommitment<G1Projective> {
70    type Scalar = Fr;
71    fn get_base() -> Self {
72        KZGCommitment(G1Projective::generator())
73    }
74
75    fn get_identity() -> Self {
76        KZGCommitment(G1Projective::zero())
77    }
78
79    fn add(&self, other: &Self) -> Self {
80        KZGCommitment(self.0.add(&other.0))
81    }
82
83    fn add_assign(&mut self, other: &Self) {
84        self.0.add_assign(&other.0)
85    }
86
87    fn sub(&self, other: &Self) -> Self {
88        KZGCommitment(self.0.sub(&other.0))
89    }
90
91    fn sub_assign(&mut self, other: &Self) {
92        self.0.sub_assign(&other.0)
93    }
94
95    fn mul(&self, exp: &Fr) -> Self {
96        KZGCommitment(self.0.mul(exp))
97    }
98
99    fn mul_assign(&mut self, exp: &Fr) {
100        self.0.mul_assign(exp)
101    }
102}
103
104impl<F: PrimeField> ToBytes for FpPolynomial<F> {
105    fn to_transcript_bytes(&self) -> Vec<u8> {
106        unimplemented!()
107    }
108
109    fn to_bytes(&self) -> Vec<u8> {
110        unimplemented!()
111    }
112}
113
114impl<F: PrimeField> HomomorphicPolyComElem for FpPolynomial<F> {
115    type Scalar = F;
116
117    fn get_base() -> Self {
118        unimplemented!()
119    }
120
121    fn get_identity() -> Self {
122        unimplemented!()
123    }
124
125    fn add(&self, other: &Self) -> Self {
126        self.add(other)
127    }
128
129    fn add_assign(&mut self, other: &Self) {
130        self.add_assign(other)
131    }
132
133    fn sub(&self, other: &Self) -> Self {
134        self.sub(other)
135    }
136
137    fn sub_assign(&mut self, other: &Self) {
138        self.sub_assign(other)
139    }
140
141    fn mul(&self, exp: &F) -> Self {
142        self.mul_scalar(exp)
143    }
144
145    fn mul_assign(&mut self, exp: &F) {
146        self.mul_scalar_assign(exp)
147    }
148}
149
150/// KZG opening proof.
151#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
152pub struct KZGOpenProof<G1: CanonicalSerialize + CanonicalDeserialize>(
153    #[serde(serialize_with = "ark_serialize", deserialize_with = "ark_deserialize")] pub G1,
154);
155
156impl<G: PrimeGroup> ToBytes for KZGOpenProof<G> {
157    fn to_bytes(&self) -> Vec<u8> {
158        let mut buf = Vec::new();
159        self.0.serialize_with_mode(&mut buf, Compress::Yes).unwrap();
160        buf
161    }
162
163    fn to_transcript_bytes(&self) -> Vec<u8> {
164        unimplemented!()
165    }
166}
167
168/// KZG commitment scheme about `PairingEngine`.
169#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
170pub struct KZGCommitmentScheme<P: Pairing> {
171    /// public parameter about G1.
172    #[serde(serialize_with = "ark_serialize", deserialize_with = "ark_deserialize")]
173    pub public_parameter_group_1: Vec<P::G1>,
174    /// public parameter about G1.
175    #[serde(serialize_with = "ark_serialize", deserialize_with = "ark_deserialize")]
176    pub public_parameter_group_2: Vec<P::G2>,
177}
178
179impl<P: Pairing> KZGCommitmentScheme<P> {
180    /// Create a new instance of a KZG polynomial commitment scheme.
181    /// `max_degree` - max degree of the polynomial,
182    /// `prng` - pseudo-random generator.
183    pub fn new<R: CryptoRng + RngCore>(max_degree: usize, prng: &mut R) -> KZGCommitmentScheme<P> {
184        let s = P::ScalarField::rand(prng);
185
186        let mut public_parameter_group_1: Vec<P::G1> = Vec::new();
187
188        let mut elem_g1 = P::G1::generator();
189
190        for _ in 0..=max_degree {
191            public_parameter_group_1.push(elem_g1.clone());
192            elem_g1 = elem_g1.mul(&s);
193        }
194
195        let mut public_parameter_group_2: Vec<P::G2> = Vec::new();
196        let elem_g2 = P::G2::generator();
197        public_parameter_group_2.push(elem_g2.clone());
198        public_parameter_group_2.push(elem_g2.mul(&s));
199
200        KZGCommitmentScheme {
201            public_parameter_group_1,
202            public_parameter_group_2,
203        }
204    }
205
206    /// Serialize the parameters to unchecked bytes.
207    pub fn to_unchecked_bytes(&self) -> Result<Vec<u8>, UzkgeError> {
208        let mut bytes = vec![];
209        let len_1 = self.public_parameter_group_1.len() as u32;
210        let len_2 = self.public_parameter_group_2.len() as u32;
211        bytes.extend(len_1.to_le_bytes());
212        bytes.extend(len_2.to_le_bytes());
213
214        for i in &self.public_parameter_group_1 {
215            let mut buf = Vec::new();
216            i.serialize_with_mode(&mut buf, Compress::No).unwrap();
217            bytes.extend(buf);
218        }
219        for i in &self.public_parameter_group_2 {
220            let mut buf = Vec::new();
221            i.serialize_with_mode(&mut buf, Compress::No).unwrap();
222            bytes.extend(buf);
223        }
224        Ok(bytes)
225    }
226
227    /// Deserialize the parameters from unchecked bytes.
228    pub fn from_unchecked_bytes(bytes: &[u8]) -> Result<Self, UzkgeError> {
229        if bytes.len() < 8 {
230            return Err(UzkgeError::DeserializationError);
231        }
232        let mut len_1_bytes = [0u8; 4];
233        let mut len_2_bytes = [0u8; 4];
234        len_1_bytes.copy_from_slice(&bytes[0..4]);
235        len_2_bytes.copy_from_slice(&bytes[4..8]);
236        let len_1 = u32::from_le_bytes(len_1_bytes) as usize;
237        let len_2 = u32::from_le_bytes(len_2_bytes) as usize;
238        let n_1 = P::G1::default().serialized_size(Compress::No);
239        let n_2 = P::G2::default().serialized_size(Compress::No);
240
241        let bytes_1 = &bytes[8..];
242        let bytes_2 = &bytes[8 + (n_1 * len_1)..];
243        let mut p1 = vec![];
244        let mut p2 = vec![];
245
246        for i in 0..len_1 {
247            let reader = &bytes_1[n_1 * i..n_1 * (i + 1)];
248            let g1 = P::G1::deserialize_with_mode(reader, Compress::No, Validate::No)
249                .map_err(|_| UzkgeError::DeserializationError)?;
250            p1.push(g1);
251        }
252
253        for i in 0..len_2 {
254            let reader = &bytes_2[n_2 * i..n_2 * (i + 1)];
255            let g2 = P::G2::deserialize_with_mode(reader, Compress::No, Validate::No)
256                .map_err(|_| UzkgeError::DeserializationError)?;
257            p2.push(g2);
258        }
259
260        Ok(Self {
261            public_parameter_group_1: p1,
262            public_parameter_group_2: p2,
263        })
264    }
265}
266
267/// KZG commitment scheme over the BN254 curve
268pub type KZGCommitmentSchemeBN254 = KZGCommitmentScheme<Bn254>;
269
270impl<'b> PolyComScheme for KZGCommitmentSchemeBN254 {
271    type Field = Fr;
272    type Commitment = KZGCommitment<G1Projective>;
273
274    fn max_degree(&self) -> usize {
275        self.public_parameter_group_1.len() - 1
276    }
277
278    fn commit(&self, polynomial: &FpPolynomial<Fr>) -> Result<Self::Commitment, UzkgeError> {
279        let coefs = polynomial.get_coefs_ref();
280
281        let degree = polynomial.degree();
282
283        if degree + 1 > self.public_parameter_group_1.len() {
284            return Err(UzkgeError::DegreeError);
285        }
286
287        let points_raw =
288            G1Projective::normalize_batch(&self.public_parameter_group_1[0..degree + 1]);
289
290        let commitment_value = G1Projective::msm(&points_raw, &coefs).unwrap();
291
292        Ok(KZGCommitment(commitment_value))
293    }
294
295    fn eval(&self, poly: &FpPolynomial<Self::Field>, point: &Self::Field) -> Self::Field {
296        poly.eval(point)
297    }
298
299    fn apply_blind_factors(
300        &self,
301        commitment: &Self::Commitment,
302        blinds: &[Self::Field],
303        zeroing_degree: usize,
304    ) -> Self::Commitment {
305        let mut commitment = commitment.0.clone();
306        for (i, blind) in blinds.iter().enumerate() {
307            let mut blind = blind.clone();
308            commitment = commitment + &(self.public_parameter_group_1[i] * &blind);
309            blind = blind.neg();
310            commitment = commitment + &(self.public_parameter_group_1[zeroing_degree + i] * &blind);
311        }
312        KZGCommitment(commitment)
313    }
314
315    fn prove(
316        &self,
317        poly: &FpPolynomial<Self::Field>,
318        x: &Self::Field,
319        max_degree: usize,
320    ) -> Result<Self::Commitment, UzkgeError> {
321        let eval = poly.eval(x);
322
323        if poly.degree() > max_degree {
324            return Err(UzkgeError::DegreeError);
325        }
326
327        let nominator = poly.sub(&FpPolynomial::from_coefs(vec![eval]));
328
329        // Negation must happen in Fq
330        let point_neg = x.neg();
331
332        // X - x
333        let vanishing_poly = FpPolynomial::from_coefs(vec![point_neg, Self::Field::one()]);
334        let (q_poly, r_poly) = nominator.div_rem(&vanishing_poly); // P(X)-P(x) / (X-x)
335
336        if !r_poly.is_zero() {
337            return Err(UzkgeError::PCSProveEvalError);
338        }
339
340        let proof = self.commit(&q_poly).unwrap();
341        Ok(proof)
342    }
343
344    fn verify(
345        &self,
346        cm: &Self::Commitment,
347        _degree: usize,
348        point: &Self::Field,
349        eval: &Self::Field,
350        proof: &Self::Commitment,
351    ) -> Result<(), UzkgeError> {
352        let g1_0 = self.public_parameter_group_1[0].clone();
353        let g2_0 = self.public_parameter_group_2[0].clone();
354        let g2_1 = self.public_parameter_group_2[1].clone();
355
356        let x_minus_point_group_element_group_2 = &g2_1.sub(&g2_0.mul(point));
357
358        let left_pairing_eval = if eval.is_zero() {
359            Bn254::pairing(&cm.0, &g2_0)
360        } else {
361            Bn254::pairing(&cm.0.sub(&g1_0.mul(eval)), &g2_0)
362        };
363
364        let right_pairing_eval = Bn254::pairing(&proof.0, x_minus_point_group_element_group_2);
365
366        if left_pairing_eval == right_pairing_eval {
367            Ok(())
368        } else {
369            Err(UzkgeError::PCSProveEvalError)
370        }
371    }
372
373    fn batch_verify_diff_points(
374        &self,
375        cm_vec: &[Self::Commitment],
376        point_vec: &[Self::Field],
377        eval_vec: &[Self::Field],
378        proofs: &[Self::Commitment],
379        challenge: &Self::Field,
380    ) -> Result<(), UzkgeError> {
381        assert!(proofs.len() > 0);
382        assert_eq!(proofs.len(), point_vec.len());
383        assert_eq!(proofs.len(), eval_vec.len());
384        assert_eq!(proofs.len(), cm_vec.len());
385
386        let g1_0 = self.public_parameter_group_1[0].clone();
387        let g2_0 = self.public_parameter_group_2[0].clone();
388        let g2_1 = self.public_parameter_group_2[1].clone();
389
390        let left_second = g2_1;
391        let right_second = g2_0;
392
393        let mut left_first = proofs[0].0.clone();
394        let mut right_first = proofs[0].0.mul(&point_vec[0]);
395        let mut right_first_val = eval_vec[0].clone();
396        let mut right_first_comm = cm_vec[0].0.clone();
397
398        let mut cur_challenge = challenge.clone();
399        for i in 1..proofs.len() {
400            let new_comm = proofs[i].0.mul(&cur_challenge);
401
402            left_first.add_assign(&new_comm);
403            right_first.add_assign(&new_comm.mul(&point_vec[i]));
404            right_first_val.add_assign(&eval_vec[i].mul(&cur_challenge));
405            right_first_comm.add_assign(&cm_vec[i].0.mul(&cur_challenge));
406
407            cur_challenge.mul_assign(challenge);
408        }
409        right_first.sub_assign(&g1_0.mul(&right_first_val));
410        right_first.add_assign(&right_first_comm);
411
412        let pairing_eval = Bn254::multi_pairing(
413            &[left_first, right_first.neg()],
414            &[left_second, right_second],
415        )
416        .0;
417
418        if pairing_eval == Fp12::<Fq12Config>::one() {
419            Ok(())
420        } else {
421            Err(UzkgeError::PCSProveEvalError)
422        }
423    }
424
425    fn shrink_to_verifier_only(&self) -> Result<Self, UzkgeError> {
426        Ok(Self {
427            public_parameter_group_1: vec![self.public_parameter_group_1[0].clone()],
428            public_parameter_group_2: vec![
429                self.public_parameter_group_2[0].clone(),
430                self.public_parameter_group_2[1].clone(),
431            ],
432        })
433    }
434}
435
436#[cfg(test)]
437mod tests_kzg_impl {
438    use ark_std::rand::SeedableRng;
439    use rand_chacha::ChaChaRng;
440
441    use super::*;
442
443    fn check_public_parameters_generation<P: Pairing>() {
444        let param_size = 5;
445        let mut prng = ChaChaRng::from_entropy();
446        let kzg_scheme = KZGCommitmentScheme::<P>::new(param_size, &mut prng);
447        let g1_power1 = kzg_scheme.public_parameter_group_1[1].clone();
448        let g2_power1 = kzg_scheme.public_parameter_group_2[1].clone();
449
450        // Check parameters for G1
451        for i in 0..param_size - 1 {
452            let elem_first_group_1 = kzg_scheme.public_parameter_group_1[i].clone();
453            let elem_next_group_1 = kzg_scheme.public_parameter_group_1[i + 1].clone();
454            let elem_next_group_1_target = P::pairing(&elem_next_group_1, &P::G2::generator());
455            let elem_next_group_1_target_recomputed = P::pairing(&elem_first_group_1, &g2_power1);
456            assert_eq!(
457                elem_next_group_1_target_recomputed,
458                elem_next_group_1_target
459            );
460        }
461
462        // Check parameters for G2
463        let elem_first_group_2 = kzg_scheme.public_parameter_group_2[0].clone();
464        let elem_second_group_2 = kzg_scheme.public_parameter_group_2[1].clone();
465        let elem_next_group_2_target = P::pairing(&P::G1::generator(), &elem_second_group_2);
466        let elem_next_group_2_target_recomputed = P::pairing(&g1_power1, &elem_first_group_2);
467
468        assert_eq!(
469            elem_next_group_2_target_recomputed,
470            elem_next_group_2_target
471        );
472    }
473
474    // Check the size of the KZG being generated.
475    fn generation_of_crs<P: Pairing>() {
476        let n = 1 << 5;
477        let mut prng = ChaChaRng::from_entropy();
478        let kzg_scheme = KZGCommitmentScheme::<P>::new(n, &mut prng);
479        assert_eq!(kzg_scheme.public_parameter_group_1.len(), n + 1);
480        assert_eq!(kzg_scheme.public_parameter_group_2.len(), 2);
481    }
482
483    #[test]
484    fn test_homomorphic_poly_com_elem() {
485        let mut prng = ChaChaRng::from_entropy();
486        let pcs = KZGCommitmentSchemeBN254::new(20, &mut prng);
487        type Field = Fr;
488        let one = Field::one();
489        let two = one.add(&one);
490        let three = two.add(&one);
491        let four = three.add(&one);
492        let six = three.add(&three);
493        let eight = six.add(&two);
494        let poly1 = FpPolynomial::from_coefs(vec![two, three, six]);
495
496        let commitment1 = pcs.commit(&poly1).unwrap();
497
498        let poly2 = FpPolynomial::from_coefs(vec![one, eight, four]);
499
500        let commitment2 = pcs.commit(&poly2).unwrap();
501
502        // Add two polynomials
503        let poly_sum = poly1.add(&poly2);
504        let commitment_sum = pcs.commit(&poly_sum).unwrap();
505        let commitment_sum_computed = commitment1.add(&commitment2);
506        assert_eq!(commitment_sum, commitment_sum_computed);
507
508        // Multiplying all the coefficients of a polynomial by some value
509        let exponent = four.add(&one);
510        let poly1_mult_5 = poly1.mul_scalar(&exponent);
511        let commitment_poly1_mult_5 = pcs.commit(&poly1_mult_5).unwrap();
512        let commitment_poly1_mult_5_hom = commitment1.mul(&exponent);
513        assert_eq!(commitment_poly1_mult_5, commitment_poly1_mult_5_hom);
514    }
515
516    #[test]
517    fn test_public_parameters() {
518        check_public_parameters_generation::<Bn254>();
519    }
520
521    #[test]
522    fn test_generation_of_crs() {
523        generation_of_crs::<Bn254>();
524    }
525
526    #[test]
527    fn test_commit() {
528        let mut prng = ChaChaRng::from_entropy();
529        let pcs = KZGCommitmentSchemeBN254::new(10, &mut prng);
530        type Field = Fr;
531        let one = Field::one();
532        let two = one.add(&one);
533        let three = two.add(&one);
534        let six = three.add(&three);
535
536        let fq_poly = FpPolynomial::from_coefs(vec![two, three, six]);
537        let commitment = pcs.commit(&fq_poly).unwrap();
538
539        let coefs_poly_scalar: Vec<_> = fq_poly.get_coefs_ref().iter().cloned().collect();
540        let mut expected_committed_value = G1Projective::zero();
541
542        // Doing the multiexp by hand
543        for (i, coef) in coefs_poly_scalar.iter().enumerate() {
544            let g_i = pcs.public_parameter_group_1[i].clone();
545            expected_committed_value = expected_committed_value.add(&g_i.mul(coef));
546        }
547        assert_eq!(expected_committed_value, commitment.0);
548    }
549
550    #[test]
551    fn test_eval() {
552        let mut prng = ChaChaRng::from_entropy();
553        let pcs = KZGCommitmentSchemeBN254::new(10, &mut prng);
554        type Field = Fr;
555        let one = Field::one();
556        let two = one.add(&one);
557        let three = two.add(&one);
558        let four = three.add(&one);
559        let six = three.add(&three);
560        let seven = six.add(&one);
561        let fq_poly = FpPolynomial::from_coefs(vec![one, two, four]);
562        let point = one;
563        let max_degree = fq_poly.degree();
564
565        let degree = fq_poly.degree();
566        let commitment_value = pcs.commit(&fq_poly).unwrap();
567
568        // Check that an error is returned if the degree of the polynomial exceeds the maximum degree.
569        let wrong_max_degree = 1;
570        let res = pcs.prove(&fq_poly, &point, wrong_max_degree);
571        assert!(res.is_err());
572
573        let proof = pcs.prove(&fq_poly, &point, max_degree).unwrap();
574
575        pcs.verify(&commitment_value, degree, &point, &seven, &proof)
576            .unwrap();
577
578        let new_pcs = pcs.shrink_to_verifier_only().unwrap();
579        new_pcs
580            .verify(&commitment_value, degree, &point, &seven, &proof)
581            .unwrap();
582
583        let wrong_eval = one;
584        let res = pcs.verify(&commitment_value, degree, &point, &wrong_eval, &proof);
585        assert!(res.is_err());
586    }
587}