Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
small_subgroup_ipa.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
8
19
20#include <array>
21#include <vector>
22
23namespace bb {
70template <typename Flavor> class SmallSubgroupIPAProver {
71 using Curve = typename Flavor::Curve;
72 using FF = typename Curve::ScalarField;
74 // The size of a multiplicative subgroup in the ScalarField of a curve
75 static constexpr size_t SUBGROUP_SIZE = Curve::SUBGROUP_SIZE;
76
77 // WITNESS_MASKING_TERM_LENGTH (shared, from constants.hpp) is the degree-1 masking term added to G to hide [G]
78 // and G(r); it must match the padding used by the builders of G (ZKSumcheckData, TranslationData).
79 static constexpr size_t MASKED_CONCATENATED_WITNESS_LENGTH = SUBGROUP_SIZE + WITNESS_MASKING_TERM_LENGTH;
80
81 // A masking term of length 3 (degree 2) is required to mask [A], A(r), and A(g*r)
82 static constexpr size_t GRAND_SUM_MASKING_TERM_LENGTH = 3;
84
85 // Length of the big sum identity polynomial C. It is equal to the length of the highest degree term X * F(X) * G(X)
87
88 // Length of the big sum identity quotient Q(X) = length(C) - length(Z_H) + 1
90
91 // The length of a random polynomial masking Prover's Sumcheck Univariates. In the case of BN254-based Flavors, we
92 // send the coefficients of the univariates, hence we choose these value to be the max sumcheck univariate length
93 // over Translator, Ultra, and Mega. In ECCVM, the Sumcheck prover will commit to its univariates, which reduces the
94 // required length from 23 to 3.
96 // Fixed generator of H
98
99 // Interpolation domain {1, g, \ldots, g^{SUBGROUP_SIZE - 1}} used by ECCVM
100 std::array<FF, SUBGROUP_SIZE> interpolation_domain;
101 // We use IFFT over BN254 scalar field
103
104 // Monomial coefficients of the concatenated polynomial extracted from ZKSumcheckData or TranslationData
106 // Lagrange coefficients of the concatenated polynomial
108
109 // The polynomial obtained by concatenating powers of sumcheck challenges or the products of
110 // `evaluation_challenge_x` and `batching_challenge_v`
113
114 // Grand sum polynomial A(X)
117 std::array<FF, SUBGROUP_SIZE> grand_sum_lagrange_coeffs;
118
119 // The RHS of the key identity, denoted C(X) in the HackMD
121
122 // Quotient of the grand sum identity polynomial C(X) by the vanishing polynomial Z_H = X^{|H|} - 1
124
125 // Steps of prove(). Each accumulates into a zero-initialized member, so each must run exactly once per
126 // instance; they are private so prove() is the only production entry point. ShpleminiTest drives them
127 // directly to construct forged witnesses without sending honest commitments.
129
131
133
134 template <typename> friend class ShpleminiTest;
135
136 // Either "Translation:" or "Libra:".
137 std::string label_prefix;
138
141 std::array<Commitment, NUM_SMALL_IPA_COMMITMENTS> witness_commitments;
142
143 public:
144 // The SmallSubgroupIPA claim
146
147 // Default constructor to initialize all polynomials, transcript, and commitment key.
150
151 // Construct prover from ZKSumcheckData. Used by all ZK Provers.
153 const std::vector<FF>& multivariate_challenge,
156 const typename Flavor::CommitmentKey& commitment_key);
157
158 // Construct prover from TranslationData. Used by ECCVMProver.
160 const FF evaluation_challenge_x,
161 const FF batching_challenge_v,
163 const typename Flavor::CommitmentKey& commitment_key);
164
165 void prove();
166
167 void compute_challenge_polynomial(const std::vector<FF>& multivariate_challenge);
168
169 void compute_eccvm_challenge_polynomial(const FF evaluation_challenge_x, const FF batching_challenge_v);
170
172 const std::array<FF, SUBGROUP_SIZE>& interpolation_domain, const EvaluationDomain<FF>& bn_evaluation_domain);
173
175 const std::vector<FF>& multivariate_challenge,
176 const size_t& log_circuit_size);
177
179
181 const std::array<FF, SUBGROUP_SIZE>& interpolation_domain,
183
184 // Returns {G, A, Q} in commitment-slot order. Code that needs a per-claim polynomial must index via
185 // `SMALL_IPA_CLAIMS[i].commitment_index`.
190 std::array<Commitment, NUM_SMALL_IPA_COMMITMENTS> get_witness_commitments() const { return witness_commitments; }
191 // Getters for test purposes
194};
195
215template <typename Curve> class SmallSubgroupIPAVerifier {
216 using FF = typename Curve::ScalarField;
217
218 static constexpr size_t SUBGROUP_SIZE = Curve::SUBGROUP_SIZE;
219
220 // The verifier has to evaluate 3 polynomials on its own, namely, the challenge polynomial F, Lagrange first
221 // L_1, and Lagrange last L_{H}.
222 static constexpr size_t NUM_BARYCENTRIC_EVALUATIONS = 3;
223
224 // The length of a random polynomial masking Prover's Sumcheck Univariates. In the case of BN254-based Flavors, we
225 // send the coefficients of the univariates, hence we choose these value to be the max sumcheck univariate length
226 // over Translator, UltraZK, and MegaZK. In ECCVM, the Sumcheck prover will commit to its univariates, which reduces
227 // the required length from 23 to 3.
229
230 public:
250 static bool check_consistency(const std::array<FF, NUM_SMALL_IPA_OPENING_CLAIMS>& small_ipa_evaluations,
251 const FF& small_ipa_eval_challenge,
252 const std::vector<FF>& challenge_polynomial,
253 const FF& inner_product_eval_claim,
254 const FF& vanishing_poly_eval)
255 {
256 // Check if Z_H(r) = 0.
257 handle_edge_cases(vanishing_poly_eval);
258 // Compute evaluations at r of F, Lagrange first, and Lagrange last for the fixed small subgroup
259 auto [challenge_poly, lagrange_first, lagrange_last] = compute_batched_barycentric_evaluations(
260 challenge_polynomial, small_ipa_eval_challenge, vanishing_poly_eval);
261
262 const FF& concatenated_at_r = small_ipa_evaluations[0];
263 const FF& grand_sum_shifted_eval = small_ipa_evaluations[1];
264 const FF& grand_sum_eval = small_ipa_evaluations[2];
265 // Slot 3 is the fixed boundary value A(1) = 0; it is enforced by the PCS opening, not by this identity.
266 const FF& quotient_eval = small_ipa_evaluations[4];
267
268 // Compute the evaluation of L_1(X) * A(X) + (X - 1/g) (A(gX) - A(X) - F(X) G(X)) + L_{|H|}(X)(A(X) - s) -
269 // Z_H(X) * Q(X)
270 FF diff = lagrange_first * grand_sum_eval;
271 diff += (small_ipa_eval_challenge - Curve::subgroup_generator_inverse) *
272 (grand_sum_shifted_eval - grand_sum_eval - concatenated_at_r * challenge_poly);
273 diff += lagrange_last * (grand_sum_eval - inner_product_eval_claim) - vanishing_poly_eval * quotient_eval;
274
275 if constexpr (Curve::is_stdlib_type) {
277 diff.self_reduce();
278 }
279 bool out = (diff.get_value() == FF(0).get_value());
280 diff.assert_equal(FF(0));
281 return out;
282 } else {
283 return (diff == FF(0));
284 };
285 };
286
300 const std::array<FF, NUM_SMALL_IPA_OPENING_CLAIMS>& libra_evaluations,
301 const FF& gemini_evaluation_challenge,
302 const std::vector<FF>& multilinear_challenge,
303 const FF& inner_product_eval_claim)
304 {
305
306 // Compute the evaluation of the vanishing polynomial Z_H(X) at the Gemini evaluation challenge.
307 const FF vanishing_poly_eval = gemini_evaluation_challenge.pow(SUBGROUP_SIZE) - FF(1);
308
309 return check_consistency(libra_evaluations,
310 gemini_evaluation_challenge,
311 compute_challenge_polynomial_coeffs<Curve>(multilinear_challenge),
312 inner_product_eval_claim,
313 vanishing_poly_eval);
314 }
327 const std::array<FF, NUM_SMALL_IPA_OPENING_CLAIMS>& small_ipa_evaluations,
328 const FF& evaluation_challenge,
329 const FF& evaluation_challenge_x,
330 const FF& batching_challenge_v,
331 const FF& inner_product_eval_claim)
332 {
333
334 // Compute the evaluation of the vanishing polynomial Z_H(X) at `evaluation_challenge`.
335 const FF vanishing_poly_eval = evaluation_challenge.pow(SUBGROUP_SIZE) - FF(1);
336
337 return check_consistency(small_ipa_evaluations,
338 evaluation_challenge,
339 compute_eccvm_challenge_coeffs<Curve>(evaluation_challenge_x,
340 batching_challenge_v,
341 NUM_TRANSLATION_EVALUATIONS,
342 NUM_DISABLED_ROWS_IN_SUMCHECK),
343 inner_product_eval_claim,
344 vanishing_poly_eval);
345 }
346
352 static void handle_edge_cases(const FF& vanishing_poly_eval)
353 {
354 bool evaluation_challenge_in_small_subgroup = false;
355 if constexpr (Curve::is_stdlib_type) {
356 evaluation_challenge_in_small_subgroup = (vanishing_poly_eval.get_value() == FF(0).get_value());
357 } else {
358 evaluation_challenge_in_small_subgroup = (vanishing_poly_eval == FF(0));
359 }
360 // The probability of this event is negligible but it has to be processed correctly
361 if (evaluation_challenge_in_small_subgroup) {
362 throw_or_abort("SmallSubgroupIPA: Evaluation challenge is in the SmallSubgroup. This would cancel out the "
363 "hiding property of the commitment.");
364 }
365 }
378 const std::vector<FF>& coeffs, const FF& r, const FF& vanishing_poly_eval)
379 {
380 FF one{ 1 };
381 FF zero{ 0 };
382 if constexpr (Curve::is_stdlib_type) {
383 auto builder = r.get_context();
384 one.convert_constant_to_fixed_witness(builder);
385 zero.convert_constant_to_fixed_witness(builder);
386 }
387
388 // Construct the denominators of the Lagrange polynomials evaluated
389 // at r
390 std::array<FF, SUBGROUP_SIZE> denominators;
391 FF running_power = one;
392 for (size_t i = 0; i < SUBGROUP_SIZE; ++i) {
393 denominators[i] = running_power * r - one; // r * g^{-i} - 1
394 running_power *= Curve::subgroup_generator_inverse;
395 }
396
397 // Invert/Batch invert denominators
398 if constexpr (Curve::is_stdlib_type) {
399 std::transform(
400 denominators.begin(), denominators.end(), denominators.begin(), [](FF& d) { return d.invert(); });
401 } else {
402 FF::batch_invert(&denominators[0], SUBGROUP_SIZE);
403 }
404
405 // Construct the evaluation of the polynomial using its evaluations over H, Lagrange first evaluated at r,
406 // Lagrange last evaluated at r
407 FF numerator = vanishing_poly_eval * FF(SUBGROUP_SIZE).invert(); // (r^n - 1) / n
409 std::inner_product(coeffs.begin(), coeffs.end(), denominators.begin(), zero),
410 denominators[0],
411 denominators[SUBGROUP_SIZE - 1]
412 };
413 std::transform(
414 result.begin(), result.end(), result.begin(), [&](FF& denominator) { return denominator * numerator; });
415 return result;
416 }
417};
418
428template <typename Curve>
429static std::vector<typename Curve::ScalarField> compute_challenge_polynomial_coeffs(
430 const std::vector<typename Curve::ScalarField>& multivariate_challenge)
431{
432
433 using FF = typename Curve::ScalarField;
434
435 // An empty challenge would index multivariate_challenge[0] out of bounds below (and yields a degenerate
436 // challenge polynomial of length 1), so reject it up front.
437 if (multivariate_challenge.empty()) {
438 throw_or_abort("SmallSubgroupIPA: multivariate_challenge must be non-empty");
439 }
440
441 std::vector<FF> challenge_polynomial_lagrange(Curve::SUBGROUP_SIZE);
442 static constexpr size_t libra_univariates_length = Curve::LIBRA_UNIVARIATES_LENGTH;
443
444 const size_t challenge_poly_length = libra_univariates_length * multivariate_challenge.size() + 1;
445 BB_ASSERT_LT(challenge_poly_length, Curve::SUBGROUP_SIZE);
446
447 FF one{ 1 };
448 FF zero{ 0 };
449 if constexpr (Curve::is_stdlib_type) {
450 auto builder = multivariate_challenge[0].get_context();
451 one.convert_constant_to_fixed_witness(builder);
452 zero.convert_constant_to_fixed_witness(builder);
453 }
454
455 challenge_polynomial_lagrange[0] = one;
456
457 // Populate the vector with the powers of the challenges
458 size_t round_idx = 0;
459 for (auto challenge : multivariate_challenge) {
460 size_t current_idx = 1 + libra_univariates_length * round_idx;
461 challenge_polynomial_lagrange[current_idx] = one;
462 for (size_t idx = current_idx + 1; idx < current_idx + libra_univariates_length; idx++) {
463 // Recursively compute the powers of the challenge up to the length of libra univariates
464 challenge_polynomial_lagrange[idx] = challenge_polynomial_lagrange[idx - 1] * challenge;
465 }
466 round_idx++;
467 }
468
469 // Ensure that the coefficients are padded with fixed witnesses obtained from 0
470 for (size_t idx = challenge_poly_length; idx < Curve::SUBGROUP_SIZE; idx++) {
471 challenge_polynomial_lagrange[idx] = zero;
472 }
473
474 return challenge_polynomial_lagrange;
475}
476
484template <typename Curve>
486 const typename Curve::ScalarField& evaluation_challenge_x,
487 const typename Curve::ScalarField& batching_challenge_v,
488 size_t num_polys,
489 size_t num_coeffs_per_poly)
490{
491 using FF = typename Curve::ScalarField;
492 std::vector<FF> coeffs_lagrange_basis(Curve::SUBGROUP_SIZE);
493 FF one{ 1 };
494 FF zero{ 0 };
495 if constexpr (Curve::is_stdlib_type) {
496 auto builder = evaluation_challenge_x.get_context();
497 one.convert_constant_to_fixed_witness(builder);
498 zero.convert_constant_to_fixed_witness(builder);
499 }
500 FF v_power = one;
501 for (size_t poly_idx = 0; poly_idx < num_polys; poly_idx++) {
502 const size_t start = num_coeffs_per_poly * poly_idx;
503 coeffs_lagrange_basis[start] = v_power;
504
505 for (size_t idx = start + 1; idx < start + num_coeffs_per_poly; idx++) {
506 coeffs_lagrange_basis[idx] = coeffs_lagrange_basis[idx - 1] * evaluation_challenge_x;
507 }
508
509 v_power *= batching_challenge_v;
510 }
511
512 const size_t challenge_poly_length = num_polys * num_coeffs_per_poly;
513 BB_ASSERT_LT(challenge_poly_length, Curve::SUBGROUP_SIZE);
514
515 // Ensure that the coefficients are padded with fixed witnesses obtained from 0
516 for (size_t idx = challenge_poly_length; idx < Curve::SUBGROUP_SIZE; idx++) {
517 coeffs_lagrange_basis[idx] = zero;
518 }
519
520 return coeffs_lagrange_basis;
521}
522} // namespace bb
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
CommitmentKey object over a pairing group 𝔾₁.
curve::Grumpkin Curve
A Curve-agnostic ZK protocol to prove inner products of small vectors.
typename Curve::AffineElement Commitment
std::shared_ptr< typename Flavor::Transcript > transcript
void compute_eccvm_challenge_polynomial(const FF evaluation_challenge_x, const FF batching_challenge_v)
Compute a (public) challenge polynomial from the evaluation and batching challenges.
const Polynomial< FF > & get_batched_polynomial() const
std::array< bb::Polynomial< FF >, NUM_SMALL_IPA_COMMITMENTS > get_witness_polynomials() const
typename Curve::ScalarField FF
void compute_challenge_polynomial(const std::vector< FF > &multivariate_challenge)
Computes the challenge polynomial F(X) based on the provided multivariate challenges.
static constexpr size_t MASKED_CONCATENATED_WITNESS_LENGTH
static constexpr size_t QUOTIENT_LENGTH
static Polynomial< FF > compute_monomial_coefficients(std::span< FF > lagrange_coeffs, const std::array< FF, SUBGROUP_SIZE > &interpolation_domain, const EvaluationDomain< FF > &bn_evaluation_domain)
Given a vector of coefficients of a polynomial in the Lagrange basis over , compute its coefficients ...
static constexpr FF subgroup_generator
std::array< FF, SUBGROUP_SIZE > interpolation_domain
void compute_grand_sum_polynomial()
Computes the grand sum polynomial .
static constexpr size_t MASKED_GRAND_SUM_LENGTH
static std::array< Polynomial< FF >, 2 > compute_lagrange_first_and_last(const std::array< FF, SUBGROUP_SIZE > &interpolation_domain, const EvaluationDomain< FF > &bn_evaluation_domain)
Compute monomial coefficients of the first and last Lagrange polynomials over the subgroup .
typename Flavor::Curve Curve
void compute_grand_sum_identity_quotient()
Efficiently compute the quotient of the grand sum identity polynomial by .
Polynomial< FF > grand_sum_polynomial_unmasked
static FF compute_claimed_inner_product(ZKSumcheckData< Flavor > &zk_sumcheck_data, const std::vector< FF > &multivariate_challenge, const size_t &log_circuit_size)
For test purposes: Compute the sum of the Libra constant term and Libra univariates evaluated at Sumc...
const Polynomial< FF > & get_challenge_polynomial() const
static constexpr size_t GRAND_SUM_MASKING_TERM_LENGTH
static constexpr size_t LIBRA_UNIVARIATES_LENGTH
void compute_grand_sum_identity_polynomial()
Compute , where is the fixed generator of .
std::array< Commitment, NUM_SMALL_IPA_COMMITMENTS > witness_commitments
Polynomial< FF > concatenated_lagrange_form
Polynomial< FF > grand_sum_identity_polynomial
Flavor::CommitmentKey commitment_key
std::array< Commitment, NUM_SMALL_IPA_COMMITMENTS > get_witness_commitments() const
Polynomial< FF > challenge_polynomial_lagrange
EvaluationDomain< FF > bn_evaluation_domain
void prove()
Compute the derived witnesses and and commit to them.
static constexpr size_t GRAND_SUM_IDENTITY_LENGTH
Polynomial< FF > grand_sum_identity_quotient
static constexpr size_t SUBGROUP_SIZE
FF compute_claimed_translation_inner_product(TranslationData< typename Flavor::Transcript > &translation_data)
Compute the batched evaluation of the last NUM_DISABLED_ROWS_IN_SUMCHECK rows of the ECCVM transcript...
std::array< FF, SUBGROUP_SIZE > grand_sum_lagrange_coeffs
Verifies the consistency of polynomial evaluations provided by the the prover.
static std::array< FF, NUM_BARYCENTRIC_EVALUATIONS > compute_batched_barycentric_evaluations(const std::vector< FF > &coeffs, const FF &r, const FF &vanishing_poly_eval)
Efficient batch evaluation of the challenge polynomial, Lagrange first, and Lagrange last.
typename Curve::ScalarField FF
static bool check_consistency(const std::array< FF, NUM_SMALL_IPA_OPENING_CLAIMS > &small_ipa_evaluations, const FF &small_ipa_eval_challenge, const std::vector< FF > &challenge_polynomial, const FF &inner_product_eval_claim, const FF &vanishing_poly_eval)
Generic consistency check agnostic to challenge polynomial .
static constexpr size_t NUM_BARYCENTRIC_EVALUATIONS
static bool check_libra_evaluations_consistency(const std::array< FF, NUM_SMALL_IPA_OPENING_CLAIMS > &libra_evaluations, const FF &gemini_evaluation_challenge, const std::vector< FF > &multilinear_challenge, const FF &inner_product_eval_claim)
A method required by ZKSumcheck. The challenge polynomial is concatenated from the powers of the sumc...
static void handle_edge_cases(const FF &vanishing_poly_eval)
Check if the random evaluation challenge is in the SmallSubgroup.
static constexpr size_t LIBRA_UNIVARIATES_LENGTH
static bool check_eccvm_evaluations_consistency(const std::array< FF, NUM_SMALL_IPA_OPENING_CLAIMS > &small_ipa_evaluations, const FF &evaluation_challenge, const FF &evaluation_challenge_x, const FF &batching_challenge_v, const FF &inner_product_eval_claim)
A method required for the verification Translation Evaluations in the ECCVMVerifier....
static constexpr size_t SUBGROUP_SIZE
A class designed to accept the ECCVM Transcript Polynomials, concatenate their masking terms in Lagra...
static constexpr size_t SUBGROUP_SIZE
Definition grumpkin.hpp:74
static constexpr uint32_t LIBRA_UNIVARIATES_LENGTH
Definition grumpkin.hpp:87
static constexpr bool is_stdlib_type
Definition grumpkin.hpp:67
static constexpr ScalarField subgroup_generator_inverse
Definition grumpkin.hpp:81
typename Group::affine_element AffineElement
Definition grumpkin.hpp:64
static constexpr ScalarField subgroup_generator
Definition grumpkin.hpp:79
AluTraceBuilder builder
Definition alu.test.cpp:124
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
constexpr size_t NUM_SMALL_IPA_COMMITMENTS
std::vector< typename Curve::ScalarField > compute_eccvm_challenge_coeffs(const typename Curve::ScalarField &evaluation_challenge_x, const typename Curve::ScalarField &batching_challenge_v, size_t num_polys, size_t num_coeffs_per_poly)
Given num_polys polynomials each contributing num_coeffs_per_poly masking coefficients,...
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
bb::VectorAffineElementPushSpan< BaseParams > out
This structure is created to contain various polynomials and constants required by ZK Sumcheck.
constexpr field invert() const noexcept
static void batch_invert(C &coeffs) noexcept
Batch invert a collection of field elements using Montgomery's trick.
void throw_or_abort(std::string const &err)
VectorField result