openvm_pairing_guest/halo2curves_shims/bls12_381/
miller_loop.rs1use alloc::vec::Vec;
2
3use halo2curves_axiom::bls12_381::{Fq, Fq12, Fq2};
4use itertools::izip;
5use openvm_ecc_guest::{
6 algebra::{DivUnsafe, Field},
7 AffinePoint,
8};
9
10use super::Bls12_381;
11use crate::pairing::{
12 Evaluatable, EvaluatedLine, LineMulMType, MillerStep, MultiMillerLoop, UnevaluatedLine,
13};
14
15pub const BLS12_381_SEED_ABS: u64 = 0xd201000000010000;
16pub const BLS12_381_PSEUDO_BINARY_ENCODING: [i8; 64] = [
20 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, 0, 0, 0, 0,
21 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, 1, 0, 0, 1, 0, 1, 1,
22];
23
24#[test]
25fn test_bls12381_pseudo_binary_encoding() {
26 let mut x: i128 = 0;
27 let mut power_of_2 = 1;
28 for b in BLS12_381_PSEUDO_BINARY_ENCODING.iter() {
29 x += (*b as i128) * power_of_2;
30 power_of_2 *= 2;
31 }
32 assert_eq!(x.unsigned_abs(), BLS12_381_SEED_ABS as u128);
33}
34
35impl MillerStep for Bls12_381 {
36 type Fp2 = Fq2;
37
38 fn miller_double_step(
40 s: &AffinePoint<Self::Fp2>,
41 ) -> (AffinePoint<Self::Fp2>, UnevaluatedLine<Self::Fp2>) {
42 let one = &Self::Fp2::ONE;
43 let two = &(one + one);
44 let three = &(one + two);
45
46 let x = &s.x;
47 let y = &s.y;
48 let lambda = &((three * x * x).div_unsafe(&(two * y)));
50 let x_2s = lambda * lambda - two * x;
52 let y_2s = lambda * (x - x_2s) - y;
54 let two_s = AffinePoint { x: x_2s, y: y_2s };
55
56 let b = lambda.neg();
63 let c = lambda * x - y;
64
65 (two_s, UnevaluatedLine { b, c })
66 }
67
68 fn miller_add_step(
69 s: &AffinePoint<Self::Fp2>,
70 q: &AffinePoint<Self::Fp2>,
71 ) -> (AffinePoint<Self::Fp2>, UnevaluatedLine<Self::Fp2>) {
72 let x_s = &s.x;
73 let y_s = &s.y;
74 let x_q = &q.x;
75 let y_q = &q.y;
76
77 let x_delta = x_s - x_q;
79 let lambda = (y_s - y_q).div_unsafe(&x_delta);
80 let x_s_plus_q = lambda * lambda - x_s - x_q;
81 let y_s_plus_q = lambda * (x_q - x_s_plus_q) - y_q;
82
83 let s_plus_q = AffinePoint {
84 x: x_s_plus_q,
85 y: y_s_plus_q,
86 };
87
88 let b = lambda.neg();
90 let c = lambda * x_s - y_s;
91
92 (s_plus_q, UnevaluatedLine { b, c })
93 }
94
95 #[allow(clippy::type_complexity)]
97 fn miller_double_and_add_step(
98 s: &AffinePoint<Self::Fp2>,
99 q: &AffinePoint<Self::Fp2>,
100 ) -> (
101 AffinePoint<Self::Fp2>,
102 UnevaluatedLine<Self::Fp2>,
103 UnevaluatedLine<Self::Fp2>,
104 ) {
105 let one = &Self::Fp2::ONE;
106 let two = &(one + one);
107
108 let x_s = &s.x;
109 let y_s = &s.y;
110 let x_q = &q.x;
111 let y_q = &q.y;
112
113 let lambda1 = &((y_s - y_q).div_unsafe(&(x_s - x_q)));
115 let x_s_plus_q = lambda1 * lambda1 - x_s - x_q;
116
117 let lambda2 = &(lambda1.neg() - (two * y_s).div_unsafe(&(x_s_plus_q - x_s)));
119 let x_s_plus_q_plus_s = lambda2 * lambda2 - x_s - x_s_plus_q;
120 let y_s_plus_q_plus_s = lambda2 * (x_s - x_s_plus_q_plus_s) - y_s;
121
122 let s_plus_q_plus_s = AffinePoint {
123 x: x_s_plus_q_plus_s,
124 y: y_s_plus_q_plus_s,
125 };
126
127 let b0 = lambda1.neg();
129 let c0 = lambda1 * x_s - y_s;
130
131 let b1 = lambda2.neg();
133 let c1 = lambda2 * x_s - y_s;
134
135 (
136 s_plus_q_plus_s,
137 UnevaluatedLine { b: b0, c: c0 },
138 UnevaluatedLine { b: b1, c: c1 },
139 )
140 }
141}
142
143#[allow(non_snake_case)]
144impl MultiMillerLoop for Bls12_381 {
145 type Fp = Fq;
146 type Fp12 = Fq12;
147
148 const SEED_ABS: u64 = BLS12_381_SEED_ABS;
149 const PSEUDO_BINARY_ENCODING: &[i8] = &BLS12_381_PSEUDO_BINARY_ENCODING;
150
151 fn evaluate_lines_vec(f: Fq12, lines: Vec<EvaluatedLine<Fq2>>) -> Fq12 {
152 let mut f = f;
153 let mut lines = lines;
154 if lines.len() % 2 == 1 {
155 f = Self::mul_by_023(&f, &lines.pop().unwrap());
156 }
157 for chunk in lines.chunks(2) {
158 if let [line0, line1] = chunk {
159 let prod = Self::mul_023_by_023(line0, line1);
160 f = Self::mul_by_02345(&f, &prod);
161 } else {
162 panic!("lines.len() % 2 should be 0 at this point");
163 }
164 }
165 f
166 }
167
168 fn pre_loop(
171 Q_acc: Vec<AffinePoint<Fq2>>,
172 Q: &[AffinePoint<Fq2>],
173 c: Option<Fq12>,
174 xy_fracs: &[(Fq, Fq)],
175 ) -> (Fq12, Vec<AffinePoint<Fq2>>) {
176 let mut f = if let Some(mut c) = c {
177 let mut c3 = c;
182 c.square_assign();
183 c3 *= &c;
184 c3
185 } else {
186 Self::Fp12::ONE
187 };
188
189 let mut Q_acc = Q_acc;
190
191 let (Q_out_double, lines_2S) = Q_acc
196 .into_iter()
197 .map(|Q| Self::miller_double_step(&Q))
198 .unzip::<_, _, Vec<_>, Vec<_>>();
199 Q_acc = Q_out_double;
200
201 let mut initial_lines = Vec::<EvaluatedLine<Fq2>>::new();
202
203 let lines_iter = izip!(lines_2S.iter(), xy_fracs.iter());
204 for (line_2S, xy_frac) in lines_iter {
205 let line = line_2S.evaluate(xy_frac);
206 initial_lines.push(line);
207 }
208
209 let (Q_out_add, lines_S_plus_Q) = Q_acc
210 .iter()
211 .zip(Q.iter())
212 .map(|(Q_acc, Q)| Self::miller_add_step(Q_acc, Q))
213 .unzip::<_, _, Vec<_>, Vec<_>>();
214 Q_acc = Q_out_add;
215
216 let lines_iter = izip!(lines_S_plus_Q.iter(), xy_fracs.iter());
217 for (lines_S_plus_Q, xy_frac) in lines_iter {
218 let line = lines_S_plus_Q.evaluate(xy_frac);
219 initial_lines.push(line);
220 }
221
222 f = Self::evaluate_lines_vec(f, initial_lines);
223
224 (f, Q_acc)
225 }
226
227 fn post_loop(
230 f: &Fq12,
231 Q_acc: Vec<AffinePoint<Fq2>>,
232 _Q: &[AffinePoint<Fq2>],
233 _c: Option<Fq12>,
234 _xy_fracs: &[(Fq, Fq)],
235 ) -> (Fq12, Vec<AffinePoint<Fq2>>) {
236 let f = f.conjugate();
244 (f, Q_acc)
245 }
246}