1use ark_ec::{
2 pairing::{MillerLoopOutput, Pairing, PairingOutput},
3 AffineRepr, Group,
4};
5use ark_ff::{One, PrimeField, Zero};
6use ark_std::{cfg_iter, ops::MulAssign, rand::Rng, vec, vec::Vec, UniformRand};
7
8#[cfg(feature = "parallel")]
9use rayon::prelude::*;
10
11#[derive(Debug, Clone)]
24pub struct RandomizedPairingChecker<E: Pairing> {
25 left: MillerLoopOutput<E>,
28 right: PairingOutput<E>,
31 lazy: bool,
34 pending: (Vec<E::G1Prepared>, Vec<E::G2Prepared>),
36 random: E::ScalarField,
37 current_random: E::ScalarField,
39}
40
41impl<E: Pairing> RandomizedPairingChecker<E> {
42 pub fn new(random: E::ScalarField, lazy: bool) -> Self {
45 Self {
46 left: MillerLoopOutput(E::TargetField::one()),
47 right: PairingOutput::zero(),
48 lazy,
49 pending: (vec![], vec![]),
50 random,
51 current_random: E::ScalarField::one(),
52 }
53 }
54
55 pub fn new_using_rng<R: Rng>(rng: &mut R, lazy: bool) -> Self {
57 Self::new(E::ScalarField::rand(rng), lazy)
58 }
59
60 pub fn add_sources_and_target(
62 &mut self,
63 a: &E::G1Affine,
64 b: impl Into<E::G2Prepared>,
65 out: &PairingOutput<E>,
66 ) {
67 let m = self.current_random.into_bigint();
68 let a_m = E::G1Prepared::from(a.mul_bigint(m));
69 if self.lazy {
70 self.pending.0.push(a_m);
71 self.pending.1.push(b.into());
72 } else {
73 self.left.0.mul_assign(E::miller_loop(a_m, b.into()).0);
74 }
75 self.right += out.mul_bigint(m);
76 self.current_random *= self.random;
77 }
78
79 pub fn add_multiple_sources_and_target(
82 &mut self,
83 a: &[E::G1Affine],
84 b: impl IntoIterator<Item = impl Into<E::G2Prepared>>,
85 out: &PairingOutput<E>,
86 ) {
87 self.add_multiple_sources_and_target_with_laziness_choice(a, b, out, self.lazy)
88 }
89
90 pub fn add_multiple_sources(
93 &mut self,
94 a: &[E::G1Affine],
95 b: impl IntoIterator<Item = impl Into<E::G2Prepared>>,
96 c: &[E::G1Affine],
97 d: impl IntoIterator<Item = impl Into<E::G2Prepared>>,
98 ) {
99 self.add_multiple_sources_with_laziness_choice(a, b, c, d, self.lazy)
100 }
101
102 pub fn add_sources(
105 &mut self,
106 a: &E::G1Affine,
107 b: impl Into<E::G2Prepared>,
108 c: &E::G1Affine,
109 d: impl Into<E::G2Prepared>,
110 ) {
111 self.add_sources_with_laziness_choice(a, b, c, d, self.lazy)
112 }
113
114 pub fn add_multiple_sources_and_target_with_laziness_choice(
117 &mut self,
118 a: &[E::G1Affine],
119 b: impl IntoIterator<Item = impl Into<E::G2Prepared>>,
120 out: &PairingOutput<E>,
121 lazy: bool,
122 ) {
123 let m = self.current_random.into_bigint();
124 let mut a_m = cfg_iter!(a)
126 .map(|a| E::G1Prepared::from(a.mul_bigint(m)))
127 .collect::<Vec<_>>();
128 if lazy {
129 self.pending.0.append(&mut a_m);
130 self.pending
131 .1
132 .append(&mut b.into_iter().map(|b| b.into()).collect());
133 } else {
134 self.left.0.mul_assign(E::multi_miller_loop(a_m, b).0);
135 }
136 self.right += out.mul_bigint(m);
137 self.current_random *= self.random;
138 }
139
140 pub fn add_multiple_sources_with_laziness_choice(
143 &mut self,
144 a: &[E::G1Affine],
145 b: impl IntoIterator<Item = impl Into<E::G2Prepared>>,
146 c: &[E::G1Affine],
147 d: impl IntoIterator<Item = impl Into<E::G2Prepared>>,
148 lazy: bool,
149 ) {
150 let m = self.current_random.into_bigint();
151 let mut a_m = cfg_iter!(a)
153 .map(|a| E::G1Prepared::from(a.mul_bigint(m)))
154 .collect::<Vec<_>>();
155 let mut c_m = cfg_iter!(c)
157 .map(|c| E::G1Prepared::from(-c.mul_bigint(m)))
158 .collect::<Vec<_>>();
159 if lazy {
160 self.pending.0.append(&mut a_m);
161 self.pending
162 .1
163 .append(&mut b.into_iter().map(|b| b.into()).collect());
164 self.pending.0.append(&mut c_m);
165 self.pending
166 .1
167 .append(&mut d.into_iter().map(|d| d.into()).collect());
168 } else {
169 self.left.0.mul_assign(E::multi_miller_loop(a_m, b).0);
170 self.left.0.mul_assign(E::multi_miller_loop(c_m, d).0);
171 }
172 self.current_random *= self.random;
173 }
174
175 pub fn add_sources_with_laziness_choice(
178 &mut self,
179 a: &E::G1Affine,
180 b: impl Into<E::G2Prepared>,
181 c: &E::G1Affine,
182 d: impl Into<E::G2Prepared>,
183 lazy: bool,
184 ) {
185 let m = self.current_random.into_bigint();
186 let am = E::G1Prepared::from(a.mul_bigint(m));
187 let cm = E::G1Prepared::from(-c.mul_bigint(m));
188 let b = b.into();
189 let d = d.into();
190 if lazy {
191 self.pending.0.push(am);
192 self.pending.0.push(cm);
193 self.pending.1.push(b);
194 self.pending.1.push(d);
195 } else {
196 self.left
197 .0
198 .mul_assign(E::multi_miller_loop([am, cm], [b, d]).0);
199 }
200 self.current_random *= self.random;
201 }
202
203 pub fn verify(&self) -> bool {
205 debug_assert_eq!(self.pending.0.len(), self.pending.1.len());
206 let left = if !self.pending.0.is_empty() {
207 let mut p = E::multi_miller_loop(self.pending.0.clone(), self.pending.1.clone());
208 p.0.mul_assign(self.left.0);
209 p
210 } else {
211 self.left
212 };
213 E::final_exponentiation(left).unwrap() == self.right
214 }
215}
216
217#[cfg(test)]
218mod test {
219 use super::*;
220 use ark_bls12_381::{Bls12_381, G1Projective, G2Projective};
221 use ark_ec::{bls12::G2Prepared, CurveGroup};
222 use ark_std::{
223 rand::{prelude::StdRng, SeedableRng},
224 UniformRand,
225 };
226 use std::time::Instant;
227
228 fn rev_vec<T: Clone>(v: &[T]) -> Vec<T> {
229 let mut x = v.to_vec();
230 x.reverse();
231 x
232 }
233
234 #[test]
235 fn test_pairing_randomize() {
236 let mut rng = StdRng::seed_from_u64(0u64);
237 let n = 10;
238 let mut t1 = 0;
239
240 let a1 = (0..n)
241 .map(|_| G1Projective::rand(&mut rng).into_affine())
242 .collect::<Vec<_>>();
243 let b1 = (0..n)
244 .map(|_| G2Projective::rand(&mut rng).into_affine())
245 .collect::<Vec<_>>();
246 let a2 = (0..n + 5)
247 .map(|_| G1Projective::rand(&mut rng).into_affine())
248 .collect::<Vec<_>>();
249 let b2 = (0..n + 5)
250 .map(|_| G2Projective::rand(&mut rng).into_affine())
251 .collect::<Vec<_>>();
252 let a3 = (0..n - 2)
253 .map(|_| G1Projective::rand(&mut rng).into_affine())
254 .collect::<Vec<_>>();
255 let b3 = (0..n - 2)
256 .map(|_| G2Projective::rand(&mut rng).into_affine())
257 .collect::<Vec<_>>();
258
259 let start = Instant::now();
260 let out1 = Bls12_381::multi_pairing(a1.clone(), b1.clone());
261 t1 += start.elapsed().as_micros();
262
263 let start = Instant::now();
264 let out2 = Bls12_381::multi_pairing(a2.clone(), b2.clone());
265 t1 += start.elapsed().as_micros();
266
267 let start = Instant::now();
268 let out3 = Bls12_381::multi_pairing(a3.clone(), b3.clone());
269 t1 += start.elapsed().as_micros();
270
271 println!("Time taken without checker {} us", t1);
272
273 for lazy in [true, false] {
274 let start = Instant::now();
275 let mut checker = RandomizedPairingChecker::<Bls12_381>::new_using_rng(&mut rng, lazy);
276 checker.add_multiple_sources_and_target(&a1, &b1, &out1);
277 checker.add_multiple_sources_and_target(&a2, &b2, &out2);
278 checker.add_multiple_sources_and_target(&a3, &b3, &out3);
279 assert!(checker.verify());
280 let l_str = if lazy { "lazy-" } else { "" };
281 println!(
282 "Time taken with {}checker {} us",
283 l_str,
284 start.elapsed().as_micros()
285 );
286
287 let mut checker = RandomizedPairingChecker::<Bls12_381>::new_using_rng(&mut rng, lazy);
289 checker.add_multiple_sources_and_target(&a1, &b1, &out2);
290 checker.add_multiple_sources_and_target(&a2, &b2, &out1);
291 assert!(!checker.verify());
292 }
293
294 let b1_prep = b1.iter().map(|b| G2Prepared::from(*b)).collect::<Vec<_>>();
295 let b2_prep = b2.iter().map(|b| G2Prepared::from(*b)).collect::<Vec<_>>();
296 let b3_prep = b3.iter().map(|b| G2Prepared::from(*b)).collect::<Vec<_>>();
297
298 for lazy in [true, false] {
299 let start = Instant::now();
300 let mut checker = RandomizedPairingChecker::<Bls12_381>::new_using_rng(&mut rng, lazy);
301 checker.add_multiple_sources_and_target(&a1, b1_prep.clone(), &out1);
302 checker.add_multiple_sources_and_target(&a2, b2_prep.clone(), &out2);
303 checker.add_multiple_sources_and_target(&a3, b3_prep.clone(), &out3);
304 assert!(checker.verify());
305 let l_str = if lazy { "lazy-" } else { "" };
306 println!(
307 "Time taken with prepared G2 and {}checker {} us",
308 l_str,
309 start.elapsed().as_micros()
310 );
311 }
312
313 let a1_rev = rev_vec(&a1);
314 let a2_rev = rev_vec(&a2);
315 let a3_rev = rev_vec(&a3);
316 let b1_rev = rev_vec(&b1);
317 let b2_rev = rev_vec(&b2);
318 let b3_rev = rev_vec(&b3);
319
320 let b1_rev_prep = b1_rev
321 .iter()
322 .map(|b| G2Prepared::from(*b))
323 .collect::<Vec<_>>();
324 let b2_rev_prep = b2_rev
325 .iter()
326 .map(|b| G2Prepared::from(*b))
327 .collect::<Vec<_>>();
328 let b3_rev_prep = b3_rev
329 .iter()
330 .map(|b| G2Prepared::from(*b))
331 .collect::<Vec<_>>();
332
333 for lazy in [true, false] {
334 let mut checker = RandomizedPairingChecker::<Bls12_381>::new_using_rng(&mut rng, lazy);
335 checker.add_multiple_sources(&a1, &b1, &a1_rev, &b1_rev);
336 assert!(checker.verify());
337 }
338
339 for lazy in [true, false] {
340 let start = Instant::now();
341 let mut checker = RandomizedPairingChecker::<Bls12_381>::new_using_rng(&mut rng, lazy);
342 checker.add_multiple_sources(&a1, &b1, &a1_rev, &b1_rev);
343 checker.add_multiple_sources(&a2, &b2, &a2_rev, &b2_rev);
344 checker.add_multiple_sources(&a3, &b3, &a3_rev, &b3_rev);
345 assert!(checker.verify());
346 let l_str = if lazy { "lazy-" } else { "" };
347 println!(
348 "Time taken with {}checker {} us",
349 l_str,
350 start.elapsed().as_micros()
351 );
352 }
353
354 for lazy in [true, false] {
355 let start = Instant::now();
356 let mut checker = RandomizedPairingChecker::<Bls12_381>::new_using_rng(&mut rng, lazy);
357 checker.add_multiple_sources(&a1, b1_prep.clone(), &a1_rev, b1_rev_prep.clone());
358 checker.add_multiple_sources(&a2, b2_prep.clone(), &a2_rev, b2_rev_prep.clone());
359 checker.add_multiple_sources(&a3, b3_prep.clone(), &a3_rev, b3_rev_prep.clone());
360 assert!(checker.verify());
361 let l_str = if lazy { "lazy-" } else { "" };
362 println!(
363 "Time taken with prepared G2 and {}checker {} us",
364 l_str,
365 start.elapsed().as_micros()
366 );
367 }
368
369 for lazy in [true, false] {
370 let start = Instant::now();
371 let mut checker = RandomizedPairingChecker::<Bls12_381>::new_using_rng(&mut rng, lazy);
372 checker.add_multiple_sources_and_target(&a1, &b1, &out1);
373 checker.add_multiple_sources_and_target(&a2, &b2, &out2);
374 checker.add_multiple_sources_and_target(&a3, &b3, &out3);
375 checker.add_multiple_sources(&a1, &b1, &a1_rev, &b1_rev);
376 checker.add_multiple_sources(&a2, &b2, &a2_rev, &b2_rev);
377 checker.add_multiple_sources(&a3, &b3, &a3_rev, &b3_rev);
378 assert!(checker.verify());
379 let l_str = if lazy { "lazy-" } else { "" };
380 println!(
381 "Time taken with {}checker {} us",
382 l_str,
383 start.elapsed().as_micros()
384 );
385 }
386
387 for lazy in [true, false] {
388 let mut checker = RandomizedPairingChecker::<Bls12_381>::new_using_rng(&mut rng, lazy);
389 checker.add_sources(&a1[0], b1[0], &a1[0], b1[0]);
390 assert!(checker.verify());
391
392 let mut checker = RandomizedPairingChecker::<Bls12_381>::new_using_rng(&mut rng, lazy);
393 checker.add_sources(&a1[0], b1[0], &a1[0], b1[0]);
394 checker.add_sources(&a1[1], b1[1], &a1[1], b1[1]);
395 checker.add_sources(&a1[2], b1[2], &a1[2], b1[2]);
396 assert!(checker.verify());
397 }
398
399 for lazy in [true, false] {
400 let out_0 = Bls12_381::pairing(&a1[0], b1[0]);
401 let out_1 = Bls12_381::pairing(&a1[1], b1[1]);
402 let out_2 = Bls12_381::pairing(&a1[2], b1[2]);
403
404 let mut checker = RandomizedPairingChecker::<Bls12_381>::new_using_rng(&mut rng, lazy);
405 checker.add_sources_and_target(&a1[0], b1[0], &out_0);
406 assert!(checker.verify());
407
408 let mut checker = RandomizedPairingChecker::<Bls12_381>::new_using_rng(&mut rng, lazy);
409 checker.add_sources_and_target(&a1[0], b1[0], &out_0);
410 checker.add_sources_and_target(&a1[1], b1[1], &out_1);
411 checker.add_sources_and_target(&a1[2], b1[2], &out_2);
412 assert!(checker.verify());
413
414 let mut checker = RandomizedPairingChecker::<Bls12_381>::new_using_rng(&mut rng, lazy);
416 let wrong_out = Bls12_381::pairing(&a1[0], b1[1]);
417 checker.add_sources_and_target(&a1[0], b1[0], &wrong_out);
418 assert!(!checker.verify());
419 }
420 }
421}