11#include <gtest/gtest.h>
28 static inline std::vector<ScalarField>
scalars{};
32 size_t total_points = input_scalars.size();
34 std::vector<Element> expected_accs(num_threads);
35 size_t range_per_thread = (total_points + num_threads - 1) / num_threads;
38 expected_thread_acc.self_set_infinity();
39 size_t start = thread_idx * range_per_thread;
40 size_t end = ((thread_idx + 1) * range_per_thread > total_points) ? total_points
41 : (thread_idx + 1) * range_per_thread;
42 bool skip = start >= total_points;
44 for (
size_t i = start; i < end; ++i) {
45 expected_thread_acc += input_points[i] * input_scalars[i];
48 expected_accs[thread_idx] = expected_thread_acc;
52 expected_acc.self_set_infinity();
53 for (
auto& acc : expected_accs) {
63 for (
size_t i = start; i < end; ++i) {
74using CurveTypes = ::testing::Types<bb::curve::BN254, bb::curve::Grumpkin>;
77#define SCALAR_MULTIPLICATION_TYPE_ALIASES \
78 using Curve = TypeParam; \
79 using ScalarField = typename Curve::ScalarField; \
85 const size_t fr_size = 254;
86 const size_t slice_bits = 7;
87 size_t num_slices = (fr_size + 6) / 7;
88 size_t last_slice_bits = fr_size - ((num_slices - 1) * slice_bits);
90 for (
size_t x = 0; x < 100; ++x) {
93 input_u256.
data[3] = input_u256.
data[3] & 0x3FFFFFFFFFFFFFFF;
94 while (input_u256 > ScalarField::modulus) {
95 input_u256 -= ScalarField::modulus;
97 std::vector<uint32_t> slices(num_slices);
100 for (
size_t i = 0; i < num_slices; ++i) {
101 size_t mask = ((1 << slice_bits) - 1UL);
102 size_t shift = slice_bits;
104 mask = ((1UL << last_slice_bits) - 1UL);
105 shift = last_slice_bits;
107 slices[num_slices - 1 - i] =
static_cast<uint32_t
>((acc & mask).
data[0]);
140 ScalarField input(input_u256);
141 input.self_from_montgomery_form();
143 ASSERT_EQ(input.data[0], input_u256.
data[0]);
144 ASSERT_EQ(input.data[1], input_u256.
data[1]);
145 ASSERT_EQ(input.data[2], input_u256.
data[2]);
146 ASSERT_EQ(input.data[3], input_u256.
data[3]);
148 for (
size_t i = 0; i < num_slices; ++i) {
151 EXPECT_EQ(result, slices[i]);
161 using Curve = TypeParam;
164 const size_t total_points = 30071;
165 const size_t num_buckets = 128;
167 std::vector<uint64_t> input_point_schedule;
168 for (
size_t i = 0; i < total_points; ++i) {
170 uint64_t bucket =
static_cast<uint64_t
>(
engine.get_random_uint8()) & 0x7f;
172 uint64_t schedule =
static_cast<uint64_t
>(bucket) + (
static_cast<uint64_t
>(i) << 32);
173 input_point_schedule.push_back(schedule);
179 input_point_schedule, TestFixture::generators, affine_data, bucket_data, 0, 0);
182 for (
auto& e : expected_buckets) {
183 e.self_set_infinity();
186 for (
size_t i = 0; i < total_points; ++i) {
187 uint64_t bucket = input_point_schedule[i] & 0xFFFFFFFF;
188 EXPECT_LT(
static_cast<size_t>(bucket), num_buckets);
189 expected_buckets[
static_cast<size_t>(bucket)] += TestFixture::generators[i];
191 for (
size_t i = 0; i < num_buckets; ++i) {
192 if (!expected_buckets[i].is_point_at_infinity()) {
193 AffineElement expected(expected_buckets[i]);
194 EXPECT_EQ(expected, bucket_data.
buckets[i]);
208 const size_t total_points = 30071;
209 const size_t num_buckets = 128;
211 std::vector<uint64_t> input_point_schedule;
212 for (
size_t i = 0; i < total_points; ++i) {
214 uint64_t bucket =
static_cast<uint64_t
>(
engine.get_random_uint8()) & 0x7f;
216 uint64_t schedule =
static_cast<uint64_t
>(bucket) + (
static_cast<uint64_t
>(i) << 32);
217 input_point_schedule.push_back(schedule);
223 input_point_schedule, TestFixture::generators, affine_data, bucket_data, 0, 0);
228 expected_acc.self_set_infinity();
230 std::vector<Element> expected_accs(num_threads);
231 size_t range_per_thread = (total_points + num_threads - 1) / num_threads;
234 expected_thread_acc.self_set_infinity();
235 size_t start = thread_idx * range_per_thread;
236 size_t end = (thread_idx == num_threads - 1) ? total_points : (thread_idx + 1) * range_per_thread;
237 bool skip = start >= total_points;
239 for (
size_t i = start; i < end; ++i) {
240 ScalarField scalar = input_point_schedule[i] & 0xFFFFFFFF;
241 expected_thread_acc += TestFixture::generators[i] * scalar;
244 expected_accs[thread_idx] = expected_thread_acc;
247 for (
size_t i = 0; i < num_threads; ++i) {
248 expected_acc += expected_accs[i];
250 AffineElement expected(expected_acc);
251 EXPECT_EQ(AffineElement(result), expected);
256 const size_t total_points = 30071;
258 std::vector<uint64_t> input_point_schedule;
259 for (
size_t i = 0; i < total_points; ++i) {
261 uint64_t bucket =
static_cast<uint64_t
>(
engine.get_random_uint8()) & 0x7f;
263 uint64_t schedule =
static_cast<uint64_t
>(bucket) + (
static_cast<uint64_t
>(i) << 32);
264 input_point_schedule.push_back(schedule);
268 &input_point_schedule[0], input_point_schedule.size(), 7);
270 for (
size_t i = 0; i < total_points; ++i) {
271 expected +=
static_cast<size_t>((input_point_schedule[i] & 0xFFFFFFFF) == 0);
273 EXPECT_EQ(result, expected);
282 const size_t num_points = 2;
283 std::vector<ScalarField> scalars(num_points);
285 const size_t normal_slice_size = 7;
286 const size_t num_buckets = 1 << normal_slice_size;
288 const size_t num_rounds = (NUM_BITS_IN_FIELD + normal_slice_size - 1) / normal_slice_size;
293 for (
size_t round_index = num_rounds - 1; round_index < num_rounds; round_index++) {
294 const size_t num_bits_in_slice =
295 (round_index == (num_rounds - 1)) ? (NUM_BITS_IN_FIELD % normal_slice_size) : normal_slice_size;
296 for (
size_t i = 0; i < num_points; ++i) {
298 size_t hi_bit = NUM_BITS_IN_FIELD - (round_index * normal_slice_size);
299 size_t lo_bit = hi_bit - normal_slice_size;
300 if (hi_bit < normal_slice_size) {
303 uint64_t
slice =
engine.get_random_uint64() & ((1 << num_bits_in_slice) - 1);
306 scalars[i].
data[0] = scalar.
data[0];
307 scalars[i].data[1] = scalar.
data[1];
308 scalars[i].data[2] = scalar.
data[2];
309 scalars[i].data[3] = scalar.
data[3];
310 scalars[i].self_to_montgomery_form();
313 std::vector<uint32_t> indices;
317 previous_round_output.self_set_infinity();
318 for (
auto x : indices) {
319 ASSERT_LT(x, num_points);
321 std::vector<uint64_t> point_schedule(scalars.size());
323 scalars, TestFixture::generators, indices, point_schedule);
325 msm_data, round_index, affine_data, bucket_data, previous_round_output, 7);
327 expected.self_set_infinity();
328 for (
size_t i = 0; i < num_points; ++i) {
329 ScalarField baz = scalars[i].to_montgomery_form();
330 expected += (TestFixture::generators[i] * baz);
332 size_t num_doublings = NUM_BITS_IN_FIELD - (normal_slice_size * (round_index + 1));
333 if (round_index == num_rounds - 1) {
336 for (
size_t i = 0; i < num_doublings; ++i) {
339 EXPECT_EQ(AffineElement(result), AffineElement(expected));
348 const size_t num_points = TestFixture::num_points;
351 AffineElement result =
354 AffineElement expected = TestFixture::naive_msm(scalars, TestFixture::generators);
355 EXPECT_EQ(result, expected);
364 const size_t num_msms =
static_cast<size_t>(
engine.get_random_uint8());
365 std::vector<AffineElement> expected(num_msms);
370 size_t vector_offset = 0;
371 for (
size_t k = 0; k < num_msms; ++k) {
372 const size_t num_points =
static_cast<size_t>(
engine.get_random_uint16()) % 400;
374 ASSERT_LT(vector_offset + num_points, TestFixture::num_points);
376 std::span<const AffineElement> batch_points(&TestFixture::generators[vector_offset], num_points);
378 vector_offset += num_points;
379 batch_points_span.push_back(batch_points);
380 batch_scalars_spans.push_back(batch_scalars);
382 expected[k] = TestFixture::naive_msm(batch_scalars_spans[k], batch_points_span[k]);
385 std::vector<AffineElement> result =
388 EXPECT_EQ(result, expected);
396 const size_t num_msms = 10;
397 std::vector<AffineElement> expected(num_msms);
404 for (
size_t k = 0; k < num_msms; ++k) {
405 const size_t num_points = 33;
406 auto& scalars = batch_scalars[k];
408 scalars.resize(num_points);
410 size_t fixture_offset = k * num_points;
413 for (
size_t i = 0; i < 13; ++i) {
416 for (
size_t i = 13; i < 23; ++i) {
417 scalars[i] = TestFixture::scalars[fixture_offset + i + 13];
419 for (
size_t i = 23; i < num_points; ++i) {
422 batch_points_span.push_back(batch_points);
423 batch_scalars_spans.push_back(batch_scalars[k]);
425 expected[k] = TestFixture::naive_msm(batch_scalars[k], batch_points);
428 std::vector<AffineElement> result =
431 EXPECT_EQ(result, expected);
439 const size_t start_index = 1234;
440 const size_t num_points = TestFixture::num_points - start_index;
447 AffineElement expected = TestFixture::naive_msm(scalar_span.
span, points);
448 EXPECT_EQ(result, expected);
456 const size_t start_index = 1234;
457 const size_t num_points = TestFixture::num_points - start_index;
458 std::vector<ScalarField> scalars(num_points);
460 for (
size_t i = 0; i < num_points; ++i) {
467 EXPECT_EQ(result, Curve::Group::affine_point_at_infinity);
475 const size_t num_points = 0;
476 std::vector<ScalarField> scalars(num_points);
477 std::vector<AffineElement> input_points(num_points);
481 EXPECT_EQ(result, Curve::Group::affine_point_at_infinity);
485template <
typename ScalarField>
488 std::vector<ScalarField> scalars(num_scalars);
489 for (
size_t i = 0; i < num_scalars; ++i) {
491 double rand_val =
static_cast<double>(rng.get_random_uint32()) /
static_cast<double>(UINT32_MAX);
492 if (rand_val < sparsity_rate) {
495 scalars[i] = ScalarField::random_element(&rng);
511 <<
"Skipping BatchMultiScalarMulStrategyComparison as BB_BENCH=1 is not passed (OR we are in wasm).\n";
518 const size_t num_msms = 3;
519 const size_t msm_max_size = 1 << 17;
520 const double max_sparsity = 0.1;
525 std::vector<AffineElement> all_commitments;
528 for (
size_t i = 0; i < num_msms; ++i) {
530 const size_t size =
engine.get_random_uint64() % msm_max_size;
531 const double sparsity =
engine.get_random_uint8() / 255.0 * max_sparsity;
532 auto scalars = generate_sparse_scalars<ScalarField>(size, sparsity,
engine);
533 scalar_storage.push_back(
std::move(scalars));
535 std::span<const AffineElement> points(&TestFixture::generators[i], size);
536 all_points.push_back(points);
537 all_scalars.push_back(scalar_storage.back());
538 all_commitments.push_back(TestFixture::naive_msm(all_scalars.back(), all_points.back()));
544 BB_BENCH_NAME((bb::detail::concat<thread_prefix, "IndividualMSMs">()));
545 for (
size_t i = 0; i < num_msms; ++i) {
549 EXPECT_EQ(result[0], all_commitments[i]);
554 BB_BENCH_NAME((bb::detail::concat<thread_prefix, "BatchMSMs">()));
556 EXPECT_EQ(result, all_commitments);
560 func.template operator()<
"1 thread ">(1);
561 func.template operator()<
"2 threads ">(2);
562 func.template operator()<
"4 threads ">(4);
563 func.template operator()<
"8 threads ">(8);
564 func.template operator()<
"16 threads ">(16);
565 func.template operator()<
"32 threads ">(32);
568TEST(ScalarMultiplication, SmallInputsExplicit)
570 uint256_t x0(0x68df84429941826a, 0xeb08934ed806781c, 0xc14b6a2e4f796a73, 0x08dc1a9a11a3c8db);
571 uint256_t y0(0x8ae5c31aa997f141, 0xe85f20c504f2c11b, 0x81a94193f3b1ce2b, 0x26f2c37372adb5b7);
572 uint256_t x1(0x80f5a592d919d32f, 0x1362652b984e51ca, 0xa0b26666f770c2a1, 0x142c6e1964e5c3c5);
573 uint256_t y1(0xb6c322ebb5ae4bc5, 0xf9fef6c7909c00f8, 0xb37ca1cc9af3b421, 0x1e331c7fa73d6a59);
574 uint256_t s0(0xe48bf12a24272e08, 0xf8dd0182577f3567, 0xec8fd222b8a6becb, 0x102d76b945612c9b);
575 uint256_t s1(0x098ae8d69f1e4e9e, 0xb5c8313c0f6040ed, 0xf78041e30cc46c44, 0x1d1e6e0c21892e13);
#define BB_BENCH_NAME(name)
BB_INLINE bool get(size_t index) const noexcept
typename Curve::ScalarField ScalarField
static std::vector< AffineElement > generators
static constexpr size_t num_points
static void SetUpTestSuite()
typename Curve::Element Element
typename Curve::Group Group
static std::vector< ScalarField > scalars
typename Curve::AffineElement AffineElement
static AffineElement naive_msm(std::span< ScalarField > input_scalars, std::span< const AffineElement > input_points)
typename Group::element Element
typename grumpkin::g1 Group
typename Group::affine_element AffineElement
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
group_elements::affine_element< Fq, Fr, Params > affine_element
constexpr uint64_t get_msb() const
static Element accumulate_buckets(BucketType &bucket_accumulators) 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 consume_point_schedule(std::span< const uint64_t > point_schedule, std::span< const AffineElement > points, AffineAdditionData &affine_data, BucketAccumulators &bucket_data, size_t num_input_points_processed, size_t num_queued_affine_points) noexcept
Given a list of points and target buckets to add into, perform required group operations.
static std::vector< AffineElement > batch_multi_scalar_mul(std::span< std::span< const AffineElement > > points, std::span< std::span< ScalarField > > scalars, bool handle_edge_cases=true) noexcept
Compute multiple multi-scalar multiplications.
static Element evaluate_pippenger_round(MSMData &msm_data, const size_t round_index, AffineAdditionData &affine_data, BucketAccumulators &bucket_data, Element previous_round_output, const size_t bits_per_slice) noexcept
Evaluate a single Pippenger round where we use the affine trick.
static void transform_scalar_and_get_nonzero_scalar_indices(std::span< typename Curve::ScalarField > scalars, std::vector< uint32_t > &consolidated_indices) noexcept
Convert scalar out of Montgomery form. Populate consolidated_indices with nonzero scalar indices.
static AffineElement msm(std::span< const AffineElement > points, PolynomialSpan< const ScalarField > _scalars, bool handle_edge_cases=false) noexcept
Helper method to evaluate a single MSM. Internally calls batch_multi_scalar_mul
const std::vector< FF > data
#define SCALAR_MULTIPLICATION_TYPE_ALIASES
std::vector< ScalarField > generate_sparse_scalars(size_t num_scalars, double sparsity_rate, auto &rng)
size_t process_buckets_count_zero_entries(uint64_t *wnaf_entries, const size_t num_entries, const uint32_t num_bits) noexcept
Entry point for Barretenberg command-line interface.
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
C slice(C const &container, size_t start)
::testing::Types< curve::BN254, curve::Grumpkin > CurveTypes
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
void set_parallel_for_concurrency(size_t num_cores)
void parallel_for(size_t num_iterations, const std::function< void(size_t)> &func)
void parallel_for_range(size_t num_points, const std::function< void(size_t, size_t)> &func, size_t no_multhreading_if_less_or_equal)
Split a loop into several loops running in parallel.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
static constexpr uint256_t modulus
Temp data structure, one created per thread!
Temp data structure, one created per thread!
std::vector< AffineElement > buckets