Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
prover.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Completed, auditors: [Federico], commit: 0e37cb8}
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
7
8#include <algorithm>
9#include <cstdlib>
10
24
25namespace bb::avm2 {
26
28using FF = Flavor::FF;
29
40 const PCSCommitmentKey& commitment_key)
41 : proving_key(std::move(input_proving_key))
42 , vk(std::move(vk))
43 , prover_polynomials(*proving_key)
44 , commitment_key(commitment_key)
45{}
46
52{
53 FF vk_hash = vk->get_hash();
54 transcript->add_to_hash_buffer("avm_vk_hash", vk_hash);
55 vinfo("AVM vk hash in prover: ", vk_hash);
56}
57
66{
67 BB_BENCH_NAME("AvmProver::execute_public_inputs_round");
68
69 using C = ColumnAndShifts;
70 // Add the public inputs to the transcript so that the Sumcheck challenge depends both on the public inputs sent in
71 // the clear and the commitments to the columns that are purtported to contain them.
73 C::public_inputs_cols_0_,
74 C::public_inputs_cols_1_,
75 C::public_inputs_cols_2_,
76 C::public_inputs_cols_3_,
77 };
78
79 for (size_t i = 0; i < public_input_columns.size(); ++i) {
80 const Polynomial& public_input_col = prover_polynomials.get(public_input_columns[i]);
81 size_t public_input_col_size = public_input_col.size();
82 // Only hash each column's used rows; the trailing rows are zero and would only add hashing/MLE
83 // cost. They stay pinned to zero by the public-input consistency check (evaluate_public_input_column
84 // in verifier.cpp / recursive_verifier.cpp binds each committed column to the cleartext zero-padded
85 // to the full trace size).
86 for (size_t j = 0; j < AVM_PUBLIC_INPUTS_COLUMN_LENGTHS[i]; ++j) {
87 // The public inputs are added to the hash buffer, but do not increase the size of the proof
88 transcript->add_to_hash_buffer("public_input_" + std::to_string(i) + "_" + std::to_string(j),
89 j < public_input_col_size ? public_input_col.at(j) : FF(0));
90 }
91 }
92}
98{
99 BB_BENCH_NAME("AvmProver::execute_wire_commitments_round");
100 // Commit to all polynomials (apart from logderivative inverse polynomials, which are committed to in the later
101 // logderivative phase)
102 auto batch = commitment_key.start_batch();
103 for (const auto& [poly, label] : zip_view(prover_polynomials.get_wires(), prover_polynomials.get_wires_labels())) {
104 batch.add_to_batch(poly, label);
105 }
106 batch.commit_and_send_to_verifier(transcript);
107}
108
110{
111 BB_BENCH_NAME("AvmProver::execute_log_derivative_inverse_round");
112
113 auto [beta, gamma] = transcript->template get_challenges<FF>(std::array<std::string, 2>{ "beta", "gamma" });
116 std::vector<std::function<void()>> tasks;
117
118 // Iterate over all LookupRelations and for each relation create a task that:
119 // 1. Resizes the inverse polynomial based on the max end_index() of the source and destination selector
120 // 2. Computes the logderivative inverse
121 bb::constexpr_for<0, std::tuple_size_v<Flavor::LookupRelations>, 1>([&]<size_t relation_idx>() {
123 tasks.push_back([&]() {
124 // We need to resize the inverse polynomials for the relation, now that the selectors have been computed.
126 Relation::Settings::INVERSES,
127 Relation::Settings::SRC_SELECTOR,
128 Relation::Settings::DST_SELECTOR);
129
130 AVM_TRACK_TIME(std::string("prove/log_derivative_inverse_round/") + std::string(Relation::NAME),
131 (compute_logderivative_inverse<FF, Relation, Flavor::ProverPolynomials, false>(
133 });
134 });
135
136 // Execute all the tasks in parallel
137 bb::parallel_for(tasks.size(), [&](size_t i) { tasks[i](); });
138}
139
141{
142 BB_BENCH_NAME("AvmProver::execute_log_derivative_inverse_commitments_round");
143 auto batch = commitment_key.start_batch();
144 // Commit to all logderivative inverse polynomials and send to verifier
145 for (auto [derived_poly, label] :
146 zip_view(prover_polynomials.get_derived(), prover_polynomials.get_derived_labels())) {
147
148 batch.add_to_batch(derived_poly, label);
149 }
150 batch.commit_and_send_to_verifier(transcript);
151}
152
158{
159 BB_BENCH_NAME("AvmProver::execute_relation_check_rounds");
160 using Sumcheck = SumcheckProver<Flavor>;
161
162 // Multiply each linearly independent subrelation contribution by `alpha^i` for i = 0, ..., NUM_SUBRELATIONS - 1.
163 const FF alpha = transcript->template get_challenge<FF>("Sumcheck:alpha");
164
165 // Generate gate challenges
166 std::vector<FF> gate_challenges = transcript->template get_dyadic_powers_of_challenge<FF>(
167 "Sumcheck:gate_challenge", ProvingKey::log_circuit_size);
168
169 Sumcheck sumcheck(ProvingKey::circuit_size,
172 alpha,
173 gate_challenges,
176
177 sumcheck_output = sumcheck.prove();
178}
179
192{
193 BB_BENCH_NAME("AvmProver::execute_pcs_rounds");
194
196 using PolynomialBatcher = GeminiProver_<Curve>::PolynomialBatcher;
197 using Challenges = Flavor::AllEntities<FF>;
198
199 // Batch polynomials using short scalars
200 auto unshifted_polys = prover_polynomials.get_unshifted();
201 auto shifted_polys = prover_polynomials.get_to_be_shifted();
202
203 // Get short batching challenges from transcript
204 Challenges challenges;
205 auto unshifted_challenges_vec = transcript->template get_short_challenges<FF>(challenges.get_unshifted_labels());
206 std::ranges::move(unshifted_challenges_vec, challenges.get_unshifted().begin());
207 auto unshifted_challenges = challenges.get_unshifted();
208 auto shifted_challenges = challenges.get_to_be_shifted();
209
210 auto index_of_max_end_index = [](const auto& polys) {
211 // this assumes non-empty, returns an iterator
212 auto it = std::ranges::max_element(
213 polys.begin(), polys.end(), [](const auto& a, const auto& b) { return a.end_index() < b.end_index(); });
214
215 // retrieves the index of the max element
216 return static_cast<size_t>(std::distance(polys.begin(), it));
217 };
218
219 auto add_scaled_batched =
220 [](Polynomial& dst, const std::span<Polynomial>& sources, const std::span<FF>& scalars, const size_t skip_idx) {
221 const size_t num_slots = bb::get_num_cpus();
222 std::vector<Polynomial> batched_polys(num_slots);
223 for (auto& poly : batched_polys) {
224 poly = Polynomial(dst.size(), dst.virtual_size(), dst.start_index());
225 }
226
227 // Chunks are consumed dynamically via an atomic counter: faster threads naturally pick up
228 // more chunks while the slot they write to stays fixed for the life of their outer task.
229 std::atomic<size_t> next_poly(0);
230
231 // Accumulate polynomials: each thread picks up the next available polynomial
232 parallel_for(num_slots, [&](size_t slot_id) {
233 while (true) {
234 const size_t poly_id = next_poly.fetch_add(1, std::memory_order_relaxed);
235 if (poly_id >= sources.size()) {
236 break;
237 }
238 if (poly_id == skip_idx) {
239 continue;
240 }
241
242 const size_t start_idx = sources[poly_id].start_index();
243 const size_t end_idx = sources[poly_id].end_index();
244 for (size_t idx = start_idx; idx < end_idx; idx++) {
245 batched_polys[slot_id].at(idx) += scalars[poly_id] * sources[poly_id][idx];
246 }
247 }
248 });
249
250 for (const auto& poly : batched_polys) {
251 dst += poly;
252 }
253 };
254
255 // Batch to be shifted polys in their to_be_shifted form
256 // Search for poly with largest end index to avoid allocating a zero polynomial of circuit size
257 size_t max_idx_shifted = index_of_max_end_index(shifted_polys);
258
259 Polynomial batched_shifted = std::move(shifted_polys[max_idx_shifted]);
260 batched_shifted *= shifted_challenges[max_idx_shifted];
261 add_scaled_batched(batched_shifted, shifted_polys, shifted_challenges, max_idx_shifted);
262
263 // Batch unshifted polys
264 // Search the not to be shifted poly of max size to avoid allocating a zero polynomial of circuit size
265 // The not to be shifted polys are in the ranges [0, WIRES_TO_BE_SHIFTED_START_IDX) and
266 // [WIRES_TO_BE_SHIFTED_END_IDX, end]
267 size_t max_idx_left_side = index_of_max_end_index(unshifted_polys.subspan(0, WIRES_TO_BE_SHIFTED_START_IDX));
268 size_t max_idx_right_side = index_of_max_end_index(unshifted_polys.subspan(WIRES_TO_BE_SHIFTED_END_IDX));
269 size_t max_idx_unshifted = unshifted_polys[max_idx_left_side].end_index() >
270 unshifted_polys[max_idx_right_side + WIRES_TO_BE_SHIFTED_END_IDX].end_index()
271 ? max_idx_left_side
272 : max_idx_right_side + WIRES_TO_BE_SHIFTED_END_IDX;
273
274 // Assign the batched unshifted polynomial according to whether the largest poly was among the to be shifted polys
275 // or not
276 Polynomial batched_unshifted;
277 if (unshifted_polys[max_idx_unshifted].end_index() > batched_shifted.end_index()) {
278 batched_unshifted = std::move(unshifted_polys[max_idx_unshifted]);
279 batched_unshifted *= unshifted_challenges[max_idx_unshifted];
280 batched_unshifted += batched_shifted;
281 } else {
282 // batched_shifted has a start_index == 1 which would assert if we directly call
283 // batched_shifted.add_scaled(unshifted_polys[..]..). We therefore first initialize
284 // a zero polynomial with start_index == 0 and size = end_index.
285 batched_unshifted = Polynomial(batched_shifted.end_index());
286 batched_unshifted += batched_shifted;
287 batched_unshifted.add_scaled(unshifted_polys[max_idx_unshifted], unshifted_challenges[max_idx_unshifted]);
288 }
289
290 add_scaled_batched(batched_unshifted,
291 unshifted_polys.subspan(0, WIRES_TO_BE_SHIFTED_START_IDX),
292 unshifted_challenges.subspan(0, WIRES_TO_BE_SHIFTED_START_IDX),
293 max_idx_unshifted);
294 add_scaled_batched(batched_unshifted,
295 unshifted_polys.subspan(WIRES_TO_BE_SHIFTED_END_IDX),
296 unshifted_challenges.subspan(WIRES_TO_BE_SHIFTED_END_IDX),
297 max_idx_unshifted >= WIRES_TO_BE_SHIFTED_END_IDX
298 ? max_idx_unshifted - WIRES_TO_BE_SHIFTED_END_IDX
299 : unshifted_polys.size());
300
301 const size_t circuit_dyadic_size = numeric::round_up_power_2(batched_unshifted.end_index());
302
303 PolynomialBatcher polynomial_batcher(circuit_dyadic_size);
304 polynomial_batcher.set_unshifted(RefVector{ batched_unshifted });
305 polynomial_batcher.set_to_be_shifted_by_one(RefVector{ batched_shifted });
306
307 const OpeningClaim prover_opening_claim = ShpleminiProver_<Curve>::prove(
308 circuit_dyadic_size, polynomial_batcher, sumcheck_output.challenge, commitment_key, transcript);
309
311}
312
314{
315 return transcript->export_proof();
316}
317
319{
320 // Add vk hash to transcript.
322
323 // Add public inputs to transcript.
324 AVM_TRACK_TIME("prove/public_inputs_round", execute_public_inputs_round());
325
326 // Compute wire commitments.
327 AVM_TRACK_TIME("prove/wire_commitments_round", execute_wire_commitments_round());
328
329 // Compute log derivative inverses.
330 AVM_TRACK_TIME("prove/log_derivative_inverse_round", execute_log_derivative_inverse_round());
331
332 // Compute commitments to logderivative inverse polynomials.
333 AVM_TRACK_TIME("prove/log_derivative_inverse_commitments_round",
335
336 // Run sumcheck subprotocol.
338
339 // Execute PCS.
340 AVM_TRACK_TIME("prove/pcs_rounds", execute_pcs_rounds());
341
342 return export_proof();
343}
344} // namespace bb::avm2
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:264
CommitmentKey object over a pairing group 𝔾₁.
CommitBatch start_batch()
Class responsible for computation of the batched multilinear polynomials required by the Gemini proto...
Definition gemini.hpp:129
static void compute_opening_proof(const CK &ck, const ProverOpeningClaim< Curve > &opening_claim, const std::shared_ptr< Transcript > &prover_trancript)
Computes the KZG commitment to an opening proof polynomial at a single evaluation point.
Definition kzg.hpp:44
Unverified claim (C,r,v) for some witness polynomial p(X) such that.
Definition claim.hpp:55
size_t start_index() const
std::size_t virtual_size() const
void add_scaled(PolynomialSpan< const Fr > other, const Fr &scaling_factor)
adds the polynomial q(X) 'other', multiplied by a scaling factor.
size_t end_index() const
Fr & at(size_t index)
Our mutable accessor, unlike operator[]. We abuse precedent a bit to differentiate at() and operator[...
std::size_t size() const
Polynomial p and an opening pair (r,v) such that p(r) = v.
Definition claim.hpp:36
A template class for a reference vector. Behaves as if std::vector<T&> was possible.
A wrapper for Relations to expose methods used by the Sumcheck prover or verifier to add the contribu...
static OpeningClaim prove(size_t circuit_size, PolynomialBatcher &polynomial_batcher, std::span< FF > multilinear_challenge, const CommitmentKey< Curve > &commitment_key, const std::shared_ptr< Transcript > &transcript, const std::array< Polynomial, NUM_SMALL_IPA_COMMITMENTS > &libra_polynomials={}, const std::vector< Polynomial > &sumcheck_round_univariates={}, const std::vector< std::array< FF, 3 > > &sumcheck_round_evaluations={})
Definition shplemini.hpp:37
The implementation of the sumcheck Prover for statements of the form for multilinear polynomials .
Definition sumcheck.hpp:304
DataType & get(ColumnAndShifts c)
Definition flavor.hpp:132
static constexpr size_t log_circuit_size
Definition flavor.hpp:193
static constexpr size_t circuit_size
Definition flavor.hpp:192
AvmFlavorSettings::FF FF
Definition flavor.hpp:43
void execute_pcs_rounds()
Run the PCS to prove that the claimed evaluations are correct.
Definition prover.cpp:191
std::shared_ptr< Transcript > transcript
Definition prover.hpp:46
SumcheckOutput< Flavor > sumcheck_output
Definition prover.hpp:58
PCSCommitmentKey commitment_key
Definition prover.hpp:60
std::shared_ptr< VerificationKey > vk
Definition prover.hpp:53
void execute_relation_check_rounds()
Run Sumcheck resulting in u = (u_1,...,u_d) challenges and all evaluations at u being calculated.
Definition prover.cpp:157
void execute_preamble_round()
Add vk hash to transcript.
Definition prover.cpp:51
ProverPolynomials prover_polynomials
Definition prover.hpp:56
Flavor::Polynomial Polynomial
Definition prover.hpp:24
void execute_log_derivative_inverse_commitments_round()
Definition prover.cpp:140
void execute_log_derivative_inverse_round()
Definition prover.cpp:109
bb::RelationParameters< FF > relation_parameters
Definition prover.hpp:50
Flavor::FF FF
Definition prover.hpp:18
AvmProver(std::shared_ptr< ProvingKey > input_proving_key, std::shared_ptr< VerificationKey > vk, const PCSCommitmentKey &commitment_key)
Definition prover.cpp:38
HonkProof construct_proof()
Definition prover.cpp:318
void execute_public_inputs_round()
Add public inputs to transcript.
Definition prover.cpp:65
void execute_wire_commitments_round()
Compute commitments to all of the witness wires (apart from the logderivative inverse wires)
Definition prover.cpp:97
HonkProof export_proof()
Definition prover.cpp:313
#define vinfo(...)
Definition log.hpp:94
FF a
FF b
std::string label
void resize_inverses(AvmFlavor::ProverPolynomials &prover_polynomials, Column inverses_col, Column src_selector_col, Column dst_selector_col)
constexpr auto WIRES_TO_BE_SHIFTED_END_IDX
Definition columns.hpp:68
AvmFlavorSettings::FF FF
Definition field.hpp:10
ColumnAndShifts
Definition columns.hpp:35
constexpr auto WIRES_TO_BE_SHIFTED_START_IDX
Definition columns.hpp:67
constexpr std::array< std::size_t, AVM_NUM_PUBLIC_INPUT_COLUMNS > AVM_PUBLIC_INPUTS_COLUMN_LENGTHS
Definition constants.hpp:19
constexpr T round_up_power_2(const T in)
Definition get_msb.hpp:76
std::vector< fr > HonkProof
Definition proof.hpp:15
size_t get_num_cpus()
Definition thread.cpp:34
void parallel_for(size_t num_iterations, const std::function< void(size_t)> &func)
Definition thread.cpp:112
VerifierCommitmentKey< Curve > vk
STL namespace.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
#define AVM_TRACK_TIME(key, body)
Definition stats.hpp:18
void add_to_batch(Polynomial< Fr > &poly, const std::string &label, bool has_duplicates_hint=false, uint32_t measured_duplicate_count=0)