openvm_pairing_guest/halo2curves_shims/bn254/
final_exp.rs1use alloc::vec::Vec;
2
3use halo2curves_axiom::{
4 bn256::{Fq, Fq12, Fq2},
5 ff::Field,
6};
7use lazy_static::lazy_static;
8use openvm_ecc_guest::{
9 algebra::{field::FieldExtension, ExpBytes},
10 AffinePoint,
11};
12
13use super::{Bn254, EXP1, EXP2, M_INV, R_INV, U27_COEFF_0, U27_COEFF_1};
14use crate::{
15 halo2curves_shims::naf::biguint_to_naf,
16 pairing::{FinalExp, MultiMillerLoop},
17};
18
19lazy_static! {
20 static ref EXP1_NAF: Vec<i8> = biguint_to_naf(&EXP1);
21 static ref EXP2_NAF: Vec<i8> = biguint_to_naf(&EXP2);
22 static ref R_INV_NAF: Vec<i8> = biguint_to_naf(&R_INV);
23 static ref M_INV_NAF: Vec<i8> = biguint_to_naf(&M_INV);
24 pub static ref UNITY_ROOT_27: Fq12 = {
25 let u0 = U27_COEFF_0.to_u64_digits();
26 let u1 = U27_COEFF_1.to_u64_digits();
27 let u_coeffs = Fq2::from_coeffs([
28 Fq::from_raw([u0[0], u0[1], u0[2], u0[3]]),
29 Fq::from_raw([u1[0], u1[1], u1[2], u1[3]]),
30 ]);
31 Fq12::from_coeffs([
32 Fq2::ZERO,
33 Fq2::ZERO,
34 u_coeffs,
35 Fq2::ZERO,
36 Fq2::ZERO,
37 Fq2::ZERO,
38 ])
39 };
40 pub static ref UNITY_ROOT_27_EXP2: Fq12 = UNITY_ROOT_27.exp_naf(true, &EXP2_NAF);
41}
42
43#[allow(non_snake_case)]
44impl FinalExp for Bn254 {
45 type Fp = Fq;
46 type Fp2 = Fq2;
47 type Fp12 = Fq12;
48
49 fn assert_final_exp_is_one(
50 f: &Self::Fp12,
51 P: &[AffinePoint<Self::Fp>],
52 Q: &[AffinePoint<Self::Fp2>],
53 ) {
54 let (c, u) = Self::final_exp_hint(f);
55 let c_inv = c.invert().unwrap();
56
57 let c_q3_inv = FieldExtension::frobenius_map(&c_inv, 3);
66 let c_q2 = FieldExtension::frobenius_map(&c, 2);
67 let c_q_inv = FieldExtension::frobenius_map(&c_inv, 1);
68 let c_mul = c_q3_inv * c_q2 * c_q_inv;
69
70 let fc = Self::multi_miller_loop_embedded_exp(P, Q, Some(c_inv));
72
73 assert_eq!(fc * c_mul * u, Fq12::ONE);
74 }
75
76 fn final_exp_hint(f: &Self::Fp12) -> (Self::Fp12, Self::Fp12) {
81 let mut c;
83 let u;
85
86 let unity_root_27 = *UNITY_ROOT_27;
87 debug_assert_eq!(unity_root_27.pow([27]), Fq12::one());
88
89 if f.exp_naf(true, &EXP1_NAF) == Fq12::ONE {
90 c = *f;
91 u = Fq12::ONE;
92 } else {
93 let f_mul_unity_root_27 = f * unity_root_27;
94 if f_mul_unity_root_27.exp_naf(true, &EXP1_NAF) == Fq12::ONE {
95 c = f_mul_unity_root_27;
96 u = unity_root_27;
97 } else {
98 c = f_mul_unity_root_27 * unity_root_27;
99 u = unity_root_27.square();
100 }
101 }
102
103 c = c.exp_naf(true, &R_INV_NAF);
106
107 c = c.exp_naf(true, &M_INV_NAF);
112
113 let mut x = c.exp_naf(true, &EXP2_NAF);
119
120 let c_inv = c.invert().unwrap();
122 let mut x3 = x.square() * x * c_inv;
123 let mut t = 0;
124 let mut tmp = x3.square();
125
126 fn tonelli_shanks_loop(x3: &mut Fq12, tmp: &mut Fq12, t: &mut i32) {
128 while *x3 != Fq12::ONE {
129 *tmp = (*x3).square();
130 *x3 *= *tmp;
131 *t += 1;
132 }
133 }
134
135 tonelli_shanks_loop(&mut x3, &mut tmp, &mut t);
136
137 let unity_root_27_exp2 = *UNITY_ROOT_27_EXP2;
138 while t != 0 {
139 tmp = unity_root_27_exp2;
140 x *= tmp;
141
142 x3 = x.square() * x * c_inv;
143 t = 0;
144 tonelli_shanks_loop(&mut x3, &mut tmp, &mut t);
145 }
146
147 debug_assert_eq!(c, x * x * x);
148 c = x;
150
151 (c, u)
152 }
153}