Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ecc_op_queue.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Raju], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
8
15namespace bb {
16
42 using Fq = Curve::BaseField; // Grumpkin's scalar field
44 Point point_at_infinity = Curve::Group::affine_point_at_infinity;
45
46 // The operations written to the queue are also performed natively; the result is stored in accumulator
48
49 EccvmOpsTable eccvm_ops_table; // table of ops in the ECCVM format
50 UltraEccOpsTable ultra_ops_table; // table of ops in the Ultra-arithmetization format
51
52 // Storage for the reconstructed eccvm ops table in contiguous memory. (Intended to be constructed once and for
53 // all prior to ECCVM construction to avoid repeated traversal of the per-subtable storage.)
54 std::vector<ECCVMOperation> eccvm_ops_reconstructed;
55
56 // Storage for the reconstructed ultra ops tables in contiguous memory. (Intended to be constructed once and for
57 // all prior to Translator circuit construction to avoid repeated traversal of the per-subtable storage.)
58 std::vector<UltraOp> ultra_ops_zk_reconstructed; // Chonk table
59 std::vector<UltraOp> ultra_ops_no_zk_reconstructed; // AVM table
60
61 // Tracks number of muls and size of eccvm in real time as the op queue is updated
63
64 public:
65 static const size_t OP_QUEUE_SIZE = 1 << CONST_OP_QUEUE_LOG_SIZE;
70
80
81 size_t num_subtables() const { return eccvm_ops_table.num_subtables(); }
82
84
91 size_t get_append_offset() const
92 {
95 return OP_QUEUE_SIZE - get_current_subtable_size() - reserved_op_slots - zk_op_slots;
96 }
97
98 void merge()
99 {
102 }
103
104 void merge_fixed_append(size_t ultra_fixed_offset)
105 {
108 }
109
111 {
112 auto [column_polynomials, hiding_op] = ultra_ops_table.construct_zk_columns();
113 this->hiding_op_for_eccvm = hiding_op;
114 this->has_hiding_op = true;
115
116 return column_polynomials;
117 }
118
123
124 // Construct column polynomials for the full aggregate ultra ops table
126 const bool include_zk_ops = true) const
127 {
128 return ultra_ops_table.construct_table_columns(include_zk_ops);
129 }
130
131 // Construct column polynomials for the aggregate table up to and including the tail subtable.
136
137 // Construct column polynomials for the most recently merged subtable
142
143 // Reconstruct the full table of eccvm ops in contiguous memory from the independent subtables
145
146 // Reconstruct the ZK-prefixed full table of ultra ops in contiguous memory from the independent subtables.
151
152 // Reconstruct the non-ZK full table of ultra ops in contiguous memory from the independent subtables.
157
158 // Excludes the optional ZK prefix; see UltraEccOpsTable::num_ultra_rows
160 size_t get_ultra_ops_count() const { return ultra_ops_table.num_ops(); } // actual operation count without padding
161 // Excludes the optional ZK prefix, same as get_ultra_ops_table_num_rows.
163
164 // Get the full table of ECCVM ops in contiguous memory; construct it if it has not been constructed already.
165 // The hiding op is always prepended at index 0.
166 std::vector<ECCVMOperation>& get_eccvm_ops()
167 {
168 if (eccvm_ops_reconstructed.empty()) {
170 // Prepend the hiding op at index 0 (required for ZK)
171 if (!has_hiding_op) {
172 throw_or_abort("Hiding op must be set before calling get_eccvm_ops()");
173 }
175 }
177 }
178
186
194
199
204 size_t get_num_rows() const { return eccvm_row_tracker.get_num_rows(); }
205
210
215 void set_eccvm_ops_for_fuzzing(std::vector<ECCVMOperation>& eccvm_ops_in)
216 {
217 eccvm_ops_reconstructed = eccvm_ops_in;
218 }
219
226 {
227 EccOpCode op_code{ .eq = true, .reset = true };
228 append_eccvm_op(ECCVMOperation{ .op_code = op_code, .base_point = Point::random_element() });
229 }
230
237 {
239 accumulator.self_set_infinity();
240 }
241
243
250 {
251 // Update the accumulator natively
252 accumulator = accumulator + to_add;
253 EccOpCode op_code{ .add = true };
254 // Store the eccvm operation
255 append_eccvm_op(ECCVMOperation{ .op_code = op_code, .base_point = to_add });
256
257 // Construct and store the operation in the ultra op format
258 return construct_and_populate_ultra_ops(op_code, to_add);
259 }
260
266 UltraOp mul_accumulate(const Point& to_mul, const Fr& scalar)
267 {
268 BB_BENCH_NAME("ECCOpQueue::mul_accumulate");
269 // Update the accumulator natively
270 accumulator = accumulator + to_mul * scalar;
271 EccOpCode op_code{ .mul = true };
272
273 // Construct and store the operation in the ultra op format
274 UltraOp ultra_op = construct_and_populate_ultra_ops(op_code, to_mul, scalar);
275
276 // Store the eccvm operation
278 .op_code = op_code,
279 .base_point = to_mul,
280 .z1 = ultra_op.z_1,
281 .z2 = ultra_op.z_2,
282 .mul_scalar_full = scalar,
283 });
284
285 return ultra_op;
286 }
287
295 {
296 UltraOp no_op{};
297 ultra_ops_table.push(no_op);
298 return no_op;
299 }
300
309 {
310 UltraOp random_op{ .op_code = EccOpCode{ .is_random_op = true,
311 .random_value_1 = Fr::random_element(),
312 .random_value_2 = Fr::random_element() },
313 .x_lo = Fr::random_element(),
314 .x_hi = Fr::random_element(),
315 .y_lo = Fr::random_element(),
316 .y_hi = Fr::random_element(),
317 .z_1 = Fr::random_element(),
318 .z_2 = Fr::random_element(),
319 .return_is_infinity = false };
320 ultra_ops_table.push(random_op);
321 return random_op;
322 }
323
330 {
331 auto expected = accumulator;
332 accumulator.self_set_infinity();
333 EccOpCode op_code{ .eq = true, .reset = true };
334 // Store eccvm operation
335 append_eccvm_op(ECCVMOperation{ .op_code = op_code, .base_point = expected });
336
337 // Construct and store the operation in the ultra op format
338 return construct_and_populate_ultra_ops(op_code, expected);
339 }
340
367 UltraOp append_hiding_op(const Fq& Px, const Fq& Py)
368 {
369 auto [ultra_op, eccvm_op] = UltraEccOpsTable::make_hiding_op_pair(Px, Py);
370
371 hiding_op_for_eccvm = eccvm_op;
372 has_hiding_op = true;
373 ultra_ops_table.push(ultra_op);
374
375 // Do NOT update the accumulator - the hiding op doesn't perform any actual EC computation
376 return ultra_op;
377 }
378
379 private:
380 // === Hiding Op State ===
381 // The hiding op exists in both the ECCVM and Ultra tables (same Px, Py values, opcode q_eq=q_reset=1) so the
382 // translation check holds. It is set by exactly one of two entry points, depending on the proving flow:
383 // - Chonk: UltraEccOpsTable::construct_zk_columns() builds the full ZK prefix (1 no-op + 3 random + 1 hiding)
384 // at the front of the reconstructed Ultra table; the hiding op lands at index 4.
385 // - Goblin AVM: append_hiding_op() pushes the Ultra side into the current subtable directly, with no surrounding
386 // prefix.
387 // In both cases the ECCVM side is stored here and prepended to the reconstructed ECCVM table at index 0 by
388 // get_eccvm_ops(), placing it at row 1 (lagrange_second) where the on-curve and eq constraints are gated off
389 // so that non-curve (x, y) values are accepted.
391 bool has_hiding_op = false;
392
410 UltraOp construct_and_populate_ultra_ops(EccOpCode op_code, const Point& point, const Fr& scalar = Fr::zero())
411 {
412 UltraOp ultra_op;
413 ultra_op.op_code = op_code;
414
415 // Decompose point coordinates (Fq) into hi-lo chunks (Fr)
416 const size_t CHUNK_SIZE = 2 * stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION;
417 uint256_t x_256(point.x);
418 uint256_t y_256(point.y);
419 ultra_op.return_is_infinity = point.is_point_at_infinity();
420 // if we have a point at infinity, set x/y to zero
421 // in the biggroup_goblin class we use `assert_equal` statements to validate
422 // the original in-circuit coordinate values are also zero
423 if (point.is_point_at_infinity()) {
424 x_256 = 0;
425 y_256 = 0;
426 }
427 ultra_op.x_lo = Fr(x_256.slice(0, CHUNK_SIZE));
428 ultra_op.x_hi = Fr(x_256.slice(CHUNK_SIZE, CHUNK_SIZE * 2));
429 ultra_op.y_lo = Fr(y_256.slice(0, CHUNK_SIZE));
430 ultra_op.y_hi = Fr(y_256.slice(CHUNK_SIZE, CHUNK_SIZE * 2));
431
432 // Split scalar into 128 bit endomorphism scalars
433 Fr z_1 = 0;
434 Fr z_2 = 0;
435 auto converted = scalar.from_montgomery_form();
436 uint256_t converted_u256(scalar);
437 // if our scalar is small, don't split.
438 if (converted_u256.get_msb() < 128) {
439 ultra_op.z_1 = scalar;
440 ultra_op.z_2 = 0;
441 } else {
442 Fr::split_into_endomorphism_scalars(converted, z_1, z_2);
443 ultra_op.z_1 = z_1.to_montgomery_form();
444 ultra_op.z_2 = z_2.to_montgomery_form();
445 }
446
447 ultra_ops_table.push(ultra_op);
448
449 return ultra_op;
450 }
451};
452
453} // namespace bb
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:264
Manages ECC operations for the Goblin proving system.
Curve::ScalarField Fr
Curve::AffineElement Point
std::array< Polynomial< Fr >, ULTRA_TABLE_WIDTH > construct_table_columns_up_to_tail() const
size_t get_ultra_ops_table_num_rows_up_to_tail() const
size_t get_append_offset() const
Compute the fixed append offset for the final APPEND merge.
size_t get_num_rows() const
Get the number of rows for the current ECCVM circuit.
EccvmOpsTable eccvm_ops_table
std::vector< ECCVMOperation > eccvm_ops_reconstructed
std::array< Polynomial< Fr >, ULTRA_TABLE_WIDTH > construct_ultra_ops_table_columns(const bool include_zk_ops=true) const
void construct_full_eccvm_ops_table()
UltraOp add_accumulate(const Point &to_add)
Write point addition op to queue and natively perform addition.
void initialize_new_subtable()
Initialize a new subtable for eccvm and ultra ops with the given merge settings.
Point get_accumulator()
size_t get_ultra_ops_table_num_rows() const
std::vector< UltraOp > ultra_ops_zk_reconstructed
UltraOp append_hiding_op(const Fq &Px, const Fq &Py)
Add a hiding op with random Px, Py values to both ECCVM and Ultra ops tables.
std::vector< UltraOp > & get_no_zk_reconstructed_ultra_ops()
ECCVMOperation hiding_op_for_eccvm
UltraEccOpsTable ultra_ops_table
void append_eccvm_op(const ECCVMOperation &op)
Append an eccvm operation to the eccvm ops table; update the eccvm row tracker.
uint32_t get_number_of_muls() const
Get number of muls for the current ECCVM circuit.
std::vector< ECCVMOperation > & get_eccvm_ops()
size_t get_num_msm_rows() const
Get the number of rows in the 'msm' column section, for all msms in the circuit.
std::array< Polynomial< Fr >, ULTRA_TABLE_WIDTH > construct_current_ultra_ops_subtable_columns() const
std::vector< UltraOp > ultra_ops_no_zk_reconstructed
UltraOp construct_and_populate_ultra_ops(EccOpCode op_code, const Point &point, const Fr &scalar=Fr::zero())
Given an ecc operation and its inputs, decompose into ultra format and populate ultra_ops.
std::vector< UltraOp > & get_zk_reconstructed_ultra_ops()
UltraOp mul_accumulate(const Point &to_mul, const Fr &scalar)
Write multiply and add op to queue and natively perform operation.
UltraOp random_op_ultra_only()
Writes randomness to the ultra ops table but adds no eccvm operations.
void construct_no_zk_reconstructed_ultra_ops_table()
size_t num_subtables() const
std::vector< std::array< Polynomial< Fr >, ULTRA_TABLE_WIDTH > > construct_subtable_columns() const
std::array< Polynomial< Fr >, ULTRA_TABLE_WIDTH > construct_zk_columns()
UltraOp no_op_ultra_only()
Writes a no-op to the ultra ops table but adds no eccvm operations.
static const size_t OP_QUEUE_SIZE
UltraOp eq_and_reset()
Write equality op using internal accumulator point.
size_t get_current_subtable_size() const
void empty_row_for_testing()
Write empty row to queue.
ECCOpQueue()
Instantiate an initial ECC op subtable.
void set_eccvm_ops_for_fuzzing(std::vector< ECCVMOperation > &eccvm_ops_in)
A fuzzing only method for setting eccvm ops directly.
void construct_zk_reconstructed_ultra_ops_table()
void merge_fixed_append(size_t ultra_fixed_offset)
EccvmRowTracker eccvm_row_tracker
size_t get_ultra_ops_count() const
void add_erroneous_equality_op_for_testing()
A testing only method that adds an erroneous equality op to the eccvm ops.
static constexpr size_t ULTRA_TABLE_WIDTH
void create_new_subtable(size_t size_hint=0)
size_t num_subtables() const
std::vector< OpFormat > get_reconstructed() const
void push(const OpFormat &op)
Class for tracking the number of rows in the ECCVM circuit and the number of muls performed as the op...
size_t get_num_rows() const
Get the number of rows for the current ECCVM circuit.
size_t get_num_msm_rows() const
Get the number of rows in the 'msm' column section, for all msms in the circuit.
uint32_t get_number_of_muls() const
void update_cached_msms(const ECCVMOperation &op)
Update cached_active_msm_count or update other row counts and reset cached_active_msm_count.
Stores a table of elliptic curve operations represented in the Ultra format.
std::pair< ColumnPolynomials, ECCVMOperation > construct_zk_columns()
size_t get_current_subtable_size() const
size_t ultra_table_size_up_to_tail() const
std::vector< ColumnPolynomials > construct_subtable_columns() const
void push(const UltraOp &op)
size_t num_ultra_rows() const
ColumnPolynomials construct_current_ultra_ops_subtable_columns() const
static constexpr size_t APPEND_TRACE_OFFSET
ColumnPolynomials construct_table_columns_up_to_tail() const
static constexpr size_t NUM_ROWS_PER_OP
void create_new_subtable(size_t size_hint=0)
static std::pair< UltraOp, ECCVMOperation > make_hiding_op_pair(const curve::BN254::BaseField &Px, const curve::BN254::BaseField &Py)
Build a hiding op as paired Ultra and ECCVM operations from raw Fq coordinates.
std::vector< UltraOp > get_no_zk_reconstructed_ultra_ops() const
static constexpr size_t TABLE_WIDTH
ColumnPolynomials construct_table_columns(const bool include_zk_ops=true) const
static constexpr size_t ZK_ULTRA_OPS
void merge_with_fixed_append_offset(size_t offset)
std::vector< UltraOp > get_zk_reconstructed_ultra_ops() const
bb::fq BaseField
Definition bn254.hpp:19
typename Group::affine_element AffineElement
Definition bn254.hpp:22
bb::fr ScalarField
Definition bn254.hpp:18
constexpr uint256_t slice(uint64_t start, uint64_t end) const
constexpr uint64_t get_msb() const
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
AffineElement base_point
Defines the opcodes for ECC operations used in both the Ultra and ECCVM formats. There are three opco...
bool return_is_infinity
EccOpCode op_code
BB_INLINE constexpr field to_montgomery_form() const noexcept
static void split_into_endomorphism_scalars(const field &k, field &k1, field &k2)
Full-width endomorphism decomposition: k ≡ k1 - k2·λ (mod r). Modifies the field elements k1 and k2.
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr field from_montgomery_form() const noexcept
static constexpr field zero()
void throw_or_abort(std::string const &err)