Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
protogalaxy_prover_impl.hpp
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
7#pragma once
16
17namespace bb {
18template <IsUltraOrMegaHonk Flavor>
20 std::shared_ptr<VerifierInstance> vk,
21 const std::string& domain_separator)
22{
23
24 BB_BENCH_NAME("ProtogalaxyProver::run_oink_prover_on_one_incomplete_instance");
25 OinkProver<Flavor> oink_prover(key, vk->vk, transcript, domain_separator + '_');
26 oink_prover.prove();
27}
28
30{
31 auto key = prover_insts_to_fold[0];
32 auto domain_separator = std::to_string(0);
33 if (!key->is_complete) {
34 run_oink_prover_on_one_incomplete_instance(key, verifier_insts_to_fold[0], domain_separator);
35 // Get the gate challenges for sumcheck/combiner computation
36 key->gate_challenges =
37 transcript->template get_powers_of_challenge<FF>(domain_separator + "_gate_challenge", CONST_PG_LOG_N);
38 }
39
40 // Complete the incoming instance
41 key = prover_insts_to_fold[1];
42 domain_separator = std::to_string(1);
43 run_oink_prover_on_one_incomplete_instance(key, verifier_insts_to_fold[1], domain_separator);
44
45 accumulator = prover_insts_to_fold[0];
46};
47
48template <IsUltraOrMegaHonk Flavor>
50 Flavor>::perturbator_round(const std::shared_ptr<const ProverInstance>& accumulator)
51{
52 BB_BENCH_NAME("ProtogalaxyProver_::perturbator_round");
53
54 const std::vector<FF> deltas = transcript->template get_powers_of_challenge<FF>("delta", CONST_PG_LOG_N);
55 // An honest prover with valid initial key computes that the perturbator is 0 in the first round
56 const Polynomial<FF> perturbator = accumulator->is_relaxed_instance
57 ? pg_internal.compute_perturbator(accumulator, deltas)
58 : Polynomial<FF>(CONST_PG_LOG_N + 1);
59 // Prover doesn't send the constant coefficient of F because this is supposed to be equal to the target sum of
60 // the accumulator which the folding verifier has from the previous iteration.
61 for (size_t idx = 1; idx <= CONST_PG_LOG_N; idx++) {
62 transcript->send_to_verifier("perturbator_" + std::to_string(idx), perturbator[idx]);
63 }
64
65 return std::make_tuple(deltas, perturbator);
66};
67
68template <IsUltraOrMegaHonk Flavor>
72 typename Flavor::FF,
74ProtogalaxyProver_<Flavor>::combiner_quotient_round(const std::vector<FF>& gate_challenges,
75 const std::vector<FF>& deltas,
76 const ProverInstances& instances)
77{
78 BB_BENCH_NAME("ProtogalaxyProver_::combiner_quotient_round");
79
80 // Generate the challenge (\alpha in the paper) at which to evaluate the perturbator (F in the paper)
81 const FF perturbator_challenge = transcript->template get_challenge<FF>("perturbator_challenge");
82
83 // Step 8. in the paper: \beta is updated to \beta + \alpha * \delta
84 const std::vector<FF> updated_gate_challenges =
85 update_gate_challenges(perturbator_challenge, gate_challenges, deltas);
86
87 const GateSeparatorPolynomial<FF> gate_separators{ updated_gate_challenges,
88 numeric::get_msb(get_max_dyadic_size()) };
89
90 // Fold the batching challenges (\alphas in the ProverInstances)
91 const UnivariateSubrelationSeparators alphas = PGInternal::compute_and_extend_alphas(instances);
92 // Fold the relation parameters in the ProverInstances
93 const UnivariateRelationParameters relation_parameters =
94 PGInternal::template compute_extended_relation_parameters<UnivariateRelationParameters>(instances);
95
96 // Note: {} is required to initialize the tuple contents. Otherwise the univariates contain garbage.
97 TupleOfTuplesOfUnivariates accumulators{};
98 auto combiner = pg_internal.compute_combiner(instances, gate_separators, relation_parameters, alphas, accumulators);
99
100 const FF perturbator_evaluation = perturbator.evaluate(perturbator_challenge);
101 const CombinerQuotient combiner_quotient = PGInternal::compute_combiner_quotient(perturbator_evaluation, combiner);
102
103 for (size_t idx = NUM_INSTANCES; idx < BATCHED_EXTENDED_LENGTH; idx++) {
104 transcript->send_to_verifier("combiner_quotient_" + std::to_string(idx), combiner_quotient.value_at(idx));
105 }
106
107 return std::make_tuple(
108 updated_gate_challenges, alphas, relation_parameters, perturbator_evaluation, combiner_quotient);
109}
110
114template <IsUltraOrMegaHonk Flavor>
116 const ProverInstances& instances,
117 const CombinerQuotient& combiner_quotient,
119 const UnivariateRelationParameters& univariate_relation_parameters,
120 const FF& perturbator_evaluation)
121{
122 BB_BENCH_NAME("ProtogalaxyProver_::update_target_sum_and_fold");
123
124 std::shared_ptr<ProverInstance> accumulator = instances[0];
125 std::shared_ptr<ProverInstance> incoming = instances[1];
126 accumulator->is_relaxed_instance = true;
127
128 // At this point the virtual sizes of the polynomials should already agree
129 BB_ASSERT_EQ(accumulator->polynomials.w_l.virtual_size(), incoming->polynomials.w_l.virtual_size());
130
131 // Step 12., \gamma challenge
132 const FF combiner_challenge = transcript->template get_challenge<FF>("combiner_quotient_challenge");
133
134 // Step 13., compute the next target sum (for its own use; verifier must compute its own values)
135 auto [vanishing_polynomial_at_challenge, lagranges] =
136 PGInternal::compute_vanishing_polynomial_and_lagranges(combiner_challenge);
137 accumulator->target_sum = perturbator_evaluation * lagranges[0] +
138 vanishing_polynomial_at_challenge * combiner_quotient.evaluate(combiner_challenge);
139
140 // Check whether the incoming key has a larger trace overflow than the accumulator. If so, the memory structure of
141 // the accumulator polynomials will not be sufficient to contain the contribution from the incoming polynomials. The
142 // solution is to simply reverse the order or the terms in the linear combination by swapping the polynomials and
143 // the lagrange coefficients between the accumulator and the incoming key.
144 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1417): make this swapping logic more robust.
145 bool swap_polys = incoming->get_overflow_size() > accumulator->get_overflow_size();
146 if (swap_polys) {
147 std::swap(accumulator->polynomials, incoming->polynomials); // swap the polys
148 std::swap(lagranges[0], lagranges[1]); // swap the lagrange coefficients so the sum is unchanged
149 accumulator->set_dyadic_size(incoming->dyadic_size()); // update dyadic size of accumulator
150 accumulator->set_overflow_size(incoming->get_overflow_size()); // swap overflow size
151 }
152
153 // Fold the prover polynomials
154
155 // Convert the polynomials into spans to remove boundary checks and if checks that normally apply when calling
156 // getter/setters in Polynomial (see SharedShiftedVirtualZeroesArray::get)
157 auto accumulator_polys = accumulator->polynomials.get_unshifted();
158 auto key_polys = incoming->polynomials.get_unshifted();
159 const size_t num_polys = key_polys.size();
160
163 acc_spans.reserve(num_polys);
164 key_spans.reserve(num_polys);
165 for (size_t i = 0; i < num_polys; ++i) {
166 acc_spans.emplace_back(static_cast<PolynomialSpan<FF>>(accumulator_polys[i]));
167 key_spans.emplace_back(static_cast<PolynomialSpan<FF>>(key_polys[i]));
168 }
169
170 parallel_for([&acc_spans, &key_spans, &lagranges, &combiner_challenge, &swap_polys](const ThreadChunk& chunk) {
171 for (auto [acc_poly, key_poly] : zip_view(acc_spans, key_spans)) {
173 acc_poly.start_index,
174 key_poly.start_index,
175 "Folding a smaller polynomial into a larger one. This is only allowed when using structured traces.");
177 acc_poly.end_index(),
178 key_poly.end_index(),
179 "Folding a smaller polynomial into a larger one. This is only allowed when using structured traces.");
180 size_t offset = acc_poly.start_index;
181 for (size_t idx : chunk.range(acc_poly.size(), offset)) {
182 if ((idx < key_poly.start_index) || (idx >= key_poly.end_index())) {
183 acc_poly[idx] *= lagranges[0];
184 } else {
185 // acc * lagranges[0] + key * lagranges[1] =
186 // acc + (key - acc) * combiner_challenge (if !swap_polys)
187 // key + (acc - key) * combiner_challenge (if swap_polys)
188 if (swap_polys) {
189 acc_poly[idx] = key_poly[idx] + (acc_poly[idx] - key_poly[idx]) * combiner_challenge;
190 } else {
191 acc_poly[idx] = acc_poly[idx] + (key_poly[idx] - acc_poly[idx]) * combiner_challenge;
192 }
193 }
194 }
195 }
196 });
197
198 {
199 BB_BENCH_NAME("ProtogalaxyProver_::update_target_sum_and_fold::update_alphas_and_relation_parameters");
200
201 parallel_for([&](const ThreadChunk& chunk) {
202 // Evaluate the combined batching α_i univariate at challenge to obtain next α_i and send it to the
203 // verifier, where i ∈ {0,...,NUM_SUBRELATIONS - 1}
204 for (size_t i : chunk.range(NUM_SUBRELATIONS)) {
205 accumulator->alphas[i] = alphas[i].evaluate(combiner_challenge);
206 }
207 });
208
209 auto univariate_params_to_fold = univariate_relation_parameters.get_to_fold();
210 auto accumulator_params_to_fold = accumulator->relation_parameters.get_to_fold();
211 parallel_for([&](const ThreadChunk& chunk) {
212 // Evaluate each relation parameter univariate at challenge to obtain the folded relation parameters.
213 for (size_t i : chunk.range(univariate_params_to_fold.size())) {
214 accumulator_params_to_fold[i] = univariate_params_to_fold[i].evaluate(combiner_challenge);
215 }
216 });
217 }
218}
219
220template <IsUltraOrMegaHonk Flavor> FoldingResult<Flavor> ProtogalaxyProver_<Flavor>::prove()
221{
222 BB_BENCH_NAME("ProtogalaxyProver::prove");
223
224 // Ensure instances are all of the same size
225 size_t max_circuit_size = 0;
226 for (size_t idx = 0; idx < NUM_INSTANCES; ++idx) {
227 max_circuit_size = std::max(max_circuit_size, prover_insts_to_fold[idx]->dyadic_size());
228 }
229 for (size_t idx = 0; idx < NUM_INSTANCES; ++idx) {
230 if (prover_insts_to_fold[idx]->dyadic_size() != max_circuit_size) {
231 info("ProtogalaxyProver: circuit size mismatch - increasing virtual size of key ",
232 idx,
233 " from ",
234 prover_insts_to_fold[idx]->dyadic_size(),
235 " to ",
236 max_circuit_size);
237 prover_insts_to_fold[idx]->polynomials.increase_polynomials_virtual_size(max_circuit_size);
238 }
239 }
240
241 run_oink_prover_on_each_incomplete_instance();
242 vinfo("oink prover on each incomplete key");
243
244 std::tie(deltas, perturbator) = perturbator_round(accumulator);
245 vinfo("perturbator round");
246
247 std::tie(accumulator->gate_challenges, alphas, relation_parameters, perturbator_evaluation, combiner_quotient) =
248 combiner_quotient_round(accumulator->gate_challenges, deltas, prover_insts_to_fold);
249 vinfo("combiner quotient round");
250
251 update_target_sum_and_fold(
252 prover_insts_to_fold, combiner_quotient, alphas, relation_parameters, perturbator_evaluation);
253 vinfo("folded");
254
255 return FoldingResult<Flavor>{ .accumulator = prover_insts_to_fold[0], .proof = transcript->export_proof() };
256}
257} // namespace bb
#define BB_ASSERT_GTE(left, right,...)
Definition assert.hpp:133
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:88
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:163
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:218
Curve::ScalarField FF
Class for all the oink rounds, which are shared between the folding prover and ultra prover.
void prove()
Oink Prover function that runs all the rounds of the verifier.
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
std::array< std::shared_ptr< ProverInstance >, NUM_INSTANCES > ProverInstances
BB_PROFILE FoldingResult< Flavor > prove()
Execute the folding prover.
std::tuple< std::vector< FF >, UnivariateSubrelationSeparators, UnivariateRelationParameters, FF, CombinerQuotient > combiner_quotient_round(const std::vector< FF > &gate_challenges, const std::vector< FF > &deltas, const ProverInstances &instances)
Steps 6 - 11 of the paper.
typename Flavor::template ProtogalaxyTupleOfTuplesOfUnivariates< NUM_INSTANCES > TupleOfTuplesOfUnivariates
void run_oink_prover_on_one_incomplete_instance(std::shared_ptr< ProverInstance >, std::shared_ptr< VerifierInstance >, const std::string &domain_separator)
For each Prover instance derived from a circuit, prior to folding, we need to complete the computatio...
void run_oink_prover_on_each_incomplete_instance()
Create inputs to folding protocol (an Oink interaction).
std::array< Univariate< FF, BATCHED_EXTENDED_LENGTH >, NUM_SUBRELATIONS - 1 > UnivariateSubrelationSeparators
void update_target_sum_and_fold(const ProverInstances &instances, const CombinerQuotient &combiner_quotient, const UnivariateSubrelationSeparators &alphas, const UnivariateRelationParameters &univariate_relation_parameters, const FF &perturbator_evaluation)
Steps 12 - 13 of the paper plus the prover folding work.
Fr & value_at(size_t i)
Fr evaluate(const Fr &u) const
Evaluate a univariate at a point u not known at compile time and assumed not to be in the domain (els...
#define vinfo(...)
Definition log.hpp:79
void info(Args... args)
Definition log.hpp:74
ssize_t offset
Definition engine.cpp:36
constexpr T get_msb(const T in)
Definition get_msb.hpp:47
Entry point for Barretenberg command-line interface.
std::vector< FF > update_gate_challenges(const FF &perturbator_challenge, const std::vector< FF > &gate_challenges, const std::vector< FF > &init_challenges)
Compute the gate challenges used in the combiner calculation.
void parallel_for(size_t num_iterations, const std::function< void(size_t)> &func)
Definition thread.cpp:111
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
The result of one iteraton of Protogalaxy proving, containing a new accumulator as well as the proof ...
std::shared_ptr< ProverInstance_< Flavor > > accumulator
Implementation of the methods for the -polynomials used in in Sumcheck.
RefArray< T, NUM_TO_FOLD > get_to_fold()
auto range(size_t size, size_t offset=0) const
Definition thread.hpp:161