Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
commitment_key.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
7#pragma once
8
27
28#include <cstddef>
29#include <cstdlib>
30#include <limits>
31#include <memory>
32#include <string_view>
33
34namespace bb {
43template <class Curve> class CommitmentKey {
44
45 using Fr = typename Curve::ScalarField;
47 using G1 = typename Curve::AffineElement;
48 static constexpr size_t EXTRA_SRS_POINTS_FOR_ECCVM_IPA = 1;
49
50 static size_t get_num_needed_srs_points(size_t num_points)
51 {
52 // NOTE 1: Currently we must round up internal space for points as our pippenger algorithm (specifically,
53 // pippenger_unsafe_optimized_for_non_dyadic_polys) will use next power of 2. This is used to simplify the
54 // recursive halving scheme. We do, however allow the polynomial to not be fully formed. Pippenger internally
55 // will pad 0s into the runtime state.
56 // NOTE 2: We then add one for ECCVM to provide for IPA verification
58 }
59
60 public:
63
64 CommitmentKey() = default;
65
73 CommitmentKey(const size_t num_points)
74 : srs(srs::get_crs_factory<Curve>()->get_crs(get_num_needed_srs_points(num_points)))
76 {}
82 bool initialized() const { return srs != nullptr; }
83
91 {
92 // Note: this fn used to expand polynomials to the dyadic size,
93 // due to a quirk in how our pippenger algo used to function.
94 // The pippenger algo has been refactored and this is no longer an issue
95 BB_BENCH_NAME("CommitmentKey::commit");
96 std::span<const G1> point_table = srs->get_monomial_points();
97 size_t consumed_srs = polynomial.start_index + polynomial.size();
98 if (consumed_srs > srs->get_monomial_size()) {
99 throw_or_abort(format("Attempting to commit to a polynomial that needs ",
100 consumed_srs,
101 " points with an SRS of size ",
102 srs->get_monomial_size()));
103 }
104
105 G1 r = scalar_multiplication::pippenger_unsafe<Curve>(polynomial, point_table);
106 Commitment point(r);
107 return point;
108 };
116 std::vector<Commitment> batch_commit(RefSpan<Polynomial<Fr>> polynomials,
117 size_t max_batch_size = std::numeric_limits<size_t>::max()) const
118 {
119 BB_BENCH_NAME("CommitmentKey::batch_commit");
120
121 // We can only commit max_batch_size at a time
122 // This is to prevent excessive memory usage in the pippenger algorithm
123 // First batch, create the commitments vector
124 std::vector<Commitment> commitments;
125
126 for (size_t i = 0; i < polynomials.size();) {
127 // Note: have to be careful how we compute this to not overlow e.g. max_batch_size + 1 would
128 size_t batch_size = std::min(max_batch_size, polynomials.size() - i);
129 size_t batch_end = i + batch_size;
130
131 // Prepare spans for batch MSM
133 std::vector<std::span<Fr>> scalar_spans;
134
135 for (auto& polynomial : polynomials.subspan(i, batch_end - i)) {
136 std::span<const G1> point_table = srs->get_monomial_points().subspan(polynomial.start_index());
137 size_t consumed_srs = polynomial.start_index() + polynomial.size();
138 if (consumed_srs > srs->get_monomial_size()) {
139 throw_or_abort(format("Attempting to commit to a polynomial that needs ",
140 consumed_srs,
141 " points with an SRS of size ",
142 srs->get_monomial_size()));
143 }
144 scalar_spans.emplace_back(polynomial.coeffs());
145 points_spans.emplace_back(point_table);
146 }
147
148 // Perform batch MSM
149 auto results = scalar_multiplication::MSM<Curve>::batch_multi_scalar_mul(points_spans, scalar_spans, false);
150 for (const auto& result : results) {
151 commitments.emplace_back(result);
152 }
153 i += batch_size;
154 }
155 return commitments;
156 };
157
158 // helper builder struct for constructing a batch to commit at once
159 struct CommitBatch {
162 std::vector<std::string> labels;
163 std::vector<Commitment> commit_and_send_to_verifier(auto transcript,
164 size_t max_batch_size = std::numeric_limits<size_t>::max())
165 {
166 std::vector<Commitment> commitments = key->batch_commit(wires, max_batch_size);
167 for (size_t i = 0; i < commitments.size(); ++i) {
168 transcript->send_to_verifier(labels[i], commitments[i]);
169 }
170
171 return commitments;
172 }
173
174 void add_to_batch(Polynomial<Fr>& poly, const std::string& label, bool mask)
175 {
176 if (mask) {
177 poly.mask();
178 }
179 wires.push_back(poly);
180 labels.push_back(label);
181 }
182 };
183
184 CommitBatch start_batch() { return CommitBatch{ this, {}, {} }; }
185
201 const std::vector<std::pair<size_t, size_t>>& active_ranges,
202 size_t final_active_wire_idx = 0)
203 {
204 BB_BENCH_NAME("CommitmentKey::commit_structured");
205 BB_ASSERT_LTE(polynomial.end_index(), srs->get_monomial_size(), "Polynomial size exceeds commitment key size.");
206 BB_ASSERT_LTE(polynomial.end_index(), dyadic_size, "Polynomial size exceeds commitment key size.");
207
208 // Percentage of nonzero coefficients beyond which we resort to the conventional commit method
209 constexpr size_t NONZERO_THRESHOLD = 75;
210
211 // Compute the number of non-zero coefficients in the polynomial
212 size_t total_num_scalars = 0;
213 for (const auto& [first, second] : active_ranges) {
214 total_num_scalars += second - first;
215 }
216
217 // Compute "active" percentage of polynomial; resort to standard commit if appropriate
218 size_t polynomial_size = final_active_wire_idx != 0 ? final_active_wire_idx : polynomial.size();
219 size_t percentage_nonzero = total_num_scalars * 100 / polynomial_size;
220 if (percentage_nonzero > NONZERO_THRESHOLD) {
221 return commit(polynomial);
222 }
223
224 // Extract the precomputed point table (contains raw SRS points at even indices and the corresponding
225 // endomorphism point (\beta*x, -y) at odd indices).
226 std::span<G1> point_table = srs->get_monomial_points();
227
228 std::vector<Fr> scalars;
229 scalars.reserve(total_num_scalars);
230 std::vector<G1> points;
231 points.reserve(total_num_scalars);
232 for (const auto& [first, second] : active_ranges) {
233 auto poly_start = &polynomial[first];
234 // Pointer to the first element past the active range. Accessing `&polynomial[second]` directly can trigger
235 // an assertion when `second == polynomial_size`, so we compute the pointer using `polynomial.data()`
236 // to ensure safe range handling.
237 auto poly_end = polynomial.data() + (second - polynomial.start_index);
238 scalars.insert(scalars.end(), poly_start, poly_end);
239
240 auto pts_start = &point_table[first];
241 auto pts_end = &point_table[second];
242 points.insert(points.end(), pts_start, pts_end);
243 }
244
245 // Call the version of pippenger which assumes all points are distinct
246 G1 r = scalar_multiplication::pippenger_unsafe<Curve>({ 0, scalars }, points);
247 return Commitment(r);
248 }
249
263 const std::vector<std::pair<size_t, size_t>>& active_ranges,
264 size_t final_active_wire_idx = 0)
265 {
266 BB_BENCH_NAME("CommitmentKey::commit_structured_with_nonzero_complement");
267 BB_ASSERT_LTE(polynomial.end_index(), srs->get_monomial_size(), "Polynomial size exceeds commitment key size.");
268
269 using BatchedAddition = BatchedAffineAddition<Curve>;
270
271 // Percentage of constant coefficients below which we resort to the conventional commit method
272 constexpr size_t CONSTANT_THRESHOLD = 50;
273
274 // Compute the active range complement over which the polynomial is assumed to be constant within each range.
275 // Note: the range from the end of the last active range to the end of the polynomial is excluded from the
276 // complement since the polynomial is assumed to be zero there.
277 std::vector<std::pair<size_t, size_t>> active_ranges_complement;
278 // Also compute total number of scalars in the constant regions
279 size_t total_num_complement_scalars = 0;
280 for (size_t i = 0; i < active_ranges.size() - 1; ++i) {
281 const size_t start = active_ranges[i].second;
282 const size_t end = active_ranges[i + 1].first;
283 if (end > start) {
284 active_ranges_complement.emplace_back(start, end);
285 total_num_complement_scalars += end - start;
286 }
287 }
288
289 size_t polynomial_size = final_active_wire_idx != 0 ? final_active_wire_idx : polynomial.size();
290 // Compute percentage of polynomial comprised of constant blocks; resort to standard commit if appropriate
291 size_t percentage_constant = total_num_complement_scalars * 100 / polynomial_size;
292
293 if (percentage_constant < CONSTANT_THRESHOLD) {
294 return commit(polynomial);
295 }
296
297 // Extract the precomputed point table (contains raw SRS points at even indices and the corresponding
298 // endomorphism point (\beta*x, -y) at odd indices).
299 std::span<G1> point_table = srs->get_monomial_points();
300
301 // Copy the raw SRS points (no endo points) corresponding to the constant regions into contiguous memory
302 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1131): Peak memory usage could be improved by
303 // performing this copy and the subsequent summation as a precomputation prior to constructing the point table.
304 std::vector<G1> points;
305
306 points.reserve(total_num_complement_scalars);
307 for (const auto& [start, end] : active_ranges_complement) {
308 for (size_t i = start; i < end; i++) {
309 points.emplace_back(point_table[i]);
310 }
311 }
312
313 // Populate the set of unique scalars with first coeff from each range (values assumed constant over each
314 // range). Also store the number of points in each sequence to be summed
315 std::vector<Fr> unique_scalars;
316 std::vector<size_t> sequence_counts;
317 for (const auto& range : active_ranges_complement) {
318 unique_scalars.emplace_back(polynomial.span[range.first]);
319 sequence_counts.emplace_back(range.second - range.first);
320 }
321
322 // Reduce each sequence to a single point
323 auto reduced_points = BatchedAddition::add_in_place(points, sequence_counts);
324
325 // Compute the full commitment as the sum of the "active" region commitment and the constant region contribution
326 Commitment result = commit_structured(polynomial, active_ranges, final_active_wire_idx);
327
328 for (auto [scalar, point] : zip_view(unique_scalars, reduced_points)) {
329 result = result + point * scalar;
330 }
331
332 return result;
333 }
334
336
338 CommitType type,
339 const std::vector<std::pair<size_t, size_t>>& active_ranges = {},
340 size_t final_active_wire_idx = 0)
341 {
342 switch (type) {
344 return commit_structured_with_nonzero_complement(poly, active_ranges, final_active_wire_idx);
346 default:
347 return commit(poly);
348 }
349 }
350};
351
352} // namespace bb
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:163
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:218
Class for handling fast batched affine addition of large sets of EC points.
CommitmentKey object over a pairing group 𝔾₁.
CommitmentKey()=default
std::vector< Commitment > batch_commit(RefSpan< Polynomial< Fr > > polynomials, size_t max_batch_size=std::numeric_limits< size_t >::max()) const
Batch commitment to multiple polynomials.
typename Curve::AffineElement G1
typename Curve::ScalarField Fr
static constexpr size_t EXTRA_SRS_POINTS_FOR_ECCVM_IPA
typename Curve::AffineElement Commitment
CommitmentKey(const size_t num_points)
Construct a new Kate Commitment Key object from existing SRS.
Commitment commit_structured(PolynomialSpan< const Fr > polynomial, const std::vector< std::pair< size_t, size_t > > &active_ranges, size_t final_active_wire_idx=0)
Efficiently commit to a polynomial whose nonzero elements are arranged in discrete blocks.
Commitment commit(PolynomialSpan< const Fr > polynomial) const
Uses the ProverSRS to create a commitment to p(X)
bool initialized() const
Checks the commitment key is properly initialized.
std::shared_ptr< srs::factories::Crs< Curve > > srs
CommitBatch start_batch()
Commitment commit_structured_with_nonzero_complement(PolynomialSpan< const Fr > polynomial, const std::vector< std::pair< size_t, size_t > > &active_ranges, size_t final_active_wire_idx=0)
Efficiently commit to a polynomial with discrete blocks of arbitrary elements and constant elements.
Commitment commit_with_type(PolynomialSpan< const Fr > poly, CommitType type, const std::vector< std::pair< size_t, size_t > > &active_ranges={}, size_t final_active_wire_idx=0)
static size_t get_num_needed_srs_points(size_t num_points)
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
void mask()
Add random values to the coefficients of a polynomial. In practice, this is used for ensuring the com...
A template class for a reference vector. Behaves as if std::vector<T&> was possible.
typename Group::affine_element AffineElement
Definition grumpkin.hpp:56
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.
std::string format(Args... args)
Definition log.hpp:21
constexpr T round_up_power_2(const T in)
Definition get_msb.hpp:52
Entry point for Barretenberg command-line interface.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::vector< std::string > labels
std::vector< Commitment > commit_and_send_to_verifier(auto transcript, size_t max_batch_size=std::numeric_limits< size_t >::max())
void add_to_batch(Polynomial< Fr > &poly, const std::string &label, bool mask)
RefVector< Polynomial< Fr > > wires
size_t size() const
std::span< Fr > span
size_t end_index() const
void throw_or_abort(std::string const &err)