Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
scalar_multiplication.hpp
Go to the documentation of this file.
1#pragma once
4
8
11
12#include "./bitvector.hpp"
14
15template <typename Curve> class MSM {
16 public:
17 using Element = typename Curve::Element;
19 using BaseField = typename Curve::BaseField;
21
23 static constexpr size_t NUM_BITS_IN_FIELD = ScalarField::modulus.get_msb() + 1;
24
32 struct MSMWorkUnit {
33 size_t batch_msm_index = 0;
34 size_t start_index = 0;
35 size_t size = 0;
36 };
38
39 struct MSMData {
40 std::span<const ScalarField> scalars;
41 std::span<const AffineElement> points;
42 std::span<const uint32_t> scalar_indices;
43 std::span<uint64_t> point_schedule;
44 };
45
50 std::vector<AffineElement> buckets;
52
53 BucketAccumulators(size_t num_buckets) noexcept
54 : buckets(num_buckets)
55 , bucket_exists(num_buckets)
56 {}
57 };
58
60 std::vector<Element> buckets;
62
63 JacobianBucketAccumulators(size_t num_buckets) noexcept
64 : buckets(num_buckets)
65 , bucket_exists(num_buckets)
66 {}
67 };
72 static constexpr size_t BATCH_SIZE = 2048;
73 // when adding affine points, we have an edge case where the number of points in the batch can overflow by 2
74 static constexpr size_t BATCH_OVERFLOW_SIZE = 2;
75 std::vector<AffineElement> points_to_add;
76 std::vector<BaseField> scalar_scratch_space;
78
84 };
85 static size_t get_num_rounds(size_t num_points) noexcept
86 {
87 const size_t bits_per_slice = get_optimal_log_num_buckets(num_points);
88 const size_t num_rounds = (NUM_BITS_IN_FIELD + (bits_per_slice - 1)) / bits_per_slice;
89 return num_rounds;
90 }
91 static void add_affine_points(AffineElement* points,
92 const size_t num_points,
93 typename Curve::BaseField* scratch_space) noexcept;
95 std::vector<uint32_t>& consolidated_indices) noexcept;
96
98 std::vector<std::vector<uint32_t>>& msm_scalar_indices) noexcept;
99 static uint32_t get_scalar_slice(const ScalarField& scalar, size_t round, size_t normal_slice_size) noexcept;
100 static size_t get_optimal_log_num_buckets(const size_t num_points) noexcept;
101 static bool use_affine_trick(const size_t num_points, const size_t num_buckets) noexcept;
102
103 static Element small_pippenger_low_memory_with_transformed_scalars(MSMData& msm_data) noexcept;
104 static Element pippenger_low_memory_with_transformed_scalars(MSMData& msm_data) noexcept;
105 static Element evaluate_small_pippenger_round(MSMData& msm_data,
106 const size_t round_index,
107 JacobianBucketAccumulators& bucket_data,
108 Element previous_round_output,
109 const size_t bits_per_slice) noexcept;
110
111 static Element evaluate_pippenger_round(MSMData& msm_data,
112 const size_t round_index,
113 AffineAdditionData& affine_data,
114 BucketAccumulators& bucket_data,
115 Element previous_round_output,
116 const size_t bits_per_slice) noexcept;
117
118 static void consume_point_schedule(std::span<const uint64_t> point_schedule,
119 std::span<const AffineElement> points,
120 AffineAdditionData& affine_data,
121 BucketAccumulators& bucket_data,
122 size_t num_input_points_processed,
123 size_t num_queued_affine_points) noexcept;
124
125 static std::vector<AffineElement> batch_multi_scalar_mul(std::vector<std::span<const AffineElement>>& points,
126 std::vector<std::span<ScalarField>>& scalars,
127 bool handle_edge_cases = true) noexcept;
128 static AffineElement msm(std::span<const AffineElement> points,
129 PolynomialSpan<const ScalarField> _scalars,
130 bool handle_edge_cases = false) noexcept;
131
132 template <typename BucketType> static Element accumulate_buckets(BucketType& bucket_accumulators) noexcept
133 {
134 auto& buckets = bucket_accumulators.buckets;
135 BB_ASSERT_GT(buckets.size(), static_cast<size_t>(0));
136 int starting_index = static_cast<int>(buckets.size() - 1);
137 Element prefix_sum;
138 bool found_start = false;
139 while (!found_start && starting_index > 0) {
140 const size_t idx = static_cast<size_t>(starting_index);
141 if (bucket_accumulators.bucket_exists.get(idx)) {
142
143 prefix_sum = buckets[idx];
144 found_start = true;
145 } else {
146 starting_index -= 1;
147 }
148 }
149 if (!found_start) {
150 return Curve::Group::point_at_infinity;
151 }
152 BB_ASSERT_GT(starting_index, 0);
153 AffineElement offset_generator = Curve::Group::affine_point_at_infinity;
155 constexpr auto gen = get_precomputed_generators<typename Curve::Group, "ECCVM_OFFSET_GENERATOR", 1>()[0];
156 offset_generator = gen;
157 } else {
158 constexpr auto gen = get_precomputed_generators<typename Curve::Group, "DEFAULT_DOMAIN_SEPARATOR", 8>()[0];
159 offset_generator = gen;
160 }
161 Element sum = prefix_sum + offset_generator;
162 for (int i = static_cast<int>(starting_index - 1); i > 0; --i) {
163 size_t idx = static_cast<size_t>(i);
164 BB_ASSERT_LT(idx, bucket_accumulators.bucket_exists.size());
165 if (bucket_accumulators.bucket_exists.get(idx)) {
166
167 prefix_sum += buckets[idx];
168 }
169 sum += prefix_sum;
170 }
171 return sum - offset_generator;
172 }
173};
174
175template <typename Curve>
178 bool handle_edge_cases = true) noexcept;
179template <typename Curve>
180typename Curve::Element pippenger_unsafe(PolynomialSpan<const typename Curve::ScalarField> scalars,
181 std::span<const typename Curve::AffineElement> points) noexcept;
182
183extern template class MSM<curve::Grumpkin>;
184extern template class MSM<curve::BN254>;
185
186// NEXT STEP ACCUMULATE BUVKETS
187} // namespace bb::scalar_multiplication
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:87
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:115
Custom class to handle packed vectors of bits.
Definition bitvector.hpp:15
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
typename Curve::BaseField BaseField
static Element evaluate_small_pippenger_round(MSMData &msm_data, const size_t round_index, JacobianBucketAccumulators &bucket_data, Element previous_round_output, const size_t bits_per_slice) noexcept
Evaluate a single Pippenger round when we do not use the Affine trick.
static Element small_pippenger_low_memory_with_transformed_scalars(MSMData &msm_data) noexcept
Top-level Pippenger algorithm where number of points is small and we are not using the Affine trick.
static Element pippenger_low_memory_with_transformed_scalars(MSMData &msm_data) noexcept
Top-level Pippenger algorithm.
static Element accumulate_buckets(BucketType &bucket_accumulators) noexcept
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 bool use_affine_trick(const size_t num_points, const size_t num_buckets) noexcept
Given a number of points and an optimal bucket size, should we use the affine trick?
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.
std::vector< MSMWorkUnit > ThreadWorkUnits
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 constexpr size_t NUM_BITS_IN_FIELD
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< ThreadWorkUnits > get_work_units(std::vector< std::span< ScalarField > > &scalars, std::vector< std::vector< uint32_t > > &msm_scalar_indices) noexcept
Split a multiple multi-scalar-multiplication into equal units of work that can be processed by thread...
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.
typename Curve::ScalarField ScalarField
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
typename Curve::AffineElement AffineElement
Curve::Element pippenger(PolynomialSpan< const typename Curve::ScalarField > scalars, std::span< const typename Curve::AffineElement > points, bool handle_edge_cases) noexcept
Curve::Element pippenger_unsafe(PolynomialSpan< const typename Curve::ScalarField > scalars, std::span< const typename Curve::AffineElement > points) noexcept
Inner sum(Cont< Inner, Args... > const &in)
Definition container.hpp:70
constexpr std::span< const typename Group::affine_element > get_precomputed_generators()
@ BN254
Definition types.hpp:10
STL namespace.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Curve::Element Element
Temp data structure, one created per thread!
Temp data structure, one created per thread!
std::span< const AffineElement > points
MSMWorkUnit describes an MSM that may be part of a larger MSM.