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 Fr k1{ 0, 0, 0, 0 };
87 Fr k2{ 0, 0, 0, 0 };
89
90 Element result;
91 Element t1 = Group::affine_one * k1;
92 AffineElement generator = Group::affine_one;
94 generator.x = generator.x * beta;
95 generator.y = -generator.y;
96 Element t2 = generator * k2;
97 result = t1 + t2;
98
99 EXPECT_EQ(result == expected, true);
100}
101
103{
104 using Curve = TypeParam;
105 using Fr = typename Curve::ScalarField;
106
107 // check that our radix sort correctly sorts!
108 constexpr size_t target_degree = 1 << 8;
109 const size_t num_rounds = scalar_multiplication::MSM<Curve>::get_num_rounds(target_degree);
110 Fr* scalars = (Fr*)(aligned_alloc(64, sizeof(Fr) * target_degree));
111
112 Fr source_scalar = Fr::random_element();
113 for (size_t i = 0; i < target_degree; ++i) {
114 source_scalar.self_sqr();
115 Fr::__copy(source_scalar, scalars[i]);
116 }
117
118 uint32_t bits_per_slice = scalar_multiplication::MSM<Curve>::get_optimal_log_num_buckets(target_degree);
119
120 for (uint32_t i = 0; i < num_rounds; ++i) {
121
122 std::vector<uint64_t> scalar_slices(target_degree);
123 std::vector<uint64_t> sorted_scalar_slices(target_degree);
124
125 for (size_t j = 0; j < target_degree; ++j) {
126 scalar_slices[j] = scalar_multiplication::MSM<Curve>::get_scalar_slice(scalars[j], i, bits_per_slice);
127 sorted_scalar_slices[j] = scalar_slices[j];
128 }
130 &sorted_scalar_slices[0], target_degree, static_cast<uint32_t>(bits_per_slice));
131
132 const auto find_entry = [scalar_slices, num_entries = target_degree](auto x) {
133 for (size_t k = 0; k < num_entries; ++k) {
134 if (scalar_slices[k] == x) {
135 return true;
136 }
137 }
138 return false;
139 };
140 for (size_t j = 0; j < target_degree; ++j) {
141 EXPECT_EQ(find_entry(sorted_scalar_slices[j]), true);
142 if (j > 0) {
143 EXPECT_EQ((sorted_scalar_slices[j] & 0x7fffffffU) >= (sorted_scalar_slices[j - 1] & 0x7fffffffU), true);
144 }
145 }
146 }
147
148 free(scalars);
149}
150
152{
153 using Curve = TypeParam;
155 // Grumpkin has at most 1 << 18 points; the test is not valid for it.
156 GTEST_SKIP() << "Skipping test for grumpkin";
157 }
158 using Element = typename Curve::Element;
159 using AffineElement = typename Curve::AffineElement;
160 using Fr = typename Curve::ScalarField;
161 using Fq = typename Curve::BaseField;
162
163 // for point ranges with more than 1 << 20 points, we split into chunks of smaller multi-exps.
164 // Check that this is done correctly
165 size_t target_degree = 1200000;
166 std::span<AffineElement> monomials = srs::get_crs_factory<Curve>()->get_crs(target_degree)->get_monomial_points();
167
168 Fr* scalars = (Fr*)(aligned_alloc(64, sizeof(Fr) * target_degree));
169
170 Fr source_scalar = Fr::random_element();
171 Fr accumulator = source_scalar;
172 for (size_t i = 0; i < target_degree; ++i) {
173 accumulator *= source_scalar;
174 Fr::__copy(accumulator, scalars[i]);
175 }
176
177 Element first = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ target_degree } }, monomials);
178 first = first.normalize();
179
180 for (size_t i = 0; i < target_degree; ++i) {
181 scalars[i].self_neg();
182 }
183
184 Element second = scalar_multiplication::pippenger<Curve>(
185 PolynomialSpan<const typename Curve::ScalarField>{ 0, { scalars, /*size*/ target_degree } }, monomials);
186 second = second.normalize();
187
188 EXPECT_EQ((first.z == second.z), true);
189 EXPECT_EQ((first.z == Fq::one()), true);
190 EXPECT_EQ((first.x == second.x), true);
191 EXPECT_EQ((first.y == -second.y), true);
192
193 aligned_free(scalars);
194}
195
197{
198 using Curve = TypeParam;
199 using Element = typename Curve::Element;
200 using AffineElement = typename Curve::AffineElement;
201 using Fr = typename Curve::ScalarField;
202
203 // we fall back to traditional scalar multiplication algorithm for small input sizes.
204 // Check this is done correctly
205 size_t num_points = 17;
206
207 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * num_points);
208
209 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (num_points * 2 + 1));
210
211 for (size_t i = 0; i < num_points; ++i) {
212 scalars[i] = Fr::random_element();
213 points[i] = AffineElement(Element::random_element());
214 }
215
216 Element expected;
217 expected.self_set_infinity();
218 for (size_t i = 0; i < num_points; ++i) {
219 Element temp = points[i] * scalars[i];
220 expected += temp;
221 }
222 expected = expected.normalize();
223
224 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ num_points } },
225 { points, /*size*/ num_points * 2 });
226 result = result.normalize();
227
228 aligned_free(scalars);
229 aligned_free(points);
230
231 EXPECT_EQ(result == expected, true);
232}
233
235{
236 using Curve = TypeParam;
237 using Element = typename Curve::Element;
238 using AffineElement = typename Curve::AffineElement;
239 using Fr = typename Curve::ScalarField;
240
241 constexpr size_t num_points = 8192;
242
243 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * num_points);
244
245 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (num_points * 2 + 1));
246
247 for (size_t i = 0; i < num_points; ++i) {
248 scalars[i] = Fr::random_element();
249 points[i] = AffineElement(Element::random_element());
250 }
251
252 Element expected;
253 expected.self_set_infinity();
254 for (size_t i = 0; i < num_points; ++i) {
255 Element temp = points[i] * scalars[i];
256 expected += temp;
257 }
258 expected = expected.normalize();
259
260 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ num_points } },
261 { points, /*size*/ num_points });
262 result = result.normalize();
263
264 aligned_free(scalars);
265 aligned_free(points);
266
267 EXPECT_EQ(result == expected, true);
268}
269
271{
272 using Curve = TypeParam;
273 using Element = typename Curve::Element;
274 using AffineElement = typename Curve::AffineElement;
275 using Fr = typename Curve::ScalarField;
276
277 constexpr size_t num_points = 128;
278
279 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * num_points);
280
281 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (num_points * 2 + 1));
282
283 AffineElement point = AffineElement(Element::random_element());
284 for (size_t i = 0; i < num_points; ++i) {
285 scalars[i] = Fr::random_element();
286 points[i] = point;
287 }
288
289 Element expected;
290 expected.self_set_infinity();
291 for (size_t i = 0; i < num_points; ++i) {
292 Element temp = points[i] * scalars[i];
293 expected += temp;
294 }
295 if (!expected.is_point_at_infinity()) {
296 expected = expected.normalize();
297 }
298 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ num_points } },
299 { points, /*size*/ num_points * 2 });
300 result = result.normalize();
301
302 aligned_free(scalars);
303 aligned_free(points);
304
305 EXPECT_EQ(result == expected, true);
306}
307
309{
310 using Curve = TypeParam;
311 using Element = typename Curve::Element;
312 using AffineElement = typename Curve::AffineElement;
313 using Fr = typename Curve::ScalarField;
314
315 constexpr size_t num_points = 8192;
316
317 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * num_points);
318
319 std::vector<AffineElement> points(num_points);
320
321 for (size_t i = 0; i < num_points; ++i) {
322 points[i] = AffineElement(Element::random_element());
323 }
324 for (size_t i = 0; i < (num_points / 4); ++i) {
325 scalars[i * 4].data[0] = engine.get_random_uint32();
326 scalars[i * 4].data[1] = engine.get_random_uint32();
327 scalars[i * 4].data[2] = engine.get_random_uint32();
328 scalars[i * 4].data[3] = engine.get_random_uint32();
329 scalars[i * 4] = scalars[i * 4].to_montgomery_form();
330 scalars[i * 4 + 1].data[0] = 0;
331 scalars[i * 4 + 1].data[1] = 0;
332 scalars[i * 4 + 1].data[2] = 0;
333 scalars[i * 4 + 1].data[3] = 0;
334 scalars[i * 4 + 1] = scalars[i * 4 + 1].to_montgomery_form();
335 scalars[i * 4 + 2].data[0] = engine.get_random_uint32();
336 scalars[i * 4 + 2].data[1] = engine.get_random_uint32();
337 scalars[i * 4 + 2].data[2] = 0;
338 scalars[i * 4 + 2].data[3] = 0;
339 scalars[i * 4 + 2] = scalars[i * 4 + 2].to_montgomery_form();
340 scalars[i * 4 + 3].data[0] = (engine.get_random_uint32() & 0x07ULL);
341 scalars[i * 4 + 3].data[1] = 0;
342 scalars[i * 4 + 3].data[2] = 0;
343 scalars[i * 4 + 3].data[3] = 0;
344 scalars[i * 4 + 3] = scalars[i * 4 + 3].to_montgomery_form();
345 }
346
347 Element expected;
348 expected.self_set_infinity();
349 for (size_t i = 0; i < num_points; ++i) {
350 Element temp = points[i] * scalars[i];
351 expected += temp;
352 }
353 expected = expected.normalize();
354
355 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ num_points } }, points);
356 result = result.normalize();
357
358 aligned_free(scalars);
359
360 EXPECT_EQ(result == expected, true);
361}
362
364{
365 using Curve = TypeParam;
366 using Element = typename Curve::Element;
367 using AffineElement = typename Curve::AffineElement;
368 using Fr = typename Curve::ScalarField;
369
370 constexpr size_t num_points = 8192;
371
372 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * num_points);
373
374 std::vector<AffineElement> points(num_points);
375
376 for (size_t i = 0; i < num_points; ++i) {
377 scalars[i] = Fr::random_element();
378 points[i] = AffineElement(Element::random_element());
379 }
380
381 Element expected;
382 expected.self_set_infinity();
383 for (size_t i = 0; i < num_points; ++i) {
384 Element temp = points[i] * scalars[i];
385 expected += temp;
386 }
387 expected = expected.normalize();
388 Element result = scalar_multiplication::pippenger_unsafe<Curve>({ 0, { scalars, /*size*/ num_points } }, points);
389 result = result.normalize();
390
391 aligned_free(scalars);
392
393 EXPECT_EQ(result == expected, true);
394}
395
396TYPED_TEST(ScalarMultiplicationTests, PippengerUnsafeShortInputs)
397{
398 using Curve = TypeParam;
399 using Element = typename Curve::Element;
400 using AffineElement = typename Curve::AffineElement;
401 using Fr = typename Curve::ScalarField;
402
403 constexpr size_t num_points = 8192;
404
405 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * num_points);
406
407 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (num_points * 2 + 1));
408
409 for (size_t i = 0; i < num_points; ++i) {
410 points[i] = AffineElement(Element::random_element());
411 }
412 for (size_t i = 0; i < (num_points / 4); ++i) {
413 scalars[i * 4].data[0] = engine.get_random_uint32();
414 scalars[i * 4].data[1] = engine.get_random_uint32();
415 scalars[i * 4].data[2] = engine.get_random_uint32();
416 scalars[i * 4].data[3] = engine.get_random_uint32();
417 scalars[i * 4] = scalars[i * 4].to_montgomery_form();
418 scalars[i * 4 + 1].data[0] = 0;
419 scalars[i * 4 + 1].data[1] = 0;
420 scalars[i * 4 + 1].data[2] = 0;
421 scalars[i * 4 + 1].data[3] = 0;
422 scalars[i * 4 + 1] = scalars[i * 4 + 1].to_montgomery_form();
423 scalars[i * 4 + 2].data[0] = engine.get_random_uint32();
424 scalars[i * 4 + 2].data[1] = engine.get_random_uint32();
425 scalars[i * 4 + 2].data[2] = 0;
426 scalars[i * 4 + 2].data[3] = 0;
427 scalars[i * 4 + 2] = scalars[i * 4 + 2].to_montgomery_form();
428 scalars[i * 4 + 3].data[0] = (engine.get_random_uint32() & 0x07ULL);
429 scalars[i * 4 + 3].data[1] = 0;
430 scalars[i * 4 + 3].data[2] = 0;
431 scalars[i * 4 + 3].data[3] = 0;
432 scalars[i * 4 + 3] = scalars[i * 4 + 3].to_montgomery_form();
433 }
434
435 Element expected;
436 expected.self_set_infinity();
437 for (size_t i = 0; i < num_points; ++i) {
438 Element temp = points[i] * scalars[i];
439 expected += temp;
440 }
441 expected = expected.normalize();
442
443 Element result = scalar_multiplication::pippenger_unsafe<Curve>({ 0, { scalars, /*size*/ num_points } },
444 { points, num_points * 2 + 1 });
445 result = result.normalize();
446
447 aligned_free(scalars);
448 aligned_free(points);
449
450 EXPECT_EQ(result == expected, true);
451}
452
454{
455 using Curve = TypeParam;
456 using Element = typename Curve::Element;
457 using AffineElement = typename Curve::AffineElement;
458 using Fr = typename Curve::ScalarField;
459
460 size_t num_points = 1;
461
462 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr) * 1);
463
464 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (num_points * 2 + 1));
465
466 for (size_t i = 0; i < num_points; ++i) {
467 scalars[i] = Fr::random_element();
468 points[i] = AffineElement(Element::random_element());
469 }
470
471 Element expected;
472 expected.self_set_infinity();
473 for (size_t i = 0; i < num_points; ++i) {
474 Element temp = points[i] * scalars[i];
475 expected += temp;
476 }
477 expected = expected.normalize();
478
479 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ num_points } },
480 { points, /*size*/ num_points * 2 });
481 result = result.normalize();
482
483 aligned_free(scalars);
484 aligned_free(points);
485
486 EXPECT_EQ(result == expected, true);
487}
488
490{
491 using Curve = TypeParam;
492 using Element = typename Curve::Element;
493 using AffineElement = typename Curve::AffineElement;
494 using Fr = typename Curve::ScalarField;
495
496 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr));
497
498 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (2 + 1));
499
500 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ 0 } }, { points, /*size*/ 0 });
501
502 aligned_free(scalars);
503 aligned_free(points);
504
505 EXPECT_EQ(result.is_point_at_infinity(), true);
506}
507
509{
510 using Curve = TypeParam;
511 using Group = typename Curve::Group;
512 using Element = typename Curve::Element;
513 using AffineElement = typename Curve::AffineElement;
514 using Fr = typename Curve::ScalarField;
515
516 Fr* scalars = (Fr*)aligned_alloc(32, sizeof(Fr));
517
518 AffineElement* points = (AffineElement*)aligned_alloc(32, sizeof(AffineElement) * (2 + 1));
519
520 scalars[0] = Fr::zero();
521 points[0] = Group::affine_one;
522 Element result = scalar_multiplication::pippenger<Curve>({ 0, { scalars, /*size*/ 1 } }, { points, /*size*/ 2 });
523
524 aligned_free(scalars);
525 aligned_free(points);
526
527 EXPECT_EQ(result.is_point_at_infinity(), true);
528}
typename Group::element Element
Definition grumpkin.hpp:63
typename grumpkin::g1 Group
Definition grumpkin.hpp:62
typename Group::affine_element AffineElement
Definition grumpkin.hpp:64
virtual uint32_t get_random_uint32()=0
static uint32_t get_scalar_slice(const ScalarField &scalar, size_t round, size_t slice_size) noexcept
Extract c-bit slice from scalar for bucket index computation.
static void add_affine_points(AffineElement *points, const size_t num_points, typename Curve::BaseField *scratch_space) noexcept
Batch add n/2 independent point pairs using Montgomery's trick.
static uint32_t get_num_rounds(size_t num_points) noexcept
static uint32_t get_optimal_log_num_buckets(size_t num_points) noexcept
Compute optimal bits per slice by minimizing cost over c in [1, MAX_SLICE_BITS)
bb::curve::BN254::Element Element
numeric::RNG & engine
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:245
size_t sort_point_schedule_and_count_zero_buckets(uint64_t *point_schedule, const size_t num_entries, const uint32_t bucket_index_bits) noexcept
Sort point schedule by bucket index and count zero-bucket entries.
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
::testing::Types< curve::BN254, curve::Grumpkin > Curves
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 to_montgomery_form() const noexcept
static void split_into_endomorphism_scalars(const field &k, field &k1, field &k2)
Full-width endomorphism decomposition: k ≡ k1 - k2·λ (mod r). Modifies the field elements k1 and 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()