Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
mega_circuit_builder.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Luke, Raju], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
11#include <array>
12#include <tuple>
13#include <unordered_map>
14#include <unordered_set>
15
16using namespace bb;
17using namespace bb::crypto;
18
19namespace bb {
20
25
32{
33 // Add the operation to the op queue
34 auto ultra_op = op_queue->add_accumulate(point);
35
36 // Add corresponding gates for the operation
37 ecc_op_tuple op_tuple = populate_ecc_op_wires(ultra_op);
38 return op_tuple;
39}
40
51template <typename FF>
53 const FF& scalar,
54 bool in_finalize)
55{
56 // Add the operation to the op queue
57 auto ultra_op = op_queue->mul_accumulate(point, scalar);
58
59 // Add corresponding gates for the operation
60 ecc_op_tuple op_tuple = populate_ecc_op_wires(ultra_op, in_finalize);
61 return op_tuple;
62}
63
71template <typename FF> ecc_op_tuple MegaCircuitBuilder_<FF>::queue_ecc_eq(bool in_finalize)
72{
73 // Add the operation to the op queue
74 auto ultra_op = op_queue->eq_and_reset();
75
76 // Add corresponding gates for the operation
77 ecc_op_tuple op_tuple = populate_ecc_op_wires(ultra_op, in_finalize);
78 op_tuple.return_is_infinity = ultra_op.return_is_infinity;
79 return op_tuple;
80}
81
89{
90 // Add the operation to the op queue
91 auto ultra_op = op_queue->no_op_ultra_only();
92
93 // Add corresponding gates for the operation
94 ecc_op_tuple op_tuple = populate_ecc_op_wires(ultra_op);
95 return op_tuple;
96}
97
110template <typename FF>
112{
113 ecc_op_tuple op_tuple;
114 op_tuple.op = get_ecc_op_idx(ultra_op.op_code);
115 op_tuple.x_lo = this->add_variable(ultra_op.x_lo);
116 op_tuple.x_hi = this->add_variable(ultra_op.x_hi);
117 op_tuple.y_lo = this->add_variable(ultra_op.y_lo);
118 op_tuple.y_hi = this->add_variable(ultra_op.y_hi);
119 op_tuple.z_1 = this->add_variable(ultra_op.z_1);
120 op_tuple.z_2 = this->add_variable(ultra_op.z_2);
121
122 // Set the indices for the op values for each of the two rows
123 uint32_t op_val_idx_1 = op_tuple.op; // genuine op code value
124 uint32_t op_val_idx_2 = this->zero_idx(); // second row value always set to 0
125 // If this is a random operation, the op values are randomized
126 if (ultra_op.op_code.is_random_op) {
127 op_val_idx_1 = this->add_variable(ultra_op.op_code.random_value_1);
128 op_val_idx_2 = this->add_variable(ultra_op.op_code.random_value_2);
129 }
130 // Populate the ecc_op block with TWO rows (matching Ultra format)
131 // Row 1: OP | x_lo | x_hi | y_lo
132 // Row 2: 0 | y_hi | z_1 | z_2
133 this->blocks.ecc_op.append_gate({ .wires = { op_val_idx_1, op_tuple.x_lo, op_tuple.x_hi, op_tuple.y_lo } });
134 this->blocks.ecc_op.append_gate({ .wires = { op_val_idx_2, op_tuple.y_hi, op_tuple.z_1, op_tuple.z_2 } });
135
136 if (in_finalize) {
137 update_used_witnesses(
138 { op_tuple.op, op_tuple.x_lo, op_tuple.x_hi, op_tuple.y_lo, op_tuple.y_hi, op_tuple.z_1, op_tuple.z_2 });
139 update_finalize_witnesses(
140 { op_tuple.op, op_tuple.x_lo, op_tuple.x_hi, op_tuple.y_lo, op_tuple.y_hi, op_tuple.z_1, op_tuple.z_2 });
141 }
142
143 return op_tuple;
144};
145
154{
155 // Add the operation to the op queue
156 auto ultra_op = op_queue->random_op_ultra_only();
157
158 // Add corresponding gates for the operation
159 (void)populate_ecc_op_wires(ultra_op);
160}
161
173template <typename FF>
175{
176 // Add the operation to the op queue (returns the UltraOp for gate creation)
177 auto ultra_op = op_queue->append_hiding_op(Px, Py);
178
179 // Add corresponding gates for the operation
180 populate_ecc_op_wires(ultra_op);
181}
182
184{
185 null_op_idx = this->zero_idx(); // constant 0 is is associated with the zero index
186 add_accum_op_idx = this->put_constant_variable(FF(EccOpCode{ .add = true }.value()));
187 mul_accum_op_idx = this->put_constant_variable(FF(EccOpCode{ .mul = true }.value()));
188 equality_op_idx = this->put_constant_variable(FF(EccOpCode{ .eq = true, .reset = true }.value()));
189}
190
199template <typename FF>
200uint32_t MegaCircuitBuilder_<FF>::read_bus_vector(BusId bus_idx, const uint32_t& read_idx_witness_idx)
201{
202 auto& bus_vector = databus[static_cast<size_t>(bus_idx)];
203 // Get the raw index into the databus column
204 const uint32_t read_idx = static_cast<uint32_t>(uint256_t(this->get_variable(read_idx_witness_idx)));
205
206 BB_ASSERT_LT(read_idx, bus_vector.size()); // Ensure that the read index is valid
207
208 // Create a variable corresponding to the result of the read. Note that we do not in general connect reads from
209 // databus via copy constraints (i.e. we create a unique variable for the result of each read)
210 FF value = this->get_variable(bus_vector[read_idx]);
211 uint32_t value_witness_idx = this->add_variable(value);
212
213 create_databus_read_gate({ read_idx_witness_idx, value_witness_idx }, bus_idx);
214 bus_vector.increment_read_count(read_idx);
215
216 return value_witness_idx;
217}
218
219template <typename FF> void MegaCircuitBuilder_<FF>::create_databus_init_read_gate(BusId bus_idx, size_t slot_idx)
220{
221 auto& bus_vector = databus[static_cast<size_t>(bus_idx)];
222 BB_ASSERT_LT(slot_idx, bus_vector.size());
223
224 const uint32_t value_witness_idx = bus_vector[slot_idx];
225 const uint32_t index_witness_idx = this->put_constant_variable(FF(static_cast<uint64_t>(slot_idx)));
226
227 create_databus_read_gate({ index_witness_idx, value_witness_idx }, bus_idx);
228 bus_vector.increment_read_count(slot_idx);
229}
230
237template <typename FF>
239{
240 auto& block = this->blocks.busread;
241 GateRowT row{};
242 row.wires = { in.value, in.index, this->zero_idx(), this->zero_idx() };
243 // Bus column k (0 <= k < NUM_BUS_COLUMNS) is selected by one of these selectors. The order here
244 // must match BusData<bus_idx>::selector in databus_lookup_relation.hpp and the bus selector
245 // order in flavor-codegen mega.ts / mega_kernel.ts.
246 const std::array<FF*, NUM_BUS_COLUMNS> databus_selectors{ &row.q_1, &row.q_2, &row.q_3, &row.q_4,
247 &row.q_5, &row.q_c, &row.q_m };
248 const size_t idx = static_cast<size_t>(bus_idx);
249 BB_ASSERT_LT(idx, databus_selectors.size());
250 *databus_selectors[idx] = 1;
251 row.gate_kind = GateKind::BusRead;
252 row.gate_value = 1;
253 block.append_gate(row);
254 this->increment_num_gates();
255}
256
262template <typename FF>
264{
265 auto& block = this->blocks.poseidon2;
266 block.append_gate({ .wires = { in.a, in.b, in.c, in.d },
271 .gate_kind = GateKind::Poseidon2Ext,
272 .gate_value = 1 });
273 this->increment_num_gates();
274}
275
280template <typename FF>
282{
283 auto& block = this->blocks.poseidon2;
284 block.append_gate(
285 { .wires = { in.a, in.b, in.c, in.d }, .gate_kind = GateKind::Poseidon2ExtInitial, .gate_value = 1 });
286 this->increment_num_gates();
287}
288
298template <typename FF>
300{
301 auto& block = this->blocks.poseidon2;
303 {
304 auto& block_for_row = block;
305 GateRowT row{};
306 row.wires = { in.a, in.b, in.c, in.d };
307 row.q_1 = rc[in.round_idx_start + 0][0];
308 row.q_2 = rc[in.round_idx_start + 1][0];
309 row.q_3 = rc[in.round_idx_start + 2][0];
310 row.q_4 = rc[in.round_idx_start + 3][0];
311 if (in.is_terminal) {
313 row.gate_value = 1;
314 } else {
315 row.q_m = rc[in.next_pair_start + 0][0];
316 row.q_c = rc[in.next_pair_start + 1][0];
317 row.q_5 = rc[in.next_pair_start + 2][0];
318 row.gate_kind = GateKind::Poseidon2QuadInt;
319 row.gate_value = 1;
320 }
321 block_for_row.append_gate(row);
322 }
323 this->increment_num_gates();
324}
325
336template <typename FF>
338{
339 auto& block = this->blocks.poseidon2;
341 block.append_gate({ .wires = { in.a, in.b, in.c, in.d },
342 .q_1 = rc[in.round_idx_start + 0][0],
343 .q_2 = rc[in.round_idx_start + 1][0],
344 .q_3 = rc[in.round_idx_start + 2][0],
346 .gate_value = 1 });
347 this->increment_num_gates();
348}
349
350template class MegaCircuitBuilder_<bb::fr>;
351} // namespace bb
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
void create_poseidon2_external_gate(const poseidon2_external_gate_< FF > &in)
Poseidon2 external-round gate. Mega routes it into the shared poseidon2 block (Ultra instead uses a d...
void queue_ecc_random_op()
Mechanism for populating two rows with randomness. This "operation" doesn't return a tuple representi...
ecc_op_tuple queue_ecc_add_accum(const g1::affine_element &point)
Add simple point addition operation to the op queue and add corresponding gates.
ecc_op_tuple queue_ecc_mul_accum(const g1::affine_element &point, const FF &scalar, bool in_finalize=false)
Add point mul-then-accumulate operation to the op queue and add corresponding gates.
void create_poseidon2_quad_internal_gate(const poseidon2_quad_internal_gate_< FF > &in)
Poseidon2 K=4 compressed internal-round gate: processes FOUR consecutive internal rounds per row.
ecc_op_tuple queue_ecc_eq(bool in_finalize=true)
Add point equality operation to the op queue based on the value of the internal accumulator and add c...
void create_databus_read_gate(const databus_lookup_gate_< FF > &in, BusId bus_idx)
Create a databus lookup/read gate.
void create_databus_init_read_gate(BusId bus_idx, size_t slot_idx)
Emit a busread gate at slot slot_idx whose value wire is the bus_vector entry at that slot.
ecc_op_tuple queue_ecc_no_op()
Add a no-op to the op queue and populate two zero rows in the ecc_op block.
void queue_ecc_hiding_op(const curve::BN254::BaseField &Px, const curve::BN254::BaseField &Py)
Add a hiding op with random (possibly non-curve) Px, Py values to the op queue and circuit.
void create_poseidon2_transition_entry_gate(const poseidon2_transition_entry_gate_< FF > &in)
Poseidon2 transition-entry gate: standard → K=4 compressed encoding boundary.
uint32_t read_bus_vector(BusId bus_idx, const uint32_t &read_idx_witness_idx)
Read from a databus column.
void create_poseidon2_initial_external_gate(const poseidon2_initial_external_gate_< FF > &in)
Poseidon2 initial linear layer gate, activates the q_poseidon2_external_initial selector and relation...
ecc_op_tuple populate_ecc_op_wires(const UltraOp &ultra_op, bool in_finalize=false)
Add goblin ecc op gates for a single operation.
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
BusId
Definition databus.hpp:75
@ Poseidon2QuadIntTerminal
@ Poseidon2TransitionEntry
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Defines the opcodes for ECC operations used in both the Ultra and ECCVM formats. There are three opco...
One gate: its wire indices, the non-gate selectors present on every block (see NON_GATE_SELECTORS),...
std::array< uint32_t, NUM_WIRES > wires
EccOpCode op_code
static constexpr std::array< std::array< FF, t >, rounds_f+rounds_p > round_constants
uint32_t d
uint32_t a
uint32_t c
uint32_t b
size_t round_idx_start