openvm_pairing_guest/halo2curves_shims/bls12_381/
final_exp.rs1use alloc::vec::Vec;
2
3use halo2curves_axiom::bls12_381::{Fq, Fq12, Fq2};
4use lazy_static::lazy_static;
5use num_bigint::BigUint;
6use openvm_ecc_guest::{algebra::Field, AffinePoint};
7
8use super::{Bls12_381, FINAL_EXP_FACTOR, LAMBDA, POLY_FACTOR};
9use crate::{
10 halo2curves_shims::naf::biguint_to_naf,
11 pairing::{FinalExp, MultiMillerLoop},
12};
13
14lazy_static! {
15 static ref FINAL_EXP_FACTOR_NAF: Vec<i8> = biguint_to_naf(&FINAL_EXP_FACTOR);
16 static ref POLY_FACTOR_NAF: Vec<i8> = biguint_to_naf(&POLY_FACTOR);
17 static ref TWENTY_SEVEN_NAF: Vec<i8> = biguint_to_naf(&BigUint::from(27u32));
18 static ref TEN_NAF: Vec<i8> = biguint_to_naf(&BigUint::from(10u32));
19 static ref FINAL_EXP_TIMES_27: BigUint = FINAL_EXP_FACTOR.clone() * BigUint::from(27u32);
20 static ref FINAL_EXP_TIMES_27_MOD_POLY: BigUint = {
21 let exp_inv = FINAL_EXP_TIMES_27.modinv(&POLY_FACTOR.clone()).unwrap();
22 exp_inv % POLY_FACTOR.clone()
23 };
24 static ref FINAL_EXP_TIMES_27_MOD_POLY_NAF: Vec<i8> =
25 biguint_to_naf(&FINAL_EXP_TIMES_27_MOD_POLY);
26 static ref LAMBDA_INV_FINAL_EXP: BigUint =
27 LAMBDA.clone().modinv(&FINAL_EXP_FACTOR.clone()).unwrap();
28 static ref LAMBDA_INV_FINAL_EXP_NAF: Vec<i8> = biguint_to_naf(&LAMBDA_INV_FINAL_EXP);
29}
30
31#[allow(non_snake_case)]
34impl FinalExp for Bls12_381 {
35 type Fp = Fq;
36 type Fp2 = Fq2;
37 type Fp12 = Fq12;
38
39 fn assert_final_exp_is_one(
42 f: &Self::Fp12,
43 P: &[AffinePoint<Self::Fp>],
44 Q: &[AffinePoint<Self::Fp2>],
45 ) {
46 let (c, s) = Self::final_exp_hint(f);
47
48 let c_conj_inv = c.conjugate().invert().unwrap();
60 let c_inv = c.invert().unwrap();
61 let c_q_inv = c_inv.frobenius_map();
62 let fc = Self::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv));
63
64 assert_eq!(fc * c_q_inv * s, Fq12::ONE);
65 }
66
67 fn final_exp_hint(f: &Self::Fp12) -> (Self::Fp12, Self::Fp12) {
72 let f_final_exp = exp_naf(f, true, &FINAL_EXP_FACTOR_NAF);
73 let root = exp_naf(&f_final_exp, true, &TWENTY_SEVEN_NAF);
74
75 let root_pth_inv = if root == Fq12::ONE {
77 Fq12::ONE
78 } else {
79 exp_naf(&root, false, &FINAL_EXP_TIMES_27_MOD_POLY_NAF)
80 };
81
82 let root = exp_naf(&f_final_exp, true, &POLY_FACTOR_NAF);
83 let root_27th_inv = if exp_naf(&root, true, &TWENTY_SEVEN_NAF) == Fq12::ONE {
85 exp_naf(&root, false, &TEN_NAF)
86 } else {
87 Fq12::ONE
88 };
89
90 let s = root_pth_inv * root_27th_inv;
93 let f = f * s;
94
95 let c = exp_naf(&f, true, &LAMBDA_INV_FINAL_EXP_NAF);
98
99 (c, s)
100 }
101}
102
103fn exp_naf(base: &Fq12, is_positive: bool, digits_naf: &[i8]) -> Fq12 {
106 #[cfg(feature = "blstrs")]
107 {
108 exp_naf_blstrs(base, is_positive, digits_naf)
109 }
110 #[cfg(not(feature = "blstrs"))]
111 {
112 exp_naf_basic(base, is_positive, digits_naf)
113 }
114}
115
116#[cfg(not(feature = "blstrs"))]
117fn exp_naf_basic(base: &Fq12, is_positive: bool, digits_naf: &[i8]) -> Fq12 {
118 if digits_naf.is_empty() {
119 return Fq12::ONE;
120 }
121
122 let base = if !is_positive {
123 base.invert().unwrap_or(Fq12::ONE)
124 } else {
125 *base
126 };
127
128 let base_inv = if digits_naf.contains(&-1) {
129 Some(base.invert().unwrap_or(Fq12::ONE))
130 } else {
131 None
132 };
133
134 let mut res = Fq12::ONE;
135 for &digit in digits_naf.iter().rev() {
136 res = res.square();
137 if digit == 1 {
138 res *= base;
139 } else if digit == -1 {
140 res *= base_inv
141 .as_ref()
142 .expect("negative digit requires available inverse");
143 }
144 }
145
146 res
147}
148
149#[cfg(feature = "blstrs")]
150fn exp_naf_blstrs(base: &Fq12, is_positive: bool, digits_naf: &[i8]) -> Fq12 {
151 use halo2curves_axiom::ff::Field;
152
153 if digits_naf.is_empty() {
154 return <Fq12 as halo2curves_axiom::ff::Field>::ONE;
155 }
156
157 let base_blstrs = unsafe { core::mem::transmute::<Fq12, blstrs::Fp12>(*base) };
159
160 let base_blstrs = if !is_positive {
161 base_blstrs.invert().unwrap_or(blstrs::Fp12::ONE)
162 } else {
163 base_blstrs
164 };
165
166 let base_inv_blstrs = if digits_naf.contains(&-1) {
167 Some(base_blstrs.invert().unwrap_or(blstrs::Fp12::ONE))
168 } else {
169 None
170 };
171
172 let mut res_blstrs = blstrs::Fp12::ONE;
173 for &digit in digits_naf.iter().rev() {
174 res_blstrs = res_blstrs.square();
175 if digit == 1 {
176 res_blstrs *= base_blstrs;
177 } else if digit == -1 {
178 res_blstrs *= base_inv_blstrs
179 .as_ref()
180 .expect("negative digit requires available inverse");
181 }
182 }
183
184 unsafe { core::mem::transmute::<blstrs::Fp12, Fq12>(res_blstrs) }
186}