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"
18
19namespace bb {
20
21template <typename Flavor> ProverInstance_<Flavor>::ProverInstance_(Circuit& circuit)
22{
23 BB_BENCH_NAME("ProverInstance(Circuit&)");
24 vinfo("Constructing ProverInstance");
25
26 // Check pairing point tagging: either no pairing points were created,
27 // or all pairing points have been aggregated into a single equivalence class
28 BB_ASSERT(circuit.pairing_points_tagging.has_single_pairing_point_tag(),
29 "Pairing points must all be aggregated together. Either no pairing points should be created, or "
30 "all created pairing points must be aggregated into a single pairing point. Found "
31 << circuit.pairing_points_tagging.num_unique_pairing_points() << " different pairing points.");
32 // Check pairing point tagging: check that the pairing points have been set to public
33 BB_ASSERT(circuit.pairing_points_tagging.has_public_pairing_points() ||
34 !circuit.pairing_points_tagging.has_pairing_points(),
35 "Pairing points must be set to public in the circuit before constructing the ProverInstance.");
36
37 // ProverInstances can be constructed multiple times, hence, we check whether the circuit has been finalized
38 if (!circuit.circuit_finalized) {
39 circuit.finalize_circuit(/* ensure_nonzero = */ true);
40 }
41 metadata.dyadic_size = compute_dyadic_size(circuit);
42
43 // Find index of last non-trivial wire value in the trace
44 circuit.blocks.compute_offsets(); // compute offset of each block within the trace
45 for (auto& block : circuit.blocks.get()) {
46 if (block.size() > 0) {
47 final_active_wire_idx = block.trace_offset() + block.size() - 1;
48 }
49 }
50
51 {
52 BB_BENCH_NAME("allocating polynomials");
53 vinfo("allocating polynomials object in prover instance...");
54
55 populate_memory_records(circuit);
56 allocate_wires();
57 allocate_permutation_argument_polynomials();
58 allocate_selectors(circuit);
59 allocate_table_lookup_polynomials(circuit);
60 allocate_lagrange_polynomials();
61
62 if constexpr (IsMegaFlavor<Flavor>) {
63 allocate_ecc_op_polynomials(circuit);
64 }
65 if constexpr (HasDataBus<Flavor>) {
66 allocate_databus_polynomials(circuit);
67 }
68
69 // Set the shifted polynomials now that all of the to_be_shifted polynomials are defined.
70 polynomials.set_shifted();
71 }
72
73 // Construct and add to proving key the wire, selector and copy constraint polynomials
74 vinfo("populating trace...");
75 TraceToPolynomials<Flavor>::populate(circuit, polynomials);
76
77 if constexpr (IsMegaFlavor<Flavor>) {
78 BB_BENCH_NAME("constructing databus polynomials");
79 construct_databus_polynomials(circuit);
80 }
81
82 // Set the lagrange polynomials
83 polynomials.lagrange_first.at(0) = 1;
84 polynomials.lagrange_last.at(final_active_wire_idx) = 1;
85
86 construct_lookup_polynomials(circuit);
87
88 // Public inputs
89 metadata.num_public_inputs = circuit.blocks.pub_inputs.size();
90 metadata.pub_inputs_offset = circuit.blocks.pub_inputs.trace_offset();
91 for (size_t i = 0; i < metadata.num_public_inputs; ++i) {
92 size_t idx = i + metadata.pub_inputs_offset;
93 public_inputs.emplace_back(polynomials.w_r[idx]);
94 }
95
96 // Copy IPA proof if present
97 ipa_proof = circuit.ipa_proof;
98
99 if (std::getenv("BB_POLY_STATS")) {
100 analyze_prover_polynomials(polynomials);
101 }
102}
103
113template <typename Flavor> size_t ProverInstance_<Flavor>::compute_dyadic_size(Circuit& circuit)
114{
115 // For the lookup argument the circuit size must be at least as large as the sum of all tables used
116 const size_t tables_size = circuit.get_tables_size();
117
118 // minimum size of execution trace due to everything else
119 size_t min_size_of_execution_trace = circuit.blocks.get_total_content_size();
120
121 // The number of gates is the maximum required by the lookup argument or everything else, plus a zero row to allow
122 // for shifts.
123 size_t total_num_gates =
124 NUM_DISABLED_ROWS_IN_SUMCHECK + NUM_ZERO_ROWS + std::max(tables_size, min_size_of_execution_trace);
125
126 // Next power of 2 (dyadic circuit size)
127 return circuit.get_circuit_subgroup_size(total_num_gates);
128}
129
130template <typename Flavor> void ProverInstance_<Flavor>::allocate_wires()
131{
132 BB_BENCH_NAME("allocate_wires");
133
134 // If no ZK, allocate only the active range of the trace; else allocate full dyadic size to allow for blinding
135 const size_t wire_size = Flavor::HasZK ? dyadic_size() : trace_active_range_size();
136
137 for (auto& wire : polynomials.get_wires()) {
138 wire = Polynomial::shiftable(wire_size, dyadic_size());
139 }
140}
141
143{
144 BB_BENCH_NAME("allocate_permutation_argument_polynomials");
145
146 // Sigma and ID polynomials are zero outside the active trace range
147 for (auto& sigma : polynomials.get_sigmas()) {
148 sigma = Polynomial::shiftable(trace_active_range_size(), dyadic_size());
149 }
150 for (auto& id : polynomials.get_ids()) {
151 id = Polynomial::shiftable(trace_active_range_size(), dyadic_size());
152 }
153
154 // If no ZK, allocate only the active range of the trace; else allocate full dyadic size to allow for blinding
155 const size_t z_perm_size = Flavor::HasZK ? dyadic_size() : trace_active_range_size();
156 polynomials.z_perm = Polynomial::shiftable(z_perm_size, dyadic_size());
157}
158
160{
161 BB_BENCH_NAME("allocate_lagrange_polynomials");
162
163 polynomials.lagrange_first = Polynomial(
164 /* size=*/1, /*virtual size=*/dyadic_size(), /*start_index=*/0);
165
166 polynomials.lagrange_last = Polynomial(
167 /* size=*/1, /*virtual size=*/dyadic_size(), /*start_index=*/final_active_wire_idx);
168}
169
170template <typename Flavor> void ProverInstance_<Flavor>::allocate_selectors(const Circuit& circuit)
171{
172 BB_BENCH_NAME("allocate_selectors");
173
174 // Define gate selectors over the block they are isolated to
175 for (auto [selector, block] : zip_view(polynomials.get_gate_selectors(), circuit.blocks.get_gate_blocks())) {
176 selector = Polynomial(block.size(), dyadic_size(), block.trace_offset());
177 }
178
179 // Set the other non-gate selector polynomials (e.g. q_l, q_r, q_m etc.) to active trace size
180 for (auto& selector : polynomials.get_non_gate_selectors()) {
181 selector = Polynomial(trace_active_range_size(), dyadic_size());
182 }
183}
184
185template <typename Flavor> void ProverInstance_<Flavor>::allocate_table_lookup_polynomials(const Circuit& circuit)
186{
187 BB_BENCH_NAME("allocate_table_lookup_and_lookup_read_polynomials");
188
189 const size_t tables_size = circuit.get_tables_size(); // cumulative size of all lookup tables
190
191 // Allocate polynomials containing the actual table data; offset to align with the lookup gate block
192 BB_ASSERT_GT(dyadic_size(), tables_size);
193 for (auto& table_poly : polynomials.get_tables()) {
194 table_poly = Polynomial(tables_size, dyadic_size());
195 }
196
197 // Read counts and tags: track which table entries have been read
198 // For non-ZK, allocate just the table size; for ZK: allocate full dyadic_size
199 const size_t counts_and_tags_size = Flavor::HasZK ? dyadic_size() : tables_size;
200 polynomials.lookup_read_counts = Polynomial(counts_and_tags_size, dyadic_size());
201 polynomials.lookup_read_tags = Polynomial(counts_and_tags_size, dyadic_size());
202
203 // Lookup inverses: used in the log-derivative lookup argument
204 // Must cover both the lookup gate block (where reads occur) and the table data itself
205 const size_t lookup_block_end = circuit.blocks.lookup.trace_offset() + circuit.blocks.lookup.size();
206 const size_t lookup_inverses_end = std::max(lookup_block_end, tables_size);
207
208 const size_t lookup_inverses_size = (Flavor::HasZK ? dyadic_size() : lookup_inverses_end);
209 polynomials.lookup_inverses = Polynomial(lookup_inverses_size, dyadic_size());
210}
211
212template <typename Flavor>
214 requires IsMegaFlavor<Flavor>
215{
216 BB_BENCH_NAME("allocate_ecc_op_polynomials");
217
218 // Allocate the ecc op wires and selector
219 // Note: ECC op wires are not blinded directly so we do not need to allocate full dyadic size for ZK
220 const size_t ecc_op_block_size = circuit.blocks.ecc_op.size();
221 for (auto& wire : polynomials.get_ecc_op_wires()) {
222 wire = Polynomial(ecc_op_block_size, dyadic_size());
223 }
224 polynomials.lagrange_ecc_op = Polynomial(ecc_op_block_size, dyadic_size());
225}
226
227template <typename Flavor>
229 requires HasDataBus<Flavor>
230{
231 BB_BENCH_NAME("allocate_databus_and_lookup_inverse_polynomials");
232
233 const size_t calldata_size = circuit.get_calldata().size();
234 const size_t sec_calldata_size = circuit.get_secondary_calldata().size();
235 const size_t return_data_size = circuit.get_return_data().size();
236
237 // Allocate only enough space for the databus data; for ZK, allocate full dyadic size
238 const size_t calldata_poly_size = Flavor::HasZK ? dyadic_size() : calldata_size;
239 const size_t sec_calldata_poly_size = Flavor::HasZK ? dyadic_size() : sec_calldata_size;
240 const size_t return_data_poly_size = Flavor::HasZK ? dyadic_size() : return_data_size;
241
242 polynomials.calldata = Polynomial(calldata_poly_size, dyadic_size());
243 polynomials.calldata_read_counts = Polynomial(calldata_poly_size, dyadic_size());
244 polynomials.calldata_read_tags = Polynomial(calldata_poly_size, dyadic_size());
245
246 polynomials.secondary_calldata = Polynomial(sec_calldata_poly_size, dyadic_size());
247 polynomials.secondary_calldata_read_counts = Polynomial(sec_calldata_poly_size, dyadic_size());
248 polynomials.secondary_calldata_read_tags = Polynomial(sec_calldata_poly_size, dyadic_size());
249
250 polynomials.return_data = Polynomial(return_data_poly_size, dyadic_size());
251 polynomials.return_data_read_counts = Polynomial(return_data_poly_size, dyadic_size());
252 polynomials.return_data_read_tags = Polynomial(return_data_poly_size, dyadic_size());
253
254 // Databus lookup inverses: used in the log-derivative lookup argument
255 // Must cover both the databus gate block (where reads occur) and the databus data itself
256 const size_t q_busread_end = circuit.blocks.busread.trace_offset() + circuit.blocks.busread.size();
257 size_t calldata_inverses_size = Flavor::HasZK ? dyadic_size() : std::max(calldata_size, q_busread_end);
258 size_t sec_calldata_inverses_size = Flavor::HasZK ? dyadic_size() : std::max(sec_calldata_size, q_busread_end);
259 size_t return_data_inverses_size = Flavor::HasZK ? dyadic_size() : std::max(return_data_size, q_busread_end);
260
261 polynomials.calldata_inverses = Polynomial(calldata_inverses_size, dyadic_size());
262 polynomials.secondary_calldata_inverses = Polynomial(sec_calldata_inverses_size, dyadic_size());
263 polynomials.return_data_inverses = Polynomial(return_data_inverses_size, dyadic_size());
264
265 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1555): Allocate minimum size >1 to avoid point at
266 // infinity commitment.
267 const size_t max_databus_column_size = std::max({ calldata_size, sec_calldata_size, return_data_size, 2UL });
268 polynomials.databus_id = Polynomial(max_databus_column_size, dyadic_size());
269}
270
271template <typename Flavor> void ProverInstance_<Flavor>::construct_lookup_polynomials(Circuit& circuit)
272{
273 {
274 BB_BENCH_NAME("constructing lookup table polynomials");
275 construct_lookup_table_polynomials<Flavor>(polynomials.get_tables(), circuit);
276 }
277 {
278 BB_BENCH_NAME("constructing lookup read counts");
279 construct_lookup_read_counts<Flavor>(polynomials.lookup_read_counts, polynomials.lookup_read_tags, circuit);
280 }
281}
282
286template <typename Flavor>
288 requires HasDataBus<Flavor>
289{
290 auto& calldata_poly = polynomials.calldata;
291 auto& calldata_read_counts = polynomials.calldata_read_counts;
292 auto& calldata_read_tags = polynomials.calldata_read_tags;
293 auto& secondary_calldata_poly = polynomials.secondary_calldata;
294 auto& secondary_calldata_read_counts = polynomials.secondary_calldata_read_counts;
295 auto& secondary_calldata_read_tags = polynomials.secondary_calldata_read_tags;
296 auto& return_data_poly = polynomials.return_data;
297 auto& return_data_read_counts = polynomials.return_data_read_counts;
298 auto& return_data_read_tags = polynomials.return_data_read_tags;
299
300 const auto& calldata = circuit.get_calldata();
301 const auto& secondary_calldata = circuit.get_secondary_calldata();
302 const auto& return_data = circuit.get_return_data();
303
304 // Note: Databus columns start from index 0. If this ever changes, make sure to also update the active range
305 // construction in ExecutionTraceUsageTracker::update(). We do not utilize a zero row for databus columns.
306 for (size_t idx = 0; idx < calldata.size(); ++idx) {
307 calldata_poly.at(idx) = circuit.get_variable(calldata[idx]); // calldata values
308 calldata_read_counts.at(idx) = calldata.get_read_count(idx); // read counts
309 calldata_read_tags.at(idx) = calldata_read_counts[idx] > 0 ? 1 : 0; // has row been read or not
310 }
311 for (size_t idx = 0; idx < secondary_calldata.size(); ++idx) {
312 secondary_calldata_poly.at(idx) = circuit.get_variable(secondary_calldata[idx]); // secondary_calldata values
313 secondary_calldata_read_counts.at(idx) = secondary_calldata.get_read_count(idx); // read counts
314 secondary_calldata_read_tags.at(idx) =
315 secondary_calldata_read_counts[idx] > 0 ? 1 : 0; // has row been read or not
316 }
317 for (size_t idx = 0; idx < return_data.size(); ++idx) {
318 return_data_poly.at(idx) = circuit.get_variable(return_data[idx]); // return data values
319 return_data_read_counts.at(idx) = return_data.get_read_count(idx); // read counts
320 return_data_read_tags.at(idx) = return_data_read_counts[idx] > 0 ? 1 : 0; // has row been read or not
321 }
322
323 auto& databus_id = polynomials.databus_id;
324 // Compute a simple identity polynomial for use in the databus lookup argument
325 for (size_t i = 0; i < databus_id.size(); ++i) {
326 databus_id.at(i) = i;
327 }
328}
329
336template <typename Flavor> void ProverInstance_<Flavor>::populate_memory_records(const Circuit& circuit)
337{
338 // Store the read/write records as indices into the full trace by accounting for the offset of the memory block.
339 uint32_t ram_rom_offset = circuit.blocks.memory.trace_offset();
340 memory_read_records.reserve(circuit.memory_read_records.size());
341 for (auto& index : circuit.memory_read_records) {
342 memory_read_records.emplace_back(index + ram_rom_offset);
343 }
344 memory_write_records.reserve(circuit.memory_write_records.size());
345 for (auto& index : circuit.memory_write_records) {
346 memory_write_records.emplace_back(index + ram_rom_offset);
347 }
348}
349
350template class ProverInstance_<UltraFlavor>;
351template class ProverInstance_<UltraZKFlavor>;
353#ifdef STARKNET_GARAGA_FLAVORS
356#endif
358template class ProverInstance_<MegaFlavor>;
359template class ProverInstance_<MegaZKFlavor>;
360template class ProverInstance_<MegaAvmFlavor>;
361
362} // namespace bb
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:113
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:225
static constexpr bool HasZK
static Polynomial shiftable(size_t virtual_size)
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 databus polynomials (calldata, secondary_calldata, return_data) and their read counts/ta...
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
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.
std::vector< MemoryValue > calldata