Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
pairing_impl.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
7#pragma once
8
9#include "./fq12.hpp"
10#include "./g1.hpp"
11#include "./g2.hpp"
13
14namespace bb::pairing {
15constexpr fq two_inv = fq(2).invert();
16inline constexpr g2::element mul_by_q(const g2::element& a)
17{
18
19 fq2 T0 = a.x.frobenius_map();
20 fq2 T1 = a.y.frobenius_map();
21 return {
24 a.z.frobenius_map(),
25 };
26}
28{
29 fq2 a = current.x.mul_by_fq(two_inv);
30 a *= current.y;
31
32 fq2 b = current.y.sqr();
33 fq2 c = current.z.sqr();
34 fq2 d = c + c;
35 d += c;
36 fq2 e = d * fq2::twist_coeff_b();
37 fq2 f = e + e;
38 f += e;
39
40 fq2 g = b + f;
41 g = g.mul_by_fq(two_inv);
42 fq2 h = current.y + current.z;
43 h = h.sqr();
44 fq2 i = b + c;
45 h -= i;
46 i = e - b;
47 fq2 j = current.x.sqr();
48 fq2 ee = e.sqr();
49 fq2 k = b - f;
50 current.x = a * k;
51
52 k = ee + ee;
53 k += ee;
54
55 c = g.sqr();
56 current.y = c - k;
57 current.z = b * h;
58
60
61 ell.vw = -h;
62 ell.vv = j + j;
63 ell.vv += j;
64}
65
67 g2::element& Q,
68 fq12::ell_coeffs& line)
69{
70 fq2 d = base.x * Q.z;
71 d = Q.x - d;
72
73 fq2 e = base.y * Q.z;
74 e = Q.y - e;
75
76 fq2 f = d.sqr();
77 fq2 g = e.sqr();
78 fq2 h = d * f;
79 fq2 i = Q.x * f;
80
81 fq2 j = Q.z * g;
82 j += h;
83 j -= i;
84 j -= i;
85
86 Q.x = d * j;
87 i -= j;
88 i *= e;
89 j = Q.y * h;
90 Q.y = i - j;
91 Q.z *= h;
92
93 h = e * base.x;
94 i = d * base.y;
95
96 h -= i;
97 line.o = fq6::mul_by_non_residue(h);
98
99 line.vv = -e;
100 line.vw = d;
101}
102
103constexpr void precompute_miller_lines(const g2::element& Q, miller_lines& lines)
104{
105 // We should not compute Miller lines if Q is the point at infinity, e(P, Q) = 1 in this case
106 if (Q.is_point_at_infinity()) {
107 throw_or_abort("Computing Miller lines when Q is the point at infinity");
108 }
109
110 g2::element Q_neg{ Q.x, -Q.y, fq2::one() };
111 g2::element work_point = Q;
112
113 size_t it = 0;
114 for (unsigned char loop_bit : loop_bits) {
115 doubling_step_for_flipped_miller_loop(work_point, lines.lines[it]);
116 ++it;
117 if (loop_bit == 1) {
118 mixed_addition_step_for_flipped_miller_loop(Q, work_point, lines.lines[it]);
119 ++it;
120 } else if (loop_bit == 3) {
121 mixed_addition_step_for_flipped_miller_loop(Q_neg, work_point, lines.lines[it]);
122 ++it;
123 }
124 }
125
126 g2::element Q1 = mul_by_q(Q);
127 g2::element Q2 = mul_by_q(Q1);
128 Q2 = -Q2;
129 mixed_addition_step_for_flipped_miller_loop(Q1, work_point, lines.lines[it]);
130 ++it;
131 mixed_addition_step_for_flipped_miller_loop(Q2, work_point, lines.lines[it]);
132}
133
135{
136 fq12 work_scalar = fq12::one();
137
138 size_t it = 0;
139 fq12::ell_coeffs work_line;
140
141 for (unsigned char loop_bit : loop_bits) {
142 work_scalar = work_scalar.sqr();
143
144 work_line.o = lines.lines[it].o;
145 work_line.vw = lines.lines[it].vw.mul_by_fq(P.y);
146 work_line.vv = lines.lines[it].vv.mul_by_fq(P.x);
147 work_scalar.self_sparse_mul(work_line);
148 ++it;
149
150 if (loop_bit != 0) {
151 work_line.o = lines.lines[it].o;
152 work_line.vw = lines.lines[it].vw.mul_by_fq(P.y);
153 work_line.vv = lines.lines[it].vv.mul_by_fq(P.x);
154 work_scalar.self_sparse_mul(work_line);
155 ++it;
156 }
157 }
158
159 work_line.o = lines.lines[it].o;
160 work_line.vw = lines.lines[it].vw.mul_by_fq(P.y);
161 work_line.vv = lines.lines[it].vv.mul_by_fq(P.x);
162 work_scalar.self_sparse_mul(work_line);
163 ++it;
164 work_line.o = lines.lines[it].o;
165 work_line.vw = lines.lines[it].vw.mul_by_fq(P.y);
166 work_line.vv = lines.lines[it].vv.mul_by_fq(P.x);
167 work_scalar.self_sparse_mul(work_line);
168 ++it;
169 return work_scalar;
170}
171
172constexpr fq12 miller_loop_batch(const g1::element* points, const miller_lines* lines, size_t num_pairs)
173{
174 fq12 work_scalar = fq12::one();
175
176 size_t it = 0;
177 fq12::ell_coeffs work_line;
178
179 for (unsigned char loop_bit : loop_bits) {
180 work_scalar = work_scalar.sqr();
181 for (size_t j = 0; j < num_pairs; ++j) {
182 work_line.o = lines[j].lines[it].o;
183 work_line.vw = lines[j].lines[it].vw.mul_by_fq(points[j].y);
184 work_line.vv = lines[j].lines[it].vv.mul_by_fq(points[j].x);
185 work_scalar.self_sparse_mul(work_line);
186 }
187 ++it;
188 if (loop_bit != 0) {
189 for (size_t j = 0; j < num_pairs; ++j) {
190 work_line.o = lines[j].lines[it].o;
191 work_line.vw = lines[j].lines[it].vw.mul_by_fq(points[j].y);
192 work_line.vv = lines[j].lines[it].vv.mul_by_fq(points[j].x);
193 work_scalar.self_sparse_mul(work_line);
194 }
195 ++it;
196 }
197 }
198
199 for (size_t j = 0; j < num_pairs; ++j) {
200 work_line.o = lines[j].lines[it].o;
201 work_line.vw = lines[j].lines[it].vw.mul_by_fq(points[j].y);
202 work_line.vv = lines[j].lines[it].vv.mul_by_fq(points[j].x);
203 work_scalar.self_sparse_mul(work_line);
204 }
205 ++it;
206 for (size_t j = 0; j < num_pairs; ++j) {
207 work_line.o = lines[j].lines[it].o;
208 work_line.vw = lines[j].lines[it].vw.mul_by_fq(points[j].y);
209 work_line.vv = lines[j].lines[it].vv.mul_by_fq(points[j].x);
210 work_scalar.self_sparse_mul(work_line);
211 }
212 ++it;
213 return work_scalar;
214}
215
217{
218 fq12 a{ elt.c0, -elt.c1 };
219 a *= elt.invert();
220 return a * a.frobenius_map_two();
221}
222
224{
225 fq12 r = elt;
226
227 for (bool neg_z_loop_bit : neg_z_loop_bits) {
228 r = r.cyclotomic_squared();
229 if (neg_z_loop_bit) {
230 r *= elt;
231 }
232 }
233 return r.unitary_inverse();
234}
235
237{
239 fq12 B = A.cyclotomic_squared();
241 fq12 D = B * C;
243 fq12 F = E.cyclotomic_squared();
245 fq12 H = D.unitary_inverse();
246 fq12 I = G.unitary_inverse();
247 fq12 J = I * E;
248 fq12 K = J * H;
249 fq12 L = B * K;
250 fq12 M = E * K;
251 fq12 N = M * elt;
252 fq12 O = L.frobenius_map_one();
253 fq12 P = O * N;
254 fq12 Q = K.frobenius_map_two();
255 fq12 R = P * Q;
256 fq12 S = elt.unitary_inverse();
257 fq12 T = L * S;
259
260 return R * U;
261}
262
263constexpr fq12 reduced_ate_pairing(const g1::affine_element& P_affine, const g2::affine_element& Q_affine)
264{
265 g1::element P(P_affine);
266 g2::element Q(Q_affine);
267
268 // Early exit condition: e(P, Q) = 1 if either P or Q are the point at infinity
270 return fq12::one();
271 }
272
273 miller_lines lines;
274 precompute_miller_lines(Q, lines);
275
276 fq12 result = miller_loop(P, lines);
277 result = final_exponentiation_easy_part(result);
278 result = final_exponentiation_tricky_part(result);
279 return result;
280}
281
283 const miller_lines* lines,
284 const size_t num_points)
285{
286 std::vector<g1::element> P(num_points);
287 for (size_t i = 0; i < num_points; ++i) {
288 P[i] = g1::element(P_affines[i]);
289 }
290 fq12 result = miller_loop_batch(&P[0], &lines[0], num_points);
291 result = final_exponentiation_easy_part(result);
292 result = final_exponentiation_tricky_part(result);
293 return result;
294}
295
297 const g2::affine_element* Q_affines,
298 const size_t num_points)
299{
300
301 std::vector<g1::element> P; // Vector of points P_i for which we compute e(P_i, Q_i)
302 std::vector<g2::element> Q; // Vector of points Q_i for which we compute e(P_i, Q_i)
303 std::vector<miller_lines> lines; // i-th element are the Miller lines of Q_i
304
305 size_t num_pairings = 0;
306 for (size_t i = 0; i < num_points; ++i) {
307 // If either P_i or Q_i is the point at infinity, then e(P_i, Q_i) = 1, so we can skip the calculation of that
308 // pairing
309 if (!P_affines[i].is_point_at_infinity() && !Q_affines[i].is_point_at_infinity()) {
310 P.emplace_back(g1::element(P_affines[i]));
311 Q.emplace_back(g2::element(Q_affines[i]));
312 lines.emplace_back(miller_lines{});
313
314 precompute_miller_lines(Q.back(), lines.back());
315
316 num_pairings += 1;
317 }
318 }
319
320 // If for every couple (P_i, Q_i) either P_i or Q_i is the point at infinity, then \prod e(P_i, Q_i) = 1
321 if (P.empty()) {
322 return fq12::one();
323 }
324
325 fq12 result = miller_loop_batch(&P[0], &lines[0], num_pairings);
326 result = final_exponentiation_easy_part(result);
327 result = final_exponentiation_tricky_part(result);
328 return result;
329}
330
331} // namespace bb::pairing
base_field c1
Definition field12.hpp:48
constexpr field12 cyclotomic_squared() const
Definition field12.hpp:232
constexpr field12 frobenius_map_three() const
Definition field12.hpp:208
constexpr field12 invert() const
Definition field12.hpp:197
constexpr field12 unitary_inverse() const
Definition field12.hpp:239
constexpr void self_sparse_mul(const ell_coeffs &ell)
Definition field12.hpp:125
static constexpr field12 one()
Definition field12.hpp:57
constexpr field12 sqr() const
Definition field12.hpp:183
base_field c0
Definition field12.hpp:47
constexpr field12 frobenius_map_two() const
Definition field12.hpp:216
constexpr field12 frobenius_map_one() const
Definition field12.hpp:224
static constexpr base_field mul_by_non_residue(const base_field &a)
Definition field6.hpp:61
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition element.hpp:33
BB_INLINE constexpr bool is_point_at_infinity() const noexcept
group_elements::element< Fq, Fr, Params > element
Definition group.hpp:41
FF a
FF b
#define G(r, i, a, b, c, d)
Definition blake2s.cpp:116
bb::avm2::Column C
constexpr void precompute_miller_lines(const g2::element &Q, miller_lines &lines)
constexpr void final_exponentiation_tricky_part(const fq12 &elt, fq12 &r)
constexpr fq12 miller_loop(const g1::element &P, const miller_lines &lines)
constexpr std::array< uint8_t, loop_length > loop_bits
Definition pairing.hpp:22
constexpr fq12 miller_loop_batch(const g1::element *points, const miller_lines *lines, size_t num_pairs)
constexpr fq12 reduced_ate_pairing(const g1::affine_element &P_affine, const g2::affine_element &Q_affine)
constexpr void final_exponentiation_easy_part(const fq12 &elt, fq12 &r)
constexpr fq two_inv
constexpr void doubling_step_for_flipped_miller_loop(g2::element &current, fq12::ell_coeffs &ell)
fq12 reduced_ate_pairing_batch_precomputed(const g1::affine_element *P_affines, const miller_lines *lines, size_t num_points)
constexpr void final_exponentiation_exp_by_neg_z(const fq12 &elt, fq12 &r)
fq12 reduced_ate_pairing_batch(const g1::affine_element *P_affines, const g2::affine_element *Q_affines, size_t num_points)
constexpr g2::element mul_by_q(const g2::element &a)
constexpr std::array< bool, neg_z_loop_length > neg_z_loop_bits
Definition pairing.hpp:26
constexpr void mixed_addition_step_for_flipped_miller_loop(const g2::element &base, g2::element &Q, fq12::ell_coeffs &line)
field< Bn254FqParams > fq
Definition fq.hpp:169
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
quadratic_field vv
Definition field12.hpp:53
quadratic_field vw
Definition field12.hpp:52
quadratic_field o
Definition field12.hpp:51
constexpr field2 sqr() const noexcept
Definition field2.hpp:74
static constexpr field2 twist_mul_by_q_y()
static constexpr field2 one()
static constexpr field2 twist_mul_by_q_x()
static constexpr field2 twist_coeff_b()
constexpr field invert() const noexcept
BB_INLINE constexpr field sqr() const noexcept
std::array< fq12::ell_coeffs, precomputed_coefficients_length > lines
Definition pairing.hpp:34
void throw_or_abort(std::string const &err)