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: 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
11#include <unordered_map>
12#include <unordered_set>
13
14using namespace bb;
15using namespace bb::crypto;
16
17namespace bb {
18
19template <typename FF> void MegaCircuitBuilder_<FF>::finalize_circuit(const bool ensure_nonzero)
20{
21 if (ensure_nonzero && !this->circuit_finalized) {
22 // do the mega part of ensuring all polynomials are nonzero; ultra part will be done inside of
23 // Ultra::finalize_circuit
24 add_mega_gates_to_ensure_all_polys_are_non_zero();
25 }
26 // All of the gates involved in finalization are part of the Ultra arithmetization
28}
29
37// TODO(https://github.com/AztecProtocol/barretenberg/issues/1066): This function adds valid (but arbitrary) gates to
38// ensure that the circuit which includes them will not result in any zero-polynomials. This method is designed to be
39// used in conjunction with the corresponding method on the Ultra builder. It handles databus and ecc-op related
40// polynomials.
42{
43 // Add a single default value to all databus columns. Note: This value must be equal across all columns in order for
44 // inter-circuit databus commitment checks to pass in IVC settings.
45
46 // Create an arbitrary calldata read gate
47 add_public_calldata(this->add_variable(BusVector::DEFAULT_VALUE)); // add one entry in calldata
48 auto raw_read_idx = static_cast<uint32_t>(get_calldata().size()) - 1; // read data that was just added
49 auto read_idx = this->add_variable(raw_read_idx);
50 update_finalize_witnesses({ read_idx, read_calldata(read_idx) });
51
52 // Create an arbitrary secondary_calldata read gate
53 add_public_secondary_calldata(this->add_variable(BusVector::DEFAULT_VALUE)); // add one entry in secondary_calldata
54 raw_read_idx = static_cast<uint32_t>(get_secondary_calldata().size()) - 1; // read data that was just added
55 read_idx = this->add_variable(raw_read_idx);
56 update_finalize_witnesses({ read_idx, read_secondary_calldata(read_idx) });
57
58 // Create an arbitrary return data read gate
59 add_public_return_data(this->add_variable(BusVector::DEFAULT_VALUE)); // add one entry in return data
60 raw_read_idx = static_cast<uint32_t>(get_return_data().size()) - 1; // read data that was just added
61 read_idx = this->add_variable(raw_read_idx);
62 update_finalize_witnesses({ read_idx, read_return_data(read_idx) });
63
64 if (op_queue->get_current_subtable_size() == 0) {
65 // Add a mul dummy op in the subtable to avoid column polynomial being zero (it has to be a mul rather than an
66 // add to ensure all 4 column polynomials contain some data)
67 this->queue_ecc_mul_accum(bb::g1::affine_element::one(), 2, /*in_finalize=*/true);
68 this->queue_ecc_eq(/*in_finalize=*/true);
69 }
70}
71
79// TODO(https://github.com/AztecProtocol/barretenberg/issues/1066): This function adds valid (but arbitrary) gates to
80// ensure that the circuit which includes them will not result in any zero-polynomials. It also ensures that the first
81// coefficient of the wire polynomials is zero, which is required for them to be shiftable.
83{
84 // Most polynomials are handled via the conventional Ultra method
86 add_mega_gates_to_ensure_all_polys_are_non_zero();
87}
88
95{
96 // Add the operation to the op queue
97 auto ultra_op = op_queue->add_accumulate(point);
98
99 // Add corresponding gates for the operation
100 ecc_op_tuple op_tuple = populate_ecc_op_wires(ultra_op);
101 return op_tuple;
102}
103
114template <typename FF>
116 const FF& scalar,
117 bool in_finalize)
118{
119 // Add the operation to the op queue
120 auto ultra_op = op_queue->mul_accumulate(point, scalar);
121
122 // Add corresponding gates for the operation
123 ecc_op_tuple op_tuple = populate_ecc_op_wires(ultra_op, in_finalize);
124 return op_tuple;
125}
126
134template <typename FF> ecc_op_tuple MegaCircuitBuilder_<FF>::queue_ecc_eq(bool in_finalize)
135{
136 // Add the operation to the op queue
137 auto ultra_op = op_queue->eq_and_reset();
138
139 // Add corresponding gates for the operation
140 ecc_op_tuple op_tuple = populate_ecc_op_wires(ultra_op, in_finalize);
141 op_tuple.return_is_infinity = ultra_op.return_is_infinity;
142 return op_tuple;
143}
144
151{
152 // Add the operation to the op queue
153 auto ultra_op = op_queue->no_op_ultra_only();
154
155 // Add corresponding gates for the operation
156 ecc_op_tuple op_tuple = populate_ecc_op_wires(ultra_op);
157 return op_tuple;
158}
159
168template <typename FF>
170{
171 ecc_op_tuple op_tuple;
172 op_tuple.op = get_ecc_op_idx(ultra_op.op_code);
173 op_tuple.x_lo = this->add_variable(ultra_op.x_lo);
174 op_tuple.x_hi = this->add_variable(ultra_op.x_hi);
175 op_tuple.y_lo = this->add_variable(ultra_op.y_lo);
176 op_tuple.y_hi = this->add_variable(ultra_op.y_hi);
177 op_tuple.z_1 = this->add_variable(ultra_op.z_1);
178 op_tuple.z_2 = this->add_variable(ultra_op.z_2);
179
180 // Set the indices for the op values for each of the two rows
181 uint32_t op_val_idx_1 = op_tuple.op; // genuine op code value
182 uint32_t op_val_idx_2 = this->zero_idx(); // second row value always set to 0
183 // If this is a random operation, the op values are randomized
184 if (ultra_op.op_code.is_random_op) {
185 op_val_idx_1 = this->add_variable(ultra_op.op_code.random_value_1);
186 op_val_idx_2 = this->add_variable(ultra_op.op_code.random_value_2);
187 }
188
189 this->blocks.ecc_op.populate_wires(op_val_idx_1, op_tuple.x_lo, op_tuple.x_hi, op_tuple.y_lo);
190 for (auto& selector : this->blocks.ecc_op.get_selectors()) {
191 selector.emplace_back(0);
192 }
193
194 this->blocks.ecc_op.populate_wires(op_val_idx_2, op_tuple.y_hi, op_tuple.z_1, op_tuple.z_2);
195 for (auto& selector : this->blocks.ecc_op.get_selectors()) {
196 selector.emplace_back(0);
197 }
198
199 if (in_finalize) {
200 update_used_witnesses(
201 { 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 });
202 update_finalize_witnesses(
203 { 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 });
204 }
205
206 return op_tuple;
207};
208
217{
218 // Add the operation to the op queue
219 auto ultra_op = op_queue->random_op_ultra_only();
220
221 // Add corresponding gates for the operation
222 (void)populate_ecc_op_wires(ultra_op);
223}
224
226{
227 null_op_idx = this->zero_idx(); // constant 0 is is associated with the zero index
228 add_accum_op_idx = this->put_constant_variable(FF(EccOpCode{ .add = true }.value()));
229 mul_accum_op_idx = this->put_constant_variable(FF(EccOpCode{ .mul = true }.value()));
230 equality_op_idx = this->put_constant_variable(FF(EccOpCode{ .eq = true, .reset = true }.value()));
231}
232
241template <typename FF>
242uint32_t MegaCircuitBuilder_<FF>::read_bus_vector(BusId bus_idx, const uint32_t& read_idx_witness_idx)
243{
244 auto& bus_vector = databus[static_cast<size_t>(bus_idx)];
245 // Get the raw index into the databus column
246 const uint32_t read_idx = static_cast<uint32_t>(uint256_t(this->get_variable(read_idx_witness_idx)));
247
248 BB_ASSERT_LT(read_idx, bus_vector.size()); // Ensure that the read index is valid
249
250 // Create a variable corresponding to the result of the read. Note that we do not in general connect reads from
251 // databus via copy constraints (i.e. we create a unique variable for the result of each read)
252 FF value = this->get_variable(bus_vector[read_idx]);
253 uint32_t value_witness_idx = this->add_variable(value);
254
255 create_databus_read_gate({ read_idx_witness_idx, value_witness_idx }, bus_idx);
256 bus_vector.increment_read_count(read_idx);
257
258 return value_witness_idx;
259}
260
267template <typename FF>
269{
270 auto& block = this->blocks.busread;
271 block.populate_wires(in.value, in.index, this->zero_idx(), this->zero_idx());
272 apply_databus_selectors(bus_idx);
273
274 this->check_selector_length_consistency();
275 ++this->num_gates;
276}
277
278template <typename FF> void MegaCircuitBuilder_<FF>::apply_databus_selectors(const BusId bus_idx)
279{
280 auto& block = this->blocks.busread;
281 switch (bus_idx) {
282 case BusId::CALLDATA: {
283 block.q_1().emplace_back(1);
284 block.q_2().emplace_back(0);
285 block.q_3().emplace_back(0);
286 break;
287 }
289 block.q_1().emplace_back(0);
290 block.q_2().emplace_back(1);
291 block.q_3().emplace_back(0);
292 break;
293 }
294 case BusId::RETURNDATA: {
295 block.q_1().emplace_back(0);
296 block.q_2().emplace_back(0);
297 block.q_3().emplace_back(1);
298 break;
299 }
300 }
301 block.q_4().emplace_back(0);
302 block.q_m().emplace_back(0);
303 block.q_c().emplace_back(0);
304 block.set_gate_selector(1);
305}
306
307template class MegaCircuitBuilder_<bb::fr>;
308} // namespace bb
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:148
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 apply_databus_selectors(BusId bus_idx)
void add_mega_gates_to_ensure_all_polys_are_non_zero()
Ensure all polynomials have at least one non-zero coefficient to avoid commiting to the zero-polynomi...
void add_ultra_and_mega_gates_to_ensure_all_polys_are_non_zero()
Ensure all polynomials have at least one non-zero coefficient to avoid commiting to the zero-polynomi...
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()
Logic for a no-op operation.
void finalize_circuit(const bool ensure_nonzero)
uint32_t read_bus_vector(BusId bus_idx, const uint32_t &read_idx_witness_idx)
Read from a databus column.
ecc_op_tuple populate_ecc_op_wires(const UltraOp &ultra_op, bool in_finalize=false)
Add goblin ecc op gates for a single operation.
void add_gates_to_ensure_all_polys_are_non_zero()
Ensure all polynomials have at least one non-zero coefficient to avoid commiting to the zero-polynomi...
void finalize_circuit(const bool ensure_nonzero)
static constexpr element one
Definition group.hpp:46
Entry point for Barretenberg command-line interface.
typename Flavor::FF FF
BusId
Definition databus.hpp:77
@ SECONDARY_CALLDATA
static constexpr bb::fr DEFAULT_VALUE
Definition databus.hpp:29
Defines the opcodes for ECC operations used in both the Ultra and ECCVM formats. There are three opco...
EccOpCode op_code