Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
grumpkin.test.cpp
Go to the documentation of this file.
1#include "grumpkin.hpp"
3#include <chrono>
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(grumpkin, CheckB)
31{
33 fr seventeen = 17;
34 EXPECT_EQ(seventeen, -b);
35}
36
37TEST(grumpkin, SubgroupGenerator)
38{
41
42 EXPECT_NE(subgroup_generator.pow(3), fq::one());
43 EXPECT_NE(subgroup_generator.pow(29), fq::one());
44 EXPECT_EQ(subgroup_generator.pow(87), fq::one());
45 EXPECT_EQ(subgroup_generator * subgroup_generator_inverse, fq::one());
46}
47
48TEST(grumpkin, OneYIsCorrect)
49{
51 auto [_, expected] = (bb::grumpkin::G1Params::b + fr::one()).sqrt();
52
53 EXPECT_EQ(one_y, -expected);
54}
55
56// =========================
57// Group-related tests
58// =========================
59
60TEST(grumpkin, RandomElement)
61{
62 grumpkin::g1::element result = grumpkin::g1::element::random_element();
63 EXPECT_EQ(result.on_curve(), true);
64}
65
66TEST(grumpkin, RandomAffineElement)
67{
68 grumpkin::g1::affine_element result = grumpkin::g1::element::random_element();
69 EXPECT_EQ(result.on_curve(), true);
70}
71
72TEST(grumpkin, Eq)
73{
74 grumpkin::g1::element a = grumpkin::g1::element::random_element();
75 grumpkin::g1::element b = a.normalize();
76
77 EXPECT_EQ(a == b, true);
78 EXPECT_EQ(a == a, true);
79
80 b.self_set_infinity();
81
82 EXPECT_EQ(a == b, false);
83 grumpkin::g1::element c = grumpkin::g1::element::random_element();
84
85 EXPECT_EQ(a == c, false);
86
87 a.self_set_infinity();
88
89 EXPECT_EQ(a == b, true);
90}
91
92TEST(grumpkin, CheckGroupModulus)
93{
94 // grumpkin::g1::affine_element expected = grumpkin::g1::affine_one;
95 grumpkin::fr exponent = -grumpkin::fr(1);
96 grumpkin::g1::element result = grumpkin::g1::one * exponent;
97 result += grumpkin::g1::one;
98 result += grumpkin::g1::one;
99 EXPECT_EQ(result.on_curve(), true);
100 EXPECT_EQ(result == grumpkin::g1::one, true);
101}
102
103TEST(grumpkin, AddExceptionTestInfinity)
104{
105 grumpkin::g1::element lhs = grumpkin::g1::element::random_element();
108
109 rhs = -lhs;
110
111 result = lhs + rhs;
112
113 EXPECT_EQ(result.is_point_at_infinity(), true);
114
116 rhs_b = rhs;
117 rhs_b.self_set_infinity();
118
119 result = lhs + rhs_b;
120
121 EXPECT_EQ(lhs == result, true);
122
123 lhs.self_set_infinity();
124 result = lhs + rhs;
125
126 EXPECT_EQ(rhs == result, true);
127}
128
129TEST(grumpkin, AddExceptionTestDbl)
130{
131 grumpkin::g1::element lhs = grumpkin::g1::element::random_element();
133 rhs = lhs;
134
136 grumpkin::g1::element expected;
137
138 result = lhs + rhs;
139 expected = lhs.dbl();
140
141 EXPECT_EQ(result == expected, true);
142}
143
144TEST(grumpkin, AddDblConsistency)
145{
146 grumpkin::g1::element a = grumpkin::g1::element::random_element();
147 grumpkin::g1::element b = grumpkin::g1::element::random_element();
148
151 grumpkin::g1::element add_result;
152 grumpkin::g1::element dbl_result;
153
154 c = a + b;
155 b = -b;
156 d = a + b;
157
158 add_result = c + d;
159 dbl_result = a.dbl();
160
161 EXPECT_EQ(add_result == dbl_result, true);
162}
163
164TEST(grumpkin, AddDblConsistencyRepeated)
165{
166 grumpkin::g1::element a = grumpkin::g1::element::random_element();
171
173 grumpkin::g1::element expected;
174
175 b = a.dbl(); // b = 2a
176 c = b.dbl(); // c = 4a
177
178 d = a + b; // d = 3a
179 e = a + c; // e = 5a
180 result = d + e; // result = 8a
181
182 expected = c.dbl(); // expected = 8a
183
184 EXPECT_EQ(result == expected, true);
185}
186
187TEST(grumpkin, MixedAddExceptionTestInfinity)
188{
190 grumpkin::g1::affine_element rhs = grumpkin::g1::element::random_element();
191 grumpkin::fq::__copy(rhs.x, lhs.x);
192 lhs.y = -rhs.y;
193
195 result = lhs + rhs;
196
197 EXPECT_EQ(result.is_point_at_infinity(), true);
198
199 lhs.self_set_infinity();
200 result = lhs + rhs;
202 rhs_c = grumpkin::g1::element(rhs);
203
204 EXPECT_EQ(rhs_c == result, true);
205}
206
207TEST(grumpkin, MixedAddExceptionTestDbl)
208{
209 grumpkin::g1::affine_element rhs = grumpkin::g1::element::random_element();
211 lhs = grumpkin::g1::element(rhs);
212
214 grumpkin::g1::element expected;
215 result = lhs + rhs;
216
217 expected = lhs.dbl();
218
219 EXPECT_EQ(result == expected, true);
220}
221
222TEST(grumpkin, AddMixedAddConsistencyCheck)
223{
224 grumpkin::g1::affine_element rhs = grumpkin::g1::element::random_element();
225 grumpkin::g1::element lhs = grumpkin::g1::element::random_element();
227 rhs_b = grumpkin::g1::element(rhs);
228
229 grumpkin::g1::element add_result;
230 grumpkin::g1::element mixed_add_result;
231 add_result = lhs + rhs_b;
232 mixed_add_result = lhs + rhs;
233
234 EXPECT_EQ(add_result == mixed_add_result, true);
235}
236
237TEST(grumpkin, OnCurve)
238{
239 for (size_t i = 0; i < 100; ++i) {
240 grumpkin::g1::element test = grumpkin::g1::element::random_element();
241 EXPECT_EQ(test.on_curve(), true);
242 grumpkin::g1::affine_element affine_test = grumpkin::g1::element::random_element();
243 EXPECT_EQ(affine_test.on_curve(), true);
244 }
245}
246TEST(grumpkin, BatchNormalize)
247{
248 size_t num_points = 2;
249 std::vector<grumpkin::g1::element> points(num_points);
250 std::vector<grumpkin::g1::element> normalized(num_points);
251 for (size_t i = 0; i < num_points; ++i) {
252 grumpkin::g1::element a = grumpkin::g1::element::random_element();
253 grumpkin::g1::element b = grumpkin::g1::element::random_element();
254 points[i] = a + b;
255 normalized[i] = points[i];
256 }
257 grumpkin::g1::element::batch_normalize(&normalized[0], num_points);
258
259 for (size_t i = 0; i < num_points; ++i) {
260 grumpkin::fq zz;
261 grumpkin::fq zzz;
262 grumpkin::fq result_x;
263 grumpkin::fq result_y;
264 zz = points[i].z.sqr();
265 zzz = points[i].z * zz;
266 result_x = normalized[i].x * zz;
267 result_y = normalized[i].y * zzz;
268
269 EXPECT_EQ((result_x == points[i].x), true);
270 EXPECT_EQ((result_y == points[i].y), true);
271 }
272}
273
274TEST(grumpkin, GroupExponentiationZeroAndOne)
275{
277
278 EXPECT_EQ(result.is_point_at_infinity(), true);
279
281
282 EXPECT_EQ(result == grumpkin::g1::affine_one, true);
283}
284
285TEST(grumpkin, GroupExponentiationConsistencyCheck)
286{
289
290 grumpkin::fr c;
291 c = a * b;
292
294 grumpkin::g1::affine_element result = input * a;
295 result = result * b;
296
297 grumpkin::g1::affine_element expected = input * c;
298
299 EXPECT_EQ(result == expected, true);
300}
301
302TEST(grumpkin, DeriveGenerators)
303{
304 constexpr size_t num_generators = 128;
305 auto result = grumpkin::g1::derive_generators("test generators", num_generators);
306 const auto is_unique = [&result](const grumpkin::g1::affine_element& y, const size_t j) {
307 for (size_t i = 0; i < result.size(); ++i) {
308 if ((i != j) && result[i] == y) {
309 return false;
310 }
311 }
312 return true;
313 };
314
315 for (size_t k = 0; k < num_generators; ++k) {
316 EXPECT_EQ(is_unique(result[k], k), true);
317 EXPECT_EQ(result[k].on_curve(), true);
318 }
319}
320
321TEST(grumpkin, BatchMul)
322{
323 constexpr size_t num_points = 1024;
324
326 for (size_t i = 0; i < num_points; ++i) {
327 points.emplace_back(grumpkin::g1::element::random_element());
328 }
329 grumpkin::g1::element::batch_normalize(&points[0], num_points);
330
332 for (size_t i = 0; i < num_points; ++i) {
333 affine_points.emplace_back(points[i]);
334 }
336
337 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
338
340 expected.reserve(num_points);
341 for (const auto& point : points) {
342 expected.emplace_back((point * exponent).normalize());
343 }
344
345 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
346 std::chrono::milliseconds diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
347 std::cout << "regular mul operations: " << diff.count() << "ms" << std::endl;
348
349 start = std::chrono::steady_clock::now();
350
351 const auto result = grumpkin::g1::element::batch_mul_with_endomorphism(affine_points, exponent);
352 end = std::chrono::steady_clock::now();
353 diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
354 std::cout << "batched mul operations: " << diff.count() << "ms" << std::endl;
355
356 for (size_t i = 0; i < num_points; ++i) {
357 EXPECT_EQ(result[i].x, expected[i].x);
358 EXPECT_EQ(result[i].y, expected[i].y);
359 }
360}
361// Checks for "bad points" in terms of sharing a y-coordinate as explained here:
362// https://github.com/AztecProtocol/aztec2-internal/issues/437
363TEST(grumpkin, BadPoints)
364{
366 auto beta_sqr = beta * beta;
367 bool res = true;
368 grumpkin::fr c(1);
369 for (size_t i = 0; i < 256; i++) {
370 auto val = c / (grumpkin::fr(1) + c);
371 if (val == beta || val == beta_sqr) {
372 res = false;
373 }
374 c *= grumpkin::fr(4);
375 }
376 EXPECT_TRUE(res);
377}
378
379TEST(grumpkin, CheckPrecomputedGenerators)
380{
381 ASSERT_TRUE((bb::check_precomputed_generators<grumpkin::g1, "pedersen_hash_length", 1UL>()));
382 ASSERT_TRUE((bb::check_precomputed_generators<grumpkin::g1, "DEFAULT_DOMAIN_SEPARATOR", 8UL>()));
383}
384
385// Regression: boundary scalars k = ceil(m * 2^256 / endo_g2) (from endomorphism_scalars.py)
386// previously triggered the negative-k2 bug in split_into_endomorphism_scalars, producing wrong
387// scalar multiplication results. We test boundaries and random samples within each band.
388TEST(grumpkin, ScalarMulNegativeK2Regression)
389{
390 // clang-format off
391 struct test_case { std::array<uint64_t, 4> limbs; const char* tag; };
392 const std::array<test_case, 3> boundary_cases = {{
393 {{ 0x71922da036dca5f4, 0xd970a56127fb8227, 0x59e26bcea0d48bac, 0x0 }, "m=1"},
394 {{ 0xe3245b406db94be8, 0xb2e14ac24ff7044e, 0xb3c4d79d41a91759, 0x0 }, "m=2"},
395 {{ 0x54b688e0a495f1dc, 0x8c51f02377f28676, 0x0da7436be27da306, 0x1 }, "m=3"},
396 }};
397 // clang-format on
398
399 for (const auto& tc : boundary_cases) {
400 grumpkin::fr base_scalar{ tc.limbs[0], tc.limbs[1], tc.limbs[2], tc.limbs[3] };
401 base_scalar.self_to_montgomery_form();
402
403 grumpkin::g1::affine_element endo_result(grumpkin::g1::one * base_scalar);
404 grumpkin::g1::affine_element naive_result =
405 naive_scalar_mul<grumpkin::g1, grumpkin::fr>(grumpkin::g1::one, base_scalar);
406 EXPECT_EQ(naive_result.on_curve(), true) << tc.tag;
407 EXPECT_EQ(endo_result.on_curve(), true) << tc.tag;
408 EXPECT_EQ(endo_result, naive_result) << tc.tag;
409
410 // Random samples within the formerly-buggy band (~2^123-2^126 wide; 122-bit offsets).
411 for (size_t i = 0; i < 100; ++i) {
413 uint256_t offset_int = (rand_bits & ((uint256_t(1) << 122) - 1)) + 1;
414 grumpkin::fr scalar = base_scalar + grumpkin::fr(offset_int);
415
418 naive_scalar_mul<grumpkin::g1, grumpkin::fr>(grumpkin::g1::one, scalar);
419 EXPECT_EQ(naive_res.on_curve(), true) << tc.tag << " offset " << i;
420 EXPECT_EQ(endo_res.on_curve(), true) << tc.tag << " offset " << i;
421 EXPECT_EQ(endo_res, naive_res) << tc.tag << " offset " << i;
422 }
423 }
424}
static constexpr ScalarField subgroup_generator_inverse
Definition grumpkin.hpp:80
static constexpr ScalarField subgroup_generator
Definition grumpkin.hpp:78
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
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
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 constexpr Fq curve_b
Definition group.hpp:51
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
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr field cube_root_of_unity()
static constexpr field one()
BB_INLINE constexpr field pow(const uint256_t &exponent) 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()
static constexpr bb::fr one_y
Definition grumpkin.hpp:42
static constexpr bb::fr b
Definition grumpkin.hpp:31