lambdaworks_crypto/commitments/
kzg.rs

1use super::traits::IsCommitmentScheme;
2use alloc::{borrow::ToOwned, vec::Vec};
3use core::{marker::PhantomData, mem};
4use lambdaworks_math::{
5    cyclic_group::IsGroup,
6    elliptic_curve::traits::IsPairing,
7    errors::DeserializationError,
8    field::{element::FieldElement, traits::IsPrimeField},
9    msm::pippenger::msm,
10    polynomial::Polynomial,
11    traits::{AsBytes, Deserializable},
12    unsigned_integer::element::UnsignedInteger,
13};
14
15#[derive(PartialEq, Clone, Debug)]
16pub struct StructuredReferenceString<G1Point, G2Point> {
17    pub powers_main_group: Vec<G1Point>,
18    pub powers_secondary_group: [G2Point; 2],
19}
20
21impl<G1Point, G2Point> StructuredReferenceString<G1Point, G2Point>
22where
23    G1Point: IsGroup,
24    G2Point: IsGroup,
25{
26    pub fn new(powers_main_group: &[G1Point], powers_secondary_group: &[G2Point; 2]) -> Self {
27        Self {
28            powers_main_group: powers_main_group.into(),
29            powers_secondary_group: powers_secondary_group.clone(),
30        }
31    }
32}
33
34#[cfg(feature = "std")]
35impl<G1Point, G2Point> StructuredReferenceString<G1Point, G2Point>
36where
37    G1Point: IsGroup + Deserializable,
38    G2Point: IsGroup + Deserializable,
39{
40    pub fn from_file(file_path: &str) -> Result<Self, crate::errors::SrsFromFileError> {
41        let bytes = std::fs::read(file_path)?;
42        Ok(Self::deserialize(&bytes)?)
43    }
44}
45
46impl<G1Point, G2Point> AsBytes for StructuredReferenceString<G1Point, G2Point>
47where
48    G1Point: IsGroup + AsBytes,
49    G2Point: IsGroup + AsBytes,
50{
51    fn as_bytes(&self) -> Vec<u8> {
52        let mut serialized_data: Vec<u8> = Vec::new();
53        // First 4 bytes encodes protocol version
54        let protocol_version: [u8; 4] = [0; 4];
55
56        serialized_data.extend(&protocol_version);
57
58        // Second 8 bytes store the amount of G1 elements to be stored, this is more than can be indexed with a 64-bit architecture, and some millions of terabytes of data if the points were compressed
59        let mut main_group_len_bytes: Vec<u8> = self.powers_main_group.len().to_le_bytes().to_vec();
60
61        // For architectures with less than 64 bits for pointers
62        // We add extra zeros at the end
63        while main_group_len_bytes.len() < 8 {
64            main_group_len_bytes.push(0)
65        }
66
67        serialized_data.extend(&main_group_len_bytes);
68
69        // G1 elements
70        for point in &self.powers_main_group {
71            serialized_data.extend(point.as_bytes());
72        }
73
74        // G2 elements
75        for point in &self.powers_secondary_group {
76            serialized_data.extend(point.as_bytes());
77        }
78
79        serialized_data
80    }
81}
82
83impl<G1Point, G2Point> Deserializable for StructuredReferenceString<G1Point, G2Point>
84where
85    G1Point: IsGroup + Deserializable,
86    G2Point: IsGroup + Deserializable,
87{
88    fn deserialize(bytes: &[u8]) -> Result<Self, DeserializationError> {
89        const MAIN_GROUP_LEN_OFFSET: usize = 4;
90        const MAIN_GROUP_OFFSET: usize = 12;
91
92        let main_group_len_u64 = u64::from_le_bytes(
93            // This unwrap can't fail since we are fixing the size of the slice
94            bytes[MAIN_GROUP_LEN_OFFSET..MAIN_GROUP_OFFSET]
95                .try_into()
96                .unwrap(),
97        );
98
99        let main_group_len = usize::try_from(main_group_len_u64)
100            .map_err(|_| DeserializationError::PointerSizeError)?;
101
102        let mut main_group: Vec<G1Point> = Vec::new();
103        let mut secondary_group: Vec<G2Point> = Vec::new();
104
105        let size_g1_point = mem::size_of::<G1Point>();
106        let size_g2_point = mem::size_of::<G2Point>();
107
108        for i in 0..main_group_len {
109            // The second unwrap shouldn't fail since the amount of bytes is fixed
110            let point = G1Point::deserialize(
111                bytes[i * size_g1_point + MAIN_GROUP_OFFSET
112                    ..i * size_g1_point + size_g1_point + MAIN_GROUP_OFFSET]
113                    .try_into()
114                    .unwrap(),
115            )?;
116            main_group.push(point);
117        }
118
119        let g2s_offset = size_g1_point * main_group_len + 12;
120        for i in 0..2 {
121            // The second unwrap shouldn't fail since the amount of bytes is fixed
122            let point = G2Point::deserialize(
123                bytes[i * size_g2_point + g2s_offset
124                    ..i * size_g2_point + g2s_offset + size_g2_point]
125                    .try_into()
126                    .unwrap(),
127            )?;
128            secondary_group.push(point);
129        }
130
131        let secondary_group_slice = [secondary_group[0].clone(), secondary_group[1].clone()];
132
133        let srs = StructuredReferenceString::new(&main_group, &secondary_group_slice);
134        Ok(srs)
135    }
136}
137
138#[derive(Clone)]
139pub struct KateZaveruchaGoldberg<F: IsPrimeField, P: IsPairing> {
140    srs: StructuredReferenceString<P::G1Point, P::G2Point>,
141    phantom: PhantomData<F>,
142}
143
144impl<F: IsPrimeField, P: IsPairing> KateZaveruchaGoldberg<F, P> {
145    pub fn new(srs: StructuredReferenceString<P::G1Point, P::G2Point>) -> Self {
146        Self {
147            srs,
148            phantom: PhantomData,
149        }
150    }
151}
152
153impl<const N: usize, F: IsPrimeField<RepresentativeType = UnsignedInteger<N>>, P: IsPairing>
154    IsCommitmentScheme<F> for KateZaveruchaGoldberg<F, P>
155{
156    type Commitment = P::G1Point;
157
158    fn commit(&self, p: &Polynomial<FieldElement<F>>) -> Self::Commitment {
159        let coefficients: Vec<_> = p
160            .coefficients
161            .iter()
162            .map(|coefficient| coefficient.representative())
163            .collect();
164        msm(
165            &coefficients,
166            &self.srs.powers_main_group[..coefficients.len()],
167        )
168        .expect("`points` is sliced by `cs`'s length")
169    }
170
171    fn open(
172        &self,
173        x: &FieldElement<F>,
174        y: &FieldElement<F>,
175        p: &Polynomial<FieldElement<F>>,
176    ) -> Self::Commitment {
177        let mut poly_to_commit = p - y;
178        poly_to_commit.ruffini_division_inplace(x);
179        self.commit(&poly_to_commit)
180    }
181
182    fn verify(
183        &self,
184        x: &FieldElement<F>,
185        y: &FieldElement<F>,
186        p_commitment: &Self::Commitment,
187        proof: &Self::Commitment,
188    ) -> bool {
189        let g1 = &self.srs.powers_main_group[0];
190        let g2 = &self.srs.powers_secondary_group[0];
191        let alpha_g2 = &self.srs.powers_secondary_group[1];
192
193        let e = P::compute_batch(&[
194            (
195                &p_commitment.operate_with(&(g1.operate_with_self(y.representative())).neg()),
196                g2,
197            ),
198            (
199                &proof.neg(),
200                &(alpha_g2.operate_with(&(g2.operate_with_self(x.representative())).neg())),
201            ),
202        ]);
203        e == Ok(FieldElement::one())
204    }
205
206    fn open_batch(
207        &self,
208        x: &FieldElement<F>,
209        ys: &[FieldElement<F>],
210        polynomials: &[Polynomial<FieldElement<F>>],
211        upsilon: &FieldElement<F>,
212    ) -> Self::Commitment {
213        let acc_polynomial = polynomials
214            .iter()
215            .rev()
216            .fold(Polynomial::zero(), |acc, polynomial| {
217                acc * upsilon.to_owned() + polynomial
218            });
219
220        let acc_y = ys
221            .iter()
222            .rev()
223            .fold(FieldElement::zero(), |acc, y| acc * upsilon.to_owned() + y);
224
225        self.open(x, &acc_y, &acc_polynomial)
226    }
227
228    fn verify_batch(
229        &self,
230        x: &FieldElement<F>,
231        ys: &[FieldElement<F>],
232        p_commitments: &[Self::Commitment],
233        proof: &Self::Commitment,
234        upsilon: &FieldElement<F>,
235    ) -> bool {
236        let acc_commitment =
237            p_commitments
238                .iter()
239                .rev()
240                .fold(P::G1Point::neutral_element(), |acc, point| {
241                    acc.operate_with_self(upsilon.to_owned().representative())
242                        .operate_with(point)
243                });
244
245        let acc_y = ys
246            .iter()
247            .rev()
248            .fold(FieldElement::zero(), |acc, y| acc * upsilon.to_owned() + y);
249        self.verify(x, &acc_y, &acc_commitment, proof)
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use alloc::vec::Vec;
256    use lambdaworks_math::{
257        cyclic_group::IsGroup,
258        elliptic_curve::{
259            short_weierstrass::{
260                curves::bls12_381::{
261                    curve::BLS12381Curve,
262                    default_types::{FrElement, FrField},
263                    pairing::BLS12381AtePairing,
264                    twist::BLS12381TwistCurve,
265                },
266                point::ShortWeierstrassProjectivePoint,
267            },
268            traits::{IsEllipticCurve, IsPairing},
269        },
270        field::element::FieldElement,
271        polynomial::Polynomial,
272        traits::{AsBytes, Deserializable},
273        unsigned_integer::element::U256,
274    };
275
276    use crate::commitments::traits::IsCommitmentScheme;
277
278    use super::{KateZaveruchaGoldberg, StructuredReferenceString};
279    use rand::Rng;
280
281    type G1 = ShortWeierstrassProjectivePoint<BLS12381Curve>;
282
283    #[allow(clippy::upper_case_acronyms)]
284    type KZG = KateZaveruchaGoldberg<FrField, BLS12381AtePairing>;
285
286    fn create_srs() -> StructuredReferenceString<
287        <BLS12381AtePairing as IsPairing>::G1Point,
288        <BLS12381AtePairing as IsPairing>::G2Point,
289    > {
290        let mut rng = rand::thread_rng();
291        let toxic_waste = FrElement::new(U256 {
292            limbs: [
293                rng.gen::<u64>(),
294                rng.gen::<u64>(),
295                rng.gen::<u64>(),
296                rng.gen::<u64>(),
297            ],
298        });
299        let g1 = BLS12381Curve::generator();
300        let g2 = BLS12381TwistCurve::generator();
301        let powers_main_group: Vec<G1> = (0..100)
302            .map(|exponent| {
303                g1.operate_with_self(toxic_waste.pow(exponent as u128).representative())
304            })
305            .collect();
306        let powers_secondary_group = [
307            g2.clone(),
308            g2.operate_with_self(toxic_waste.representative()),
309        ];
310        StructuredReferenceString::new(&powers_main_group, &powers_secondary_group)
311    }
312
313    #[test]
314    fn kzg_1() {
315        let kzg = KZG::new(create_srs());
316        let p = Polynomial::<FrElement>::new(&[FieldElement::one(), FieldElement::one()]);
317        let p_commitment: <BLS12381AtePairing as IsPairing>::G1Point = kzg.commit(&p);
318        let x = -FieldElement::one();
319        let y = p.evaluate(&x);
320        let proof = kzg.open(&x, &y, &p);
321        assert_eq!(y, FieldElement::zero());
322        assert_eq!(proof, BLS12381Curve::generator());
323        assert!(kzg.verify(&x, &y, &p_commitment, &proof));
324    }
325
326    #[test]
327    fn poly_9000_constant_should_verify_proof() {
328        let kzg = KZG::new(create_srs());
329        let p = Polynomial::new(&[FieldElement::from(9000)]);
330        let p_commitment: <BLS12381AtePairing as IsPairing>::G1Point = kzg.commit(&p);
331        let x = FieldElement::one();
332        let y = FieldElement::from(9000);
333        let proof = kzg.open(&x, &y, &p);
334        assert!(kzg.verify(&x, &y, &p_commitment, &proof));
335    }
336
337    #[test]
338    fn poly_9000_batched_should_verify() {
339        let kzg = KZG::new(create_srs());
340        let p0 = Polynomial::<FrElement>::new(&[FieldElement::from(9000)]);
341        let p0_commitment: <BLS12381AtePairing as IsPairing>::G1Point = kzg.commit(&p0);
342
343        let x = FieldElement::one();
344        let y0 = FieldElement::from(9000);
345        let upsilon = &FieldElement::from(1);
346
347        let proof = kzg.open_batch(&x, &[y0.clone()], &[p0], upsilon);
348
349        assert!(kzg.verify_batch(&x, &[y0], &[p0_commitment], &proof, upsilon));
350    }
351
352    #[test]
353    fn two_poly_9000_batched_should_verify() {
354        let kzg = KZG::new(create_srs());
355        let p0 = Polynomial::<FrElement>::new(&[FieldElement::from(9000)]);
356        let p0_commitment: <BLS12381AtePairing as IsPairing>::G1Point = kzg.commit(&p0);
357
358        let x = FieldElement::one();
359        let y0 = FieldElement::from(9000);
360        let upsilon = &FieldElement::from(1);
361
362        let proof = kzg.open_batch(&x, &[y0.clone(), y0.clone()], &[p0.clone(), p0], upsilon);
363
364        assert!(kzg.verify_batch(
365            &x,
366            &[y0.clone(), y0],
367            &[p0_commitment.clone(), p0_commitment],
368            &proof,
369            upsilon
370        ));
371    }
372
373    #[test]
374    fn two_poly_batched_should_verify() {
375        let kzg = KZG::new(create_srs());
376
377        let x = FieldElement::from(3);
378
379        let p0 = Polynomial::<FrElement>::new(&[FieldElement::from(9000)]);
380        let p0_commitment: <BLS12381AtePairing as IsPairing>::G1Point = kzg.commit(&p0);
381        let y0 = FieldElement::from(9000);
382
383        let p1 = Polynomial::<FrElement>::new(&[
384            FieldElement::from(1),
385            FieldElement::from(2),
386            -FieldElement::from(1),
387        ]);
388        let p1_commitment: <BLS12381AtePairing as IsPairing>::G1Point = kzg.commit(&p1);
389        let y1 = p1.evaluate(&x);
390
391        let upsilon = &FieldElement::from(1);
392
393        let proof = kzg.open_batch(&x, &[y0.clone(), y1.clone()], &[p0, p1], upsilon);
394
395        assert!(kzg.verify_batch(
396            &x,
397            &[y0, y1],
398            &[p0_commitment, p1_commitment],
399            &proof,
400            upsilon
401        ));
402    }
403
404    #[test]
405    fn serialize_deserialize_srs() {
406        let srs = create_srs();
407        let bytes = srs.as_bytes();
408        let deserialized: StructuredReferenceString<
409            ShortWeierstrassProjectivePoint<BLS12381Curve>,
410            ShortWeierstrassProjectivePoint<BLS12381TwistCurve>,
411        > = StructuredReferenceString::deserialize(&bytes).unwrap();
412
413        assert_eq!(srs, deserialized);
414    }
415
416    #[test]
417    #[cfg(feature = "std")]
418    fn load_srs_from_file() {
419        type TestSrsType = StructuredReferenceString<
420            ShortWeierstrassProjectivePoint<BLS12381Curve>,
421            ShortWeierstrassProjectivePoint<BLS12381TwistCurve>,
422        >;
423
424        let base_dir = env!("CARGO_MANIFEST_DIR");
425        let srs_file = base_dir.to_owned() + "/src/commitments/test_srs/srs_3_g1_elements.bin";
426
427        let srs = TestSrsType::from_file(&srs_file).unwrap();
428
429        assert_eq!(srs.powers_main_group.len(), 3);
430    }
431}