10#include <gtest/gtest.h>
27 static inline std::vector<ScalarField>
scalars{};
31 size_t total_points = input_scalars.size();
33 std::vector<Element> expected_accs(num_threads);
34 size_t range_per_thread = (total_points + num_threads - 1) / num_threads;
37 expected_thread_acc.self_set_infinity();
38 size_t start = thread_idx * range_per_thread;
39 size_t end = ((thread_idx + 1) * range_per_thread > total_points) ? total_points
40 : (thread_idx + 1) * range_per_thread;
41 bool skip = start >= total_points;
43 for (
size_t i = start; i < end; ++i) {
44 expected_thread_acc += input_points[i] * input_scalars[i];
47 expected_accs[thread_idx] = expected_thread_acc;
51 expected_acc.self_set_infinity();
52 for (
auto& acc : expected_accs) {
62 for (
size_t i = start; i < end; ++i) {
73using CurveTypes = ::testing::Types<bb::curve::BN254, bb::curve::Grumpkin>;
76#define SCALAR_MULTIPLICATION_TYPE_ALIASES \
77 using Curve = TypeParam; \
78 using ScalarField = typename Curve::ScalarField; \
84 const size_t fr_size = 254;
85 const size_t slice_bits = 7;
86 size_t num_slices = (fr_size + 6) / 7;
87 size_t last_slice_bits = fr_size - ((num_slices - 1) * slice_bits);
89 for (
size_t x = 0; x < 100; ++x) {
92 input_u256.
data[3] = input_u256.
data[3] & 0x3FFFFFFFFFFFFFFF;
93 while (input_u256 > ScalarField::modulus) {
94 input_u256 -= ScalarField::modulus;
96 std::vector<uint32_t> slices(num_slices);
99 for (
size_t i = 0; i < num_slices; ++i) {
100 size_t mask = ((1 << slice_bits) - 1UL);
101 size_t shift = slice_bits;
103 mask = ((1UL << last_slice_bits) - 1UL);
104 shift = last_slice_bits;
106 slices[num_slices - 1 - i] =
static_cast<uint32_t
>((acc & mask).
data[0]);
139 ScalarField input(input_u256);
140 input.self_from_montgomery_form();
142 ASSERT_EQ(input.data[0], input_u256.
data[0]);
143 ASSERT_EQ(input.data[1], input_u256.
data[1]);
144 ASSERT_EQ(input.data[2], input_u256.
data[2]);
145 ASSERT_EQ(input.data[3], input_u256.
data[3]);
147 for (
size_t i = 0; i < num_slices; ++i) {
150 EXPECT_EQ(result, slices[i]);
160 using Curve = TypeParam;
163 const size_t total_points = 30071;
164 const size_t num_buckets = 128;
166 std::vector<uint64_t> input_point_schedule;
167 for (
size_t i = 0; i < total_points; ++i) {
171 uint64_t schedule =
static_cast<uint64_t
>(bucket) + (
static_cast<uint64_t
>(i) << 32);
172 input_point_schedule.push_back(schedule);
178 input_point_schedule, TestFixture::generators, affine_data, bucket_data, 0, 0);
181 for (
auto& e : expected_buckets) {
182 e.self_set_infinity();
185 for (
size_t i = 0; i < total_points; ++i) {
186 uint64_t bucket = input_point_schedule[i] & 0xFFFFFFFF;
187 EXPECT_LT(
static_cast<size_t>(bucket), num_buckets);
188 expected_buckets[
static_cast<size_t>(bucket)] += TestFixture::generators[i];
190 for (
size_t i = 0; i < num_buckets; ++i) {
191 if (!expected_buckets[i].is_point_at_infinity()) {
192 AffineElement expected(expected_buckets[i]);
193 EXPECT_EQ(expected, bucket_data.
buckets[i]);
207 const size_t total_points = 30071;
208 const size_t num_buckets = 128;
210 std::vector<uint64_t> input_point_schedule;
211 for (
size_t i = 0; i < total_points; ++i) {
215 uint64_t schedule =
static_cast<uint64_t
>(bucket) + (
static_cast<uint64_t
>(i) << 32);
216 input_point_schedule.push_back(schedule);
222 input_point_schedule, TestFixture::generators, affine_data, bucket_data, 0, 0);
227 expected_acc.self_set_infinity();
229 std::vector<Element> expected_accs(num_threads);
230 size_t range_per_thread = (total_points + num_threads - 1) / num_threads;
233 expected_thread_acc.self_set_infinity();
234 size_t start = thread_idx * range_per_thread;
235 size_t end = (thread_idx == num_threads - 1) ? total_points : (thread_idx + 1) * range_per_thread;
236 bool skip = start >= total_points;
238 for (
size_t i = start; i < end; ++i) {
239 ScalarField scalar = input_point_schedule[i] & 0xFFFFFFFF;
240 expected_thread_acc += TestFixture::generators[i] * scalar;
243 expected_accs[thread_idx] = expected_thread_acc;
246 for (
size_t i = 0; i < num_threads; ++i) {
247 expected_acc += expected_accs[i];
249 AffineElement expected(expected_acc);
250 EXPECT_EQ(AffineElement(result), expected);
255 const size_t total_points = 30071;
257 std::vector<uint64_t> input_point_schedule;
258 for (
size_t i = 0; i < total_points; ++i) {
262 uint64_t schedule =
static_cast<uint64_t
>(bucket) + (
static_cast<uint64_t
>(i) << 32);
263 input_point_schedule.push_back(schedule);
267 &input_point_schedule[0], input_point_schedule.size(), 7);
269 for (
size_t i = 0; i < total_points; ++i) {
270 expected +=
static_cast<size_t>((input_point_schedule[i] & 0xFFFFFFFF) == 0);
272 EXPECT_EQ(result, expected);
281 const size_t num_points = 2;
282 std::vector<ScalarField> scalars(num_points);
284 const size_t normal_slice_size = 7;
285 const size_t num_buckets = 1 << normal_slice_size;
287 const size_t num_rounds = (NUM_BITS_IN_FIELD + normal_slice_size - 1) / normal_slice_size;
292 for (
size_t round_index = num_rounds - 1; round_index < num_rounds; round_index++) {
293 const size_t num_bits_in_slice =
294 (round_index == (num_rounds - 1)) ? (NUM_BITS_IN_FIELD % normal_slice_size) : normal_slice_size;
295 for (
size_t i = 0; i < num_points; ++i) {
297 size_t hi_bit = NUM_BITS_IN_FIELD - (round_index * normal_slice_size);
298 size_t lo_bit = hi_bit - normal_slice_size;
299 if (hi_bit < normal_slice_size) {
305 scalars[i].
data[0] = scalar.
data[0];
306 scalars[i].data[1] = scalar.
data[1];
307 scalars[i].data[2] = scalar.
data[2];
308 scalars[i].data[3] = scalar.
data[3];
309 scalars[i].self_to_montgomery_form();
312 std::vector<uint32_t> indices;
316 previous_round_output.self_set_infinity();
317 for (
auto x : indices) {
318 ASSERT_LT(x, num_points);
320 std::vector<uint64_t> point_schedule(scalars.size());
322 scalars, TestFixture::generators, indices, point_schedule);
324 msm_data, round_index, affine_data, bucket_data, previous_round_output, 7);
326 expected.self_set_infinity();
327 for (
size_t i = 0; i < num_points; ++i) {
328 ScalarField baz = scalars[i].to_montgomery_form();
329 expected += (TestFixture::generators[i] * baz);
331 size_t num_doublings = NUM_BITS_IN_FIELD - (normal_slice_size * (round_index + 1));
332 if (round_index == num_rounds - 1) {
335 for (
size_t i = 0; i < num_doublings; ++i) {
338 EXPECT_EQ(AffineElement(result), AffineElement(expected));
347 const size_t num_points = TestFixture::num_points;
350 AffineElement result =
353 AffineElement expected = TestFixture::naive_msm(scalars, TestFixture::generators);
354 EXPECT_EQ(result, expected);
363 std::vector<AffineElement> expected(num_msms);
368 size_t vector_offset = 0;
369 for (
size_t k = 0; k < num_msms; ++k) {
372 ASSERT_LT(vector_offset + num_points, TestFixture::num_points);
374 std::span<const AffineElement> batch_points(&TestFixture::generators[vector_offset], num_points);
376 vector_offset += num_points;
377 batch_points_span.push_back(batch_points);
378 batch_scalars_spans.push_back(batch_scalars);
380 expected[k] = TestFixture::naive_msm(batch_scalars_spans[k], batch_points_span[k]);
383 std::vector<AffineElement> result =
386 EXPECT_EQ(result, expected);
394 const size_t num_msms = 10;
395 std::vector<AffineElement> expected(num_msms);
402 for (
size_t k = 0; k < num_msms; ++k) {
403 const size_t num_points = 33;
404 auto& scalars = batch_scalars[k];
406 scalars.resize(num_points);
408 size_t fixture_offset = k * num_points;
411 for (
size_t i = 0; i < 13; ++i) {
414 for (
size_t i = 13; i < 23; ++i) {
415 scalars[i] = TestFixture::scalars[fixture_offset + i + 13];
417 for (
size_t i = 23; i < num_points; ++i) {
420 batch_points_span.push_back(batch_points);
421 batch_scalars_spans.push_back(batch_scalars[k]);
423 expected[k] = TestFixture::naive_msm(batch_scalars[k], batch_points);
426 std::vector<AffineElement> result =
429 EXPECT_EQ(result, expected);
437 const size_t start_index = 1234;
438 const size_t num_points = TestFixture::num_points - start_index;
445 AffineElement expected = TestFixture::naive_msm(scalar_span.
span, points);
446 EXPECT_EQ(result, expected);
454 const size_t start_index = 1234;
455 const size_t num_points = TestFixture::num_points - start_index;
456 std::vector<ScalarField> scalars(num_points);
458 for (
size_t i = 0; i < num_points; ++i) {
465 EXPECT_EQ(result, Curve::Group::affine_point_at_infinity);
473 const size_t num_points = 0;
474 std::vector<ScalarField> scalars(num_points);
475 std::vector<AffineElement> input_points(num_points);
479 EXPECT_EQ(result, Curve::Group::affine_point_at_infinity);
482TEST(ScalarMultiplication, SmallInputsExplicit)
484 uint256_t x0(0x68df84429941826a, 0xeb08934ed806781c, 0xc14b6a2e4f796a73, 0x08dc1a9a11a3c8db);
485 uint256_t y0(0x8ae5c31aa997f141, 0xe85f20c504f2c11b, 0x81a94193f3b1ce2b, 0x26f2c37372adb5b7);
486 uint256_t x1(0x80f5a592d919d32f, 0x1362652b984e51ca, 0xa0b26666f770c2a1, 0x142c6e1964e5c3c5);
487 uint256_t y1(0xb6c322ebb5ae4bc5, 0xf9fef6c7909c00f8, 0xb37ca1cc9af3b421, 0x1e331c7fa73d6a59);
488 uint256_t s0(0xe48bf12a24272e08, 0xf8dd0182577f3567, 0xec8fd222b8a6becb, 0x102d76b945612c9b);
489 uint256_t s1(0x098ae8d69f1e4e9e, 0xb5c8313c0f6040ed, 0xf78041e30cc46c44, 0x1d1e6e0c21892e13);
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
virtual uint64_t get_random_uint64()=0
virtual uint8_t get_random_uint8()=0
virtual uint16_t get_random_uint16()=0
virtual uint256_t get_random_uint256()=0
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 std::vector< AffineElement > batch_multi_scalar_mul(std::vector< std::span< const AffineElement > > &points, std::vector< std::span< ScalarField > > &scalars, bool handle_edge_cases=true) noexcept
Compute multiple multi-scalar multiplications.
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 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
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)
TEST(MegaCircuitBuilder, CopyConstructor)
C slice(C const &container, size_t start)
::testing::Types< curve::BN254, curve::Grumpkin > CurveTypes
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
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