Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
prover_instance.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Completed, auditors: [Sergei], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#include "prover_instance.hpp"
22
23namespace bb {
24
25template <typename Flavor> ProverInstance_<Flavor>::ProverInstance_(Circuit& circuit)
26{
27 BB_BENCH_NAME("ProverInstance(Circuit&)");
28 vinfo("Constructing ProverInstance");
29
30 // Check pairing point tagging: either no pairing points were created,
31 // or all pairing points have been aggregated into a single equivalence class
32 BB_ASSERT(circuit.pairing_points_tagging.has_single_pairing_point_tag(),
33 "Pairing points must all be aggregated together. Either no pairing points should be created, or "
34 "all created pairing points must be aggregated into a single pairing point. Found "
35 << circuit.pairing_points_tagging.num_unique_pairing_points() << " different pairing points.");
36 // Check pairing point tagging: check that the pairing points have been set to public
37 BB_ASSERT(circuit.pairing_points_tagging.has_public_pairing_points() ||
38 !circuit.pairing_points_tagging.has_pairing_points(),
39 "Pairing points must be set to public in the circuit before constructing the ProverInstance.");
40
41 // ProverInstances can be constructed multiple times, hence, we check whether the circuit has been finalized
42 {
43 BB_BENCH_NAME("finalize_circuit");
44 if (!circuit.circuit_finalized) {
45 circuit.finalize_circuit();
46 }
47 // Compute block offsets before dyadic size so that compute_dyadic_size can account for the lookup table offset
48 circuit.blocks.compute_offsets(TRACE_OFFSET);
49 metadata.dyadic_size = compute_dyadic_size(circuit);
50
51 // Find index of last non-trivial wire value in the trace
52 for (auto& block : circuit.blocks.get()) {
53 if (block.size() > 0) {
54 final_active_wire_idx = block.trace_end() - 1;
55 }
56 }
57 }
58
59 {
60 BB_BENCH_NAME("allocating polynomials");
61 vinfo("allocating polynomials object in prover instance...");
62
63 populate_memory_records(circuit);
64 allocate_wires();
65 allocate_permutation_argument_polynomials();
66 allocate_selectors(circuit);
67 if constexpr (Flavor::HasLogDerivLookup) {
68 allocate_table_lookup_polynomials(circuit);
69 }
70 allocate_lagrange_polynomials();
71
72 if constexpr (Flavor::HasEccOpQueue) {
73 allocate_ecc_op_polynomials(circuit);
74 }
75 if constexpr (Flavor::HasDataBus) {
76 allocate_databus_polynomials(circuit);
77 }
78
79 // Set the shifted polynomials now that all of the to_be_shifted polynomials are defined.
80 polynomials.set_shifted();
81 }
82
85 }
86
87 // Construct and add to proving key the wire, selector and copy constraint polynomials
88 vinfo("populating trace...");
89 TraceToPolynomials<Flavor>::populate(circuit, polynomials);
90
91 if constexpr (Flavor::HasDataBus) {
92 BB_BENCH_NAME("constructing databus polynomials");
93 construct_databus_polynomials(circuit);
94 }
95
96 // Set the lagrange polynomials (lagrange_first at first active row after disabled region)
97 polynomials.lagrange_first().at(TRACE_OFFSET) = 1;
98 polynomials.lagrange_last().at(final_active_wire_idx) = 1;
99
100 if constexpr (Flavor::HasLogDerivLookup) {
101 construct_lookup_polynomials(circuit);
102 }
103
104 // Public inputs
105 metadata.num_public_inputs = circuit.blocks.pub_inputs.size();
106 metadata.pub_inputs_offset = circuit.blocks.pub_inputs.trace_offset();
107 for (size_t i = 0; i < metadata.num_public_inputs; ++i) {
108 size_t idx = i + metadata.pub_inputs_offset;
109 public_inputs.emplace_back(polynomials.w_r()[idx]);
110 }
111
112 // Copy IPA proof if present
113 ipa_proof = circuit.ipa_proof;
114
115 if (std::getenv("BB_POLY_STATS")) {
116 analyze_prover_polynomials(polynomials);
117 }
120 }
121}
122
132template <typename Flavor> size_t ProverInstance_<Flavor>::compute_dyadic_size(Circuit& circuit)
133{
134 // For the lookup argument the circuit size must be at least as large as the sum of all tables used
135 const size_t tables_size = circuit.get_tables_size();
136
137 // minimum size of execution trace due to everything else
138 size_t min_size_of_execution_trace = circuit.blocks.get_total_content_size();
139
140 // Tables are placed at the lookup block's trace offset, so account for blocks preceding lookup
141 const size_t tables_end = circuit.blocks.lookup.trace_offset() + tables_size;
142 const size_t trace_end = TRACE_OFFSET + NUM_ZERO_ROWS + min_size_of_execution_trace;
143 size_t total_num_gates = std::max(tables_end, trace_end);
144
145 // Next power of 2 (dyadic circuit size)
146 return circuit.get_circuit_subgroup_size(total_num_gates);
147}
148
149template <typename Flavor> void ProverInstance_<Flavor>::allocate_wires()
150{
151 BB_BENCH_NAME("allocate_wires");
152
153 const size_t wire_size = trace_active_range_size();
154
155 for (auto& wire : polynomials.get_wires()) {
156 wire = Polynomial::shiftable(wire_size, dyadic_size(), Flavor::HasZK);
157 }
158}
159
161{
162 BB_BENCH_NAME("allocate_permutation_argument_polynomials");
163
164 // Sigma and ID polynomials are zero outside the active trace range. Inside the active range,
165 // compute_permutation_argument_polynomials writes every cell (identity init + cycle linkages),
166 // so the backing memory can be left uninitialized.
167 for (auto& sigma : polynomials.get_sigmas()) {
168 sigma = Polynomial::shiftable(trace_active_range_size(), dyadic_size(), Polynomial::DontZeroMemory::FLAG);
169 }
170 for (auto& id : polynomials.get_ids()) {
171 id = Polynomial::shiftable(trace_active_range_size(), dyadic_size(), Polynomial::DontZeroMemory::FLAG);
172 }
173
174 polynomials.z_perm() = Polynomial::shiftable(trace_active_range_size(), dyadic_size(), Flavor::HasZK);
175}
176
178{
179 BB_BENCH_NAME("allocate_lagrange_polynomials");
180
181 polynomials.lagrange_first() = Polynomial(
182 /* size=*/1, /*virtual size=*/dyadic_size(), /*start_index=*/TRACE_OFFSET);
183
184 polynomials.lagrange_last() = Polynomial(
185 /* size=*/1, /*virtual size=*/dyadic_size(), /*start_index=*/final_active_wire_idx);
186}
187
188template <typename Flavor> void ProverInstance_<Flavor>::allocate_selectors(const Circuit& circuit)
189{
190 BB_BENCH_NAME("allocate_selectors");
191
192 // Each gate selector is sized to its trace block; `Flavor::Generated::get_gate_blocks` yields
193 // those blocks in `polynomials.get_gate_selectors()` order.
194 for (auto [selector, block] :
195 zip_view(polynomials.get_gate_selectors(), Flavor::Generated::get_gate_blocks(circuit.blocks))) {
196 selector = Polynomial(block.size(), dyadic_size(), block.trace_offset());
197 }
198
199 // Set the other non-gate selector polynomials (e.g. q_l, q_r, q_m etc.) to active trace size
200 for (auto& selector : polynomials.get_non_gate_selectors()) {
201 selector = Polynomial(trace_active_range_size(), dyadic_size());
202 }
203}
204
205template <typename Flavor>
207 requires(Flavor::HasLogDerivLookup)
208{
209 BB_BENCH_NAME("allocate_table_lookup_and_lookup_read_polynomials");
210
211 const size_t tables_size = circuit.get_tables_size(); // cumulative size of all lookup tables
212 const size_t table_offset = circuit.blocks.lookup.trace_offset();
213 const size_t tables_end = table_offset + tables_size;
214
215 // Tables start at the lookup block's trace offset, which is always past the disabled region
216 BB_ASSERT_GTE(table_offset, TRACE_OFFSET);
217 // Allocate polynomials containing the actual table data. Back only [TRACE_OFFSET, tables_end):
218 // rows below TRACE_OFFSET are the disabled region and read as zero, so there is no need to
219 // materialise them. This keeps the table columns small regardless of where the lookup block
220 // lands in the trace.
221 BB_ASSERT_GTE(dyadic_size(), tables_end);
222 for (auto& table_poly : polynomials.get_tables()) {
223 table_poly = Polynomial(tables_end - TRACE_OFFSET, dyadic_size(), TRACE_OFFSET);
224 }
225
226 // Read counts and tags: track which table entries have been read
227 polynomials.lookup_read_counts() = Polynomial(tables_end, dyadic_size());
228 polynomials.lookup_read_tags() = Polynomial(tables_end, dyadic_size());
229
230 // Lookup inverses: used in the log-derivative lookup argument
231 // Must cover both the lookup gate block (where reads occur) and the table data itself
232 const size_t lookup_block_end = circuit.blocks.lookup.trace_end();
233 const size_t lookup_inverses_end = std::max(lookup_block_end, tables_end);
234
235 polynomials.lookup_inverses() = Polynomial(lookup_inverses_end, dyadic_size());
236
237 if constexpr (Flavor::HasZK) {
238 polynomials.lookup_read_counts().add_masking();
239 polynomials.lookup_read_tags().add_masking();
240 polynomials.lookup_inverses().add_masking();
241 }
242}
243
244template <typename Flavor>
246 requires Flavor::HasEccOpQueue
247{
248 BB_BENCH_NAME("allocate_ecc_op_polynomials");
249
250 // Allocate the ecc op wires and selector
251 // Note: ECC op wires are not masked (they use random ops for ZK)
252 const size_t ecc_op_end = circuit.blocks.ecc_op.trace_end();
253 for (auto& wire : polynomials.get_ecc_op_wires()) {
254 wire = Polynomial(ecc_op_end, dyadic_size());
255 }
256 polynomials.lagrange_ecc_op() = Polynomial(ecc_op_end, dyadic_size());
257}
258
259template <typename Flavor>
260void ProverInstance_<Flavor>::allocate_databus_polynomials(const Circuit& circuit)
261 requires Flavor::HasDataBus
262{
263 BB_BENCH_NAME("allocate_databus_and_lookup_inverse_polynomials");
264
265 // Databus data uses NUM_DISABLED_ROWS_IN_SUMCHECK as its offset rather than Flavor::TRACE_OFFSET so that
266 // commitments match across the IVC boundary (a non-ZK kernel's returndata is copy-constrained to a MegaZK
267 // hiding kernel's kernel_calldata). MegaZK additionally requires this offset to clear the masking region
268 // [1, NUM_DISABLED_ROWS_IN_SUMCHECK); non-ZK Mega mirrors the layout even though it has no masking.
269 const auto offset_size = [](size_t content) -> size_t { return NUM_DISABLED_ROWS_IN_SUMCHECK + content; };
270
271 // Databus inverses must cover both the databus gate block (where reads occur) and the data itself.
272 const size_t q_busread_end = circuit.blocks.busread.trace_end();
273
274 size_t max_databus_column_size = 0;
275
276 auto bus_data = polynomials.get_databus_entities(); // [bus0_values, bus0_counts, bus1_..., ...]
277 auto bus_inverses = polynomials.get_databus_inverses(); // [bus0_inv, bus1_inv, ...]
278 auto bus_indicators = polynomials.get_databus_indicators(); // [bus0_indicator, bus1_indicator, ...]
279 bb::constexpr_for<0, Flavor::NUM_BUS_COLUMNS, 1>([&]<size_t bus_idx>() {
280 // Map the flavor's local bus index to the builder's `get_bus_vector` slot (kernel_calldata=0,
281 // first..fifth_app_calldata=1..5, return_data=6). For full MegaFlavor this is the identity;
282 // for other flavors (e.g. MegaAppFlavor with only `return_data`) the mapping shifts so the right
283 // builder bus is read.
284 constexpr size_t builder_bus_idx = Flavor::BUILDER_BUS_INDICES[bus_idx];
285 const size_t bus_size = circuit.get_bus_vector(builder_bus_idx).size();
286 max_databus_column_size = std::max(max_databus_column_size, bus_size);
287
288 auto& values_poly = bus_data[2 * bus_idx];
289 auto& read_counts_poly = bus_data[(2 * bus_idx) + 1];
290 auto& inverse_poly = bus_inverses[bus_idx];
291 auto& indicator_poly = bus_indicators[bus_idx];
292
293 // Values + read_counts: sized to the bus data shifted by TRACE_OFFSET.
294 values_poly = Polynomial(offset_size(bus_size), dyadic_size());
295 read_counts_poly = Polynomial(offset_size(bus_size), dyadic_size());
296
297 // Inverse polynomial: sized to cover both the busread gate block and the shifted bus data.
298 inverse_poly = Polynomial(std::max(offset_size(bus_size), q_busread_end), dyadic_size());
299
300 // Indicator polynomial: 1 on the column's data rows (offset..offset+bus_size), 0 elsewhere.
301 indicator_poly = Polynomial(offset_size(bus_size), dyadic_size());
302
303 if constexpr (Flavor::HasZK) {
304 // Mask databus witness polynomials. The kernel_calldata values column (bus_idx == 0) is NOT
305 // masked; its read_counts column is.
306 if constexpr (bus_idx != 0) {
307 values_poly.add_masking();
308 }
309 read_counts_poly.add_masking();
310 inverse_poly.add_masking();
311 }
312 });
313
314 polynomials.databus_id() = Polynomial(offset_size(max_databus_column_size), dyadic_size());
315}
316
317template <typename Flavor>
319 requires(Flavor::HasLogDerivLookup)
320{
321 {
322 BB_BENCH_NAME("constructing lookup table polynomials");
323 construct_lookup_table_polynomials<Flavor>(polynomials.get_tables(), circuit);
324 }
325 {
326 BB_BENCH_NAME("constructing lookup read counts");
327 construct_lookup_read_counts<Flavor>(polynomials.lookup_read_counts(), polynomials.lookup_read_tags(), circuit);
328 }
329}
330
334template <typename Flavor>
335void ProverInstance_<Flavor>::construct_databus_polynomials(Circuit& circuit)
336 requires Flavor::HasDataBus
337{
338 // Databus offset of NUM_DISABLED_ROWS_IN_SUMCHECK is forced by cross-flavor commitment compatibility and
339 // MegaZK masking; see allocate_databus_polynomials for the rationale.
340 size_t max_bus_size = 0;
341 auto bus_data = polynomials.get_databus_entities();
342 bb::constexpr_for<0, Flavor::NUM_BUS_COLUMNS, 1>([&]<size_t bus_idx>() {
343 constexpr size_t builder_bus_idx = Flavor::BUILDER_BUS_INDICES[bus_idx];
344 const auto& bus_vec = circuit.get_bus_vector(builder_bus_idx);
345 max_bus_size = std::max(max_bus_size, bus_vec.size());
346 auto& values_poly = bus_data[2 * bus_idx];
347 auto& read_counts_poly = bus_data[(2 * bus_idx) + 1];
348 for (size_t idx = 0; idx < bus_vec.size(); ++idx) {
349 values_poly.at(NUM_DISABLED_ROWS_IN_SUMCHECK + idx) = circuit.get_variable(bus_vec[idx]);
350 read_counts_poly.at(NUM_DISABLED_ROWS_IN_SUMCHECK + idx) = bus_vec.get_read_count(idx);
351 }
352 });
353
354 // Compute a simple identity polynomial for use in the databus lookup argument.
355 auto& databus_id = polynomials.databus_id();
356 for (size_t i = 0; i < max_bus_size; ++i) {
357 databus_id.at(NUM_DISABLED_ROWS_IN_SUMCHECK + i) = i;
358 }
359
360 // Populate per-bus indicator polynomials: 1 on the bus's data rows, 0 elsewhere (default).
361 auto indicators = polynomials.get_databus_indicators();
362 for (size_t bus_idx = 0; bus_idx < Flavor::NUM_BUS_COLUMNS; ++bus_idx) {
363 const size_t builder_bus_idx = Flavor::BUILDER_BUS_INDICES[bus_idx];
364 const size_t bus_size = circuit.get_bus_vector(builder_bus_idx).size();
365 auto& indicator = indicators[bus_idx];
366 for (size_t i = 0; i < bus_size; ++i) {
367 indicator.at(NUM_DISABLED_ROWS_IN_SUMCHECK + i) = 1;
368 }
369 }
370}
371
378template <typename Flavor> void ProverInstance_<Flavor>::populate_memory_records(const Circuit& circuit)
379{
380 // Store the read/write records as indices into the full trace by accounting for the offset of the memory block.
381 uint32_t ram_rom_offset = circuit.blocks.memory.trace_offset();
382 memory_read_records.reserve(circuit.memory_read_records.size());
383 for (auto& index : circuit.memory_read_records) {
384 memory_read_records.emplace_back(index + ram_rom_offset);
385 }
386 memory_write_records.reserve(circuit.memory_write_records.size());
387 for (auto& index : circuit.memory_write_records) {
388 memory_write_records.emplace_back(index + ram_rom_offset);
389 }
390 rom_logup_records.reserve(circuit.rom_logup_records.size());
391 for (auto& index : circuit.rom_logup_records) {
392 rom_logup_records.emplace_back(index + ram_rom_offset);
393 }
394}
395
396template class ProverInstance_<UltraFlavor>;
397template class ProverInstance_<UltraZKFlavor>;
398template class ProverInstance_<UltraKeccakFlavor>;
399#ifdef STARKNET_GARAGA_FLAVORS
400template class ProverInstance_<UltraStarknetFlavor>;
401template class ProverInstance_<UltraStarknetZKFlavor>;
402#endif
403template class ProverInstance_<UltraKeccakZKFlavor>;
404template class ProverInstance_<MegaFlavor>;
405template class ProverInstance_<MegaZKFlavor>;
406template class ProverInstance_<MegaAvmFlavor>;
407template class ProverInstance_<MegaAppFlavor>;
408template class ProverInstance_<MegaKernelFlavor>;
409
410} // namespace bb
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_GTE(left, right,...)
Definition assert.hpp:128
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:264
static constexpr bool HasZK
static Polynomial shiftable(size_t virtual_size, bool masked=false)
Utility to create a shiftable polynomial of given virtual size.
Contains all the information required by a Honk prover to create a proof, constructed from a finalize...
void allocate_selectors(const Circuit &)
ProverInstance_()=default
size_t compute_dyadic_size(Circuit &)
Compute the minimum dyadic (power-of-2) circuit size.
void allocate_table_lookup_polynomials(const Circuit &)
void populate_memory_records(const Circuit &circuit)
void allocate_permutation_argument_polynomials()
typename Flavor::CircuitBuilder Circuit
typename Flavor::Polynomial Polynomial
void allocate_ecc_op_polynomials(const Circuit &) void allocate_databus_polynomials(const Circuit &) Flavor void construct_databus_polynomials(Circuit &) Flavor void construct_lookup_polynomials(Circuit &circuit)
static void populate(Builder &builder, ProverPolynomials &)
Given a circuit, populate a proving key with wire polys, selector polys, and sigma/id polys.
#define vinfo(...)
Definition log.hpp:94
bool use_memory_profile
MemoryProfile GLOBAL_MEMORY_PROFILE
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
void analyze_prover_polynomials(ProverPolynomials &polynomials)
Analyze prover polynomials and print per-polynomial statistics about value sizes.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Contains various functions that help construct Honk Sigma and Id polynomials.
void add_checkpoint(const std::string &stage)