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
26TEST(grumpkin, CheckB)
27{
29 fr seventeen = 17;
30 EXPECT_EQ(seventeen, -b);
31}
32
33TEST(grumpkin, RandomElement)
34{
35 grumpkin::g1::element result = grumpkin::g1::element::random_element();
36 EXPECT_EQ(result.on_curve(), true);
37}
38
39TEST(grumpkin, RandomAffineElement)
40{
41 grumpkin::g1::affine_element result = grumpkin::g1::element::random_element();
42 EXPECT_EQ(result.on_curve(), true);
43}
44
45TEST(grumpkin, Eq)
46{
47 grumpkin::g1::element a = grumpkin::g1::element::random_element();
48 grumpkin::g1::element b = a.normalize();
49
50 EXPECT_EQ(a == b, true);
51 EXPECT_EQ(a == a, true);
52
53 b.self_set_infinity();
54
55 EXPECT_EQ(a == b, false);
56 grumpkin::g1::element c = grumpkin::g1::element::random_element();
57
58 EXPECT_EQ(a == c, false);
59
60 a.self_set_infinity();
61
62 EXPECT_EQ(a == b, true);
63}
64
65TEST(grumpkin, CheckGroupModulus)
66{
67 // grumpkin::g1::affine_element expected = grumpkin::g1::affine_one;
68 grumpkin::fr exponent = -grumpkin::fr(1);
69 grumpkin::g1::element result = grumpkin::g1::one * exponent;
70 result += grumpkin::g1::one;
71 result += grumpkin::g1::one;
72 EXPECT_EQ(result.on_curve(), true);
73 EXPECT_EQ(result == grumpkin::g1::one, true);
74}
75
76TEST(grumpkin, AddExceptionTestInfinity)
77{
78 grumpkin::g1::element lhs = grumpkin::g1::element::random_element();
81
82 rhs = -lhs;
83
84 result = lhs + rhs;
85
86 EXPECT_EQ(result.is_point_at_infinity(), true);
87
89 rhs_b = rhs;
90 rhs_b.self_set_infinity();
91
92 result = lhs + rhs_b;
93
94 EXPECT_EQ(lhs == result, true);
95
97 result = lhs + rhs;
98
99 EXPECT_EQ(rhs == result, true);
100}
101
102TEST(grumpkin, AddExceptionTestDbl)
103{
104 grumpkin::g1::element lhs = grumpkin::g1::element::random_element();
106 rhs = lhs;
107
109 grumpkin::g1::element expected;
110
111 result = lhs + rhs;
112 expected = lhs.dbl();
113
114 EXPECT_EQ(result == expected, true);
115}
116
117TEST(grumpkin, AddDblConsistency)
118{
119 grumpkin::g1::element a = grumpkin::g1::element::random_element();
120 grumpkin::g1::element b = grumpkin::g1::element::random_element();
121
124 grumpkin::g1::element add_result;
125 grumpkin::g1::element dbl_result;
126
127 c = a + b;
128 b = -b;
129 d = a + b;
130
131 add_result = c + d;
132 dbl_result = a.dbl();
133
134 EXPECT_EQ(add_result == dbl_result, true);
135}
136
137TEST(grumpkin, AddDblConsistencyRepeated)
138{
139 grumpkin::g1::element a = grumpkin::g1::element::random_element();
144
146 grumpkin::g1::element expected;
147
148 b = a.dbl(); // b = 2a
149 c = b.dbl(); // c = 4a
150
151 d = a + b; // d = 3a
152 e = a + c; // e = 5a
153 result = d + e; // result = 8a
154
155 expected = c.dbl(); // expected = 8a
156
157 EXPECT_EQ(result == expected, true);
158}
159
160TEST(grumpkin, MixedAddExceptionTestInfinity)
161{
163 grumpkin::g1::affine_element rhs = grumpkin::g1::element::random_element();
164 grumpkin::fq::__copy(rhs.x, lhs.x);
165 lhs.y = -rhs.y;
166
168 result = lhs + rhs;
169
170 EXPECT_EQ(result.is_point_at_infinity(), true);
171
172 lhs.self_set_infinity();
173 result = lhs + rhs;
175 rhs_c = grumpkin::g1::element(rhs);
176
177 EXPECT_EQ(rhs_c == result, true);
178}
179
180TEST(grumpkin, MixedAddExceptionTestDbl)
181{
182 grumpkin::g1::affine_element rhs = grumpkin::g1::element::random_element();
184 lhs = grumpkin::g1::element(rhs);
185
187 grumpkin::g1::element expected;
188 result = lhs + rhs;
189
190 expected = lhs.dbl();
191
192 EXPECT_EQ(result == expected, true);
193}
194
195TEST(grumpkin, AddMixedAddConsistencyCheck)
196{
197 grumpkin::g1::affine_element rhs = grumpkin::g1::element::random_element();
198 grumpkin::g1::element lhs = grumpkin::g1::element::random_element();
200 rhs_b = grumpkin::g1::element(rhs);
201
202 grumpkin::g1::element add_result;
203 grumpkin::g1::element mixed_add_result;
204 add_result = lhs + rhs_b;
205 mixed_add_result = lhs + rhs;
206
207 EXPECT_EQ(add_result == mixed_add_result, true);
208}
209
210TEST(grumpkin, OnCurve)
211{
212 for (size_t i = 0; i < 100; ++i) {
213 grumpkin::g1::element test = grumpkin::g1::element::random_element();
214 EXPECT_EQ(test.on_curve(), true);
215 grumpkin::g1::affine_element affine_test = grumpkin::g1::element::random_element();
216 EXPECT_EQ(affine_test.on_curve(), true);
217 }
218}
219TEST(grumpkin, BatchNormalize)
220{
221 size_t num_points = 2;
222 std::vector<grumpkin::g1::element> points(num_points);
223 std::vector<grumpkin::g1::element> normalized(num_points);
224 for (size_t i = 0; i < num_points; ++i) {
225 grumpkin::g1::element a = grumpkin::g1::element::random_element();
226 grumpkin::g1::element b = grumpkin::g1::element::random_element();
227 points[i] = a + b;
228 normalized[i] = points[i];
229 }
230 grumpkin::g1::element::batch_normalize(&normalized[0], num_points);
231
232 for (size_t i = 0; i < num_points; ++i) {
233 grumpkin::fq zz;
234 grumpkin::fq zzz;
235 grumpkin::fq result_x;
236 grumpkin::fq result_y;
237 zz = points[i].z.sqr();
238 zzz = points[i].z * zz;
239 result_x = normalized[i].x * zz;
240 result_y = normalized[i].y * zzz;
241
242 EXPECT_EQ((result_x == points[i].x), true);
243 EXPECT_EQ((result_y == points[i].y), true);
244 }
245}
246
247TEST(grumpkin, GroupExponentiationZeroAndOne)
248{
250
251 EXPECT_EQ(result.is_point_at_infinity(), true);
252
254
255 EXPECT_EQ(result == grumpkin::g1::affine_one, true);
256}
257
258TEST(grumpkin, GroupExponentiationConsistencyCheck)
259{
262
263 grumpkin::fr c;
264 c = a * b;
265
267 grumpkin::g1::affine_element result = input * a;
268 result = result * b;
269
270 grumpkin::g1::affine_element expected = input * c;
271
272 EXPECT_EQ(result == expected, true);
273}
274
275TEST(grumpkin, DeriveGenerators)
276{
277 constexpr size_t num_generators = 128;
278 auto result = grumpkin::g1::derive_generators("test generators", num_generators);
279 const auto is_unique = [&result](const grumpkin::g1::affine_element& y, const size_t j) {
280 for (size_t i = 0; i < result.size(); ++i) {
281 if ((i != j) && result[i] == y) {
282 return false;
283 }
284 }
285 return true;
286 };
287
288 for (size_t k = 0; k < num_generators; ++k) {
289 EXPECT_EQ(is_unique(result[k], k), true);
290 EXPECT_EQ(result[k].on_curve(), true);
291 }
292}
293
294TEST(grumpkin, BatchMul)
295{
296 constexpr size_t num_points = 1024;
297
299 for (size_t i = 0; i < num_points; ++i) {
300 points.emplace_back(grumpkin::g1::element::random_element());
301 }
302 grumpkin::g1::element::batch_normalize(&points[0], num_points);
303
305 for (size_t i = 0; i < num_points; ++i) {
306 affine_points.emplace_back(points[i]);
307 }
309
310 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
311
313 expected.reserve(num_points);
314 for (const auto& point : points) {
315 expected.emplace_back((point * exponent).normalize());
316 }
317
318 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
319 std::chrono::milliseconds diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
320 std::cout << "regular mul operations: " << diff.count() << "ms" << std::endl;
321
322 start = std::chrono::steady_clock::now();
323
324 const auto result = grumpkin::g1::element::batch_mul_with_endomorphism(affine_points, exponent);
325 end = std::chrono::steady_clock::now();
326 diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
327 std::cout << "batched mul operations: " << diff.count() << "ms" << std::endl;
328
329 for (size_t i = 0; i < num_points; ++i) {
330 EXPECT_EQ(result[i].x, expected[i].x);
331 EXPECT_EQ(result[i].y, expected[i].y);
332 }
333}
334// Checks for "bad points" in terms of sharing a y-coordinate as explained here:
335// https://github.com/AztecProtocol/aztec2-internal/issues/437
336TEST(grumpkin, BadPoints)
337{
339 auto beta_sqr = beta * beta;
340 bool res = true;
341 grumpkin::fr c(1);
342 for (size_t i = 0; i < 256; i++) {
343 auto val = c / (grumpkin::fr(1) + c);
344 if (val == beta || val == beta_sqr) {
345 res = false;
346 }
347 c *= grumpkin::fr(4);
348 }
349 EXPECT_TRUE(res);
350}
351
352TEST(grumpkin, CheckPrecomputedGenerators)
353{
354 ASSERT_TRUE((bb::check_precomputed_generators<grumpkin::g1, "pedersen_hash_length", 1UL>()));
355 ASSERT_TRUE((bb::check_precomputed_generators<grumpkin::g1, "DEFAULT_DOMAIN_SEPARATOR", 8UL>()));
356}
357
358// Regression: boundary scalars k = ceil(m * 2^256 / endo_g2) (from endomorphism_scalars.py)
359// previously triggered the negative-k2 bug in split_into_endomorphism_scalars, producing wrong
360// scalar multiplication results. We test boundaries and random samples within each band.
361TEST(grumpkin, ScalarMulNegativeK2Regression)
362{
363 // clang-format off
364 struct test_case { std::array<uint64_t, 4> limbs; const char* tag; };
365 const std::array<test_case, 3> boundary_cases = {{
366 {{ 0x71922da036dca5f4, 0xd970a56127fb8227, 0x59e26bcea0d48bac, 0x0 }, "m=1"},
367 {{ 0xe3245b406db94be8, 0xb2e14ac24ff7044e, 0xb3c4d79d41a91759, 0x0 }, "m=2"},
368 {{ 0x54b688e0a495f1dc, 0x8c51f02377f28676, 0x0da7436be27da306, 0x1 }, "m=3"},
369 }};
370 // clang-format on
371
372 for (const auto& tc : boundary_cases) {
373 grumpkin::fr base_scalar{ tc.limbs[0], tc.limbs[1], tc.limbs[2], tc.limbs[3] };
374 base_scalar.self_to_montgomery_form();
375
376 grumpkin::g1::affine_element endo_result(grumpkin::g1::one * base_scalar);
377 grumpkin::g1::affine_element naive_result =
378 naive_scalar_mul<grumpkin::g1, grumpkin::fr>(grumpkin::g1::one, base_scalar);
379 EXPECT_EQ(naive_result.on_curve(), true) << tc.tag;
380 EXPECT_EQ(endo_result.on_curve(), true) << tc.tag;
381 EXPECT_EQ(endo_result, naive_result) << tc.tag;
382
383 // Random samples within the formerly-buggy band (~2^123-2^126 wide; 122-bit offsets).
384 for (size_t i = 0; i < 100; ++i) {
386 uint256_t offset_int = (rand_bits & ((uint256_t(1) << 122) - 1)) + 1;
387 grumpkin::fr scalar = base_scalar + grumpkin::fr(offset_int);
388
391 naive_scalar_mul<grumpkin::g1, grumpkin::fr>(grumpkin::g1::one, scalar);
392 EXPECT_EQ(naive_res.on_curve(), true) << tc.tag << " offset " << i;
393 EXPECT_EQ(endo_res.on_curve(), true) << tc.tag << " offset " << i;
394 EXPECT_EQ(endo_res, naive_res) << tc.tag << " offset " << i;
395 }
396 }
397}
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()
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()