openvm_pairing_guest/bls12_381/
pairing.rs
1use alloc::vec::Vec;
2
3use itertools::izip;
4use openvm_algebra_guest::{
5 field::{ComplexConjugate, FieldExtension},
6 DivUnsafe, Field,
7};
8use openvm_ecc_guest::AffinePoint;
9#[cfg(target_os = "zkvm")]
10use {
11 crate::{PairingBaseFunct7, OPCODE, PAIRING_FUNCT3},
12 core::mem::MaybeUninit,
13 openvm_platform::custom_insn_r,
14 openvm_rv32im_guest,
15 openvm_rv32im_guest::hint_buffer_u32,
16};
17
18use super::{Bls12_381, Fp, Fp12, Fp2, BLS12_381_PSEUDO_BINARY_ENCODING, BLS12_381_SEED_ABS};
19use crate::pairing::{
20 exp_check_fallback, Evaluatable, EvaluatedLine, FromLineMType, LineMulMType, MillerStep,
21 MultiMillerLoop, PairingCheck, PairingCheckError, PairingIntrinsics, UnevaluatedLine,
22};
23#[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))]
24use crate::{
25 bls12_381::utils::{
26 convert_bls12381_fp2_to_halo2_fq2, convert_bls12381_fp_to_halo2_fq,
27 convert_bls12381_halo2_fq12_to_fp12,
28 },
29 halo2curves_shims::bls12_381::Bls12_381 as Halo2CurvesBls12_381,
30 pairing::FinalExp,
31};
32
33impl Evaluatable<Fp, Fp2> for UnevaluatedLine<Fp2> {
34 fn evaluate(&self, xy_frac: &(Fp, Fp)) -> EvaluatedLine<Fp2> {
35 let (x_over_y, y_inv) = xy_frac;
36 EvaluatedLine {
38 b: self.b.mul_base(x_over_y),
39 c: self.c.mul_base(y_inv),
40 }
41 }
42}
43
44impl FromLineMType<Fp2> for Fp12 {
45 fn from_evaluated_line_m_type(line: EvaluatedLine<Fp2>) -> Fp12 {
48 Fp12::from_coeffs([line.c, Fp2::ZERO, line.b, Fp2::ONE, Fp2::ZERO, Fp2::ZERO])
49 }
50}
51
52impl LineMulMType<Fp2, Fp12> for Bls12_381 {
54 fn mul_023_by_023(l0: &EvaluatedLine<Fp2>, l1: &EvaluatedLine<Fp2>) -> [Fp2; 5] {
56 let b0 = &l0.b;
58 let c0 = &l0.c;
59 let b1 = &l1.b;
61 let c1 = &l1.c;
62
63 let x0 = c0 * c1 + Bls12_381::XI;
67 let x2 = c0 * b1 + c1 * b0;
68 let x3 = c0 + c1;
69 let x4 = b0 * b1;
70 let x5 = b0 + b1;
71
72 [x0, x2, x3, x4, x5]
73 }
74
75 fn mul_by_023(f: &Fp12, l: &EvaluatedLine<Fp2>) -> Fp12 {
77 Fp12::from_evaluated_line_m_type(l.clone()) * f
80 }
81
82 fn mul_by_02345(f: &Fp12, x: &[Fp2; 5]) -> Fp12 {
84 let o0 = &x[0]; let o1 = &x[1]; let o2 = &x[3]; let o4 = &x[2]; let o5 = &x[4]; let xi = &Bls12_381::XI;
104
105 let self_coeffs = &f.c;
106 let s0 = &self_coeffs[0];
107 let s1 = &self_coeffs[2];
108 let s2 = &self_coeffs[4];
109 let s3 = &self_coeffs[1];
110 let s4 = &self_coeffs[3];
111 let s5 = &self_coeffs[5];
112
113 let c00 = s0 * o0 + xi * &(s1 * o2 + s2 * o1 + s3 * o5 + s4 * o4);
122 let c01 = s0 * o1 + s1 * o0 + xi * &(s2 * o2 + s4 * o5 + s5 * o4);
123 let c02 = s0 * o2 + s1 * o1 + s2 * o0 + s3 * o4 + xi * &(s5 * o5);
124 let c10 = s3 * o0 + xi * &(s1 * o5 + s2 * o4 + s4 * o2 + s5 * o1);
125 let c11 = s0 * o4 + s3 * o1 + s4 * o0 + xi * &(s2 * o5 + s5 * o2);
126 let c12 = s0 * o5 + s1 * o4 + s3 * o2 + s4 * o1 + s5 * o0;
127
128 Fp12::from_coeffs([c00, c10, c01, c11, c02, c12])
129 }
130}
131
132#[allow(non_snake_case)]
133impl MultiMillerLoop for Bls12_381 {
134 type Fp = Fp;
135 type Fp12 = Fp12;
136
137 const SEED_ABS: u64 = BLS12_381_SEED_ABS;
138 const PSEUDO_BINARY_ENCODING: &[i8] = &BLS12_381_PSEUDO_BINARY_ENCODING;
139
140 fn evaluate_lines_vec(f: Self::Fp12, lines: Vec<EvaluatedLine<Self::Fp2>>) -> Self::Fp12 {
141 let mut f = f;
142 let mut lines = lines;
143 if lines.len() % 2 == 1 {
144 f = Self::mul_by_023(&f, &lines.pop().unwrap());
145 }
146 for chunk in lines.chunks(2) {
147 if let [line0, line1] = chunk {
148 let prod = Self::mul_023_by_023(line0, line1);
149 f = Self::mul_by_02345(&f, &prod);
150 } else {
151 panic!("lines.len() % 2 should be 0 at this point");
152 }
153 }
154 f
155 }
156
157 fn pre_loop(
159 Q_acc: Vec<AffinePoint<Self::Fp2>>,
160 Q: &[AffinePoint<Self::Fp2>],
161 c: Option<Self::Fp12>,
162 xy_fracs: &[(Self::Fp, Self::Fp)],
163 ) -> (Self::Fp12, Vec<AffinePoint<Self::Fp2>>) {
164 let mut f = if let Some(mut c) = c {
165 let mut c3 = c.clone();
169 c.square_assign();
170 c3 *= &c;
171 c3
172 } else {
173 Self::Fp12::ONE
174 };
175
176 let mut Q_acc = Q_acc;
177
178 let (Q_out_double, lines_2S) = Q_acc
182 .into_iter()
183 .map(|Q| Self::miller_double_step(&Q))
184 .unzip::<_, _, Vec<_>, Vec<_>>();
185 Q_acc = Q_out_double;
186
187 let mut initial_lines = Vec::<EvaluatedLine<Self::Fp2>>::new();
188
189 let lines_iter = izip!(lines_2S.iter(), xy_fracs.iter());
190 for (line_2S, xy_frac) in lines_iter {
191 let line = line_2S.evaluate(xy_frac);
192 initial_lines.push(line);
193 }
194
195 let (Q_out_add, lines_S_plus_Q) = Q_acc
196 .iter()
197 .zip(Q.iter())
198 .map(|(Q_acc, Q)| Self::miller_add_step(Q_acc, Q))
199 .unzip::<_, _, Vec<_>, Vec<_>>();
200 Q_acc = Q_out_add;
201
202 let lines_iter = izip!(lines_S_plus_Q.iter(), xy_fracs.iter());
203 for (lines_S_plus_Q, xy_frac) in lines_iter {
204 let line = lines_S_plus_Q.evaluate(xy_frac);
205 initial_lines.push(line);
206 }
207
208 f = Self::evaluate_lines_vec(f, initial_lines);
209
210 (f, Q_acc)
211 }
212
213 fn post_loop(
215 f: &Self::Fp12,
216 Q_acc: Vec<AffinePoint<Self::Fp2>>,
217 _Q: &[AffinePoint<Self::Fp2>],
218 _c: Option<Self::Fp12>,
219 _xy_fracs: &[(Self::Fp, Self::Fp)],
220 ) -> (Self::Fp12, Vec<AffinePoint<Self::Fp2>>) {
221 let mut f = f.clone();
224 f.conjugate_assign();
225 (f, Q_acc)
226 }
227}
228
229#[allow(non_snake_case)]
230impl PairingCheck for Bls12_381 {
231 type Fp = Fp;
232 type Fp2 = Fp2;
233 type Fp12 = Fp12;
234
235 #[allow(unused_variables)]
236 fn pairing_check_hint(
237 P: &[AffinePoint<Self::Fp>],
238 Q: &[AffinePoint<Self::Fp2>],
239 ) -> (Self::Fp12, Self::Fp12) {
240 #[cfg(not(target_os = "zkvm"))]
241 {
242 #[cfg(not(feature = "halo2curves"))]
243 panic!("`halo2curves` feature must be enabled to use pairing check hint on host");
244
245 #[cfg(feature = "halo2curves")]
246 {
247 let p_halo2 = P
248 .iter()
249 .map(|p| {
250 AffinePoint::new(
251 convert_bls12381_fp_to_halo2_fq(p.x.clone()),
252 convert_bls12381_fp_to_halo2_fq(p.y.clone()),
253 )
254 })
255 .collect::<Vec<_>>();
256 let q_halo2 = Q
257 .iter()
258 .map(|q| {
259 AffinePoint::new(
260 convert_bls12381_fp2_to_halo2_fq2(q.x.clone()),
261 convert_bls12381_fp2_to_halo2_fq2(q.y.clone()),
262 )
263 })
264 .collect::<Vec<_>>();
265 let fq12 = Halo2CurvesBls12_381::multi_miller_loop(&p_halo2, &q_halo2);
266 let (c_fq12, s_fq12) = Halo2CurvesBls12_381::final_exp_hint(&fq12);
267 let c = convert_bls12381_halo2_fq12_to_fp12(c_fq12);
268 let s = convert_bls12381_halo2_fq12_to_fp12(s_fq12);
269 (c, s)
270 }
271 }
272 #[cfg(target_os = "zkvm")]
273 {
274 let hint = MaybeUninit::<(Fp12, Fp12)>::uninit();
275 let p_fat_ptr = (P.as_ptr() as u32, P.len() as u32);
277 let q_fat_ptr = (Q.as_ptr() as u32, Q.len() as u32);
278 unsafe {
279 custom_insn_r!(
280 opcode = OPCODE,
281 funct3 = PAIRING_FUNCT3,
282 funct7 = ((Bls12_381::PAIRING_IDX as u8) * PairingBaseFunct7::PAIRING_MAX_KINDS + PairingBaseFunct7::HintFinalExp as u8),
283 rd = Const "x0",
284 rs1 = In &p_fat_ptr,
285 rs2 = In &q_fat_ptr
286 );
287 let ptr = hint.as_ptr() as *const u8;
288 hint_buffer_u32!(ptr, (48 * 12 * 2) / 4);
289 hint.assume_init()
290 }
291 }
292 }
293
294 fn pairing_check(
295 P: &[AffinePoint<Self::Fp>],
296 Q: &[AffinePoint<Self::Fp2>],
297 ) -> Result<(), PairingCheckError> {
298 Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| {
299 let f = Self::multi_miller_loop(P, Q);
300 exp_check_fallback(&f, &Self::FINAL_EXPONENT)
301 })
302 }
303}
304
305#[allow(non_snake_case)]
306impl Bls12_381 {
307 fn try_honest_pairing_check(
311 P: &[AffinePoint<<Self as PairingCheck>::Fp>],
312 Q: &[AffinePoint<<Self as PairingCheck>::Fp2>],
313 ) -> Option<Result<(), PairingCheckError>> {
314 let (c, s) = Self::pairing_check_hint(P, Q);
315
316 let c_q = FieldExtension::frobenius_map(&c, 1);
322
323 let c_conj = c.conjugate();
329 if c_conj == Fp12::ZERO {
330 return None;
331 }
332 let c_conj_inv = Fp12::ONE.div_unsafe(&c_conj);
333 let fc = Self::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv));
334
335 if fc * s == c_q {
336 Some(Ok(()))
337 } else {
338 None
339 }
340 }
341}