airbender_crypto/bls12_381/curves/
pairing_impl.rs1use super::*;
2use crate::bls12_381::{Fq12, Fq2};
3use ark_ec::bls12::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::BitIteratorBE;
11use ark_ff::One;
12use ark_ff::{CyclotomicMultSubgroup, Field, Zero};
13use ark_serialize::CanonicalDeserialize;
14use ark_serialize::CanonicalSerialize;
15
16impl Bls12_381 {
17 fn ell(f: &mut Fq12, coeffs: &EllCoeff<Config>, p: &G1Affine) {
19 let mut c0 = coeffs.0;
20 let mut c1 = coeffs.1;
21 let mut c2 = coeffs.2;
22 let (px, py) = p.xy().unwrap();
23
24 match Config::TWIST_TYPE {
25 TwistType::M => {
26 c2.mul_assign_by_fp(&py);
27 c1.mul_assign_by_fp(&px);
28 f.mul_by_014(&c0, &c1, &c2);
29 }
30 TwistType::D => {
31 c0.mul_assign_by_fp(&py);
32 c1.mul_assign_by_fp(&px);
33 f.mul_by_034(&c0, &c1, &c2);
34 }
35 }
36 }
37
38 fn exp_by_x(f: &Fq12, result: &mut Fq12) {
40 *result = *f;
41 Self::spec_cyclotomic_exp_by_x_inplace(result);
42
43 if Config::X_IS_NEGATIVE {
44 result.cyclotomic_inverse_in_place();
45 }
46 }
47
48 fn spec_cyclotomic_exp_by_x_inplace(f: &mut Fq12) {
49 use ark_ff::Zero;
50 if f.is_zero() {
51 return;
52 }
53
54 Self::fast_exp_loop_with_naf(f, Self::X_NAF.iter().copied());
55 }
56
57 const X_NAF: [i8; 65] = [
58 1, 0, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0,
61 ];
62
63 fn fast_exp_loop_with_naf<I: Iterator<Item = i8>>(f: &mut Fq12, e: I) {
65 let self_inverse = f.cyclotomic_inverse().unwrap();
66 let mut res = Fq12::one();
67 let mut found_nonzero = false;
68 for value in e {
69 if found_nonzero {
70 res.cyclotomic_square_in_place();
71 }
72
73 if value != 0 {
74 found_nonzero = true;
75
76 if value > 0 {
77 res *= &*f;
78 } else {
79 res *= &self_inverse;
80 }
81 }
82 }
83 *f = res;
84 }
85}
86
87impl Pairing for Bls12_381 {
88 type BaseField = Fq;
89 type ScalarField = crate::bls12_381::Fr;
90 type G1 = G1Projective;
91 type G1Affine = G1Affine;
92 type G1Prepared = ark_ec::bls12::G1Prepared<Config>;
93 type G2 = G2Projective;
94 type G2Affine = G2Affine;
95 type G2Prepared = G2PreparedNoAlloc;
96 type TargetField = Fq12;
97
98 fn multi_miller_loop(
99 a: impl IntoIterator<Item = impl Into<Self::G1Prepared>>,
100 b: impl IntoIterator<Item = impl Into<Self::G2Prepared>>,
101 ) -> ark_ec::pairing::MillerLoopOutput<Self> {
102 let mut a = a.into_iter();
103 let mut b = b.into_iter();
104 let mut result = Fq12::one();
105 loop {
106 match (a.next(), b.next()) {
107 (Some(p), Some(q)) => {
108 let p: Self::G1Prepared = p.into();
109 if p.is_zero() {
110 continue;
111 }
112 let q: Self::G2Prepared = q.into();
113 if q.is_zero() {
114 continue;
115 }
116
117 let mut f = Fq12::one();
118 let mut ell_coeffs = q.ell_coeffs.iter();
119
120 for i in BitIteratorBE::without_leading_zeros(Config::X).skip(1) {
121 f.square_in_place();
122 Self::ell(&mut f, &ell_coeffs.next().unwrap(), &p.0);
123 if i {
124 Self::ell(&mut f, &ell_coeffs.next().unwrap(), &p.0);
125 }
126 }
127
128 result *= f;
129 }
130 (None, None) => break,
131 _ => {
132 panic!("Caller must check input lengths");
133 }
134 }
135 }
136
137 if Config::X_IS_NEGATIVE {
138 result.cyclotomic_inverse_in_place();
139 }
140
141 ark_ec::pairing::MillerLoopOutput(result)
142 }
143
144 fn final_exponentiation(
145 f: ark_ec::pairing::MillerLoopOutput<Self>,
146 ) -> Option<PairingOutput<Self>> {
147 let f = f.0;
153 let mut f1 = f;
154 f1.cyclotomic_inverse_in_place();
155
156 f.inverse().map(|mut f2| {
157 let mut r = f1 * &f2;
160
161 f2 = r;
163 r.frobenius_map_in_place(2);
165
166 r *= &f2;
169
170 let mut y0 = r.cyclotomic_square();
173 let mut y1 = Fq12::zero();
175 Self::exp_by_x(&r, &mut y1);
176 let mut y2 = r;
178 y2.cyclotomic_inverse_in_place();
179 y1 *= &y2;
181 Self::exp_by_x(&y1, &mut y2);
183 y1.cyclotomic_inverse_in_place();
185 y1 *= &y2;
187 Self::exp_by_x(&y1, &mut y2);
189 y1.frobenius_map_in_place(1);
191 y1 *= &y2;
193 r *= &y0;
195 Self::exp_by_x(&y1, &mut y0);
197 Self::exp_by_x(&y0, &mut y2);
199 y0 = y1;
201 y0.frobenius_map_in_place(2);
202 y1.cyclotomic_inverse_in_place();
204 y1 *= &y2;
206 y1 *= &y0;
208 r *= &y1;
210 PairingOutput(r)
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(); BLS12_381_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>>;
228 BLS12_381_NUM_ELL_COEFFS] =
229 [const { core::mem::MaybeUninit::uninit() }; BLS12_381_NUM_ELL_COEFFS];
230
231 let mut r = G2HomProjective {
232 x: q.x,
233 y: q.y,
234 z: Fq2::one(),
235 };
236
237 for bit in BitIteratorBE::new(Config::X).skip(1) {
238 ell_coeffs[i].write(r.double_in_place(&two_inv));
239 i += 1;
240
241 if bit {
242 ell_coeffs[i].write(r.add_in_place(&q));
243 i += 1;
244 }
245 }
246
247 assert_eq!(i, ell_coeffs.len());
248
249 Self {
250 ell_coeffs: unsafe { ell_coeffs.map(|el| el.assume_init()) },
251 infinity: false,
252 }
253 }
254 }
255}
256
257#[derive(Clone, Copy, Debug)]
258struct G2HomProjective {
259 x: Fq2,
260 y: Fq2,
261 z: Fq2,
262}
263
264impl G2HomProjective {
265 fn double_in_place(&mut self, two_inv: &Fq) -> EllCoeff<Config> {
266 let mut a = self.x * &self.y;
270 a.mul_assign_by_fp(two_inv);
271 let b = self.y.square();
272 let c = self.z.square();
273 let e = <Config as Bls12Config>::G2Config::COEFF_B * &(c.double() + &c);
274 let f = e.double() + &e;
275 let mut g = b + &f;
276 g.mul_assign_by_fp(two_inv);
277 let h = (self.y + &self.z).square() - &(b + &c);
278 let i = e - &b;
279 let j = self.x.square();
280 let e_square = e.square();
281
282 self.x = a * &(b - &f);
283 self.y = g.square() - &(e_square.double() + &e_square);
284 self.z = b * &h;
285 match Config::TWIST_TYPE {
286 TwistType::M => (i, j.double() + &j, -h),
287 TwistType::D => (-h, j.double() + &j, i),
288 }
289 }
290
291 fn add_in_place(&mut self, q: &G2Affine) -> EllCoeff<Config> {
292 let theta = self.y - &(q.y * &self.z);
295 let lambda = self.x - &(q.x * &self.z);
296 let c = theta.square();
297 let d = lambda.square();
298 let e = lambda * &d;
299 let f = self.z * &c;
300 let g = self.x * &d;
301 let h = e + &f - &g.double();
302 self.x = lambda * &h;
303 self.y = theta * &(g - &h) - &(e * &self.y);
304 self.z *= &e;
305 let j = theta * &q.x - &(lambda * &q.y);
306
307 match Config::TWIST_TYPE {
308 TwistType::M => (j, -theta, lambda),
309 TwistType::D => (lambda, -theta, j),
310 }
311 }
312}
313
314impl Default for G2PreparedNoAlloc {
315 fn default() -> Self {
316 Self::from(G2Affine::generator())
317 }
318}
319
320impl From<G2Projective> for G2PreparedNoAlloc {
321 fn from(q: G2Projective) -> Self {
322 q.into_affine().into()
323 }
324}
325
326impl<'a> From<&'a G2Affine> for G2PreparedNoAlloc {
327 fn from(other: &'a G2Affine) -> Self {
328 (*other).into()
329 }
330}
331
332impl<'a> From<&'a G2Projective> for G2PreparedNoAlloc {
333 fn from(q: &'a G2Projective) -> Self {
334 q.into_affine().into()
335 }
336}
337
338impl G2PreparedNoAlloc {
339 pub fn is_zero(&self) -> bool {
340 self.infinity
341 }
342}
343
344pub const BLS12_381_NUM_ELL_COEFFS: usize = const {
345 let num_bits_except_top_one = (u64::BITS - Config::X[0].leading_zeros() - 1) as usize;
346 let mut result = num_bits_except_top_one;
347 let num_non_zero_bits = Config::X[0].count_ones();
348 result += num_non_zero_bits as usize - 1;
350
351 result
352};
353
354#[derive(Clone, Debug, PartialEq, Eq)]
355pub struct G2PreparedNoAlloc {
356 pub ell_coeffs: [ark_ec::bls12::g2::EllCoeff<Config>; BLS12_381_NUM_ELL_COEFFS],
357 pub infinity: bool,
358}
359
360impl CanonicalSerialize for G2PreparedNoAlloc {
361 fn serialize_with_mode<W: ark_serialize::Write>(
362 &self,
363 _writer: W,
364 _compress: ark_serialize::Compress,
365 ) -> Result<(), ark_serialize::SerializationError> {
366 unimplemented!("not supported");
367 }
368
369 fn serialized_size(&self, _compress: ark_serialize::Compress) -> usize {
370 unimplemented!("not supported");
371 }
372}
373
374impl ark_serialize::Valid for G2PreparedNoAlloc {
375 fn check(&self) -> Result<(), ark_serialize::SerializationError> {
376 unimplemented!("not supported");
377 }
378}
379
380impl CanonicalDeserialize for G2PreparedNoAlloc {
381 fn deserialize_with_mode<R: ark_serialize::Read>(
382 _reader: R,
383 _compress: ark_serialize::Compress,
384 _validate: ark_serialize::Validate,
385 ) -> Result<Self, ark_serialize::SerializationError> {
386 unimplemented!("not supported");
387 }
388}
389
390#[cfg(test)]
391mod test {
392 use super::*;
393
394 #[test]
395 fn compute_x_naf() {
396 let t = ark_ff::biginteger::arithmetic::find_naf(Config::X);
397 let t: Vec<_> = t.into_iter().rev().collect();
398 dbg!(t);
399 }
400}