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"
19
20namespace bb {
21
22template <typename Flavor> ProverInstance_<Flavor>::ProverInstance_(Circuit& circuit)
23{
24 BB_BENCH_NAME("ProverInstance(Circuit&)");
25 vinfo("Constructing ProverInstance");
26
27 // Check pairing point tagging: either no pairing points were created,
28 // or all pairing points have been aggregated into a single equivalence class
29 BB_ASSERT(circuit.pairing_points_tagging.has_single_pairing_point_tag(),
30 "Pairing points must all be aggregated together. Either no pairing points should be created, or "
31 "all created pairing points must be aggregated into a single pairing point. Found "
32 << circuit.pairing_points_tagging.num_unique_pairing_points() << " different pairing points.");
33 // Check pairing point tagging: check that the pairing points have been set to public
34 BB_ASSERT(circuit.pairing_points_tagging.has_public_pairing_points() ||
35 !circuit.pairing_points_tagging.has_pairing_points(),
36 "Pairing points must be set to public in the circuit before constructing the ProverInstance.");
37
38 // ProverInstances can be constructed multiple times, hence, we check whether the circuit has been finalized
39 {
40 BB_BENCH_NAME("finalize_circuit");
41 if (!circuit.circuit_finalized) {
42 circuit.finalize_circuit();
43 }
44 // Compute block offsets before dyadic size so that compute_dyadic_size can account for the lookup table offset
45 circuit.blocks.compute_offsets(TRACE_OFFSET);
46 metadata.dyadic_size = compute_dyadic_size(circuit);
47
48 // Find index of last non-trivial wire value in the trace
49 for (auto& block : circuit.blocks.get()) {
50 if (block.size() > 0) {
51 final_active_wire_idx = block.trace_end() - 1;
52 }
53 }
54 }
55
56 {
57 BB_BENCH_NAME("allocating polynomials");
58 vinfo("allocating polynomials object in prover instance...");
59
60 populate_memory_records(circuit);
61 allocate_wires();
62 allocate_permutation_argument_polynomials();
63 allocate_selectors(circuit);
64 allocate_table_lookup_polynomials(circuit);
65 allocate_lagrange_polynomials();
66
67 if constexpr (IsMegaFlavor<Flavor>) {
68 allocate_ecc_op_polynomials(circuit);
69 }
70 if constexpr (HasDataBus<Flavor>) {
71 allocate_databus_polynomials(circuit);
72 }
73
74 // Set the shifted polynomials now that all of the to_be_shifted polynomials are defined.
75 polynomials.set_shifted();
76 }
77
80 }
81
82 // Construct and add to proving key the wire, selector and copy constraint polynomials
83 vinfo("populating trace...");
84 TraceToPolynomials<Flavor>::populate(circuit, polynomials);
85
86 if constexpr (IsMegaFlavor<Flavor>) {
87 BB_BENCH_NAME("constructing databus polynomials");
88 construct_databus_polynomials(circuit);
89 }
90
91 // Set the lagrange polynomials (lagrange_first at first active row after disabled region)
92 polynomials.lagrange_first.at(TRACE_OFFSET) = 1;
93 polynomials.lagrange_last.at(final_active_wire_idx) = 1;
94
95 construct_lookup_polynomials(circuit);
96
97 // Public inputs
98 metadata.num_public_inputs = circuit.blocks.pub_inputs.size();
99 metadata.pub_inputs_offset = circuit.blocks.pub_inputs.trace_offset();
100 for (size_t i = 0; i < metadata.num_public_inputs; ++i) {
101 size_t idx = i + metadata.pub_inputs_offset;
102 public_inputs.emplace_back(polynomials.w_r[idx]);
103 }
104
105 // Copy IPA proof if present
106 ipa_proof = circuit.ipa_proof;
107
108 if (std::getenv("BB_POLY_STATS")) {
109 analyze_prover_polynomials(polynomials);
110 }
113 }
114}
115
125template <typename Flavor> size_t ProverInstance_<Flavor>::compute_dyadic_size(Circuit& circuit)
126{
127 // For the lookup argument the circuit size must be at least as large as the sum of all tables used
128 const size_t tables_size = circuit.get_tables_size();
129
130 // minimum size of execution trace due to everything else
131 size_t min_size_of_execution_trace = circuit.blocks.get_total_content_size();
132
133 // Tables are placed at the lookup block's trace offset, so account for blocks preceding lookup
134 const size_t tables_end = circuit.blocks.lookup.trace_offset() + tables_size;
135 const size_t trace_end = TRACE_OFFSET + NUM_ZERO_ROWS + min_size_of_execution_trace;
136 size_t total_num_gates = std::max(tables_end, trace_end);
137
138 // Next power of 2 (dyadic circuit size)
139 return circuit.get_circuit_subgroup_size(total_num_gates);
140}
141
142template <typename Flavor> void ProverInstance_<Flavor>::allocate_wires()
143{
144 BB_BENCH_NAME("allocate_wires");
145
146 const size_t wire_size = trace_active_range_size();
147
148 for (auto& wire : polynomials.get_wires()) {
149 wire = Polynomial::shiftable(wire_size, dyadic_size(), Flavor::HasZK);
150 }
151}
152
154{
155 BB_BENCH_NAME("allocate_permutation_argument_polynomials");
156
157 // Sigma and ID polynomials are zero outside the active trace range. Inside the active range,
158 // compute_permutation_argument_polynomials writes every cell (identity init + cycle linkages),
159 // so the backing memory can be left uninitialized.
160 for (auto& sigma : polynomials.get_sigmas()) {
161 sigma = Polynomial::shiftable(trace_active_range_size(), dyadic_size(), Polynomial::DontZeroMemory::FLAG);
162 }
163 for (auto& id : polynomials.get_ids()) {
164 id = Polynomial::shiftable(trace_active_range_size(), dyadic_size(), Polynomial::DontZeroMemory::FLAG);
165 }
166
167 polynomials.z_perm = Polynomial::shiftable(trace_active_range_size(), dyadic_size(), Flavor::HasZK);
168}
169
171{
172 BB_BENCH_NAME("allocate_lagrange_polynomials");
173
174 polynomials.lagrange_first = Polynomial(
175 /* size=*/1, /*virtual size=*/dyadic_size(), /*start_index=*/TRACE_OFFSET);
176
177 polynomials.lagrange_last = Polynomial(
178 /* size=*/1, /*virtual size=*/dyadic_size(), /*start_index=*/final_active_wire_idx);
179}
180
181template <typename Flavor> void ProverInstance_<Flavor>::allocate_selectors(const Circuit& circuit)
182{
183 BB_BENCH_NAME("allocate_selectors");
184
185 // Define gate selectors over the block they are isolated to
186 for (auto [selector, block] : zip_view(polynomials.get_gate_selectors(), circuit.blocks.get_gate_blocks())) {
187 selector = Polynomial(block.size(), dyadic_size(), block.trace_offset());
188 }
189
190 // Set the other non-gate selector polynomials (e.g. q_l, q_r, q_m etc.) to active trace size
191 for (auto& selector : polynomials.get_non_gate_selectors()) {
192 selector = Polynomial(trace_active_range_size(), dyadic_size());
193 }
194}
195
196template <typename Flavor> void ProverInstance_<Flavor>::allocate_table_lookup_polynomials(const Circuit& circuit)
197{
198 BB_BENCH_NAME("allocate_table_lookup_and_lookup_read_polynomials");
199
200 const size_t tables_size = circuit.get_tables_size(); // cumulative size of all lookup tables
201 const size_t table_offset = circuit.blocks.lookup.trace_offset();
202 const size_t tables_end = table_offset + tables_size;
203
204 // Tables start at the lookup block's trace offset, which is always past the disabled region
205 BB_ASSERT_GTE(table_offset, TRACE_OFFSET);
206 // Allocate polynomials containing the actual table data; offset to align with the lookup gate block
207 BB_ASSERT_GTE(dyadic_size(), tables_end);
208 for (auto& table_poly : polynomials.get_tables()) {
209 table_poly = Polynomial(tables_end, dyadic_size());
210 }
211
212 // Read counts and tags: track which table entries have been read
213 polynomials.lookup_read_counts = Polynomial(tables_end, dyadic_size());
214 polynomials.lookup_read_tags = Polynomial(tables_end, dyadic_size());
215
216 // Lookup inverses: used in the log-derivative lookup argument
217 // Must cover both the lookup gate block (where reads occur) and the table data itself
218 const size_t lookup_block_end = circuit.blocks.lookup.trace_end();
219 const size_t lookup_inverses_end = std::max(lookup_block_end, tables_end);
220
221 polynomials.lookup_inverses = Polynomial(lookup_inverses_end, dyadic_size());
222
223 if constexpr (Flavor::HasZK) {
224 polynomials.lookup_read_counts.add_masking();
225 polynomials.lookup_read_tags.add_masking();
226 polynomials.lookup_inverses.add_masking();
227 }
228}
229
230template <typename Flavor>
232 requires IsMegaFlavor<Flavor>
233{
234 BB_BENCH_NAME("allocate_ecc_op_polynomials");
235
236 // Allocate the ecc op wires and selector
237 // Note: ECC op wires are not masked (they use random ops for ZK)
238 const size_t ecc_op_end = circuit.blocks.ecc_op.trace_end();
239 for (auto& wire : polynomials.get_ecc_op_wires()) {
240 wire = Polynomial(ecc_op_end, dyadic_size());
241 }
242 polynomials.lagrange_ecc_op = Polynomial(ecc_op_end, dyadic_size());
243}
244
245template <typename Flavor>
247 requires HasDataBus<Flavor>
248{
249 BB_BENCH_NAME("allocate_databus_and_lookup_inverse_polynomials");
250
251 // Databus data uses NUM_DISABLED_ROWS_IN_SUMCHECK as its offset rather than Flavor::TRACE_OFFSET so that
252 // commitments match across the IVC boundary (a non-ZK kernel's return_data is copy-constrained to a MegaZK
253 // hiding kernel's kernel_calldata). MegaZK additionally requires this offset to clear the masking region
254 // [1, NUM_DISABLED_ROWS_IN_SUMCHECK); non-ZK Mega mirrors the layout even though it has no masking.
255 const auto offset_size = [](size_t content) -> size_t { return NUM_DISABLED_ROWS_IN_SUMCHECK + content; };
256
257 // Databus inverses must cover both the databus gate block (where reads occur) and the data itself.
258 const size_t q_busread_end = circuit.blocks.busread.trace_end();
259
260 size_t max_databus_column_size = 0;
261
262 bb::constexpr_for<0, NUM_BUS_COLUMNS, 1>([&]<size_t bus_idx>() {
263 const size_t bus_size = circuit.get_bus_vector(bus_idx).size();
264 max_databus_column_size = std::max(max_databus_column_size, bus_size);
265
266 // Values + read_counts: sized to the bus data shifted by TRACE_OFFSET.
267 auto entities = polynomials.template databus_entities_for_bus<bus_idx>();
268 for (auto& entity : entities) {
269 entity = Polynomial(offset_size(bus_size), dyadic_size());
270 }
271
272 // Inverse polynomial: sized to cover both the busread gate block and the shifted bus data.
273 auto inverse_ref = polynomials.template databus_inverse_for_bus<bus_idx>();
274 inverse_ref[0] = Polynomial(std::max(offset_size(bus_size), q_busread_end), dyadic_size());
275
276 if constexpr (Flavor::HasZK) {
277 // Mask databus witness polynomials. The kernel_calldata values column (bus_idx == 0) is NOT
278 // masked; its read_counts column is.
279 auto& values_poly = entities[0];
280 auto& read_counts_poly = entities[1];
281 if constexpr (bus_idx != 0) {
282 values_poly.add_masking();
283 }
284 read_counts_poly.add_masking();
285 inverse_ref[0].add_masking();
286 }
287 });
288
289 polynomials.databus_id = Polynomial(offset_size(max_databus_column_size), dyadic_size());
290}
291
292template <typename Flavor> void ProverInstance_<Flavor>::construct_lookup_polynomials(Circuit& circuit)
293{
294 {
295 BB_BENCH_NAME("constructing lookup table polynomials");
296 construct_lookup_table_polynomials<Flavor>(polynomials.get_tables(), circuit);
297 }
298 {
299 BB_BENCH_NAME("constructing lookup read counts");
300 construct_lookup_read_counts<Flavor>(polynomials.lookup_read_counts, polynomials.lookup_read_tags, circuit);
301 }
302}
303
307template <typename Flavor>
309 requires HasDataBus<Flavor>
310{
311 // Databus offset of NUM_DISABLED_ROWS_IN_SUMCHECK is forced by cross-flavor commitment compatibility and
312 // MegaZK masking; see allocate_databus_polynomials for the rationale.
313 size_t max_bus_size = 0;
314 bb::constexpr_for<0, NUM_BUS_COLUMNS, 1>([&]<size_t bus_idx>() {
315 const auto& bus_vec = circuit.get_bus_vector(bus_idx);
316 max_bus_size = std::max(max_bus_size, bus_vec.size());
317 auto entities = polynomials.template databus_entities_for_bus<bus_idx>();
318 auto& values_poly = entities[0];
319 auto& read_counts_poly = entities[1];
320 for (size_t idx = 0; idx < bus_vec.size(); ++idx) {
321 values_poly.at(NUM_DISABLED_ROWS_IN_SUMCHECK + idx) = circuit.get_variable(bus_vec[idx]);
322 read_counts_poly.at(NUM_DISABLED_ROWS_IN_SUMCHECK + idx) = bus_vec.get_read_count(idx);
323 }
324 });
325
326 // Compute a simple identity polynomial for use in the databus lookup argument.
327 auto& databus_id = polynomials.databus_id;
328 for (size_t i = 0; i < max_bus_size; ++i) {
329 databus_id.at(NUM_DISABLED_ROWS_IN_SUMCHECK + i) = i;
330 }
331}
332
339template <typename Flavor> void ProverInstance_<Flavor>::populate_memory_records(const Circuit& circuit)
340{
341 // Store the read/write records as indices into the full trace by accounting for the offset of the memory block.
342 uint32_t ram_rom_offset = circuit.blocks.memory.trace_offset();
343 memory_read_records.reserve(circuit.memory_read_records.size());
344 for (auto& index : circuit.memory_read_records) {
345 memory_read_records.emplace_back(index + ram_rom_offset);
346 }
347 memory_write_records.reserve(circuit.memory_write_records.size());
348 for (auto& index : circuit.memory_write_records) {
349 memory_write_records.emplace_back(index + ram_rom_offset);
350 }
351}
352
353template class ProverInstance_<UltraFlavor>;
354template class ProverInstance_<UltraZKFlavor>;
356#ifdef STARKNET_GARAGA_FLAVORS
359#endif
361template class ProverInstance_<MegaFlavor>;
362template class ProverInstance_<MegaZKFlavor>;
363template class ProverInstance_<MegaAvmFlavor>;
364
365} // 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
void construct_lookup_polynomials(Circuit &circuit)
size_t compute_dyadic_size(Circuit &)
Compute the minimum dyadic (power-of-2) circuit size.
void allocate_ecc_op_polynomials(const Circuit &)
void allocate_permutation_argument_polynomials()
void allocate_table_lookup_polynomials(const Circuit &)
typename Flavor::CircuitBuilder Circuit
void populate_memory_records(const Circuit &circuit)
Copy RAM/ROM record of reads and writes from the circuit to the instance.
void construct_databus_polynomials(Circuit &)
Populate the per-bus databus polynomials (values and read counts) and the identity polynomial.
void allocate_databus_polynomials(const Circuit &)
typename Flavor::Polynomial Polynomial
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)