Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
decider_proving_key.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
13
14namespace bb {
15
22template <IsUltraOrMegaHonk Flavor> size_t DeciderProvingKey_<Flavor>::compute_dyadic_size(Circuit& circuit)
23{
24 // for the lookup argument the circuit size must be at least as large as the sum of all tables used
25 const size_t min_size_due_to_lookups = circuit.get_tables_size();
26
27 // minimum size of execution trace due to everything else
28 size_t min_size_of_execution_trace = circuit.blocks.get_total_content_size();
29
30 // The number of gates is the maximum required by the lookup argument or everything else, plus an optional zero row
31 // to allow for shifts.
32 size_t total_num_gates =
33 NUM_DISABLED_ROWS_IN_SUMCHECK + num_zero_rows + std::max(min_size_due_to_lookups, min_size_of_execution_trace);
34
35 // Next power of 2 (dyadic circuit size)
36 return circuit.get_circuit_subgroup_size(total_num_gates);
37}
38
39template <IsUltraOrMegaHonk Flavor> void DeciderProvingKey_<Flavor>::allocate_wires()
40{
41 PROFILE_THIS_NAME("allocate_wires");
42
43 for (auto& wire : polynomials.get_wires()) {
44 wire = Polynomial::shiftable(dyadic_size());
45 }
46}
47
49{
50 PROFILE_THIS_NAME("allocate_permutation_argument_polynomials");
51
52 for (auto& sigma : polynomials.get_sigmas()) {
53 sigma = Polynomial(dyadic_size());
54 }
55 for (auto& id : polynomials.get_ids()) {
56 id = Polynomial(dyadic_size());
57 }
58 polynomials.z_perm = Polynomial::shiftable(dyadic_size());
59}
60
61template <IsUltraOrMegaHonk Flavor> void DeciderProvingKey_<Flavor>::allocate_lagrange_polynomials()
62{
63 PROFILE_THIS_NAME("allocate_lagrange_polynomials");
64
65 // First and last lagrange polynomials (in the full circuit size)
66 polynomials.lagrange_first = Polynomial(
67 /* size=*/1, /*virtual size=*/dyadic_size(), /*start_index=*/0);
68
69 // Even though lagrange_last has a single non-zero element, we cannot set its size to 0 as different
70 // keys being folded might have lagrange_last set at different indexes and folding does not work
71 // correctly unless the polynomial is allocated in the correct range to accomodate this
72 polynomials.lagrange_last = Polynomial(
73 /* size=*/dyadic_size(), /*virtual size=*/dyadic_size(), /*start_index=*/0);
74}
75
76template <IsUltraOrMegaHonk Flavor> void DeciderProvingKey_<Flavor>::allocate_selectors(const Circuit& circuit)
77{
78 PROFILE_THIS_NAME("allocate_selectors");
79
80 // Define gate selectors over the block they are isolated to
81 for (auto [selector, block] : zip_view(polynomials.get_gate_selectors(), circuit.blocks.get_gate_blocks())) {
82
83 // TODO(https://github.com/AztecProtocol/barretenberg/issues/914): q_arith is currently used
84 // in memory block.
85 if (&block == &circuit.blocks.arithmetic) {
86 size_t arith_size = circuit.blocks.memory.trace_offset() - circuit.blocks.arithmetic.trace_offset() +
87 circuit.blocks.memory.get_fixed_size(is_structured);
88 selector = Polynomial(arith_size, dyadic_size(), circuit.blocks.arithmetic.trace_offset());
89 } else {
90 selector = Polynomial(block.get_fixed_size(is_structured), dyadic_size(), block.trace_offset());
91 }
92 }
93
94 // Set the other non-gate selector polynomials (e.g. q_l, q_r, q_m etc.) to full size
95 for (auto& selector : polynomials.get_non_gate_selectors()) {
96 selector = Polynomial(dyadic_size());
97 }
98}
99
100template <IsUltraOrMegaHonk Flavor>
102{
103 PROFILE_THIS_NAME("allocate_table_lookup_and_lookup_read_polynomials");
104
105 size_t table_offset = circuit.blocks.lookup.trace_offset();
106 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1193): can potentially improve memory footprint
107 const size_t max_tables_size = dyadic_size() - table_offset;
108 BB_ASSERT_GT(dyadic_size(), max_tables_size);
109
110 // Allocate the polynomials containing the actual table data
111 if constexpr (IsUltraOrMegaHonk<Flavor>) {
112 for (auto& poly : polynomials.get_tables()) {
113 poly = Polynomial(max_tables_size, dyadic_size(), table_offset);
114 }
115 }
116
117 // Allocate the read counts and tags polynomials
118 polynomials.lookup_read_counts = Polynomial(max_tables_size, dyadic_size(), table_offset);
119 polynomials.lookup_read_tags = Polynomial(max_tables_size, dyadic_size(), table_offset);
120
121 const size_t lookup_block_end =
122 static_cast<size_t>(circuit.blocks.lookup.trace_offset() + circuit.blocks.lookup.get_fixed_size(is_structured));
123 const auto tables_end = circuit.blocks.lookup.trace_offset() + max_tables_size;
124
125 // Allocate the lookup_inverses polynomial
126
127 const size_t lookup_inverses_start = table_offset;
128 const size_t lookup_inverses_end = std::max(lookup_block_end, tables_end);
129
130 polynomials.lookup_inverses =
131 Polynomial(lookup_inverses_end - lookup_inverses_start, dyadic_size(), lookup_inverses_start);
132}
133
134template <IsUltraOrMegaHonk Flavor>
136 requires IsMegaFlavor<Flavor>
137{
138 PROFILE_THIS_NAME("allocate_ecc_op_polynomials");
139
140 // Allocate the ecc op wires and selector
141 const size_t ecc_op_block_size = circuit.blocks.ecc_op.get_fixed_size(is_structured);
142 for (auto& wire : polynomials.get_ecc_op_wires()) {
143 wire = Polynomial(ecc_op_block_size, dyadic_size());
144 }
145 polynomials.lagrange_ecc_op = Polynomial(ecc_op_block_size, dyadic_size());
146}
147
148template <IsUltraOrMegaHonk Flavor>
150 requires HasDataBus<Flavor>
151{
152 PROFILE_THIS_NAME("allocate_databus_and_lookup_inverse_polynomials");
153 polynomials.calldata = Polynomial(MAX_DATABUS_SIZE, dyadic_size());
154 polynomials.calldata_read_counts = Polynomial(MAX_DATABUS_SIZE, dyadic_size());
155 polynomials.calldata_read_tags = Polynomial(MAX_DATABUS_SIZE, dyadic_size());
156 polynomials.secondary_calldata = Polynomial(MAX_DATABUS_SIZE, dyadic_size());
157 polynomials.secondary_calldata_read_counts = Polynomial(MAX_DATABUS_SIZE, dyadic_size());
158 polynomials.secondary_calldata_read_tags = Polynomial(MAX_DATABUS_SIZE, dyadic_size());
159 polynomials.return_data = Polynomial(MAX_DATABUS_SIZE, dyadic_size());
160 polynomials.return_data_read_counts = Polynomial(MAX_DATABUS_SIZE, dyadic_size());
161 polynomials.return_data_read_tags = Polynomial(MAX_DATABUS_SIZE, dyadic_size());
162
163 // Allocate log derivative lookup argument inverse polynomials
164 const size_t q_busread_end =
165 circuit.blocks.busread.trace_offset() + circuit.blocks.busread.get_fixed_size(is_structured);
166 const size_t calldata_size = circuit.get_calldata().size();
167 const size_t secondary_calldata_size = circuit.get_secondary_calldata().size();
168 const size_t return_data_size = circuit.get_return_data().size();
169
170 polynomials.databus_id = Polynomial(
171 std::max({ calldata_size, secondary_calldata_size, return_data_size, q_busread_end }), dyadic_size());
172
173 polynomials.calldata_inverses = Polynomial(std::max(calldata_size, q_busread_end), dyadic_size());
174 polynomials.secondary_calldata_inverses =
175 Polynomial(std::max(secondary_calldata_size, q_busread_end), dyadic_size());
176 polynomials.return_data_inverses = Polynomial(std::max(return_data_size, q_busread_end), dyadic_size());
177}
178
186template <IsUltraOrMegaHonk Flavor>
188 requires HasDataBus<Flavor>
189{
190 auto& calldata_poly = polynomials.calldata;
191 auto& calldata_read_counts = polynomials.calldata_read_counts;
192 auto& calldata_read_tags = polynomials.calldata_read_tags;
193 auto& secondary_calldata_poly = polynomials.secondary_calldata;
194 auto& secondary_calldata_read_counts = polynomials.secondary_calldata_read_counts;
195 auto& secondary_calldata_read_tags = polynomials.secondary_calldata_read_tags;
196 auto& return_data_poly = polynomials.return_data;
197 auto& return_data_read_counts = polynomials.return_data_read_counts;
198 auto& return_data_read_tags = polynomials.return_data_read_tags;
199
200 const auto& calldata = circuit.get_calldata();
201 const auto& secondary_calldata = circuit.get_secondary_calldata();
202 const auto& return_data = circuit.get_return_data();
203
204 // Note: Databus columns start from index 0. If this ever changes, make sure to also update the active range
205 // construction in ExecutionTraceUsageTracker::update(). We do not utilize a zero row for databus columns.
206 for (size_t idx = 0; idx < calldata.size(); ++idx) {
207 calldata_poly.at(idx) = circuit.get_variable(calldata[idx]); // calldata values
208 calldata_read_counts.at(idx) = calldata.get_read_count(idx); // read counts
209 calldata_read_tags.at(idx) = calldata_read_counts[idx] > 0 ? 1 : 0; // has row been read or not
210 }
211 for (size_t idx = 0; idx < secondary_calldata.size(); ++idx) {
212 secondary_calldata_poly.at(idx) = circuit.get_variable(secondary_calldata[idx]); // secondary_calldata values
213 secondary_calldata_read_counts.at(idx) = secondary_calldata.get_read_count(idx); // read counts
214 secondary_calldata_read_tags.at(idx) =
215 secondary_calldata_read_counts[idx] > 0 ? 1 : 0; // has row been read or not
216 }
217 for (size_t idx = 0; idx < return_data.size(); ++idx) {
218 return_data_poly.at(idx) = circuit.get_variable(return_data[idx]); // return data values
219 return_data_read_counts.at(idx) = return_data.get_read_count(idx); // read counts
220 return_data_read_tags.at(idx) = return_data_read_counts[idx] > 0 ? 1 : 0; // has row been read or not
221 }
222
223 auto& databus_id = polynomials.databus_id;
224 // Compute a simple identity polynomial for use in the databus lookup argument
225 for (size_t i = 0; i < databus_id.size(); ++i) {
226 databus_id.at(i) = i;
228}
246template <IsUltraOrMegaHonk Flavor>
248 requires IsMegaFlavor<Flavor>
250 auto& blocks = circuit.blocks;
251 auto& overflow_block = circuit.blocks.overflow;
253 // Set has_overflow to true if a nonzero fixed size has been prescribed for the overflow block
254 blocks.has_overflow = (overflow_block.get_fixed_size() > 0);
256 blocks.compute_offsets(/*is_structured=*/true); // compute the offset of each fixed size block
257
258 // Check each block for capacity overflow; if necessary move gates into the overflow block
259 for (auto& block : blocks.get()) {
260 size_t block_size = block.size();
261 uint32_t fixed_block_size = block.get_fixed_size();
262 if (block_size > fixed_block_size && block != overflow_block) {
263 // Disallow overflow in blocks that are not expected to be used by App circuits
264 if (&block == &blocks.pub_inputs) {
265 std::ostringstream oss;
266 oss << "WARNING: Number of public inputs (" << block_size
267 << ") cannot exceed capacity specified in structured trace: " << fixed_block_size;
268 throw_or_abort(oss.str());
269 }
270 if (&block == &blocks.ecc_op) {
271 std::ostringstream oss;
272 oss << "WARNING: Number of ecc op gates (" << block_size
273 << ") cannot exceed capacity specified in structured trace: " << fixed_block_size;
274 throw_or_abort(oss.str());
275 }
276
277 // Set has_overflow to true if at least one block exceeds its capacity
278 blocks.has_overflow = true;
279
280 // The circuit memory read/write records store the indices at which a RAM/ROM read/write has occurred. If
281 // the block containing RAM/ROM gates overflows, the indices of the corresponding gates in the memory
282 // records need to be updated to reflect their new position in the overflow block
283 if (&block == &blocks.memory) {
284 uint32_t overflow_cur_idx =
285 overflow_block.trace_offset() + static_cast<uint32_t>(overflow_block.size());
286 overflow_cur_idx -= block.trace_offset(); // we'll add block.trace_offset to everything later
287 uint32_t offset = overflow_cur_idx + 1; // +1 accounts for duplication of final gate
288 for (auto& idx : circuit.memory_read_records) {
289 // last gate in the main block will be duplicated; if necessary, duplicate the memory read idx too
290 if (idx == fixed_block_size - 1) {
291 circuit.memory_read_records.push_back(overflow_cur_idx);
292 }
293 if (idx >= fixed_block_size) {
294 idx -= fixed_block_size; // redefine index from zero
295 idx += offset; // shift to correct location in overflow block
296 }
297 }
298 for (auto& idx : circuit.memory_write_records) {
299 // last gate in the main block will be duplicated; if necessary, duplicate the memory write idx too
300 if (idx == fixed_block_size - 1) {
301 circuit.memory_write_records.push_back(overflow_cur_idx);
302 }
303 if (idx >= fixed_block_size) {
304 idx -= fixed_block_size; // redefine index from zero
305 idx += offset; // shift to correct location in overflow block
306 }
307 }
308 }
309
310 // Move the excess wire and selector data from the offending block to the overflow block
311 size_t overflow_start = fixed_block_size - 1; // the final gate in the main block is duplicated
312 size_t overflow_end = block_size;
313 for (auto [wire, overflow_wire] : zip_view(block.wires, overflow_block.wires)) {
314 for (size_t i = overflow_start; i < overflow_end; ++i) {
315 overflow_wire.push_back(wire[i]);
316 }
317 wire.resize(fixed_block_size); // shrink the main block to its max capacity
318 }
319 for (auto [selector, overflow_selector] : zip_view(block.get_selectors(), overflow_block.get_selectors())) {
320 for (size_t i = overflow_start; i < overflow_end; ++i) {
321 overflow_selector.push_back(selector[i]);
322 }
323 selector.resize(fixed_block_size); // shrink the main block to its max capacity
324 }
325 // Convert duplicated final gate in the main block to a 'dummy' gate by turning off all selectors. This
326 // ensures it can be read into by the previous gate but does not itself try to read into the next gate.
327 for (auto& selector : block.get_gate_selectors()) {
328 BB_ASSERT_EQ(selector.empty(), false);
329 selector.set_back(0);
330 }
331 }
332 }
333
334 // Set the fixed size of the overflow block to its current size
335 if (overflow_block.size() > overflow_block.get_fixed_size()) {
336 info("WARNING: Structured trace overflow mechanism in use. Performance may be degraded!");
337 overflow_block.fixed_size = static_cast<uint32_t>(overflow_block.size());
338 blocks.summarize();
339 }
340}
341
347template <IsUltraOrMegaHonk Flavor> void DeciderProvingKey_<Flavor>::populate_memory_records(const Circuit& circuit)
348{
349 // Store the read/write records as indices into the full trace by accounting for the offset of the memory block.
350 uint32_t ram_rom_offset = circuit.blocks.memory.trace_offset();
351 memory_read_records.reserve(circuit.memory_read_records.size());
352 for (auto& index : circuit.memory_read_records) {
353 memory_read_records.emplace_back(index + ram_rom_offset);
354 }
355 memory_write_records.reserve(circuit.memory_write_records.size());
356 for (auto& index : circuit.memory_write_records) {
357 memory_write_records.emplace_back(index + ram_rom_offset);
358 }
359}
360
364#ifdef STARKNET_GARAGA_FLAVORS
367#endif
370template class DeciderProvingKey_<MegaFlavor>;
372
373} // namespace bb
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:87
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:59
A DeciderProvingKey is normally constructed from a finalized circuit and it contains all the informat...
void populate_memory_records(const Circuit &circuit)
Copy RAM/ROM record of reads and writes from the circuit to the proving key.
size_t compute_dyadic_size(Circuit &)
Helper method to compute quantities like total number of gates and dyadic circuit size.
typename Flavor::Polynomial Polynomial
void allocate_ecc_op_polynomials(const Circuit &)
void allocate_databus_polynomials(const Circuit &)
typename Flavor::CircuitBuilder Circuit
void allocate_table_lookup_polynomials(const Circuit &)
void allocate_selectors(const Circuit &)
static void move_structured_trace_overflow_to_overflow_block(Circuit &circuit)
Check that the number of gates in each block does not exceed its fixed capacity. Move any overflow to...
void construct_databus_polynomials(Circuit &)
void info(Args... args)
Definition log.hpp:70
ssize_t offset
Definition engine.cpp:36
Entry point for Barretenberg command-line interface.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
#define PROFILE_THIS_NAME(name)
Definition op_count.hpp:16
Contains various functions that help construct Honk Sigma and Id polynomials.
std::vector< FF > calldata
void throw_or_abort(std::string const &err)