Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
g1.test.cpp
Go to the documentation of this file.
1#include "g1.hpp"
4#include <gtest/gtest.h>
5
6using namespace bb;
7
8namespace {
9// Double-and-add scalar mul without endomorphism, used as reference for differential testing.
10template <typename Group, typename Fr>
11typename Group::affine_element naive_scalar_mul(const typename Group::element& base, const Fr& scalar)
12{
13 typename Group::element acc = Group::point_at_infinity;
14 typename Group::element runner = base;
15 uint256_t bits(scalar);
16 for (size_t i = 0; i < 256; ++i) {
17 if (bits.get_bit(i)) {
18 acc = acc + runner;
19 }
20 runner = runner.dbl();
21 }
22 return typename Group::affine_element(acc);
23}
24} // namespace
25
26// =========================
27// Parameter-related tests
28// =========================
29
30TEST(g1, BIsCorrect)
31{
33 fq expected = fq(3);
34
35 EXPECT_EQ(b, expected);
36}
37
38TEST(g1, OneYIsCorrect)
39{
41 auto [_, expected] = (Bn254G1Params::b + fq::one()).sqrt();
42
43 EXPECT_EQ(one_y, expected);
44}
45
46// =========================
47// Group-related tests
48// =========================
49
50TEST(g1, RandomElement)
51{
52 g1::element result = g1::element::random_element();
53 EXPECT_EQ(result.on_curve(), true);
54}
55
56TEST(g1, RandomAffineElement)
57{
58 g1::affine_element result = g1::element::random_element();
59 EXPECT_EQ(result.on_curve(), true);
60}
61
62TEST(g1, Eq)
63{
64 g1::element a = g1::element::random_element();
65 g1::element b = a.normalize();
66
67 EXPECT_EQ(a == b, true);
68 EXPECT_EQ(a == a, true);
69
70 b.self_set_infinity();
71
72 EXPECT_EQ(a == b, false);
73 g1::element c = g1::element::random_element();
74
75 EXPECT_EQ(a == c, false);
76
77 a.self_set_infinity();
78
79 EXPECT_EQ(a == b, true);
80}
81
82TEST(g1, MixedAddCheckAgainstConstants)
83{
84 fq a_x{ 0x92716caa6cac6d26, 0x1e6e234136736544, 0x1bb04588cde00af0, 0x9a2ac922d97e6f5 };
85 fq a_y{ 0x9e693aeb52d79d2d, 0xf0c1895a61e5e975, 0x18cd7f5310ced70f, 0xac67920a22939ad };
86 fq a_z{ 0xfef593c9ce1df132, 0xe0486f801303c27d, 0x9bbd01ab881dc08e, 0x2a589badf38ec0f9 };
87 fq b_x{ 0xa1ec5d1398660db8, 0x6be3e1f6fd5d8ab1, 0x69173397dd272e11, 0x12575bbfe1198886 };
88 fq b_y{ 0xcfbfd4441138823e, 0xb5f817e28a1ef904, 0xefb7c5629dcc1c42, 0x1a9ed3d6f846230e };
89 fq expected_x{ 0x2a9d0201fccca20, 0x36f969b294f31776, 0xee5534422a6f646, 0x911dbc6b02310b6 };
90 fq expected_y{ 0x14c30aaeb4f135ef, 0x9c27c128ea2017a1, 0xf9b7d80c8315eabf, 0x35e628df8add760 };
91 fq expected_z{ 0xa43fe96673d10eb3, 0x88fbe6351753d410, 0x45c21cc9d99cb7d, 0x3018020aa6e9ede5 };
92 g1::element lhs;
94 g1::element result;
95 g1::element expected;
96 lhs.x = a_x.to_montgomery_form();
97 lhs.y = a_y.to_montgomery_form();
98 lhs.z = a_z.to_montgomery_form();
99 rhs.x = b_x.to_montgomery_form();
100 rhs.y = b_y.to_montgomery_form();
101 expected.x = expected_x.to_montgomery_form();
102 expected.y = expected_y.to_montgomery_form();
103 expected.z = expected_z.to_montgomery_form();
104 result = lhs + rhs;
105
106 EXPECT_EQ(result == expected, true);
107}
108
109TEST(g1, DblCheckAgainstConstants)
110{
111 fq a_x{ 0x8d1703aa518d827f, 0xd19cc40779f54f63, 0xabc11ce30d02728c, 0x10938940de3cbeec };
112 fq a_y{ 0xcf1798994f1258b4, 0x36307a354ad90a25, 0xcd84adb348c63007, 0x6266b85241aff3f };
113 fq a_z{ 0xe213e18fd2df7044, 0xb2f42355982c5bc8, 0xf65cf5150a3a9da1, 0xc43bde08b03aca2 };
114 fq expected_x{ 0xd5c6473044b2e67c, 0x89b185ea20951f3a, 0x4ac597219cf47467, 0x2d00482f63b12c86 };
115 fq expected_y{ 0x4e7e6c06a87e4314, 0x906a877a71735161, 0xaa7b9893cc370d39, 0x62f206bef795a05 };
116 fq expected_z{ 0x8813bdca7b0b115a, 0x929104dffdfabd22, 0x3fff575136879112, 0x18a299c1f683bdca };
117 g1::element lhs;
118 g1::element result;
119 g1::element expected;
120 lhs.x = a_x.to_montgomery_form();
121 lhs.y = a_y.to_montgomery_form();
122 lhs.z = a_z.to_montgomery_form();
123 expected.x = expected_x.to_montgomery_form();
124 expected.y = expected_y.to_montgomery_form();
125 expected.z = expected_z.to_montgomery_form();
126
127 result = lhs.dbl();
128 result.self_dbl();
129 result.self_dbl();
130
131 EXPECT_EQ(result == expected, true);
132}
133
134TEST(g1, AddCheckAgainstConstants)
135{
136 fq a_x{ 0x184b38afc6e2e09a, 0x4965cd1c3687f635, 0x334da8e7539e71c4, 0xf708d16cfe6e14 };
137 fq a_y{ 0x2a6ff6ffc739b3b6, 0x70761d618b513b9, 0xbf1645401de26ba1, 0x114a1616c164b980 };
138 fq a_z{ 0x10143ade26bbd57a, 0x98cf4e1f6c214053, 0x6bfdc534f6b00006, 0x1875e5068ababf2c };
139 fq b_x{ 0xafdb8a15c98bf74c, 0xac54df622a8d991a, 0xc6e5ae1f3dad4ec8, 0x1bd3fb4a59e19b52 };
140 fq b_y{ 0x21b3bb529bec20c0, 0xaabd496406ffb8c1, 0xcd3526c26ac5bdcb, 0x187ada6b8693c184 };
141 fq b_z{ 0xffcd440a228ed652, 0x8a795c8f234145f1, 0xd5279cdbabb05b95, 0xbdf19ba16fc607a };
142 fq expected_x{ 0x18764da36aa4cd81, 0xd15388d1fea9f3d3, 0xeb7c437de4bbd748, 0x2f09b712adf6f18f };
143 fq expected_y{ 0x50c5f3cab191498c, 0xe50aa3ce802ea3b5, 0xd9d6125b82ebeff8, 0x27e91ba0686e54fe };
144 fq expected_z{ 0xe4b81ef75fedf95, 0xf608edef14913c75, 0xfd9e178143224c96, 0xa8ae44990c8accd };
145 g1::element lhs;
146 g1::element rhs;
147 g1::element result;
148 g1::element expected;
149
150 lhs.x = a_x.to_montgomery_form();
151 lhs.y = a_y.to_montgomery_form();
152 lhs.z = a_z.to_montgomery_form();
153 rhs.x = b_x.to_montgomery_form();
154 rhs.y = b_y.to_montgomery_form();
155 rhs.z = b_z.to_montgomery_form();
156 expected.x = expected_x.to_montgomery_form();
157 expected.y = expected_y.to_montgomery_form();
158 expected.z = expected_z.to_montgomery_form();
159
160 result = lhs + rhs;
161
162 EXPECT_EQ(result == expected, true);
163}
164
165TEST(g1, AddExceptionTestInfinity)
166{
167 g1::element lhs = g1::element::random_element();
168 g1::element rhs;
169 g1::element result;
170
171 rhs = -lhs;
172
173 result = lhs + rhs;
174
175 EXPECT_EQ(result.is_point_at_infinity(), true);
176
177 g1::element rhs_b;
178 rhs_b = rhs;
179 rhs_b.self_set_infinity();
180
181 result = lhs + rhs_b;
182
183 EXPECT_EQ(lhs == result, true);
184
185 lhs.self_set_infinity();
186 result = lhs + rhs;
187
188 EXPECT_EQ(rhs == result, true);
189}
190
191TEST(g1, TestInfinity)
192{
193 g1::affine_element inf_affine = g1::affine_element::infinity();
194 EXPECT_EQ(inf_affine.is_point_at_infinity(), true);
195
196 g1::element inf_element = g1::element::infinity();
197 EXPECT_EQ(inf_element.is_point_at_infinity(), true);
198}
199
200TEST(g1, AddExceptionTestDbl)
201{
202 g1::element lhs = g1::element::random_element();
203 g1::element rhs;
204 rhs = lhs;
205
206 g1::element result;
207 g1::element expected;
208
209 result = lhs + rhs;
210 expected = lhs.dbl();
211
212 EXPECT_EQ(result == expected, true);
213}
214
215TEST(g1, AddAffineTest)
216{
217 g1::element lhs = g1::element::random_element();
218 g1::affine_element lhs_affine(lhs);
219
220 g1::element rhs = g1::element::random_element();
221 g1::affine_element rhs_affine(rhs);
222
223 g1::element expected = lhs + rhs;
224 g1::affine_element result = lhs_affine + rhs_affine;
225 EXPECT_EQ(g1::element(result) == expected, true);
226}
227
228TEST(g1, AddDblConsistency)
229{
230 g1::element a = g1::element::random_element();
231 g1::element b = g1::element::random_element();
232
233 g1::element c;
234 g1::element d;
235 g1::element add_result;
236 g1::element dbl_result;
237
238 c = a + b;
239 b = -b;
240 d = a + b;
241
242 add_result = c + d;
243 dbl_result = a.dbl();
244
245 EXPECT_EQ(add_result == dbl_result, true);
246}
247
248TEST(g1, AddDblConsistencyRepeated)
249{
250 g1::element a = g1::element::random_element();
252 g1::element c;
253 g1::element d;
254 g1::element e;
255
256 g1::element result;
257 g1::element expected;
258
259 b = a.dbl(); // b = 2a
260 c = b.dbl(); // c = 4a
261
262 d = a + b; // d = 3a
263 e = a + c; // e = 5a
264 result = d + e; // result = 8a
265
266 expected = c.dbl(); // expected = 8a
267
268 EXPECT_EQ(result == expected, true);
269}
270
271TEST(g1, MixedAddExceptionTestInfinity)
272{
273 g1::element lhs = g1::one;
274 g1::affine_element rhs = g1::element::random_element();
275 fq::__copy(rhs.x, lhs.x);
276 lhs.y = -rhs.y;
277
278 g1::element result;
279 result = lhs + rhs;
280
281 EXPECT_EQ(result.is_point_at_infinity(), true);
282
283 lhs.self_set_infinity();
284 result = lhs + rhs;
285 g1::element rhs_c;
286 rhs_c = g1::element(rhs);
287
288 EXPECT_EQ(rhs_c == result, true);
289}
290
291TEST(g1, MixedAddExceptionTestDbl)
292{
293 g1::affine_element rhs = g1::element::random_element();
294 g1::element lhs;
295 lhs = g1::element(rhs);
296
297 g1::element result;
298 g1::element expected;
299 result = lhs + rhs;
300
301 expected = lhs.dbl();
302
303 EXPECT_EQ(result == expected, true);
304}
305
306TEST(g1, AddMixedAddConsistencyCheck)
307{
308 g1::affine_element rhs = g1::element::random_element();
309 g1::element lhs = g1::element::random_element();
310 g1::element rhs_b;
311 rhs_b = g1::element(rhs);
312
313 g1::element add_result;
314 g1::element mixed_add_result;
315 add_result = lhs + rhs_b;
316 mixed_add_result = lhs + rhs;
317
318 EXPECT_EQ(add_result == mixed_add_result, true);
319}
320
321TEST(g1, BatchNormalize)
322{
323 size_t num_points = 2;
324 std::vector<g1::element> points(num_points);
325 std::vector<g1::element> normalized(num_points);
326 for (size_t i = 0; i < num_points; ++i) {
327 g1::element a = g1::element::random_element();
328 g1::element b = g1::element::random_element();
329 points[i] = a + b;
330 normalized[i] = points[i];
331 }
332 g1::element::batch_normalize(&normalized[0], num_points);
333
334 for (size_t i = 0; i < num_points; ++i) {
335 fq zz;
336 fq zzz;
337 fq result_x;
338 fq result_y;
339 zz = points[i].z.sqr();
340 zzz = points[i].z * zz;
341 result_x = normalized[i].x * zz;
342 result_y = normalized[i].y * zzz;
343
344 EXPECT_EQ((result_x == points[i].x), true);
345 EXPECT_EQ((result_y == points[i].y), true);
346 }
347}
348
349TEST(g1, GroupExponentiationCheckAgainstConstants)
350{
351 fr a{ 0xb67299b792199cf0, 0xc1da7df1e7e12768, 0x692e427911532edf, 0x13dd85e87dc89978 };
353
354 fq expected_x{ 0x9bf840faf1b4ba00, 0xe81b7260d068e663, 0x7610c9a658d2c443, 0x278307cd3d0cddb0 };
355 fq expected_y{ 0xf6ed5fb779ebecb, 0x414ca771acbe183c, 0xe3692cb56dfbdb67, 0x3d3c5ed19b080a3 };
356
357 g1::affine_element expected;
358 expected.x = expected_x.to_montgomery_form();
359 expected.y = expected_y.to_montgomery_form();
360
361 g1::affine_element result(g1::one * a);
362
363 EXPECT_EQ(result == expected, true);
364}
365
366TEST(g1, OperatorOrdering)
367{
368 // fq a_x{ 0x92716caa6cac6d26, 0x1e6e234136736544, 0x1bb04588cde00af0, 0x9a2ac922d97e6f5 };
369 // fq a_y{ 0x9e693aeb52d79d2d, 0xf0c1895a61e5e975, 0x18cd7f5310ced70f, 0xac67920a22939ad };
370 // fq a_z{ 0xfef593c9ce1df132, 0xe0486f801303c27d, 0x9bbd01ab881dc08e, 0x2a589badf38ec0f9 };
371 fr scalar{ 0xcfbfd4441138823e, 0xb5f817e28a1ef904, 0xefb7c5629dcc1c42, 0x1a9ed3d6f846230e };
372 // fq expected_x{ 0x2a9d0201fccca20, 0x36f969b294f31776, 0xee5534422a6f646, 0x911dbc6b02310b6 };
373 // fq expected_y{ 0x14c30aaeb4f135ef, 0x9c27c128ea2017a1, 0xf9b7d80c8315eabf, 0x35e628df8add760 };
374 // fq expected_z{ 0xa43fe96673d10eb3, 0x88fbe6351753d410, 0x45c21cc9d99cb7d, 0x3018020aa6e9ede5 };
375
378
379 g1::element c = a + b;
380 g1::element d = b + a;
381 EXPECT_EQ(c, d);
382
383 g1::element e = a * scalar;
384 g1::element f = b * scalar;
385 g1::affine_element g = b * scalar;
386 g1::affine_element h = a * scalar;
387 EXPECT_EQ(e, f);
388 EXPECT_EQ(g, h);
389}
390
391TEST(g1, GroupExponentiationZeroAndOne)
392{
394
395 EXPECT_EQ(result.is_point_at_infinity(), true);
396
397 result = g1::one * fr::one();
398
399 EXPECT_EQ(result == g1::affine_one, true);
400}
401
402TEST(g1, GroupExponentiationConsistencyCheck)
403{
406
407 fr c;
408 c = a * b;
409
411 g1::affine_element result(g1::element(input) * a);
412 result = g1::affine_element(g1::element(result) * b);
413
415
416 EXPECT_EQ(result == expected, true);
417}
418
419TEST(g1, DeriveGenerators)
420{
421 constexpr size_t num_generators = 128;
422 auto result = g1::derive_generators("test domain", 128);
423
424 const auto is_unique = [&result](const g1::affine_element& y, const size_t j) {
425 for (size_t i = 0; i < result.size(); ++i) {
426 if ((i != j) && result[i] == y) {
427 return false;
428 }
429 }
430 return true;
431 };
432
433 for (size_t k = 0; k < num_generators; ++k) {
434 EXPECT_EQ(is_unique(result[k], k), true);
435 EXPECT_EQ(result[k].on_curve(), true);
436 }
437}
438
439TEST(g1, Serialize)
440{
441 g1::affine_element expected = g1::element::random_element();
442
443 std::vector<uint8_t> buffer(sizeof(g1::affine_element));
444
445 g1::affine_element::serialize_to_buffer(expected, &buffer[0]);
446
447 g1::affine_element result = g1::affine_element::serialize_from_buffer(&buffer[0]);
448
449 EXPECT_EQ(result == expected, true);
450}
451template <class T> void write(const T t)
452{
453 FILE* fp = fopen("/dev/null", "wb");
454 static_cast<void>(fwrite(&t, sizeof(t), 1, fp));
455 static_cast<void>(fclose(fp));
456}
457
458#if !defined(__wasm__)
459TEST(g1, InitializationCheck)
460{
461 // NOLINTNEXTLINE not our fault googletest uses `goto`!
462 EXPECT_NO_THROW(write<g1::affine_element>({}));
463}
464#endif
465
466TEST(g1, CheckPrecomputedGenerators)
467{
468 ASSERT_TRUE((bb::check_precomputed_generators<g1, "biggroup table offset generator", 1UL>()));
469 ASSERT_TRUE((bb::check_precomputed_generators<g1, "biggroup offset generator", 1UL>()));
470 ASSERT_TRUE((bb::check_precomputed_generators<g1, "ECCVM_OFFSET_GENERATOR", 1UL>()));
471 ASSERT_TRUE((bb::check_precomputed_generators<g1, "test generators", 2UL>()));
472}
473
474// Regression: boundary scalars k = ceil(m * 2^256 / endo_g2) (from endomorphism_scalars.py)
475// previously triggered the negative-k2 bug in split_into_endomorphism_scalars, producing wrong
476// scalar multiplication results. We test boundaries and random samples within each band.
477TEST(g1, ScalarMulNegativeK2Regression)
478{
479 // clang-format off
480 struct test_case { std::array<uint64_t, 4> limbs; const char* tag; };
481 const std::array<test_case, 3> boundary_cases = {{
482 {{ 0x01624731e1195570, 0x3ba491482db4da14, 0x59e26bcea0d48bac, 0x0 }, "m=1"},
483 {{ 0x02c48e63c232aadf, 0x774922905b69b428, 0xb3c4d79d41a91758, 0x0 }, "m=2"},
484 {{ 0x0426d595a34c004e, 0xb2edb3d8891e8e3c, 0x0da7436be27da304, 0x1 }, "m=3"},
485 }};
486 // clang-format on
487
488 for (const auto& tc : boundary_cases) {
489 fr base_scalar{ tc.limbs[0], tc.limbs[1], tc.limbs[2], tc.limbs[3] };
490 base_scalar.self_to_montgomery_form();
491
492 g1::affine_element endo_result(g1::one * base_scalar);
493 g1::affine_element naive_result = naive_scalar_mul<g1, fr>(g1::one, base_scalar);
494 EXPECT_EQ(naive_result.on_curve(), true) << tc.tag;
495 EXPECT_EQ(endo_result.on_curve(), true) << tc.tag;
496 EXPECT_EQ(endo_result, naive_result) << tc.tag;
497
498 // Random samples within the formerly-buggy band (~2^123-2^126 wide; 122-bit offsets).
499 for (size_t i = 0; i < 100; ++i) {
500 uint256_t rand_bits(fr::random_element());
501 uint256_t offset_int = (rand_bits & ((uint256_t(1) << 122) - 1)) + 1;
502 fr scalar = base_scalar + fr(offset_int);
503
504 g1::affine_element endo_res(g1::one * scalar);
505 g1::affine_element naive_res = naive_scalar_mul<g1, fr>(g1::one, scalar);
506 EXPECT_EQ(naive_res.on_curve(), true) << tc.tag << " offset " << i;
507 EXPECT_EQ(endo_res.on_curve(), true) << tc.tag << " offset " << i;
508 EXPECT_EQ(endo_res, naive_res) << tc.tag << " offset " << i;
509 }
510 }
511}
constexpr bool is_point_at_infinity() const noexcept
constexpr bool on_curve() const noexcept
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition element.hpp:33
constexpr element dbl() const noexcept
constexpr void self_dbl() noexcept
BB_INLINE constexpr bool on_curve() const noexcept
BB_INLINE constexpr void self_set_infinity() noexcept
BB_INLINE constexpr bool is_point_at_infinity() const noexcept
group class. Represents an elliptic curve group element. Group is parametrised by Fq and Fr
Definition group.hpp:36
group_elements::affine_element< Fq, Fr, Params > affine_element
Definition group.hpp:42
static constexpr element one
Definition group.hpp:46
static constexpr affine_element affine_one
Definition group.hpp:48
group_elements::element< Fq, Fr, Params > element
Definition group.hpp:41
static std::vector< affine_element > derive_generators(const std::vector< uint8_t > &domain_separator_bytes, const size_t num_generators, const size_t starting_index=0)
Derives generator points via hash-to-curve.
Definition group.hpp:87
FF a
FF b
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:50
void write(const T t)
Definition g1.test.cpp:451
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FqParams > fq
Definition fq.hpp:153
field< Bn254FrParams > fr
Definition fr.hpp:155
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr fq b
Definition g1.hpp:31
static constexpr fq one_y
Definition g1.hpp:25
static constexpr field one()
BB_INLINE constexpr field to_montgomery_form() const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr field sqr() const noexcept
static BB_INLINE void __copy(const field &a, field &r) noexcept
BB_INLINE constexpr void self_to_montgomery_form() &noexcept
static constexpr field zero()