Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
gemini_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
9#include "gemini.hpp"
10
47namespace bb {
48template <typename Curve>
49template <typename Transcript>
51 Fr circuit_size,
52 PolynomialBatcher& polynomial_batcher,
53 std::span<Fr> multilinear_challenge,
54 const CommitmentKey<Curve>& commitment_key,
55 const std::shared_ptr<Transcript>& transcript,
56 bool has_zk)
57{
58 // To achieve fixed proof size in Ultra and Mega, the multilinear opening challenge is be padded to a fixed size.
59 const size_t virtual_log_n = multilinear_challenge.size();
60 const size_t log_n = numeric::get_msb(static_cast<uint32_t>(circuit_size));
61 const size_t n = 1 << log_n;
62
63 // To achieve ZK, we mask the batched polynomial by a random polynomial of the same size
64 if (has_zk) {
65 Polynomial random_polynomial = Polynomial::random(n);
66 transcript->send_to_verifier("Gemini:masking_poly_comm", commitment_key.commit(random_polynomial));
67 // In the provers, the size of multilinear_challenge is `virtual_log_n`, but we need to evaluate the
68 // hiding polynomial as multilinear in log_n variables
69 transcript->send_to_verifier("Gemini:masking_poly_eval",
70 random_polynomial.evaluate_mle(multilinear_challenge.subspan(0, log_n)));
71 // Initialize batched unshifted poly with the random masking poly so that the full batched poly is masked
72 polynomial_batcher.set_random_polynomial(std::move(random_polynomial));
73 }
74
75 // Get the batching challenge
76 const Fr rho = transcript->template get_challenge<Fr>("rho");
77
78 Fr running_scalar = has_zk ? rho : 1; // ρ⁰ is used to batch the hiding polynomial
79
80 Polynomial A_0 = polynomial_batcher.compute_batched(rho, running_scalar);
81
82 // Construct the d-1 Gemini foldings of A₀(X)
83 std::vector<Polynomial> fold_polynomials = compute_fold_polynomials(log_n, multilinear_challenge, A_0, has_zk);
84
85 // If virtual_log_n >= log_n, pad the fold commitments with dummy group elements [1]_1.
86 for (size_t l = 0; l < virtual_log_n - 1; l++) {
87 std::string label = "Gemini:FOLD_" + std::to_string(l + 1);
88 // When has_zk is true, we are sending commitments to 0. Seems to work, but maybe brittle.
89 transcript->send_to_verifier(label, commitment_key.commit(fold_polynomials[l]));
90 }
91 const Fr r_challenge = transcript->template get_challenge<Fr>("Gemini:r");
92
93 const bool gemini_challenge_in_small_subgroup = (has_zk) && (r_challenge.pow(Curve::SUBGROUP_SIZE) == Fr(1));
94
95 // If Gemini evaluation challenge lands in the multiplicative subgroup used by SmallSubgroupIPA protocol, the
96 // evaluations of prover polynomials at this challenge would leak witness data.
97 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1194). Handle edge cases in PCS
98 if (gemini_challenge_in_small_subgroup) {
99 throw_or_abort("Gemini evaluation challenge is in the SmallSubgroup.");
100 }
101
102 // Compute polynomials A₀₊(X) = F(X) + G(X)/r and A₀₋(X) = F(X) - G(X)/r
103 auto [A_0_pos, A_0_neg] = polynomial_batcher.compute_partially_evaluated_batch_polynomials(r_challenge);
104 // Construct claims for the d + 1 univariate evaluations A₀₊(r), A₀₋(-r), and Foldₗ(−r^{2ˡ}), l = 1, ..., d-1
105 std::vector<Claim> claims = construct_univariate_opening_claims(
106 virtual_log_n, std::move(A_0_pos), std::move(A_0_neg), std::move(fold_polynomials), r_challenge);
107
108 for (size_t l = 1; l <= virtual_log_n; l++) {
109 std::string label = "Gemini:a_" + std::to_string(l);
110 transcript->send_to_verifier(label, claims[l].opening_pair.evaluation);
111 }
112
113 // If running Gemini for the Translator VM polynomials, A₀(r) = A₀₊(r) + P₊(rˢ) and A₀(-r) = A₀₋(-r) + P₋(rˢ)
114 // where s is the size of the interleaved group assumed even. The prover sends P₊(rˢ) and P₋(rˢ) to the verifier
115 // so it can reconstruct the evaluation of A₀(r) and A₀(-r) respectively
116 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1282)
117 if (polynomial_batcher.has_interleaved()) {
118 auto [P_pos, P_neg] = polynomial_batcher.compute_partially_evaluated_interleaved_polynomial(r_challenge);
119 Fr r_pow = r_challenge.pow(polynomial_batcher.get_group_size());
120 Fr P_pos_eval = P_pos.evaluate(r_pow);
121 Fr P_neg_eval = P_neg.evaluate(r_pow);
122 claims.emplace_back(Claim{ std::move(P_pos), { r_pow, P_pos_eval } });
123 transcript->send_to_verifier("Gemini:P_pos", P_pos_eval);
124 claims.emplace_back(Claim{ std::move(P_neg), { r_pow, P_neg_eval } });
125 transcript->send_to_verifier("Gemini:P_neg", P_neg_eval);
126 }
127
128 return claims;
129};
130
138template <typename Curve>
140 const size_t log_n, std::span<const Fr> multilinear_challenge, const Polynomial& A_0, const bool& has_zk)
141{
142 const size_t num_threads = get_num_cpus_pow2();
143
144 const size_t virtual_log_n = multilinear_challenge.size();
145
146 constexpr size_t efficient_operations_per_thread = 64; // A guess of the number of operation for which there
147 // would be a point in sending them to a separate thread
148
149 // Reserve and allocate space for m-1 Fold polynomials, the foldings of the full batched polynomial A₀
150 std::vector<Polynomial> fold_polynomials;
151 fold_polynomials.reserve(virtual_log_n - 1);
152 for (size_t l = 0; l < log_n - 1; ++l) {
153 // size of the previous polynomial/2
154 const size_t n_l = 1 << (log_n - l - 1);
155
156 // A_l_fold = Aₗ₊₁(X) = (1-uₗ)⋅even(Aₗ)(X) + uₗ⋅odd(Aₗ)(X)
157 fold_polynomials.emplace_back(Polynomial(n_l));
158 }
159
160 // A_l = Aₗ(X) is the polynomial being folded
161 // in the first iteration, we take the batched polynomial
162 // in the next iteration, it is the previously folded one
163 auto A_l = A_0.data();
164 for (size_t l = 0; l < log_n - 1; ++l) {
165 // size of the previous polynomial/2
166 const size_t n_l = 1 << (log_n - l - 1);
167
168 // Use as many threads as it is useful so that 1 thread doesn't process 1 element, but make sure that there is
169 // at least 1
170 size_t num_used_threads = std::min(n_l / efficient_operations_per_thread, num_threads);
171 num_used_threads = num_used_threads ? num_used_threads : 1;
172 size_t chunk_size = n_l / num_used_threads;
173 size_t last_chunk_size = (n_l % chunk_size) ? (n_l % num_used_threads) : chunk_size;
174
175 // Opening point is the same for all
176 const Fr u_l = multilinear_challenge[l];
177
178 // A_l_fold = Aₗ₊₁(X) = (1-uₗ)⋅even(Aₗ)(X) + uₗ⋅odd(Aₗ)(X)
179 auto A_l_fold = fold_polynomials[l].data();
180
181 parallel_for(num_used_threads, [&](size_t i) {
182 size_t current_chunk_size = (i == (num_used_threads - 1)) ? last_chunk_size : chunk_size;
183 for (std::ptrdiff_t j = (std::ptrdiff_t)(i * chunk_size);
184 j < (std::ptrdiff_t)((i * chunk_size) + current_chunk_size);
185 j++) {
186 // fold(Aₗ)[j] = (1-uₗ)⋅even(Aₗ)[j] + uₗ⋅odd(Aₗ)[j]
187 // = (1-uₗ)⋅Aₗ[2j] + uₗ⋅Aₗ[2j+1]
188 // = Aₗ₊₁[j]
189 A_l_fold[j] = A_l[j << 1] + u_l * (A_l[(j << 1) + 1] - A_l[j << 1]);
190 }
191 });
192 // set Aₗ₊₁ = Aₗ for the next iteration
193 A_l = A_l_fold;
194 }
195
196 // Perform virtual rounds.
197 // After the first `log_n - 1` rounds, the prover's `fold` univariates stabilize. With ZK, the verifier multiplies
198 // the evaluations by 0, otherwise, when `virtual_log_n > log_n`, the prover honestly computes and sends the
199 // constant folds.
200 const auto& last = fold_polynomials.back();
201 const Fr u_last = multilinear_challenge[log_n - 1];
202 const Fr final_eval = last.at(0) + u_last * (last.at(1) - last.at(0));
203 Polynomial const_fold(1);
204 // Temporary fix: when we're running a zk proof, the verifier uses a `padding_indicator_array`. So the evals in
205 // rounds past `log_n - 1` will be ignored. Hence the prover also needs to ignore them, otherwise Shplonk will fail.
206 const_fold.at(0) = final_eval * Fr(static_cast<int>(!has_zk));
207 fold_polynomials.emplace_back(const_fold);
208
209 // FOLD_{log_n+1}, ..., FOLD_{d_v-1}
210 Fr tail = Fr(1);
211 for (size_t k = log_n; k < virtual_log_n - 1; ++k) {
212 tail *= (Fr(1) - multilinear_challenge[k]); // multiply by (1 - u_k)
213 Polynomial next_const(1);
214 next_const.at(0) = final_eval * tail * Fr(static_cast<int>(!has_zk));
215 fold_polynomials.emplace_back(next_const);
216 }
217
218 return fold_polynomials;
219};
220
242template <typename Curve>
244 const size_t log_n,
245 Polynomial&& A_0_pos,
246 Polynomial&& A_0_neg,
247 std::vector<Polynomial>&& fold_polynomials,
248 const Fr& r_challenge)
249{
250 std::vector<Claim> claims;
251
252 // Compute evaluation of partially evaluated batch polynomial (positive) A₀₊(r)
253 Fr a_0_pos = A_0_pos.evaluate(r_challenge);
254 claims.emplace_back(Claim{ std::move(A_0_pos), { r_challenge, a_0_pos } });
255 // Compute evaluation of partially evaluated batch polynomial (negative) A₀₋(-r)
256 Fr a_0_neg = A_0_neg.evaluate(-r_challenge);
257 claims.emplace_back(Claim{ std::move(A_0_neg), { -r_challenge, a_0_neg } });
258
259 // Compute univariate opening queries rₗ = r^{2ˡ} for l = 0, 1, ..., m-1
260 std::vector<Fr> r_squares = gemini::powers_of_evaluation_challenge(r_challenge, log_n);
261
262 // Each fold polynomial Aₗ has to be opened at −r^{2ˡ} and r^{2ˡ}. To avoid storing two copies of Aₗ for l = 1,...,
263 // m-1, we use a flag that is processed by ShplonkProver.
264 const bool gemini_fold = true;
265
266 // Compute the remaining m opening pairs {−r^{2ˡ}, Aₗ(−r^{2ˡ})}, l = 1, ..., m-1.
267 for (size_t l = 0; l < log_n - 1; ++l) {
268 Fr evaluation = fold_polynomials[l].evaluate(-r_squares[l + 1]);
269 claims.emplace_back(Claim{ std::move(fold_polynomials[l]), { -r_squares[l + 1], evaluation }, gemini_fold });
270 }
271
272 return claims;
273};
274
275} // namespace bb
CommitmentKey object over a pairing group 𝔾₁.
Commitment commit(PolynomialSpan< const Fr > polynomial) const
Uses the ProverSRS to create a commitment to p(X)
Class responsible for computation of the batched multilinear polynomials required by the Gemini proto...
Definition gemini.hpp:123
std::pair< Polynomial, Polynomial > compute_partially_evaluated_interleaved_polynomial(const Fr &r_challenge)
Compute the partially evaluated polynomials P₊(X, r) and P₋(X, -r)
Definition gemini.hpp:298
void set_random_polynomial(Polynomial &&random)
Definition gemini.hpp:171
Polynomial compute_batched(const Fr &challenge, Fr &running_scalar)
Compute batched polynomial A₀ = F + G/X as the linear combination of all polynomials to be opened.
Definition gemini.hpp:195
std::pair< Polynomial, Polynomial > compute_partially_evaluated_batch_polynomials(const Fr &r_challenge)
Compute partially evaluated batched polynomials A₀(X, r) = A₀₊ = F + G/r, A₀(X, -r) = A₀₋ = F - G/r.
Definition gemini.hpp:257
static std::vector< Claim > construct_univariate_opening_claims(const size_t log_n, Polynomial &&A_0_pos, Polynomial &&A_0_neg, std::vector< Polynomial > &&fold_polynomials, const Fr &r_challenge)
Computes/aggragates d+1 univariate polynomial opening claims of the form {polynomial,...
typename Curve::ScalarField Fr
Definition gemini.hpp:104
static std::vector< Claim > prove(const Fr circuit_size, PolynomialBatcher &polynomial_batcher, std::span< Fr > multilinear_challenge, const CommitmentKey< Curve > &commitment_key, const std::shared_ptr< Transcript > &transcript, bool has_zk=false)
static std::vector< Polynomial > compute_fold_polynomials(const size_t log_n, std::span< const Fr > multilinear_challenge, const Polynomial &A_0, const bool &has_zk=false)
Computes d-1 fold polynomials Fold_i, i = 1, ..., d-1.
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
Fr evaluate_mle(std::span< const Fr > evaluation_points, bool shift=false) const
evaluate multi-linear extension p(X_0,…,X_{n-1}) = \sum_i a_i*L_i(X_0,…,X_{n-1}) at u = (u_0,...
Fr & at(size_t index)
Our mutable accessor, unlike operator[]. We abuse precedent a bit to differentiate at() and operator[...
Polynomial p and an opening pair (r,v) such that p(r) = v.
Definition claim.hpp:34
static constexpr size_t SUBGROUP_SIZE
Definition grumpkin.hpp:67
std::vector< Fr > powers_of_evaluation_challenge(const Fr r, const size_t num_squares)
Compute squares of folding challenge r.
Definition gemini.hpp:92
constexpr T get_msb(const T in)
Definition get_msb.hpp:47
Entry point for Barretenberg command-line interface.
size_t get_num_cpus_pow2()
Definition thread.hpp:18
void parallel_for(size_t num_iterations, const std::function< void(size_t)> &func)
Definition thread.cpp:72
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
Curve::ScalarField Fr
void throw_or_abort(std::string const &err)