Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
shplonk.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Khashayar], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
14
29namespace bb {
30
36template <typename Curve> class ShplonkProver_ {
37 using Fr = typename Curve::ScalarField;
39
40 public:
49 static Polynomial compute_batched_quotient(const size_t virtual_log_n,
50 std::span<const ProverOpeningClaim<Curve>> opening_claims,
51 const Fr& nu,
52 std::span<Fr> gemini_fold_pos_evaluations,
53 std::span<const ProverOpeningClaim<Curve>> libra_opening_claims,
54 std::span<const ProverOpeningClaim<Curve>> sumcheck_round_claims)
55 {
56 // Find the maximum polynomial size among all claims to determine the dyadic size of the batched polynomial.
57 size_t max_poly_size{ 0 };
58
59 for (const auto& claim_set : { opening_claims, libra_opening_claims, sumcheck_round_claims }) {
60 for (const auto& claim : claim_set) {
61 max_poly_size = std::max(max_poly_size, claim.polynomial.size());
62 }
63 }
64 // The polynomials in Sumcheck Round claims and Libra opening claims are generally not dyadic,
65 // so we round up to the next power of 2.
66 max_poly_size = numeric::round_up_power_2(max_poly_size);
67
68 // Q(X) = ∑ⱼ νʲ ⋅ ( fⱼ(X) − vⱼ) / ( X − xⱼ )
69 Polynomial Q(max_poly_size);
70 Polynomial tmp(max_poly_size);
71
72 Fr current_nu = Fr::one();
73
74 size_t fold_idx = 0;
75 for (const auto& claim : opening_claims) {
76
77 // Gemini Fold Polynomials have to be opened at -r^{2^j} and r^{2^j}.
78 if (claim.gemini_fold) {
79 tmp = claim.polynomial;
80 tmp.at(0) = tmp[0] - gemini_fold_pos_evaluations[fold_idx++];
81 tmp.factor_roots(-claim.opening_pair.challenge);
82 // Add the claim quotient to the batched quotient polynomial
83 Q.add_scaled(tmp, current_nu);
84 current_nu *= nu;
85 }
86
87 // Compute individual claim quotient tmp = ( fⱼ(X) − vⱼ) / ( X − xⱼ )
88 tmp = claim.polynomial;
89 tmp.at(0) = tmp[0] - claim.opening_pair.evaluation;
90 tmp.factor_roots(claim.opening_pair.challenge);
91 // Add the claim quotient to the batched quotient polynomial
92 Q.add_scaled(tmp, current_nu);
93 current_nu *= nu;
94 }
95 // We use the same batching challenge for Gemini and Libra opening claims. The number of the claims
96 // batched before adding Libra commitments and evaluations is bounded by 2 * `virtual_log_n`,
97 // which is the number of fold claims including the dummy ones.
98 if (!libra_opening_claims.empty()) {
99 current_nu = nu.pow(2 * virtual_log_n);
100 }
101
102 for (const auto& claim : libra_opening_claims) {
103 // Compute individual claim quotient tmp = ( fⱼ(X) − vⱼ) / ( X − xⱼ )
104 tmp = claim.polynomial;
105 tmp.at(0) = tmp[0] - claim.opening_pair.evaluation;
106 tmp.factor_roots(claim.opening_pair.challenge);
107
108 // Add the claim quotient to the batched quotient polynomial
109 Q.add_scaled(tmp, current_nu);
110 current_nu *= nu;
111 }
112
113 for (const auto& claim : sumcheck_round_claims) {
114
115 // Compute individual claim quotient tmp = ( fⱼ(X) − vⱼ) / ( X − xⱼ )
116 tmp = claim.polynomial;
117 tmp.at(0) = tmp[0] - claim.opening_pair.evaluation;
118 tmp.factor_roots(claim.opening_pair.challenge);
119
120 // Add the claim quotient to the batched quotient polynomial
121 Q.add_scaled(tmp, current_nu);
122 current_nu *= nu;
123 }
124 // Return batched quotient polynomial Q(X)
125 return Q;
126 };
127
139 const size_t virtual_log_n,
140 std::span<ProverOpeningClaim<Curve>> opening_claims,
141 Polynomial& batched_quotient_Q,
142 const Fr& nu_challenge,
143 const Fr& z_challenge,
144 std::span<Fr> gemini_fold_pos_evaluations,
145 std::span<ProverOpeningClaim<Curve>> libra_opening_claims = {},
146 std::span<ProverOpeningClaim<Curve>> sumcheck_opening_claims = {})
147 {
148 // Our main use case is the opening of Gemini fold polynomials and each Gemini fold is opened at 2 points.
149 const size_t num_gemini_opening_claims = 2 * opening_claims.size();
150 const size_t num_opening_claims =
151 num_gemini_opening_claims + libra_opening_claims.size() + sumcheck_opening_claims.size();
152
153 // {ẑⱼ(z)}ⱼ , where ẑⱼ(r) = 1/zⱼ(z) = 1/(z - xⱼ)
154 std::vector<Fr> inverse_vanishing_evals;
155 inverse_vanishing_evals.reserve(num_opening_claims);
156 for (const auto& claim : opening_claims) {
157 if (claim.gemini_fold) {
158 inverse_vanishing_evals.emplace_back(z_challenge + claim.opening_pair.challenge);
159 }
160 inverse_vanishing_evals.emplace_back(z_challenge - claim.opening_pair.challenge);
161 }
162
163 // Add the terms (z - uₖ) for k = 0, …, d−1 where d is the number of rounds in Sumcheck
164 for (const auto& claim : libra_opening_claims) {
165 inverse_vanishing_evals.emplace_back(z_challenge - claim.opening_pair.challenge);
166 }
167
168 for (const auto& claim : sumcheck_opening_claims) {
169 inverse_vanishing_evals.emplace_back(z_challenge - claim.opening_pair.challenge);
170 }
171
172 Fr::batch_invert(inverse_vanishing_evals);
173
174 // G(X) = Q(X) - Q_z(X) = Q(X) - ∑ⱼ νʲ ⋅ ( fⱼ(X) − vⱼ) / ( z − xⱼ ),
175 // s.t. G(r) = 0
176 Polynomial G(std::move(batched_quotient_Q)); // G(X) = Q(X)
177
178 // G₀ = ∑ⱼ νʲ ⋅ vⱼ / ( z − xⱼ )
179 Fr current_nu = Fr::one();
180 Polynomial tmp(G.size());
181 size_t idx = 0;
182
183 size_t fold_idx = 0;
184 for (auto& claim : opening_claims) {
185
186 if (claim.gemini_fold) {
187 tmp = claim.polynomial;
188 tmp.at(0) = tmp[0] - gemini_fold_pos_evaluations[fold_idx++];
189 Fr scaling_factor = current_nu * inverse_vanishing_evals[idx++]; // = νʲ / (z − xⱼ )
190 // G -= νʲ ⋅ ( fⱼ(X) − vⱼ) / ( z − xⱼ )
191 G.add_scaled(tmp, -scaling_factor);
192
193 current_nu *= nu_challenge;
194 }
195 // tmp = νʲ ⋅ ( fⱼ(X) − vⱼ) / ( z − xⱼ )
196 claim.polynomial.at(0) = claim.polynomial[0] - claim.opening_pair.evaluation;
197 Fr scaling_factor = current_nu * inverse_vanishing_evals[idx++]; // = νʲ / (z − xⱼ )
198
199 // G -= νʲ ⋅ ( fⱼ(X) − vⱼ) / ( z − xⱼ )
200 G.add_scaled(claim.polynomial, -scaling_factor);
201
202 current_nu *= nu_challenge;
203 }
204
205 // Take into account the constant proof size in Gemini
206 if (!libra_opening_claims.empty()) {
207 current_nu = nu_challenge.pow(2 * virtual_log_n);
208 }
209
210 for (auto& claim : libra_opening_claims) {
211 // Compute individual claim quotient tmp = ( fⱼ(X) − vⱼ) / ( X − xⱼ )
212 claim.polynomial.at(0) = claim.polynomial[0] - claim.opening_pair.evaluation;
213 Fr scaling_factor = current_nu * inverse_vanishing_evals[idx++]; // = νʲ / (z − xⱼ )
214
215 // Add the claim quotient to the batched quotient polynomial
216 G.add_scaled(claim.polynomial, -scaling_factor);
217 current_nu *= nu_challenge;
218 }
219
220 for (auto& claim : sumcheck_opening_claims) {
221 claim.polynomial.at(0) = claim.polynomial[0] - claim.opening_pair.evaluation;
222 Fr scaling_factor = current_nu * inverse_vanishing_evals[idx++]; // = νʲ / (z − xⱼ )
223
224 // Add the claim quotient to the batched quotient polynomial
225 G.add_scaled(claim.polynomial, -scaling_factor);
226 current_nu *= nu_challenge;
227 }
228 // Return opening pair (z, 0) and polynomial G(X) = Q(X) - Q_z(X)
229 return { .polynomial = G, .opening_pair = { .challenge = z_challenge, .evaluation = Fr::zero() } };
230 };
239 std::span<const ProverOpeningClaim<Curve>> opening_claims)
240 {
241 std::vector<Fr> gemini_fold_pos_evaluations;
242 gemini_fold_pos_evaluations.reserve(opening_claims.size());
243
244 for (const auto& claim : opening_claims) {
245 if (claim.gemini_fold) {
246 // -r^{2^i} is stored in the claim
247 const Fr evaluation_point = -claim.opening_pair.challenge;
248 // Compute Fold_i(r^{2^i})
249 const Fr evaluation = claim.polynomial.evaluate(evaluation_point);
250 gemini_fold_pos_evaluations.emplace_back(evaluation);
251 }
252 }
253 return gemini_fold_pos_evaluations;
254 }
255
265 template <typename Transcript>
267 std::span<ProverOpeningClaim<Curve>> opening_claims,
268 const std::shared_ptr<Transcript>& transcript,
269 std::span<ProverOpeningClaim<Curve>> libra_opening_claims = {},
270 std::span<ProverOpeningClaim<Curve>> sumcheck_round_claims = {},
271 const size_t virtual_log_n = 0)
272 {
273 const Fr nu = transcript->template get_challenge<Fr>("Shplonk:nu");
274
275 // Compute the evaluations Fold_i(r^{2^i}) for i>0.
276 std::vector<Fr> gemini_fold_pos_evaluations = compute_gemini_fold_pos_evaluations(opening_claims);
277
278 auto batched_quotient = compute_batched_quotient(virtual_log_n,
279 opening_claims,
280 nu,
281 gemini_fold_pos_evaluations,
282 libra_opening_claims,
283 sumcheck_round_claims);
284 auto batched_quotient_commitment = commitment_key.commit(batched_quotient);
285 transcript->send_to_verifier("Shplonk:Q", batched_quotient_commitment);
286 const Fr z = transcript->template get_challenge<Fr>("Shplonk:z");
287
289 opening_claims,
290 batched_quotient,
291 nu,
292 z,
293 gemini_fold_pos_evaluations,
294 libra_opening_claims,
295 sumcheck_round_claims);
296 }
297};
298
342template <typename Curve> class ShplonkVerifier_ {
343 using Fr = typename Curve::ScalarField;
344 using GroupElement = typename Curve::Element;
347
348 // Random challenges
349 std::vector<Fr> pows_of_nu;
350 // Commitment to quotient polynomial
352 // Partial evaluation challenge
354 // Commitments \f$[f_1], \dots, [f_n]\f$
355 std::vector<Commitment> commitments;
356 // Scalar coefficients of \f$[f_1], \dots, [f_n]\f$ in the MSM needed to compute the commitment to the partially
357 // evaluated quotient
358 std::vector<Fr> scalars;
359 // Coefficient of the identity in partially evaluated quotient
361 // Target evaluation
363
364 public:
365 template <typename Transcript>
366 ShplonkVerifier_(std::vector<Commitment>& polynomial_commitments,
367 std::shared_ptr<Transcript>& transcript,
368 const size_t num_claims)
369 : pows_of_nu({ Fr(1), transcript->template get_challenge<Fr>("Shplonk:nu") })
370 , quotient(transcript->template receive_from_prover<Commitment>("Shplonk:Q"))
371 , z_challenge(transcript->template get_challenge<Fr>("Shplonk:z"))
372 , commitments({ quotient })
373 , scalars{ Fr{ 1 } }
374 {
375 BB_ASSERT_GT(num_claims, 1U, "Using Shplonk with just one claim. Should use batch reduction.");
376 const size_t num_commitments = commitments.size();
377 commitments.reserve(num_commitments);
378 scalars.reserve(num_commitments);
379 pows_of_nu.reserve(num_claims);
380
381 commitments.insert(commitments.end(), polynomial_commitments.begin(), polynomial_commitments.end());
382 scalars.insert(scalars.end(), commitments.size() - 1, Fr(0)); // Initialized as circuit constants
383 // The first two powers of nu have already been initialized, we need another `num_claims - 2` powers to batch
384 // all the claims
385 for (size_t idx = 0; idx < num_claims - 2; idx++) {
386 pows_of_nu.emplace_back(pows_of_nu.back() * pows_of_nu[1]);
387 }
388
389 if constexpr (Curve::is_stdlib_type) {
390 evaluation.convert_constant_to_fixed_witness(pows_of_nu[1].get_context());
391 }
392 }
393
403 {
404 commitments.emplace_back(g1_identity);
406 GroupElement result = GroupElement::batch_mul(commitments, scalars);
407
408 return { { z_challenge, evaluation }, result };
409 }
410
425 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1475): Compute g1_identity inside the function body
427 {
428 commitments.emplace_back(g1_identity);
430
431 return { commitments, scalars, z_challenge };
432 }
433
441 template <typename Transcript>
443 std::shared_ptr<Transcript>& transcript)
444 {
445 // Initialize Shplonk verifier
446 const size_t num_claims = claims.size();
447 std::vector<Commitment> polynomial_commiments;
448 polynomial_commiments.reserve(num_claims);
449 for (const auto& claim : claims) {
450 polynomial_commiments.emplace_back(claim.commitment);
451 }
452 ShplonkVerifier_<Curve> verifier(polynomial_commiments, transcript, num_claims);
453
454 // Compute { 1 / (z - x_i) }
455 std::vector<Fr> inverse_vanishing_evals;
456 inverse_vanishing_evals.reserve(num_claims);
457 if constexpr (Curve::is_stdlib_type) {
458 for (const auto& claim : claims) {
459 inverse_vanishing_evals.emplace_back((verifier.z_challenge - claim.opening_pair.challenge).invert());
460 }
461 } else {
462 for (const auto& claim : claims) {
463 inverse_vanishing_evals.emplace_back(verifier.z_challenge - claim.opening_pair.challenge);
464 }
465 Fr::batch_invert(inverse_vanishing_evals);
466 }
467
468 // Update the Shplonk verifier state with each claim
469 // For each claim: s_i -= ν^i / (z - x_i) and θ += ν^i * v_i / (z - x_i)
470 for (size_t idx = 0; idx < claims.size(); idx++) {
471 // Compute ν^i / (z - x_i)
472 auto scalar_factor = verifier.pows_of_nu[idx] * inverse_vanishing_evals[idx];
473 // s_i -= ν^i / (z - x_i)
474 verifier.scalars[idx + 1] -= scalar_factor;
475 // θ += ν^i * v_i / (z - x_i)
476 verifier.identity_scalar_coefficient += scalar_factor * claims[idx].opening_pair.evaluation;
477 }
478
479 return verifier;
480 };
481
492 template <typename Transcript>
494 std::span<const OpeningClaim<Curve>> claims,
495 std::shared_ptr<Transcript>& transcript)
496 {
497 auto verifier = ShplonkVerifier_::reduce_verification_no_finalize(claims, transcript);
498 return verifier.finalize(g1_identity);
499 };
500
510 static std::vector<Fr> compute_inverted_gemini_denominators(const Fr& shplonk_eval_challenge,
511 const std::vector<Fr>& gemini_eval_challenge_powers)
512 {
513 std::vector<Fr> denominators;
514 const size_t virtual_log_n = gemini_eval_challenge_powers.size();
515 const size_t num_gemini_claims = 2 * virtual_log_n;
516 denominators.reserve(num_gemini_claims);
517
518 for (const auto& gemini_eval_challenge_power : gemini_eval_challenge_powers) {
519 // Place 1/(z - r ^ {2^j})
520 denominators.emplace_back(shplonk_eval_challenge - gemini_eval_challenge_power);
521 // Place 1/(z + r ^ {2^j})
522 denominators.emplace_back(shplonk_eval_challenge + gemini_eval_challenge_power);
523 }
524
525 if constexpr (!Curve::is_stdlib_type) {
526 Fr::batch_invert(denominators);
527 } else {
528 for (auto& denominator : denominators) {
529 denominator = denominator.invert();
530 }
531 }
532 return denominators;
533 }
534};
535
541template <typename Fr>
542static std::vector<Fr> compute_shplonk_batching_challenge_powers(const Fr& shplonk_batching_challenge,
543 const size_t virtual_log_n,
544 bool has_zk = false,
545 bool committed_sumcheck = false)
546{
547 // Minimum number of powers: 2 * virtual_log_n for the Gemini fold claims
548 size_t num_powers = 2 * virtual_log_n;
549 // Each round univariate is opened at 0, 1, and a round challenge.
550 static constexpr size_t NUM_COMMITTED_SUMCHECK_CLAIMS_PER_ROUND = 3;
551
552 // Shplonk evaluation and batching challenges are re-used in SmallSubgroupIPA.
553 if (has_zk) {
554 num_powers += NUM_SMALL_IPA_EVALUATIONS;
555 }
556
557 // Commited sumcheck adds 3 claims per round.
558 if (committed_sumcheck) {
559 num_powers += NUM_COMMITTED_SUMCHECK_CLAIMS_PER_ROUND * virtual_log_n;
560 }
561
562 std::vector<Fr> result;
563 result.reserve(num_powers);
564 result.emplace_back(Fr{ 1 });
565 for (size_t idx = 1; idx < num_powers; idx++) {
566 result.emplace_back(result[idx - 1] * shplonk_batching_challenge);
567 }
568 return result;
569}
570} // namespace bb
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:113
CommitmentKey object over a pairing group 𝔾₁.
Commitment commit(PolynomialSpan< const Fr > polynomial) const
Uses the ProverSRS to create a commitment to p(X)
Unverified claim (C,r,v) for some witness polynomial p(X) such that.
Definition claim.hpp:55
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
void add_scaled(PolynomialSpan< const Fr > other, const Fr &scaling_factor)
adds the polynomial q(X) 'other', multiplied by a scaling factor.
Fr & at(size_t index)
Our mutable accessor, unlike operator[]. We abuse precedent a bit to differentiate at() and operator[...
void factor_roots(const Fr &root)
Divides p(X) by (X-r) in-place. Assumes that p(rⱼ)=0 for all j.
Polynomial p and an opening pair (r,v) such that p(r) = v.
Definition claim.hpp:36
Shplonk Prover.
Definition shplonk.hpp:36
static std::vector< Fr > compute_gemini_fold_pos_evaluations(std::span< const ProverOpeningClaim< Curve > > opening_claims)
Compute evaluations of fold polynomials Fold_i at r^{2^i} for i>0. TODO(https://github....
Definition shplonk.hpp:238
static Polynomial compute_batched_quotient(const size_t virtual_log_n, std::span< const ProverOpeningClaim< Curve > > opening_claims, const Fr &nu, std::span< Fr > gemini_fold_pos_evaluations, std::span< const ProverOpeningClaim< Curve > > libra_opening_claims, std::span< const ProverOpeningClaim< Curve > > sumcheck_round_claims)
Compute batched quotient polynomial Q(X) = ∑ⱼ νʲ ⋅ ( fⱼ(X) − vⱼ) / ( X − xⱼ )
Definition shplonk.hpp:49
static ProverOpeningClaim< Curve > prove(const CommitmentKey< Curve > &commitment_key, std::span< ProverOpeningClaim< Curve > > opening_claims, const std::shared_ptr< Transcript > &transcript, std::span< ProverOpeningClaim< Curve > > libra_opening_claims={}, std::span< ProverOpeningClaim< Curve > > sumcheck_round_claims={}, const size_t virtual_log_n=0)
Returns a batched opening claim equivalent to a set of opening claims consisting of polynomials,...
Definition shplonk.hpp:266
typename Curve::ScalarField Fr
Definition shplonk.hpp:37
static ProverOpeningClaim< Curve > compute_partially_evaluated_batched_quotient(const size_t virtual_log_n, std::span< ProverOpeningClaim< Curve > > opening_claims, Polynomial &batched_quotient_Q, const Fr &nu_challenge, const Fr &z_challenge, std::span< Fr > gemini_fold_pos_evaluations, std::span< ProverOpeningClaim< Curve > > libra_opening_claims={}, std::span< ProverOpeningClaim< Curve > > sumcheck_opening_claims={})
Compute partially evaluated batched quotient polynomial difference Q(X) - Q_z(X)
Definition shplonk.hpp:138
bb::Polynomial< Fr > Polynomial
Definition shplonk.hpp:38
Shplonk Verifier.
Definition shplonk.hpp:342
std::vector< Fr > pows_of_nu
Definition shplonk.hpp:349
typename Curve::ScalarField Fr
Definition shplonk.hpp:343
BatchOpeningClaim< Curve > export_batch_opening_claim(const Commitment &g1_identity)
Export a BatchOpeningClaim instead of performing final batch_mul.
Definition shplonk.hpp:426
static OpeningClaim< Curve > reduce_verification(Commitment g1_identity, std::span< const OpeningClaim< Curve > > claims, std::shared_ptr< Transcript > &transcript)
Recomputes the new claim commitment [G] given the proof and the challenge r. No verification happens ...
Definition shplonk.hpp:493
ShplonkVerifier_(std::vector< Commitment > &polynomial_commitments, std::shared_ptr< Transcript > &transcript, const size_t num_claims)
Definition shplonk.hpp:366
std::vector< Commitment > commitments
Definition shplonk.hpp:355
typename Curve::AffineElement Commitment
Definition shplonk.hpp:345
static std::vector< Fr > compute_inverted_gemini_denominators(const Fr &shplonk_eval_challenge, const std::vector< Fr > &gemini_eval_challenge_powers)
Computes .
Definition shplonk.hpp:510
static ShplonkVerifier_< Curve > reduce_verification_no_finalize(std::span< const OpeningClaim< Curve > > claims, std::shared_ptr< Transcript > &transcript)
Instantiate a Shplonk verifier and update its state with the provided claims.
Definition shplonk.hpp:442
typename Curve::Element GroupElement
Definition shplonk.hpp:344
OpeningClaim< Curve > finalize(const Commitment &g1_identity)
Finalize the Shplonk verification and return the KZG opening claim.
Definition shplonk.hpp:402
std::vector< Fr > scalars
Definition shplonk.hpp:358
Commitment quotient
Definition shplonk.hpp:351
Representation of the Grumpkin Verifier Commitment Key inside a bn254 circuit.
typename Group::element Element
Definition grumpkin.hpp:62
static constexpr bool is_stdlib_type
Definition grumpkin.hpp:69
typename Group::affine_element AffineElement
Definition grumpkin.hpp:63
#define G(r, i, a, b, c, d)
Definition blake2s.cpp:116
constexpr T round_up_power_2(const T in)
Definition get_msb.hpp:75
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
An accumulator consisting of the Shplonk evaluation challenge and vectors of commitments and scalars.
Definition claim.hpp:155
static constexpr field one()
BB_INLINE constexpr field pow(const uint256_t &exponent) const noexcept
static void batch_invert(C &coeffs) noexcept
Batch invert a collection of field elements using Montgomery's trick.
static constexpr field zero()