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.
47 // The accumulator is kept in jacobian form and scalar muls are deferred and evaluated as a batch on read:
48 // evaluating each mul eagerly on an affine accumulator costs a full scalar mul plus a normalization
49 // (field inversion) per op.
50 Curve::Element accumulator = Curve::Element::infinity();
51 std::vector<Point> deferred_mul_points;
52 std::vector<Fr> deferred_mul_scalars;
53
54 // Fold the deferred scalar muls into the accumulator via a single batched MSM.
56 {
57 if (deferred_mul_points.empty()) {
58 return;
59 }
60 accumulator += Curve::Element::straus_msm(deferred_mul_points, deferred_mul_scalars);
61 deferred_mul_points.clear();
63 }
64
66 {
67 accumulator.self_set_infinity();
68 deferred_mul_points.clear();
70 }
71
72 EccvmOpsTable eccvm_ops_table; // table of ops in the ECCVM format
73 UltraEccOpsTable ultra_ops_table; // table of ops in the Ultra-arithmetization format
74
75 // Storage for the reconstructed eccvm ops table in contiguous memory. (Intended to be constructed once and for
76 // all prior to ECCVM construction to avoid repeated traversal of the per-subtable storage.)
77 std::vector<ECCVMOperation> eccvm_ops_reconstructed;
78
79 // Storage for the reconstructed ultra ops tables in contiguous memory. (Intended to be constructed once and for
80 // all prior to Translator circuit construction to avoid repeated traversal of the per-subtable storage.)
81 std::vector<UltraOp> ultra_ops_zk_reconstructed; // Chonk table
82 std::vector<UltraOp> ultra_ops_no_zk_reconstructed; // AVM table
83
84 // Tracks number of muls and size of eccvm in real time as the op queue is updated
86
87 public:
88 static const size_t OP_QUEUE_SIZE = 1 << CONST_OP_QUEUE_LOG_SIZE;
93
103
104 size_t num_subtables() const { return eccvm_ops_table.num_subtables(); }
105
107
112 static size_t get_append_offset_for_verifier() { return get_append_offset(bb::HIDING_KERNEL_ULTRA_OPS); }
113
114 // Shift size of the merge / row offset at which the fixed-append subtable's polynomial begins.
115 // See UltraEccOpsTable::compute_fixed_append_offset.
116 static constexpr size_t compute_fixed_append_offset(size_t append_offset, bool include_zk_prefix = true)
117 {
118 return UltraEccOpsTable::compute_fixed_append_offset(append_offset, include_zk_prefix);
119 }
120
121 void merge()
122 {
125 }
126
127 void merge_fixed_append(size_t ultra_fixed_offset)
128 {
131 }
132
134 {
135 auto [column_polynomials, hiding_op] = ultra_ops_table.construct_zk_columns();
136 this->hiding_op_for_eccvm = hiding_op;
137 this->has_hiding_op = true;
138
139 return column_polynomials;
140 }
141
146
147 // Construct column polynomials for the full aggregate ultra ops table
149 const bool include_zk_ops = true) const
150 {
151 return ultra_ops_table.construct_table_columns(include_zk_ops);
152 }
153
154 // Construct column polynomials for the aggregate table up to and including the tail subtable.
159
160 // Construct column polynomials for the most recently merged subtable
165
166 // Reconstruct the full table of eccvm ops in contiguous memory from the independent subtables
168
169 // Reconstruct the ZK-prefixed full table of ultra ops in contiguous memory from the independent subtables.
174
175 // Reconstruct the non-ZK full table of ultra ops in contiguous memory from the independent subtables.
180
181 // Excludes the optional ZK prefix; see UltraEccOpsTable::num_ultra_rows
183 size_t get_ultra_ops_count() const { return ultra_ops_table.num_ops(); } // actual operation count without padding
184 // Excludes the optional ZK prefix, same as get_ultra_ops_table_num_rows.
186
187 // Get the full table of ECCVM ops in contiguous memory; construct it if it has not been constructed already.
188 // The hiding op is always prepended at index 0.
189 std::vector<ECCVMOperation>& get_eccvm_ops()
190 {
191 if (eccvm_ops_reconstructed.empty()) {
193 // Prepend the hiding op at index 0 (required for ZK)
194 if (!has_hiding_op) {
195 throw_or_abort("Hiding op must be set before calling get_eccvm_ops()");
196 }
198 }
200 }
201
209
217
222
227 size_t get_num_rows() const { return eccvm_row_tracker.get_num_rows(); }
228
233
238 void set_eccvm_ops_for_fuzzing(std::vector<ECCVMOperation>& eccvm_ops_in)
239 {
240 eccvm_ops_reconstructed = eccvm_ops_in;
241 }
242
249 {
250 EccOpCode op_code{ .eq = true, .reset = true };
251 append_eccvm_op(ECCVMOperation{ .op_code = op_code, .base_point = Point::random_element() });
252 }
253
264
266 {
268 return Point(accumulator);
269 }
270
277 {
278 // Update the accumulator natively
279 accumulator += to_add;
280 EccOpCode op_code{ .add = true };
281 // Store the eccvm operation
282 append_eccvm_op(ECCVMOperation{ .op_code = op_code, .base_point = to_add });
283
284 // Construct and store the operation in the ultra op format
285 return construct_and_populate_ultra_ops(op_code, to_add);
286 }
287
293 UltraOp mul_accumulate(const Point& to_mul, const Fr& scalar)
294 {
295 BB_BENCH_NAME("ECCOpQueue::mul_accumulate");
296 // Defer the native accumulator update; the buffered muls are folded in as a batch when the
297 // accumulator is next read (see flush_deferred_muls).
298 deferred_mul_points.push_back(to_mul);
299 deferred_mul_scalars.push_back(scalar);
300 EccOpCode op_code{ .mul = true };
301
302 // Construct and store the operation in the ultra op format
303 UltraOp ultra_op = construct_and_populate_ultra_ops(op_code, to_mul, scalar);
304
305 // Store the eccvm operation
307 .op_code = op_code,
308 .base_point = to_mul,
309 .z1 = ultra_op.z_1,
310 .z2 = ultra_op.z_2,
311 .mul_scalar_full = scalar,
312 });
313
314 return ultra_op;
315 }
316
324 {
325 UltraOp no_op{};
326 ultra_ops_table.push(no_op);
327 return no_op;
328 }
329
338 {
339 UltraOp random_op{ .op_code = EccOpCode{ .is_random_op = true,
340 .random_value_1 = Fr::random_element(),
341 .random_value_2 = Fr::random_element() },
342 .x_lo = Fr::random_element(),
343 .x_hi = Fr::random_element(),
344 .y_lo = Fr::random_element(),
345 .y_hi = Fr::random_element(),
346 .z_1 = Fr::random_element(),
347 .z_2 = Fr::random_element(),
348 .return_is_infinity = false };
349 ultra_ops_table.push(random_op);
350 return random_op;
351 }
352
359 {
360 const Point expected = get_accumulator();
362 EccOpCode op_code{ .eq = true, .reset = true };
363 // Store eccvm operation
364 append_eccvm_op(ECCVMOperation{ .op_code = op_code, .base_point = expected });
365
366 // Construct and store the operation in the ultra op format
367 return construct_and_populate_ultra_ops(op_code, expected);
368 }
369
396 UltraOp append_hiding_op(const Fq& Px, const Fq& Py)
397 {
398 auto [ultra_op, eccvm_op] = UltraEccOpsTable::make_hiding_op_pair(Px, Py);
399
400 hiding_op_for_eccvm = eccvm_op;
401 has_hiding_op = true;
402 ultra_ops_table.push(ultra_op);
403
404 // Do NOT update the accumulator - the hiding op doesn't perform any actual EC computation
405 return ultra_op;
406 }
407
408 private:
415 static size_t get_append_offset(size_t current_subtable_size)
416 {
419 return OP_QUEUE_SIZE - current_subtable_size - reserved_op_slots - zk_op_slots;
420 }
421
422 // === Hiding Op State ===
423 // The hiding op exists in both the ECCVM and Ultra tables (same Px, Py values, opcode q_eq=q_reset=1) so the
424 // translation check holds. It is set by exactly one of two entry points, depending on the proving flow:
425 // - Chonk: UltraEccOpsTable::construct_zk_columns() builds the full ZK prefix (1 no-op + 3 random + 1 hiding)
426 // at the front of the reconstructed Ultra table; the hiding op lands at index 4.
427 // - Goblin AVM: append_hiding_op() pushes the Ultra side into the current subtable directly, with no surrounding
428 // prefix.
429 // In both cases the ECCVM side is stored here and prepended to the reconstructed ECCVM table at index 0 by
430 // get_eccvm_ops(), placing it at row 1 (lagrange_second) where the on-curve and eq constraints are gated off
431 // so that non-curve (x, y) values are accepted.
433 bool has_hiding_op = false;
434
452 UltraOp construct_and_populate_ultra_ops(EccOpCode op_code, const Point& point, const Fr& scalar = Fr::zero())
453 {
454 UltraOp ultra_op;
455 ultra_op.op_code = op_code;
456
457 // Decompose point coordinates (Fq) into hi-lo chunks (Fr)
458 const size_t CHUNK_SIZE = 2 * stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION;
459 uint256_t x_256(point.x);
460 uint256_t y_256(point.y);
461 ultra_op.return_is_infinity = point.is_point_at_infinity();
462 // if we have a point at infinity, set x/y to zero
463 // in the biggroup_goblin class we use `assert_equal` statements to validate
464 // the original in-circuit coordinate values are also zero
465 if (point.is_point_at_infinity()) {
466 x_256 = 0;
467 y_256 = 0;
468 }
469 ultra_op.x_lo = Fr(x_256.slice(0, CHUNK_SIZE));
470 ultra_op.x_hi = Fr(x_256.slice(CHUNK_SIZE, CHUNK_SIZE * 2));
471 ultra_op.y_lo = Fr(y_256.slice(0, CHUNK_SIZE));
472 ultra_op.y_hi = Fr(y_256.slice(CHUNK_SIZE, CHUNK_SIZE * 2));
473
474 // Split scalar into 128 bit endomorphism scalars
475 Fr z_1 = 0;
476 Fr z_2 = 0;
477 auto converted = scalar.from_montgomery_form();
478 uint256_t converted_u256(scalar);
479 // if our scalar is small, don't split.
480 if (converted_u256.get_msb() < 128) {
481 ultra_op.z_1 = scalar;
482 ultra_op.z_2 = 0;
483 } else {
484 Fr::split_into_endomorphism_scalars(converted, z_1, z_2);
485 ultra_op.z_1 = z_1.to_montgomery_form();
486 ultra_op.z_2 = z_2.to_montgomery_form();
487 }
488
489 ultra_ops_table.push(ultra_op);
490
491 return ultra_op;
492 }
493};
494
495} // 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_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()
void flush_deferred_muls()
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< Fr > deferred_mul_scalars
std::vector< Point > deferred_mul_points
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.
static size_t get_append_offset(size_t current_subtable_size)
Compute the fixed append offset for the final APPEND merge.
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
void reset_accumulator()
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.
static size_t get_append_offset_for_verifier()
Curve::Element accumulator
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()
static constexpr size_t compute_fixed_append_offset(size_t append_offset, bool include_zk_prefix=true)
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.
size_t get_append_offset_for_prover() const
Compute the fixed append offset for the final APPEND merge.
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
static constexpr size_t compute_fixed_append_offset(size_t append_offset, bool include_zk_prefix=true)
Shift size of the APPEND merge: the start row of the appended subtable's polynomial in the merged tab...
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
typename Group::element Element
Definition bn254.hpp:21
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)