Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ecc_ops_table.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
14#include <deque>
15namespace bb {
16
22
37struct EccOpCode {
39 bool add = false;
40 bool mul = false;
41 bool eq = false;
42 bool reset = false;
43 bool operator==(const EccOpCode& other) const = default;
44
45 bool is_random_op = false;
48
49 [[nodiscard]] uint32_t value() const
50 {
51 if (is_random_op) {
52 throw_or_abort("EccOpCode::value() should not be called on a random op");
53 }
54 auto res = static_cast<uint32_t>(add);
55 res += res;
56 res += static_cast<uint32_t>(mul);
57 res += res;
58 res += static_cast<uint32_t>(eq);
59 res += res;
60 res += static_cast<uint32_t>(reset);
61 return res;
62 }
63};
64
65struct UltraOp {
76
83 {
85 return { Fq(0), Fq(0) };
86 }
87 auto x = Fq((uint256_t(x_hi) << 2 * stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION) + uint256_t(x_lo));
88 auto y = Fq((uint256_t(y_hi) << 2 * stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION) + uint256_t(y_lo));
89
90 return { x, y };
91 }
92};
93
96 using AffineElement = Curve::Group::affine_element;
103 bool operator==(const ECCVMOperation& other) const = default;
104};
105
115template <typename OpFormat> class EccOpsTable {
116 using Subtable = std::vector<OpFormat>;
117 std::deque<Subtable> table;
118 Subtable current_subtable; // used to store the current subtable before it is added to the table
119 public:
120 size_t size() const
121 {
122 ASSERT(current_subtable.empty(),
123 "Current subtable should be merged before computing the size of the full table of ecc ops.");
124 size_t total = 0;
125 for (const auto& subtable : table) {
126 total += subtable.size();
127 }
128
129 return total;
130 }
131
132 size_t num_subtables() const { return table.size(); }
133
134 auto& get() const { return table; }
135
136 void push(const OpFormat& op)
137 {
138 // Get the reference of the subtable to update
139
140 current_subtable.push_back(op);
141 }
142
143 void create_new_subtable(size_t size_hint = 0)
144 {
145 ASSERT(current_subtable.empty(), "Cannot create a new subtable until the current subtable has been merged.");
146 current_subtable.reserve(size_hint);
147 }
148
149 // const version of operator[]
150 const OpFormat& operator[](size_t index) const
151 {
152 ASSERT(current_subtable.empty(),
153 "Current subtable should be merged before attempting to index into the full table.");
154 BB_ASSERT_LT(index, size());
155 // simple linear search to find the correct subtable
156 for (const auto& subtable : table) {
157 if (index < subtable.size()) {
158 return subtable[index]; // found the correct subtable
159 }
160 index -= subtable.size(); // move to the next subtable
161 }
162 return table.front().front(); // should never reach here
163 }
164
165 // highly inefficient copy-based reconstruction of the table for use in ECCVM/Translator
166 std::vector<OpFormat> get_reconstructed() const
167 {
168 ASSERT(current_subtable.empty(),
169 "current subtable should be merged before reconstructing the full table of operations.");
170
171 std::vector<OpFormat> reconstructed_table;
172 reconstructed_table.reserve(size());
173 for (const auto& subtable : table) {
174 for (const auto& op : subtable) {
175 reconstructed_table.push_back(op);
176 }
177 }
178 return reconstructed_table;
179 }
180
182 {
183 if (current_subtable.empty()) {
184 return; // nothing to merge
185 }
186
187 // Based on merge settings add the current subtable to either the beginning or end of the full table
188 if (settings == MergeSettings::PREPEND) {
189 table.push_front(std::move(current_subtable));
190 } else {
191 table.push_back(std::move(current_subtable));
192 }
193
194 current_subtable.clear(); // clear the current subtable after merging
195 ASSERT(current_subtable.empty(), "current subtable should be empty after merging. Check the merge logic.");
196 }
197};
198
204
219 public:
220 static constexpr size_t TABLE_WIDTH = 4; // dictated by the number of wires in the Ultra arithmetization
221 static constexpr size_t NUM_ROWS_PER_OP = 2; // A single ECC op is split across two width-4 rows
222
223 private:
229
230 size_t current_subtable_idx = 0; // index of the current subtable being constructed
232
233 // For fixed-location append functionality (ultra ops only)
235 bool has_fixed_append = false;
236
237 public:
238 size_t size() const { return table.size(); }
239 size_t ultra_table_size() const
240 {
241 size_t base_size = table.size() * NUM_ROWS_PER_OP;
242 if (has_fixed_append && fixed_append_offset.has_value()) {
243 // Include zeros gap and final subtable at fixed location
244 size_t last_subtable_size = 0;
245 if (!table.get().empty()) {
246 // The last subtable in deque is the fixed-location one
247 last_subtable_size = table.get().back().size() * NUM_ROWS_PER_OP;
248 }
249 return std::max(base_size, fixed_append_offset.value() + last_subtable_size);
250 }
251 return base_size;
252 }
255 void create_new_subtable(size_t size_hint = 0) { table.create_new_subtable(size_hint); }
256 void push(const UltraOp& op) { table.push(op); }
258 {
259 if (settings == MergeSettings::APPEND) {
260 // All appends are treated as fixed-location for ultra ops
261 ASSERT(!has_fixed_append, "Can only perform fixed-location append once");
262 // Set fixed location at which to append ultra ops. If nullopt --> append right after prepended tables
264 has_fixed_append = true;
265 table.merge(settings);
267 } else { // MergeSettings::PREPEND
268 table.merge(settings);
270 }
271 }
272
273 std::vector<UltraOp> get_reconstructed() const { return table.get_reconstructed(); }
274
275 // Construct the columns of the full ultra ecc ops table
277 {
278 const size_t poly_size = ultra_table_size();
279
280 if (has_fixed_append) {
281 // Handle fixed-location append: prepended tables first, then appended table at fixed offset
283 }
284
285 // Normal case: all subtables in order
286 const size_t subtable_start_idx = 0; // include all subtables
287 const size_t subtable_end_idx = table.num_subtables();
288 return construct_column_polynomials_from_subtables(poly_size, subtable_start_idx, subtable_end_idx);
289 }
290
291 // Construct the columns of the previous full ultra ecc ops table
293 {
294 const size_t poly_size = previous_ultra_table_size();
295 const size_t subtable_start_idx = current_subtable_idx == 0 ? 1 : 0;
296 const size_t subtable_end_idx = current_subtable_idx == 0 ? table.num_subtables() : table.num_subtables() - 1;
297
298 return construct_column_polynomials_from_subtables(poly_size, subtable_start_idx, subtable_end_idx);
299 }
300
301 // Construct the columns of the current ultra ecc ops subtable which is either the first or the last one
302 // depening on whether it has been prepended or appended
304 {
305 const size_t poly_size = current_ultra_subtable_size();
306 const size_t subtable_start_idx = current_subtable_idx;
307 const size_t subtable_end_idx = current_subtable_idx + 1;
308
309 return construct_column_polynomials_from_subtables(poly_size, subtable_start_idx, subtable_end_idx);
310 }
311
312 private:
320 static void write_op_to_polynomials(ColumnPolynomials& column_polynomials, const UltraOp& op, const size_t row_idx)
321 {
322 column_polynomials[0].at(row_idx) = !op.op_code.is_random_op ? op.op_code.value() : op.op_code.random_value_1;
323 column_polynomials[1].at(row_idx) = op.x_lo;
324 column_polynomials[2].at(row_idx) = op.x_hi;
325 column_polynomials[3].at(row_idx) = op.y_lo;
326 column_polynomials[0].at(row_idx + 1) = !op.op_code.is_random_op ? 0 : op.op_code.random_value_2;
327 column_polynomials[1].at(row_idx + 1) = op.y_hi;
328 column_polynomials[2].at(row_idx + 1) = op.z_1;
329 column_polynomials[3].at(row_idx + 1) = op.z_2;
330 }
331
337 {
338 ColumnPolynomials column_polynomials;
339 for (auto& poly : column_polynomials) {
340 poly = Polynomial<Fr>(poly_size); // Initialized to zeros
341 }
342
343 // Process all prepended subtables (all except last)
344 size_t i = 0;
345 for (size_t subtable_idx = 0; subtable_idx < table.num_subtables() - 1; ++subtable_idx) {
346 const auto& subtable = table.get()[subtable_idx];
347 for (const auto& op : subtable) {
348 write_op_to_polynomials(column_polynomials, op, i);
349 i += NUM_ROWS_PER_OP;
350 }
351 }
352
353 // Place the appended subtable at the fixed offset
354 size_t append_position = fixed_append_offset.value_or(i);
355 const auto& appended_subtable = table.get()[table.num_subtables() - 1];
356
357 size_t j = append_position;
358 for (const auto& op : appended_subtable) {
359 write_op_to_polynomials(column_polynomials, op, j);
360 j += NUM_ROWS_PER_OP;
361 }
362
363 // Any gap between prepended tables and appended table remains zeros (from initialization)
364 return column_polynomials;
365 }
366
374 const size_t subtable_start_idx,
375 const size_t subtable_end_idx) const
376 {
377 ColumnPolynomials column_polynomials;
378 for (auto& poly : column_polynomials) {
379 poly = Polynomial<Fr>(poly_size);
380 }
381
382 size_t i = 0;
383 for (size_t subtable_idx = subtable_start_idx; subtable_idx < subtable_end_idx; ++subtable_idx) {
384 const auto& subtable = table.get()[subtable_idx];
385 for (const auto& op : subtable) {
386 write_op_to_polynomials(column_polynomials, op, i);
387 i += NUM_ROWS_PER_OP;
388 }
389 }
390 return column_polynomials;
391 }
392};
393
394} // namespace bb
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:115
#define ASSERT(expression,...)
Definition assert.hpp:49
A table of ECC operations.
std::vector< OpFormat > Subtable
size_t size() const
void create_new_subtable(size_t size_hint=0)
std::deque< Subtable > table
auto & get() const
Subtable current_subtable
size_t num_subtables() const
std::vector< OpFormat > get_reconstructed() const
const OpFormat & operator[](size_t index) const
void merge(MergeSettings settings=MergeSettings::PREPEND)
void push(const OpFormat &op)
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
Stores a table of elliptic curve operations represented in the Ultra format.
std::optional< size_t > fixed_append_offset
void push(const UltraOp &op)
ColumnPolynomials construct_column_polynomials_from_subtables(const size_t poly_size, const size_t subtable_start_idx, const size_t subtable_end_idx) const
Construct polynomials corresponding to the columns of the reconstructed ultra ops table for the given...
ColumnPolynomials construct_current_ultra_ops_subtable_columns() const
ColumnPolynomials construct_table_columns() const
std::array< Polynomial< Fr >, TABLE_WIDTH > ColumnPolynomials
std::array< std::span< Fr >, TABLE_WIDTH > TableView
ColumnPolynomials construct_previous_table_columns() const
static constexpr size_t NUM_ROWS_PER_OP
void create_new_subtable(size_t size_hint=0)
ColumnPolynomials construct_column_polynomials_with_fixed_append(const size_t poly_size) const
Construct polynomials with fixed-location append.
size_t current_ultra_subtable_size() const
size_t previous_ultra_table_size() const
std::vector< UltraOp > get_reconstructed() const
static void write_op_to_polynomials(ColumnPolynomials &column_polynomials, const UltraOp &op, const size_t row_idx)
Write a single UltraOp to the column polynomials at the given position.
static constexpr size_t TABLE_WIDTH
size_t ultra_table_size() const
void merge(MergeSettings settings=MergeSettings::PREPEND, std::optional< size_t > offset=std::nullopt)
bb::fq BaseField
Definition bn254.hpp:19
bb::fr ScalarField
Definition bn254.hpp:18
ssize_t offset
Definition engine.cpp:36
Entry point for Barretenberg command-line interface.
MergeSettings
The MergeSettings define whether an current subtable will be added at the beginning (PREPEND) or at t...
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
AffineElement base_point
Curve::Group::affine_element AffineElement
bool operator==(const ECCVMOperation &other) const =default
Defines the opcodes for ECC operations used in both the Ultra and ECCVM formats. There are three opco...
bool operator==(const EccOpCode &other) const =default
uint32_t value() const
curve::BN254::ScalarField Fr
bool return_is_infinity
EccOpCode op_code
std::array< Fq, 2 > get_base_point_standard_form() const
Get the point in standard form i.e. as two coordinates x and y in the base field or as a point at inf...
curve::BN254::BaseField Fq
void throw_or_abort(std::string const &err)