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 let protocol_version: [u8; 4] = [0; 4];
55
56 serialized_data.extend(&protocol_version);
57
58 let mut main_group_len_bytes: Vec<u8> = self.powers_main_group.len().to_le_bytes().to_vec();
60
61 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 for point in &self.powers_main_group {
71 serialized_data.extend(point.as_bytes());
72 }
73
74 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 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 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 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}