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