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 <unordered_map>
12#include <unordered_set>
13
14using namespace bb;
15using namespace bb::crypto;
16
17namespace bb {
18
23
30{
31 // Add the operation to the op queue
32 auto ultra_op = op_queue->add_accumulate(point);
33
34 // Add corresponding gates for the operation
35 ecc_op_tuple op_tuple = populate_ecc_op_wires(ultra_op);
36 return op_tuple;
37}
38
49template <typename FF>
51 const FF& scalar,
52 bool in_finalize)
53{
54 // Add the operation to the op queue
55 auto ultra_op = op_queue->mul_accumulate(point, scalar);
56
57 // Add corresponding gates for the operation
58 ecc_op_tuple op_tuple = populate_ecc_op_wires(ultra_op, in_finalize);
59 return op_tuple;
60}
61
69template <typename FF> ecc_op_tuple MegaCircuitBuilder_<FF>::queue_ecc_eq(bool in_finalize)
70{
71 // Add the operation to the op queue
72 auto ultra_op = op_queue->eq_and_reset();
73
74 // Add corresponding gates for the operation
75 ecc_op_tuple op_tuple = populate_ecc_op_wires(ultra_op, in_finalize);
76 op_tuple.return_is_infinity = ultra_op.return_is_infinity;
77 return op_tuple;
78}
79
87{
88 // Add the operation to the op queue
89 auto ultra_op = op_queue->no_op_ultra_only();
90
91 // Add corresponding gates for the operation
92 ecc_op_tuple op_tuple = populate_ecc_op_wires(ultra_op);
93 return op_tuple;
94}
95
108template <typename FF>
110{
111 ecc_op_tuple op_tuple;
112 op_tuple.op = get_ecc_op_idx(ultra_op.op_code);
113 op_tuple.x_lo = this->add_variable(ultra_op.x_lo);
114 op_tuple.x_hi = this->add_variable(ultra_op.x_hi);
115 op_tuple.y_lo = this->add_variable(ultra_op.y_lo);
116 op_tuple.y_hi = this->add_variable(ultra_op.y_hi);
117 op_tuple.z_1 = this->add_variable(ultra_op.z_1);
118 op_tuple.z_2 = this->add_variable(ultra_op.z_2);
119
120 // Set the indices for the op values for each of the two rows
121 uint32_t op_val_idx_1 = op_tuple.op; // genuine op code value
122 uint32_t op_val_idx_2 = this->zero_idx(); // second row value always set to 0
123 // If this is a random operation, the op values are randomized
124 if (ultra_op.op_code.is_random_op) {
125 op_val_idx_1 = this->add_variable(ultra_op.op_code.random_value_1);
126 op_val_idx_2 = this->add_variable(ultra_op.op_code.random_value_2);
127 }
128 // Populate the ecc_op block with TWO rows (matching Ultra format)
129 // Row 1: OP | x_lo | x_hi | y_lo
130 // Row 2: 0 | y_hi | z_1 | z_2
131 this->blocks.ecc_op.populate_wires(op_val_idx_1, op_tuple.x_lo, op_tuple.x_hi, op_tuple.y_lo);
132 for (auto& selector : this->blocks.ecc_op.get_selectors()) {
133 selector.emplace_back(0);
134 }
135 this->blocks.ecc_op.populate_wires(op_val_idx_2, op_tuple.y_hi, op_tuple.z_1, op_tuple.z_2);
136 for (auto& selector : this->blocks.ecc_op.get_selectors()) {
137 selector.emplace_back(0);
138 }
139
140 if (in_finalize) {
141 update_used_witnesses(
142 { 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 });
143 update_finalize_witnesses(
144 { 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 });
145 }
146
147 return op_tuple;
148};
149
158{
159 // Add the operation to the op queue
160 auto ultra_op = op_queue->random_op_ultra_only();
161
162 // Add corresponding gates for the operation
163 (void)populate_ecc_op_wires(ultra_op);
164}
165
177template <typename FF>
179{
180 // Add the operation to the op queue (returns the UltraOp for gate creation)
181 auto ultra_op = op_queue->append_hiding_op(Px, Py);
182
183 // Add corresponding gates for the operation
184 populate_ecc_op_wires(ultra_op);
185}
186
188{
189 null_op_idx = this->zero_idx(); // constant 0 is is associated with the zero index
190 add_accum_op_idx = this->put_constant_variable(FF(EccOpCode{ .add = true }.value()));
191 mul_accum_op_idx = this->put_constant_variable(FF(EccOpCode{ .mul = true }.value()));
192 equality_op_idx = this->put_constant_variable(FF(EccOpCode{ .eq = true, .reset = true }.value()));
193}
194
203template <typename FF>
204uint32_t MegaCircuitBuilder_<FF>::read_bus_vector(BusId bus_idx, const uint32_t& read_idx_witness_idx)
205{
206 auto& bus_vector = databus[static_cast<size_t>(bus_idx)];
207 // Get the raw index into the databus column
208 const uint32_t read_idx = static_cast<uint32_t>(uint256_t(this->get_variable(read_idx_witness_idx)));
209
210 BB_ASSERT_LT(read_idx, bus_vector.size()); // Ensure that the read index is valid
211
212 // Create a variable corresponding to the result of the read. Note that we do not in general connect reads from
213 // databus via copy constraints (i.e. we create a unique variable for the result of each read)
214 FF value = this->get_variable(bus_vector[read_idx]);
215 uint32_t value_witness_idx = this->add_variable(value);
216
217 create_databus_read_gate({ read_idx_witness_idx, value_witness_idx }, bus_idx);
218 bus_vector.increment_read_count(read_idx);
219
220 return value_witness_idx;
221}
222
229template <typename FF>
231{
232 auto& block = this->blocks.busread;
233 block.populate_wires(in.value, in.index, this->zero_idx(), this->zero_idx());
234 apply_databus_selectors(bus_idx);
235
236 this->check_selector_length_consistency();
237 this->increment_num_gates();
238}
239
240template <typename FF> void MegaCircuitBuilder_<FF>::apply_databus_selectors(const BusId bus_idx)
241{
242 auto& block = this->blocks.busread;
243 // Bus column k is selected by q_{k+1}; all other wire-linear selectors stay zero on this row.
244 const size_t idx = static_cast<size_t>(bus_idx);
245 block.q_1().emplace_back(idx == 0 ? 1 : 0);
246 block.q_2().emplace_back(idx == 1 ? 1 : 0);
247 block.q_3().emplace_back(idx == 2 ? 1 : 0);
248 block.q_4().emplace_back(idx == 3 ? 1 : 0);
249 block.q_5().emplace_back(0);
250 block.q_m().emplace_back(idx == 4 ? 1 : 0);
251 block.q_c().emplace_back(0);
252 block.set_gate_selector(1);
253}
254
259template <typename FF>
261{
262 auto& block = this->blocks.poseidon2_external;
263 block.populate_wires(in.a, in.b, in.c, in.d);
264 block.q_m().emplace_back(0);
265 block.q_1().emplace_back(0);
266 block.q_2().emplace_back(0);
267 block.q_3().emplace_back(0);
268 block.q_c().emplace_back(0);
269 block.q_4().emplace_back(0);
270 block.q_5().emplace_back(0);
271 block.set_gate_selector(GateKind::Poseidon2ExtInitial, 1);
272 this->check_selector_length_consistency();
273 this->increment_num_gates();
274}
275
285template <typename FF>
287{
288 auto& block = this->blocks.poseidon2_quad_internal;
289 block.populate_wires(in.a, in.b, in.c, in.d);
291 block.q_1().emplace_back(rc[in.round_idx_start + 0][0]);
292 block.q_2().emplace_back(rc[in.round_idx_start + 1][0]);
293 block.q_3().emplace_back(rc[in.round_idx_start + 2][0]);
294 block.q_4().emplace_back(rc[in.round_idx_start + 3][0]);
295 if (in.is_terminal) {
296 block.q_m().emplace_back(0);
297 block.q_c().emplace_back(0);
298 block.q_5().emplace_back(0);
299 block.set_gate_selector(GateKind::Poseidon2QuadIntTerminal, 1);
300 } else {
301 block.q_m().emplace_back(rc[in.next_pair_start + 0][0]);
302 block.q_c().emplace_back(rc[in.next_pair_start + 1][0]);
303 block.q_5().emplace_back(rc[in.next_pair_start + 2][0]);
304 block.set_gate_selector(GateKind::Poseidon2QuadInt, 1);
305 }
306 this->check_selector_length_consistency();
307 this->increment_num_gates();
308}
309
320template <typename FF>
322{
323 auto& block = this->blocks.poseidon2_quad_internal;
324 block.populate_wires(in.a, in.b, in.c, in.d);
326 block.q_m().emplace_back(0);
327 block.q_1().emplace_back(rc[in.round_idx_start + 0][0]);
328 block.q_2().emplace_back(rc[in.round_idx_start + 1][0]);
329 block.q_3().emplace_back(rc[in.round_idx_start + 2][0]);
330 block.q_4().emplace_back(0);
331 block.q_5().emplace_back(0);
332 block.q_c().emplace_back(0);
333 block.set_gate_selector(GateKind::Poseidon2TransitionEntry, 1);
334 this->check_selector_length_consistency();
335 this->increment_num_gates();
336}
337
338template class MegaCircuitBuilder_<bb::fr>;
339} // namespace bb
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
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.
void apply_databus_selectors(BusId bus_idx)
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.
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
Defines the opcodes for ECC operations used in both the Ultra and ECCVM formats. There are three opco...
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