airbender_crypto/bn254/curves/
pairing_impl.rs1use super::*;
2use crate::bn254::Fq12;
3use ark_ec::bn::g2::EllCoeff;
4use ark_ec::pairing::Pairing;
5use ark_ec::pairing::PairingOutput;
6use ark_ec::short_weierstrass::SWCurveConfig;
7use ark_ec::AdditiveGroup;
8use ark_ec::AffineRepr;
9use ark_ec::CurveGroup;
10use ark_ff::One;
11use ark_ff::{CyclotomicMultSubgroup, Field};
12use ark_serialize::CanonicalDeserialize;
13use ark_serialize::CanonicalSerialize;
14
15impl Bn254 {
16 fn ell(f: &mut Fq12, coeffs: &EllCoeff<Config>, p: &G1Affine) {
18 let mut c0 = coeffs.0;
19 let mut c1 = coeffs.1;
20 let mut c2 = coeffs.2;
21
22 match Config::TWIST_TYPE {
23 TwistType::M => {
24 c2.mul_assign_by_fp(&p.y);
25 c1.mul_assign_by_fp(&p.x);
26 f.mul_by_014(&c0, &c1, &c2);
27 }
28 TwistType::D => {
29 c0.mul_assign_by_fp(&p.y);
30 c1.mul_assign_by_fp(&p.x);
31 f.mul_by_034(&c0, &c1, &c2);
32 }
33 }
34 }
35
36 fn exp_by_neg_x(mut f: Fq12) -> Fq12 {
37 Self::spec_cyclotomic_exp_by_x_inplace(&mut f);
38
39 if !Config::X_IS_NEGATIVE {
40 f.cyclotomic_inverse_in_place();
41 }
42 f
43 }
44
45 fn spec_cyclotomic_exp_by_x_inplace(f: &mut Fq12) {
46 use ark_ff::Zero;
47 if f.is_zero() {
48 return;
49 }
50
51 Self::fast_exp_loop_with_naf(f, Self::X_NAF.iter().copied());
52 }
53
54 const X_NAF: [i8; 63] = [
55 1, 0, 0, 0, 1, 0, 1, 0, 0, -1, 0, 1, 0, 1, 0, -1, 0, 0, 1, 0, 1, 0, -1, 0, -1, 0, -1, 0, 1,
56 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, -1,
57 0, 0, 0, 1,
58 ];
59
60 fn fast_exp_loop_with_naf<I: Iterator<Item = i8>>(f: &mut Fq12, e: I) {
62 let self_inverse = f.cyclotomic_inverse().unwrap();
63 let mut res = Fq12::one();
64 let mut found_nonzero = false;
65 for value in e {
66 if found_nonzero {
67 res.cyclotomic_square_in_place();
68 }
69
70 if value != 0 {
71 found_nonzero = true;
72
73 if value > 0 {
74 res *= &*f;
75 } else {
76 res *= &self_inverse;
77 }
78 }
79 }
80 *f = res;
81 }
82}
83
84impl Pairing for Bn254 {
85 type BaseField = Fq;
86 type ScalarField = crate::bn254::Fr;
87 type G1 = G1Projective;
88 type G1Affine = G1Affine;
89 type G1Prepared = ark_ec::bn::G1Prepared<Config>;
90 type G2 = G2Projective;
91 type G2Affine = G2Affine;
92 type G2Prepared = G2PreparedNoAlloc;
93 type TargetField = Fq12;
94
95 fn multi_miller_loop(
96 a: impl IntoIterator<Item = impl Into<Self::G1Prepared>>,
97 b: impl IntoIterator<Item = impl Into<Self::G2Prepared>>,
98 ) -> ark_ec::pairing::MillerLoopOutput<Self> {
99 let mut a = a.into_iter();
100 let mut b = b.into_iter();
101 let mut result = Fq12::one();
102 loop {
103 match (a.next(), b.next()) {
104 (Some(p), Some(q)) => {
105 let p: Self::G1Prepared = p.into();
106 if p.is_zero() {
107 continue;
108 }
109 let q: Self::G2Prepared = q.into();
110 if q.is_zero() {
111 continue;
112 }
113
114 let mut f = Fq12::one();
115 let mut ell_coeffs = q.ell_coeffs.iter();
116
117 for i in (1..Config::ATE_LOOP_COUNT.len()).rev() {
118 if i != Config::ATE_LOOP_COUNT.len() - 1 {
119 f.square_in_place();
120 }
121
122 Self::ell(&mut f, ell_coeffs.next().unwrap(), &p.0);
123
124 let bit = Config::ATE_LOOP_COUNT[i - 1];
125 if bit == 1 || bit == -1 {
126 Self::ell(&mut f, &ell_coeffs.next().unwrap(), &p.0);
127 }
128 }
129
130 if Config::X_IS_NEGATIVE {
131 f.cyclotomic_inverse_in_place();
132 }
133
134 Self::ell(&mut f, ell_coeffs.next().unwrap(), &p.0);
135 Self::ell(&mut f, ell_coeffs.next().unwrap(), &p.0);
136
137 result *= f;
138 }
139 (None, None) => break,
140 _ => {
141 panic!("Caller must check input lengths");
142 }
143 }
144 }
145
146 ark_ec::pairing::MillerLoopOutput(result)
147 }
148
149 fn final_exponentiation(
150 f: ark_ec::pairing::MillerLoopOutput<Self>,
151 ) -> Option<PairingOutput<Self>> {
152 let f: crate::bn254::Fq12 = f.0;
156
157 let mut f1 = f;
159 f1.cyclotomic_inverse_in_place();
160
161 f.inverse().map(|mut f2| {
162 let mut r = f1 * &f2;
165
166 f2 = r;
168 r.frobenius_map_in_place(2);
170
171 r *= &f2;
174
175 let y0 = Self::exp_by_neg_x(r);
187 let y1 = y0.cyclotomic_square();
188 let y2 = y1.cyclotomic_square();
189 let mut y3 = y2 * &y1;
190 let y4 = Self::exp_by_neg_x(y3);
191 let y5 = y4.cyclotomic_square();
192 let mut y6 = Self::exp_by_neg_x(y5);
193 y3.cyclotomic_inverse_in_place();
194 y6.cyclotomic_inverse_in_place();
195 let y7 = y6 * &y4;
196 let mut y8 = y7 * &y3;
197 let y9 = y8 * &y1;
198 let y10 = y8 * &y4;
199 let y11 = y10 * &r;
200 let mut y12 = y9;
201 y12.frobenius_map_in_place(1);
202 let y13 = y12 * &y11;
203 y8.frobenius_map_in_place(2);
204 let y14 = y8 * &y13;
205 r.cyclotomic_inverse_in_place();
206 let mut y15 = r * &y9;
207 y15.frobenius_map_in_place(3);
208 let y16 = y15 * &y14;
209
210 PairingOutput(y16)
211 })
212 }
213}
214
215impl From<G2Affine> for G2PreparedNoAlloc {
216 fn from(q: G2Affine) -> Self {
217 if q.infinity {
218 Self {
220 ell_coeffs: [Default::default(); BN254_NUM_ELL_COEFFS],
221 infinity: true,
222 }
223 } else {
224 use ark_ff::{AdditiveGroup, One};
225 let two_inv = Fq::one().double().inverse().unwrap();
226 let mut i = 0;
227 let mut ell_coeffs: [core::mem::MaybeUninit<EllCoeff<Config>>; BN254_NUM_ELL_COEFFS] =
228 [const { core::mem::MaybeUninit::uninit() }; BN254_NUM_ELL_COEFFS];
229
230 let mut r = G2HomProjective {
231 x: q.x,
232 y: q.y,
233 z: Fq2::one(),
234 };
235
236 let neg_q = -q;
237
238 for bit in Config::ATE_LOOP_COUNT.iter().rev().skip(1) {
239 ell_coeffs[i].write(r.double_in_place(&two_inv));
240 i += 1;
241
242 let coeff = match bit {
243 1 => r.add_in_place(&q),
244 -1 => r.add_in_place(&neg_q),
245 _ => continue,
246 };
247 ell_coeffs[i].write(coeff);
248 i += 1;
249 }
250
251 let q1 = mul_by_char(q);
252 let mut q2 = mul_by_char(q1);
253
254 if Config::X_IS_NEGATIVE {
255 r.y = -r.y;
256 }
257
258 q2.y = -q2.y;
259
260 ell_coeffs[i].write(r.add_in_place(&q1));
261 i += 1;
262 ell_coeffs[i].write(r.add_in_place(&q2));
263 i += 1;
264
265 assert_eq!(i, ell_coeffs.len());
266
267 Self {
268 ell_coeffs: unsafe { ell_coeffs.map(|el| el.assume_init()) },
269 infinity: false,
270 }
271 }
272 }
273}
274
275#[derive(Clone, Copy, Debug)]
276struct G2HomProjective {
277 x: Fq2,
278 y: Fq2,
279 z: Fq2,
280}
281
282impl G2HomProjective {
283 fn double_in_place(&mut self, two_inv: &Fq) -> EllCoeff<Config> {
284 let mut a = self.x * &self.y;
288 a.mul_assign_by_fp(two_inv);
289 let b = self.y.square();
290 let c = self.z.square();
291 let e = <Config as BnConfig>::G2Config::COEFF_B * &(c.double() + &c);
292 let f = e.double() + &e;
293 let mut g = b + &f;
294 g.mul_assign_by_fp(two_inv);
295 let h = (self.y + &self.z).square() - &(b + &c);
296 let i = e - &b;
297 let j = self.x.square();
298 let e_square = e.square();
299
300 self.x = a * &(b - &f);
301 self.y = g.square() - &(e_square.double() + &e_square);
302 self.z = b * &h;
303 match Config::TWIST_TYPE {
304 TwistType::M => (i, j.double() + &j, -h),
305 TwistType::D => (-h, j.double() + &j, i),
306 }
307 }
308
309 fn add_in_place(&mut self, q: &G2Affine) -> EllCoeff<Config> {
310 let theta = self.y - &(q.y * &self.z);
313 let lambda = self.x - &(q.x * &self.z);
314 let c = theta.square();
315 let d = lambda.square();
316 let e = lambda * &d;
317 let f = self.z * &c;
318 let g = self.x * &d;
319 let h = e + &f - &g.double();
320 self.x = lambda * &h;
321 self.y = theta * &(g - &h) - &(e * &self.y);
322 self.z *= &e;
323 let j = theta * &q.x - &(lambda * &q.y);
324
325 match Config::TWIST_TYPE {
326 TwistType::M => (j, -theta, lambda),
327 TwistType::D => (lambda, -theta, j),
328 }
329 }
330}
331
332impl Default for G2PreparedNoAlloc {
333 fn default() -> Self {
334 Self::from(G2Affine::generator())
335 }
336}
337
338impl From<G2Projective> for G2PreparedNoAlloc {
339 fn from(q: G2Projective) -> Self {
340 q.into_affine().into()
341 }
342}
343
344impl<'a> From<&'a G2Affine> for G2PreparedNoAlloc {
345 fn from(other: &'a G2Affine) -> Self {
346 (*other).into()
347 }
348}
349
350impl<'a> From<&'a G2Projective> for G2PreparedNoAlloc {
351 fn from(q: &'a G2Projective) -> Self {
352 q.into_affine().into()
353 }
354}
355
356impl G2PreparedNoAlloc {
357 pub fn is_zero(&self) -> bool {
358 self.infinity
359 }
360}
361
362fn mul_by_char(r: G2Affine) -> G2Affine {
363 use ark_ff::Field;
365
366 let mut s = r;
367 s.x.frobenius_map_in_place(1);
368 s.x *= &Config::TWIST_MUL_BY_Q_X;
369 s.y.frobenius_map_in_place(1);
370 s.y *= &Config::TWIST_MUL_BY_Q_Y;
371
372 s
373}
374
375pub const BN254_NUM_ELL_COEFFS: usize = const {
376 let mut result = 2;
377
378 let mut i = 0;
379 while i < Config::ATE_LOOP_COUNT.len() - 1 {
380 result += 1;
381 if Config::ATE_LOOP_COUNT[i] != 0 {
382 result += 1;
383 }
384
385 i += 1;
386 }
387
388 result
389};
390
391#[derive(Clone, Debug, PartialEq, Eq)]
392pub struct G2PreparedNoAlloc {
393 pub ell_coeffs: [ark_ec::bn::g2::EllCoeff<Config>; BN254_NUM_ELL_COEFFS],
396 pub infinity: bool,
397}
398
399impl CanonicalSerialize for G2PreparedNoAlloc {
400 fn serialize_with_mode<W: ark_serialize::Write>(
401 &self,
402 _writer: W,
403 _compress: ark_serialize::Compress,
404 ) -> Result<(), ark_serialize::SerializationError> {
405 unimplemented!("not supported");
406 }
407
408 fn serialized_size(&self, _compress: ark_serialize::Compress) -> usize {
409 unimplemented!("not supported");
410 }
411}
412
413impl ark_serialize::Valid for G2PreparedNoAlloc {
414 fn check(&self) -> Result<(), ark_serialize::SerializationError> {
415 unimplemented!("not supported");
416 }
417}
418
419impl CanonicalDeserialize for G2PreparedNoAlloc {
420 fn deserialize_with_mode<R: ark_serialize::Read>(
421 _reader: R,
422 _compress: ark_serialize::Compress,
423 _validate: ark_serialize::Validate,
424 ) -> Result<Self, ark_serialize::SerializationError> {
425 unimplemented!("not supported");
426 }
427}
428
429#[cfg(test)]
430mod test {
431 use super::*;
432
433 #[test]
434 fn compute_x_naf() {
435 let t = ark_ff::biginteger::arithmetic::find_naf(Config::X);
436 let t: Vec<_> = t.into_iter().rev().collect();
437 dbg!(t);
438 }
439}