Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
scalar_multiplication.test.cpp
Go to the documentation of this file.
1
17
18#include <cstddef>
19#include <vector>
20
21using namespace bb;
22
23namespace {
25}
26
27template <typename Curve> class ScalarMultiplicationTests : public ::testing::Test {
28 public:
30};
31
32using Curves = ::testing::Types<curve::BN254, curve::Grumpkin>;
33
35
37{
38 using Curve = TypeParam;
39 using Element = typename Curve::Element;
40 using AffineElement = typename Curve::AffineElement;
41 using Fq = typename Curve::BaseField;
42
43 constexpr size_t num_points = 20;
44 AffineElement* points = (AffineElement*)(aligned_alloc(64, sizeof(AffineElement) * (num_points)));
45 Fq* scratch_space = (Fq*)(aligned_alloc(64, sizeof(Fq) * (num_points * 2)));
46 Fq* lambda = (Fq*)(aligned_alloc(64, sizeof(Fq) * (num_points * 2)));
47
48 Element* points_copy = (Element*)(aligned_alloc(64, sizeof(Element) * (num_points)));
49 for (size_t i = 0; i < num_points; ++i) {
50 points[i] = AffineElement(Element::random_element());
51 points_copy[i].x = points[i].x;
52 points_copy[i].y = points[i].y;
53 points_copy[i].z = Fq::one();
54 }
55
56 size_t count = num_points - 1;
57 for (size_t i = num_points - 2; i < num_points; i -= 2) {
58 points_copy[count--] = points_copy[i] + points_copy[i + 1];
59 points_copy[count + 1] = points_copy[count + 1].normalize();
60 }
61
62 scalar_multiplication::MSM<Curve>::add_affine_points(points, num_points, scratch_space);
63 for (size_t i = num_points - 1; i > num_points - 1 - (num_points / 2); --i) {
64 EXPECT_EQ((points[i].x == points_copy[i].x), true);
65 EXPECT_EQ((points[i].y == points_copy[i].y), true);
66 }
67 aligned_free(lambda);
68 aligned_free(points);
69 aligned_free(points_copy);
70 aligned_free(scratch_space);
71}
72
74{
75 using Curve = TypeParam;
76 using Group = typename Curve::Group;
77 using Element = typename Curve::Element;
78 using AffineElement = typename Curve::AffineElement;
79 using Fr = typename Curve::ScalarField;
80 using Fq = typename Curve::BaseField;
81
82 Fr scalar = Fr::random_element();
83
84 Element expected = Group::one * scalar;
85
86 // we want to test that we can split a scalar into two half-length components, using the same location in memory.
87 Fr* k1_t = &scalar;
88 Fr* k2_t = (Fr*)&scalar.data[2];
89
90 Fr::split_into_endomorphism_scalars(scalar, *k1_t, *k2_t);
91 Fr k1{ (*k1_t).data[0], (*k1_t).data[1], 0, 0 };
92 Fr k2{ (*k2_t).data[0], (*k2_t).data[1], 0, 0 };
93#if !defined(__clang__) && defined(__GNUC__)
94#pragma GCC diagnostic pop
95#endif
96 Element result;
97 Element t1 = Group::affine_one * k1;
98 AffineElement generator = Group::affine_one;
100 generator.x = generator.x * beta;
101 generator.y = -generator.y;
102 Element t2 = generator * k2;
103 result = t1 + t2;
104
105 EXPECT_EQ(result == expected, true);
106}
107
109{
110 using Curve = TypeParam;
111 using Fr = typename Curve::ScalarField;
112
113 // check that our radix sort correctly sorts!
114 constexpr size_t target_degree = 1 << 8;
115 const size_t num_rounds = scalar_multiplication::MSM<Curve>::get_num_rounds(target_degree);
116 Fr* scalars = (Fr*)(aligned_alloc(64, sizeof(Fr) * target_degree));
117
118 Fr source_scalar = Fr::random_element();
119 for (size_t i = 0; i < target_degree; ++i) {
120 source_scalar.self_sqr();
121 Fr::__copy(source_scalar, scalars[i]);
122 }
123
124 size_t bits_per_slice = scalar_multiplication::MSM<Curve>::get_optimal_log_num_buckets(target_degree);
125
126 for (size_t i = 0; i < num_rounds; ++i) {
127
128 std::vector<uint64_t> scalar_slices(target_degree);
129 std::vector<uint64_t> sorted_scalar_slices(target_degree);
130
131 for (size_t j = 0; j < target_degree; ++j) {
132 scalar_slices[j] = scalar_multiplication::MSM<Curve>::get_scalar_slice(scalars[j], i, bits_per_slice);
133 sorted_scalar_slices[j] = scalar_slices[j];
134 }
136 &sorted_scalar_slices[0], target_degree, static_cast<uint32_t>(bits_per_slice));
137
138 const auto find_entry = [scalar_slices, num_entries = target_degree](auto x) {
139 for (size_t k = 0; k < num_entries; ++k) {
140 if (scalar_slices[k] == x) {
141 return true;
142 }
143 }
144 return false;
145 };
146 for (size_t j = 0; j < target_degree; ++j) {
147 EXPECT_EQ(find_entry(sorted_scalar_slices[j]), true);
148 if (j > 0) {
149 EXPECT_EQ((sorted_scalar_slices[j] & 0x7fffffffU) >= (sorted_scalar_slices[j - 1] & 0x7fffffffU), true);
150 }
151 }
152 }
153
154 free(scalars);
155}
156
158{
159 using Curve = TypeParam;
161 // Grumpkin has at most 1 << 18 points; the test is not valid for it.
162 GTEST_SKIP() << "Skipping test for grumpkin";
163 }
164 using Element = typename Curve::Element;
165 using AffineElement = typename Curve::AffineElement;
166 using Fr = typename Curve::ScalarField;
167 using Fq = typename Curve::BaseField;
168
169 // for point ranges with more than 1 << 20 points, we split into chunks of smaller multi-exps.
170 // Check that this is done correctly
171 size_t target_degree = 1200000;
172 std::span<AffineElement> monomials = srs::get_crs_factory<Curve>()->get_crs(target_degree)->get_monomial_points();
173
174 Fr* scalars = (Fr*)(aligned_alloc(64, sizeof(Fr) * target_degree));
175
176 Fr source_scalar = Fr::random_element();
177 Fr accumulator = source_scalar;
178 for (size_t i = 0; i < target_degree; ++i) {
179 accumulator *= source_scalar;
180 Fr::__copy(accumulator, scalars[i]);
181 }
182
183 Element first = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ target_degree } }, monomials);
184 first = first.normalize();
185
186 for (size_t i = 0; i < target_degree; ++i) {
187 scalars[i].self_neg();
188 }
189
190 Element second = scalar_multiplication::pippenger<Curve>(
191 PolynomialSpan<const typename Curve::ScalarField>{ 0, { scalars, /*size*/ target_degree } }, monomials);
192 second = second.normalize();
193
194 EXPECT_EQ((first.z == second.z), true);
195 EXPECT_EQ((first.z == Fq::one()), true);
196 EXPECT_EQ((first.x == second.x), true);
197 EXPECT_EQ((first.y == -second.y), true);
198
199 aligned_free(scalars);
200}
201
203{
204 using Curve = TypeParam;
205 using Element = typename Curve::Element;
206 using AffineElement = typename Curve::AffineElement;
207 using Fr = typename Curve::ScalarField;
208
209 // we fall back to traditional scalar multiplication algorithm for small input sizes.
210 // Check this is done correctly
211 size_t num_points = 17;
212
213 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * num_points);
214
215 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (num_points * 2 + 1));
216
217 for (size_t i = 0; i < num_points; ++i) {
218 scalars[i] = Fr::random_element();
219 points[i] = AffineElement(Element::random_element());
220 }
221
222 Element expected;
223 expected.self_set_infinity();
224 for (size_t i = 0; i < num_points; ++i) {
225 Element temp = points[i] * scalars[i];
226 expected += temp;
227 }
228 expected = expected.normalize();
229
230 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ num_points } },
231 { points, /*size*/ num_points * 2 });
232 result = result.normalize();
233
234 aligned_free(scalars);
235 aligned_free(points);
236
237 EXPECT_EQ(result == expected, true);
238}
239
241{
242 using Curve = TypeParam;
243 using Element = typename Curve::Element;
244 using AffineElement = typename Curve::AffineElement;
245 using Fr = typename Curve::ScalarField;
246
247 constexpr size_t num_points = 8192;
248
249 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * num_points);
250
251 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (num_points * 2 + 1));
252
253 for (size_t i = 0; i < num_points; ++i) {
254 scalars[i] = Fr::random_element();
255 points[i] = AffineElement(Element::random_element());
256 }
257
258 Element expected;
259 expected.self_set_infinity();
260 for (size_t i = 0; i < num_points; ++i) {
261 Element temp = points[i] * scalars[i];
262 expected += temp;
263 }
264 expected = expected.normalize();
265
266 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ num_points } },
267 { points, /*size*/ num_points });
268 result = result.normalize();
269
270 aligned_free(scalars);
271 aligned_free(points);
272
273 EXPECT_EQ(result == expected, true);
274}
275
277{
278 using Curve = TypeParam;
279 using Element = typename Curve::Element;
280 using AffineElement = typename Curve::AffineElement;
281 using Fr = typename Curve::ScalarField;
282
283 constexpr size_t num_points = 128;
284
285 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * num_points);
286
287 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (num_points * 2 + 1));
288
289 AffineElement point = AffineElement(Element::random_element());
290 for (size_t i = 0; i < num_points; ++i) {
291 scalars[i] = Fr::random_element();
292 points[i] = point;
293 }
294
295 Element expected;
296 expected.self_set_infinity();
297 for (size_t i = 0; i < num_points; ++i) {
298 Element temp = points[i] * scalars[i];
299 expected += temp;
300 }
301 if (!expected.is_point_at_infinity()) {
302 expected = expected.normalize();
303 }
304 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ num_points } },
305 { points, /*size*/ num_points * 2 });
306 result = result.normalize();
307
308 aligned_free(scalars);
309 aligned_free(points);
310
311 EXPECT_EQ(result == expected, true);
312}
313
315{
316 using Curve = TypeParam;
317 using Element = typename Curve::Element;
318 using AffineElement = typename Curve::AffineElement;
319 using Fr = typename Curve::ScalarField;
320
321 constexpr size_t num_points = 8192;
322
323 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * num_points);
324
325 std::vector<AffineElement> points(num_points);
326
327 for (size_t i = 0; i < num_points; ++i) {
328 points[i] = AffineElement(Element::random_element());
329 }
330 for (size_t i = 0; i < (num_points / 4); ++i) {
331 scalars[i * 4].data[0] = engine.get_random_uint32();
332 scalars[i * 4].data[1] = engine.get_random_uint32();
333 scalars[i * 4].data[2] = engine.get_random_uint32();
334 scalars[i * 4].data[3] = engine.get_random_uint32();
335 scalars[i * 4] = scalars[i * 4].to_montgomery_form();
336 scalars[i * 4 + 1].data[0] = 0;
337 scalars[i * 4 + 1].data[1] = 0;
338 scalars[i * 4 + 1].data[2] = 0;
339 scalars[i * 4 + 1].data[3] = 0;
340 scalars[i * 4 + 1] = scalars[i * 4 + 1].to_montgomery_form();
341 scalars[i * 4 + 2].data[0] = engine.get_random_uint32();
342 scalars[i * 4 + 2].data[1] = engine.get_random_uint32();
343 scalars[i * 4 + 2].data[2] = 0;
344 scalars[i * 4 + 2].data[3] = 0;
345 scalars[i * 4 + 2] = scalars[i * 4 + 2].to_montgomery_form();
346 scalars[i * 4 + 3].data[0] = (engine.get_random_uint32() & 0x07ULL);
347 scalars[i * 4 + 3].data[1] = 0;
348 scalars[i * 4 + 3].data[2] = 0;
349 scalars[i * 4 + 3].data[3] = 0;
350 scalars[i * 4 + 3] = scalars[i * 4 + 3].to_montgomery_form();
351 }
352
353 Element expected;
354 expected.self_set_infinity();
355 for (size_t i = 0; i < num_points; ++i) {
356 Element temp = points[i] * scalars[i];
357 expected += temp;
358 }
359 expected = expected.normalize();
360
361 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ num_points } }, points);
362 result = result.normalize();
363
364 aligned_free(scalars);
365
366 EXPECT_EQ(result == expected, true);
367}
368
370{
371 using Curve = TypeParam;
372 using Element = typename Curve::Element;
373 using AffineElement = typename Curve::AffineElement;
374 using Fr = typename Curve::ScalarField;
375
376 constexpr size_t num_points = 8192;
377
378 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * num_points);
379
380 std::vector<AffineElement> points(num_points);
381
382 for (size_t i = 0; i < num_points; ++i) {
383 scalars[i] = Fr::random_element();
384 points[i] = AffineElement(Element::random_element());
385 }
386
387 Element expected;
388 expected.self_set_infinity();
389 for (size_t i = 0; i < num_points; ++i) {
390 Element temp = points[i] * scalars[i];
391 expected += temp;
392 }
393 expected = expected.normalize();
394 Element result = scalar_multiplication::pippenger_unsafe<Curve>({ 0, { scalars, /*size*/ num_points } }, points);
395 result = result.normalize();
396
397 aligned_free(scalars);
398
399 EXPECT_EQ(result == expected, true);
400}
401
402TYPED_TEST(ScalarMultiplicationTests, PippengerUnsafeShortInputs)
403{
404 using Curve = TypeParam;
405 using Element = typename Curve::Element;
406 using AffineElement = typename Curve::AffineElement;
407 using Fr = typename Curve::ScalarField;
408
409 constexpr size_t num_points = 8192;
410
411 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * num_points);
412
413 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (num_points * 2 + 1));
414
415 for (size_t i = 0; i < num_points; ++i) {
416 points[i] = AffineElement(Element::random_element());
417 }
418 for (size_t i = 0; i < (num_points / 4); ++i) {
419 scalars[i * 4].data[0] = engine.get_random_uint32();
420 scalars[i * 4].data[1] = engine.get_random_uint32();
421 scalars[i * 4].data[2] = engine.get_random_uint32();
422 scalars[i * 4].data[3] = engine.get_random_uint32();
423 scalars[i * 4] = scalars[i * 4].to_montgomery_form();
424 scalars[i * 4 + 1].data[0] = 0;
425 scalars[i * 4 + 1].data[1] = 0;
426 scalars[i * 4 + 1].data[2] = 0;
427 scalars[i * 4 + 1].data[3] = 0;
428 scalars[i * 4 + 1] = scalars[i * 4 + 1].to_montgomery_form();
429 scalars[i * 4 + 2].data[0] = engine.get_random_uint32();
430 scalars[i * 4 + 2].data[1] = engine.get_random_uint32();
431 scalars[i * 4 + 2].data[2] = 0;
432 scalars[i * 4 + 2].data[3] = 0;
433 scalars[i * 4 + 2] = scalars[i * 4 + 2].to_montgomery_form();
434 scalars[i * 4 + 3].data[0] = (engine.get_random_uint32() & 0x07ULL);
435 scalars[i * 4 + 3].data[1] = 0;
436 scalars[i * 4 + 3].data[2] = 0;
437 scalars[i * 4 + 3].data[3] = 0;
438 scalars[i * 4 + 3] = scalars[i * 4 + 3].to_montgomery_form();
439 }
440
441 Element expected;
442 expected.self_set_infinity();
443 for (size_t i = 0; i < num_points; ++i) {
444 Element temp = points[i] * scalars[i];
445 expected += temp;
446 }
447 expected = expected.normalize();
448
449 Element result = scalar_multiplication::pippenger_unsafe<Curve>({ 0, { scalars, /*size*/ num_points } },
450 { points, num_points * 2 + 1 });
451 result = result.normalize();
452
453 aligned_free(scalars);
454 aligned_free(points);
455
456 EXPECT_EQ(result == expected, true);
457}
458
460{
461 using Curve = TypeParam;
462 using Element = typename Curve::Element;
463 using AffineElement = typename Curve::AffineElement;
464 using Fr = typename Curve::ScalarField;
465
466 size_t num_points = 1;
467
468 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * 1);
469
470 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (num_points * 2 + 1));
471
472 for (size_t i = 0; i < num_points; ++i) {
473 scalars[i] = Fr::random_element();
474 points[i] = AffineElement(Element::random_element());
475 }
476
477 Element expected;
478 expected.self_set_infinity();
479 for (size_t i = 0; i < num_points; ++i) {
480 Element temp = points[i] * scalars[i];
481 expected += temp;
482 }
483 expected = expected.normalize();
484
485 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ num_points } },
486 { points, /*size*/ num_points * 2 });
487 result = result.normalize();
488
489 aligned_free(scalars);
490 aligned_free(points);
491
492 EXPECT_EQ(result == expected, true);
493}
494
496{
497 using Curve = TypeParam;
498 using Element = typename Curve::Element;
499 using AffineElement = typename Curve::AffineElement;
500 using Fr = typename Curve::ScalarField;
501
502 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr));
503
504 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (2 + 1));
505
506 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ 0 } }, { points, /*size*/ 0 });
507
508 aligned_free(scalars);
509 aligned_free(points);
510
511 EXPECT_EQ(result.is_point_at_infinity(), true);
512}
513
515{
516 using Curve = TypeParam;
517 using Group = typename Curve::Group;
518 using Element = typename Curve::Element;
519 using AffineElement = typename Curve::AffineElement;
520 using Fr = typename Curve::ScalarField;
521
522 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr));
523
524 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (2 + 1));
525
526 scalars[0] = Fr::zero();
527 points[0] = Group::affine_one;
528 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ 1 } }, { points, /*size*/ 2 });
529
530 aligned_free(scalars);
531 aligned_free(points);
532
533 EXPECT_EQ(result.is_point_at_infinity(), true);
534}
typename Group::element Element
Definition grumpkin.hpp:55
typename grumpkin::g1 Group
Definition grumpkin.hpp:54
typename Group::affine_element AffineElement
Definition grumpkin.hpp:56
virtual uint32_t get_random_uint32()=0
static size_t get_num_rounds(size_t num_points) noexcept
static uint32_t get_scalar_slice(const ScalarField &scalar, size_t round, size_t normal_slice_size) noexcept
Given a scalar that is NOT in Montgomery form, extract a slice_size-bit chunk.
static void add_affine_points(AffineElement *points, const size_t num_points, typename Curve::BaseField *scratch_space) noexcept
static size_t get_optimal_log_num_buckets(const size_t num_points) noexcept
For a given number of points, compute the optimal Pippenger bucket size.
numeric::RNG & engine
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:190
size_t process_buckets_count_zero_entries(uint64_t *wnaf_entries, const size_t num_entries, const uint32_t num_bits) noexcept
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
::testing::Types< curve::BN254, curve::Grumpkin > Curves
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Curve::Element Element
static constexpr field cube_root_of_unity()
static constexpr field one()
BB_INLINE constexpr field to_montgomery_form() const noexcept
static void split_into_endomorphism_scalars(const field &k, field &k1, field &k2)
BB_INLINE constexpr void self_sqr() &noexcept
BB_INLINE constexpr void self_neg() &noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
static BB_INLINE void __copy(const field &a, field &r) noexcept
static constexpr field zero()