Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
pairing.test.cpp
Go to the documentation of this file.
1#include "pairing.hpp"
2#include <gtest/gtest.h>
3
4using namespace bb;
5
6static constexpr uint256_t Z = 4965661367192848881UL;
7
8TEST(pairing, ReducedAtePairingCheckAgainstConstants)
9{
10 constexpr g1::affine_element P = {
11 uint256_t(0x956e256b9db00c13, 0x66d29ac18e1b2bff, 0x5d6f055e34402f6e, 0x5bfcbaaff0feb62),
12 uint256_t(0x564099dc0ef0a96, 0xa97eca7453f67dd2, 0x850e976b207e8c18, 0x20187f89a1d789cd)
13 };
14 constexpr g2::affine_element Q = {
15 { uint256_t(0x3b25f1ad9a7f9cd2, 0xddb8b066d21ce86, 0xf8a4e318abd3cff7, 0x1272ee5f2e7e9dc1),
16 uint256_t(0xc7b14ea54dc1436f, 0x1f9384eb12b6941a, 0x3afe17a00720e8e3, 0x2a171f424ab98d8) },
17 { uint256_t(0x890d5a50c1d88e96, 0x6ae79a7a2b439172, 0x4c120a629ced363c, 0x295bd556fe685dd),
18 uint256_t(0xa3189c7f120d4738, 0x4416da0df17c8ee, 0x4cc514acc1c2ac45, 0xb17d8f998e4ebe6) }
19 };
20 constexpr fq12 expected = {
21
22 { { uint256_t(0xd3b91c8dc40a9b8c, 0x5c8a39a470fcb4ea, 0x763e904e585a87e7, 0x2026f0077c50afa4),
23 uint256_t(0xddc69495371e5f38, 0x290bfc6512704e60, 0xc208c0f8e90bd52f, 0x2e82c92370a2f000) },
24 { uint256_t(0xdcbc2917451b8e12, 0x183016aa113a74eb, 0x9a2ff2a059f7d14d, 0x1166fc0ed488820c),
25 uint256_t(0x3b2c1e19e47214ff, 0x374df83e0ac59c1a, 0x3e1c5ed4fd611cb2, 0x26179258a104da1a) },
26 { uint256_t(0xc948bdff07912922, 0x3417ba2a42303918, 0x89336b54f20ff8a9, 0xb7eed88572fcac4),
27
28 uint256_t(0x85524385a79574ba, 0xe7746ad78e659d8e, 0x997e4848cc70eca5, 0x2a9e3f37c50e6c9a) } },
29
30 { { uint256_t(0xc7eed1ca5aaa5a82, 0xea8d1f0be1ef0d7, 0xd7d539fd8136038a, 0x27196e24cd6d028e),
31 uint256_t(0xcb7b6528984002e4, 0x1d3221c223e0587, 0xda44f3e957677f97, 0x1e3df34445cc3876) },
32 { uint256_t(0xf3e958491c2b4c43, 0x1dbafe473f7034b9, 0x129efae93ff9d8c9, 0xdedbf49d35171b9),
33 uint256_t(0x7da7c99cf811a603, 0xfcb99b8309663279, 0x1d80151ef8fcdb59, 0x1b09a01856170269) },
34 { uint256_t(0xa048b10941003960, 0x73d941c906a24cd0, 0x9c10f82a6bf78e2e, 0x13a41dbdd3d616d),
35 uint256_t(0x31d7525fa8914a4c, 0xe1ed738718e2e8b8, 0x18305c749a9d97a2, 0x20534d878e1e9db0) } }
36 };
37
38#if defined(__wasm__)
39 const fq12 result = pairing::reduced_ate_pairing(P, Q);
40#else
41 constexpr fq12 result = pairing::reduced_ate_pairing(P, Q);
42 static_assert(result == expected); // test to see if compiler can evaluate bilinear pairing at compile time
43#endif
44
45 EXPECT_EQ(result, expected);
46}
47
48TEST(pairing, PisInfinity)
49{
50 g1::affine_element P = g1::element::infinity();
51 g2::affine_element Q = g2::element::random_element();
52
54 fq12 expected = fq12::one().from_montgomery_form();
55
56 EXPECT_EQ(result, expected);
57}
58
59TEST(pairing, QisInfinity)
60{
61 g1::affine_element P = g1::element::random_element();
62 g2::affine_element Q = g2::element::infinity();
63
65 fq12 expected = fq12::one().from_montgomery_form();
66
67 EXPECT_EQ(result, expected);
68}
69
70TEST(pairing, ReduceAtePairingBatchWithPointsAtInfinity)
71{
72
73 g1::affine_element P1 = g1::element::random_element();
74 g1::affine_element P2 = g1::element::random_element();
75 g1::affine_element P3 = g1::element::infinity();
76 g2::affine_element Q1 = g2::element::random_element();
77 g2::affine_element Q2 = g2::element::infinity();
78 g2::affine_element Q3 = g2::element::random_element();
79
80 std::vector<g1::affine_element> P{ P1, P2, P3 };
81 std::vector<g2::affine_element> Q{ Q1, Q2, Q3 };
82
85
86 EXPECT_EQ(result, expected);
87}
88
89TEST(pairing, ReduceAtePairingBatchOnlyPointsAtInfinity)
90{
91 g1::affine_element P1 = g1::element::infinity();
92 g1::affine_element P2 = g1::element::infinity();
93 g2::affine_element Q1 = g2::element::infinity();
94 g2::affine_element Q2 = g2::element::infinity();
95
98
100 fq12 expected = fq12::one().from_montgomery_form();
101
102 EXPECT_EQ(result, expected);
103}
104
105TEST(pairing, ReducedAtePairingConsistencyCheck)
106{
107 g1::affine_element P = g1::element::random_element();
108 g2::affine_element Q = g2::element::random_element();
109
110 fr scalar = fr::random_element();
111
112 g1::affine_element Pmul = P * scalar;
113 g2::affine_element Qmul = Q * scalar;
114
117
118 EXPECT_EQ(result, expected);
119}
120
121TEST(pairing, ReducedAtePairingConsistencyCheckBatch)
122{
123 size_t num_points = 10;
124
125 std::vector<g1::affine_element> P_a(num_points);
126 std::vector<g2::affine_element> Q_a(num_points);
127 std::vector<g1::affine_element> P_b(num_points);
128 std::vector<g2::affine_element> Q_b(num_points);
129 std::vector<fr> scalars(num_points + num_points);
130 for (size_t i = 0; i < 10; ++i) {
131 scalars[i] = fr::random_element();
132 scalars[i + num_points] = fr::random_element();
133 g1::affine_element P = g1::element::random_element();
134 g2::affine_element Q = g2::element::random_element();
135 P_a[i] = P;
136 Q_a[i] = Q;
137 P_b[i] = P;
138 Q_b[i] = Q;
139 }
140
141 for (size_t i = 0; i < 10; ++i) {
142 P_a[i] = P_a[i] * scalars[i];
143 Q_b[i] = Q_b[i] * scalars[i];
144 P_b[i] = P_b[i] * scalars[i + num_points];
145 Q_a[i] = Q_a[i] * scalars[i + num_points];
146 }
147
148 fq12 result = pairing::reduced_ate_pairing_batch(&P_a[0], &Q_a[0], num_points).from_montgomery_form();
149 fq12 expected = pairing::reduced_ate_pairing_batch(&P_b[0], &Q_b[0], num_points).from_montgomery_form();
150
151 EXPECT_EQ(result, expected);
152}
153
154TEST(pairing, ReducedAtePairingPrecomputeConsistencyCheckBatch)
155{
156 size_t num_points = 10;
157 std::vector<g1::affine_element> P_a(num_points);
158 std::vector<g2::affine_element> Q_a(num_points);
159 std::vector<g1::affine_element> P_b(num_points);
160 std::vector<g2::affine_element> Q_b(num_points);
161 std::vector<pairing::miller_lines> precompute_miller_lines(num_points);
162 std::vector<fr> scalars(num_points + num_points);
163 for (size_t i = 0; i < 10; ++i) {
164 scalars[i] = fr::random_element();
165 scalars[i + num_points] = fr::random_element();
166 g1::affine_element P = g1::element::random_element();
167 g2::affine_element Q = g2::element::random_element();
168 P_a[i] = P;
169 Q_a[i] = Q;
170 P_b[i] = P;
171 Q_b[i] = Q;
172 }
173 for (size_t i = 0; i < 10; ++i) {
174 P_a[i] = P_a[i] * scalars[i];
175 Q_b[i] = Q_b[i] * scalars[i];
176 P_b[i] = P_b[i] * scalars[i + num_points];
177 Q_a[i] = Q_a[i] * scalars[i + num_points];
178 }
179 for (size_t i = 0; i < 10; ++i) {
180 g2::element jac;
181 jac = g2::element(Q_a[i]);
182 pairing::precompute_miller_lines(jac, precompute_miller_lines[i]);
183 }
184 fq12 result = pairing::reduced_ate_pairing_batch_precomputed(&P_a[0], &precompute_miller_lines[0], num_points)
186 fq12 expected = pairing::reduced_ate_pairing_batch(&P_b[0], &Q_b[0], num_points).from_montgomery_form();
187
188 EXPECT_EQ(result, expected);
189}
190
191TEST(pairing, DoublingStep)
192{
193 fq2 scalar = fq2::random_element();
194 g2::affine_element Q_affine = g2::element::random_element();
195 pairing::g2Projective Q{ scalar * Q_affine.x, scalar * Q_affine.y, scalar };
196 pairing::g2Projective work_point = Q;
197 fq12::ell_coeffs line;
198
200
201 EXPECT_EQ(work_point.x,
202 Q.x * Q.y.mul_by_fq(fq(2).invert()) * (Q.y.sqr() - g2::curve_b.mul_by_fq(fq(9)) * Q.z.sqr()));
203 EXPECT_EQ(work_point.y,
204 (Q.y.sqr() + g2::curve_b.mul_by_fq(fq(9)) * Q.z.sqr()).mul_by_fq(fq(2).invert()).sqr() -
205 g2::curve_b.sqr().mul_by_fq(fq(27)) * Q.z.sqr().sqr());
206 EXPECT_EQ(work_point.z, Q.y.sqr() * Q.y * Q.z.mul_by_fq(fq(2)));
207 EXPECT_EQ(line.o, Q.y * Q.z.mul_by_fq(fq(2)));
208 EXPECT_EQ(line.w, Q.x.sqr().mul_by_fq(fq(3)));
209 EXPECT_EQ(line.vw, g2::curve_b.mul_by_fq(fq(3)) * Q.z.sqr() - Q.y.sqr());
210
211 // Check correctness by comparing against the rescaling of the affine calculation
212 fq2 gradient = Q.x.sqr().mul_by_fq(fq(3)) * (Q.z * Q.y.mul_by_fq(fq(2))).invert();
213 fq2 multiplier = Q.y * Q.z.mul_by_fq(fq(2));
214 EXPECT_EQ(line.o, multiplier);
215 EXPECT_EQ(line.w, gradient * multiplier);
216 EXPECT_EQ(line.vw, -(gradient * Q.x * Q.z.invert() - Q.y * Q.z.invert()) * multiplier);
217}
218
219TEST(pairing, AdditionStep)
220{
221 fq2 scalar_current = fq2::random_element();
222 g2::affine_element Q_affine = g2::element::random_element();
223 g2::affine_element current_affine = g2::element::random_element();
224 pairing::g2Projective Q{ Q_affine.x, Q_affine.y, fq2::one() };
225 pairing::g2Projective current{ scalar_current * current_affine.x,
226 scalar_current * current_affine.y,
227 scalar_current };
228 pairing::g2Projective work_point = current;
229 fq12::ell_coeffs line;
230
232
233 fq2 lambda = current.x - Q.x * current.z;
234 fq2 theta = current.y - Q.y * current.z;
235 fq2 E = lambda.sqr() * lambda;
236 fq2 F = current.z * theta.sqr();
237 fq2 G = current.x * lambda.sqr();
238 fq2 H = E + F - G.mul_by_fq(2);
239
240 EXPECT_EQ(work_point.x, lambda * H);
241 EXPECT_EQ(work_point.y, theta * (G - H) - current.y * E);
242 EXPECT_EQ(work_point.z, current.z * E);
243 EXPECT_EQ(line.o, lambda);
244 EXPECT_EQ(line.w, theta);
245 EXPECT_EQ(line.vw, theta * Q.x - lambda * Q.y);
246
247 // Check correctness by comparing against the rescaling of the affine calculation
248 fq2 gradient = (Q.y - current.y * current.z.invert()) * (Q.x - current.x * current.z.invert()).invert();
249 fq2 multiplier = current.x - current.z * Q.x;
250 EXPECT_EQ(line.o, multiplier);
251 EXPECT_EQ(line.w, gradient * multiplier);
252 EXPECT_EQ(line.vw, (gradient * Q.x * Q.z.invert() - Q.y * Q.z.invert()) * multiplier);
253}
254
255TEST(pairing, PairingLinearityCheck)
256{
257 g1::affine_element P = g1::affine_element::random_element();
258 g2::affine_element Q = g2::element::random_element();
259
260 fq12 result = pairing::reduced_ate_pairing(P, Q);
261 fq12 result_pow = pairing::reduced_ate_pairing(P + P, Q);
262 fq12 result_pow_2 = pairing::reduced_ate_pairing(P, Q + Q);
263
264 EXPECT_EQ(result_pow, result.sqr());
265 EXPECT_EQ(result_pow_2, result.sqr());
266}
267
268TEST(pairing, FinalExponentiation)
269{
270 auto pow = [](const fq12& a, const fq& b) {
271 fq12 result = fq12::one();
272 fq12 base = a;
273 for (size_t i = 0; i < 256; ++i) {
274 if ((b.data[0] >> i) & 1) {
275 result *= base;
276 }
277 base = base.sqr();
278 }
279 return result;
280 };
281
282 fq z = fq(Z);
283 fq z2 = z.sqr();
284 fq z3 = z * z2;
285 fq mu0 = z3 * fq(12) + z2 * fq(12) + z * fq(6) + fq(1);
286 fq mu1 = z3 * fq(12) + z2 * fq(6) + z * fq(4);
287 fq mu2 = z3 * fq(12) + z2 * fq(6) + z * fq(6);
288 fq mu3 = z3 * fq(12) + z2 * fq(6) + z * fq(4) - fq(1);
289 fq12 element = fq12::random_element();
290
293
294 fq12 expected = element;
295 expected = pairing::final_exponentiation_easy_part(expected);
296 expected = pow(expected, mu0) + pow(expected, mu1).frobenius_map_one() + pow(expected, mu2).frobenius_map_two() +
297 pow(expected, mu3).frobenius_map_three();
298}
299
300TEST(pairing, Constants)
301{
302 uint256_t six_z_plus_two = uint256_t(1);
303 for (const uint8_t& bit : pairing::loop_bits) {
304 six_z_plus_two = six_z_plus_two + six_z_plus_two;
305 if (bit == 1) {
306 six_z_plus_two = six_z_plus_two + uint256_t(1);
307 } else if (bit == 3) {
308 six_z_plus_two = six_z_plus_two - uint256_t(1);
309 }
310 }
311 EXPECT_EQ(six_z_plus_two, uint256_t(6) * Z + uint256_t(2));
312
313 uint256_t z_check = uint256_t(1);
314 for (const bool& bit : pairing::z_loop_bits) {
315 z_check = z_check + z_check;
316 if (bit) {
317 z_check = z_check + uint256_t(1);
318 }
319 }
320 EXPECT_EQ(z_check, Z);
321}
constexpr field12 frobenius_map_three() const
Definition field12.hpp:183
static constexpr field12 random_element(numeric::RNG *engine=nullptr)
Definition field12.hpp:217
static constexpr field12 one()
Definition field12.hpp:57
constexpr field12 sqr() const
Definition field12.hpp:158
constexpr field12 from_montgomery_form() const
Definition field12.hpp:234
constexpr field12 frobenius_map_two() const
Definition field12.hpp:191
constexpr field12 frobenius_map_one() const
Definition field12.hpp:199
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition element.hpp:33
group_elements::element< Fq, Fr, Params > element
Definition group.hpp:41
static constexpr Fq curve_b
Definition group.hpp:51
FF a
FF b
#define G(r, i, a, b, c, d)
Definition blake2s.cpp:116
constexpr void doubling_step_for_miller_loop(g2Projective &work_point, fq12::ell_coeffs &line)
Doubling step for Miller loop calculation.
constexpr std::array< uint8_t, loop_length > loop_bits
Definition pairing.hpp:33
constexpr fq12 final_exponentiation_tricky_part(const fq12 &elt)
fq12 reduced_ate_pairing_batch_precomputed(const g1::affine_element *P_affines, const miller_lines *lines, const size_t num_points)
constexpr fq12 reduced_ate_pairing(const g1::affine_element &P_affine, const g2::affine_element &Q_affine)
constexpr fq12 final_exponentiation_easy_part(const fq12 &elt)
constexpr void mixed_addition_step_for_miller_loop(const g2Projective &Q, g2Projective &work_point, fq12::ell_coeffs &line)
Addition step for Miller loop calculation.
constexpr void precompute_miller_lines(const g2Projective &Q, miller_lines &lines)
Precomputation of Miller lines for a point Q in G2.
fq12 reduced_ate_pairing_batch(const g1::affine_element *P_affines, const g2::affine_element *Q_affines, const size_t num_points)
constexpr std::array< bool, z_loop_length > z_loop_bits
Definition pairing.hpp:39
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FqParams > fq
Definition fq.hpp:153
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
quadratic_field w
Definition field12.hpp:52
quadratic_field vw
Definition field12.hpp:53
quadratic_field o
Definition field12.hpp:51
constexpr field2 sqr() const noexcept
Definition field2.hpp:74
constexpr field2 mul_by_fq(const base_field &a) const noexcept
constexpr field2 invert() const noexcept
Definition field2.hpp:154
static field2 random_element(numeric::RNG *engine=nullptr)
Definition field2.hpp:201
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr field sqr() const noexcept