Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
small_subgroup_ipa.test.cpp
Go to the documentation of this file.
2#include "../pcs_test_utils.hpp"
5
6#include <array>
7#include <gtest/gtest.h>
8#include <vector>
9
10namespace bb {
11
12template <typename Flavor> class SmallSubgroupIPATest : public ::testing::Test {
13 public:
14 using Curve = typename Flavor::Curve;
16 using FF = typename Curve::ScalarField;
17
19
20 static constexpr size_t log_circuit_size = 7;
21 static constexpr size_t circuit_size = 1ULL << log_circuit_size;
22
23 // Number of sumcheck challenges in a padded transcript: Grumpkin (committed sumcheck) runs at the fixed
24 // CONST_ECCVM_LOG_N, whose Libra concatenation is what fits the subgroup; BN254 pads to CONST_PROOF_SIZE_LOG_N.
25 static constexpr size_t num_sumcheck_challenges =
26 std::is_same_v<Curve, curve::Grumpkin> ? CONST_ECCVM_LOG_N : CONST_PROOF_SIZE_LOG_N;
27
29
31
32 static std::vector<FF> generate_random_vector(const size_t size)
33 {
34 std::vector<FF> multivariate_challenge(size);
35 for (auto& challenge : multivariate_challenge) {
36 challenge = FF::random_element();
37 }
38 return multivariate_challenge;
39 }
40
41 // A helper to evaluate the small-IPA witness polynomials at the verifier's claimed points (r, g*r, r, 1, r).
42 // `witness_polynomials` is the 3-array {G, A, Q} returned by SmallSubgroupIPAProver::get_witness_polynomials();
43 // we expand to the full 5-claim layout via SMALL_IPA_CLAIMS[i].commitment_index. The fourth slot is the A(1) = 0
44 // boundary opening required for soundness.
58};
59
60using TestFlavors = ::testing::Types<BN254Settings, GrumpkinSettings>;
62
63// Check the correctness of the computation of the claimed inner product and various polynomials needed for the
64// SmallSubgroupIPA.
65TYPED_TEST(SmallSubgroupIPATest, ProverComputationsCorrectness)
66{
67 using ZKData = ZKSumcheckData<TypeParam>;
68 using SmallSubgroupIPA = SmallSubgroupIPAProver<TypeParam>;
69 using FF = typename TypeParam::FF;
70 static constexpr size_t SUBGROUP_SIZE = TypeParam::SUBGROUP_SIZE;
71
72 using CK = typename TypeParam::CommitmentKey;
73
74 // SmallSubgroupIPAProver requires at least CURVE::SUBGROUP_SIZE + 3 elements in the ck.
75 static constexpr size_t log_subgroup_size = static_cast<size_t>(numeric::get_msb(SUBGROUP_SIZE));
76 CK ck = create_commitment_key<CK>(std::max<size_t>(this->circuit_size, 1ULL << (log_subgroup_size + 1)));
77
78 auto prover_transcript = TypeParam::Transcript::test_prover_init_empty();
79
80 ZKData zk_sumcheck_data(this->log_circuit_size, prover_transcript, ck);
81 std::vector<FF> multivariate_challenge = this->generate_random_vector(this->log_circuit_size);
82
83 const FF claimed_inner_product = SmallSubgroupIPA::compute_claimed_inner_product(
84 zk_sumcheck_data, multivariate_challenge, this->log_circuit_size);
85
86 SmallSubgroupIPA small_subgroup_ipa_prover =
87 SmallSubgroupIPA(zk_sumcheck_data, multivariate_challenge, claimed_inner_product, prover_transcript, ck);
88
89 small_subgroup_ipa_prover.prove();
90
91 const Polynomial batched_polynomial = small_subgroup_ipa_prover.get_batched_polynomial();
92 const auto witness_polynomials = small_subgroup_ipa_prover.get_witness_polynomials();
93 const Polynomial libra_concatenated_polynomial = witness_polynomials[0]; // [G]
94 const Polynomial batched_quotient = witness_polynomials[2]; // [Q]
95 const Polynomial challenge_polynomial = small_subgroup_ipa_prover.get_challenge_polynomial();
96
97 // Check that claimed inner product coincides with the inner product of libra_concatenated_polynomial and
98 // challenge_polynomial. Since libra_concatenated_polynomial is masked, we also check that masking does not affect
99 // the evaluations over H
100 FF inner_product = FF(0);
101 const std::array<FF, SUBGROUP_SIZE> domain = zk_sumcheck_data.interpolation_domain;
102 for (size_t idx = 0; idx < SUBGROUP_SIZE; idx++) {
103 inner_product +=
104 challenge_polynomial.evaluate(domain[idx]) * libra_concatenated_polynomial.evaluate(domain[idx]);
105 }
106 EXPECT_TRUE(inner_product == claimed_inner_product);
107
108 // Check that batched polynomial is divisible by Z_H(X)
109 bool ipa_claim_consistency = true;
110 for (size_t idx = 0; idx < SUBGROUP_SIZE; idx++) {
111 ipa_claim_consistency = (batched_polynomial.evaluate(zk_sumcheck_data.interpolation_domain[idx]) == FF{ 0 }) &&
112 ipa_claim_consistency;
113 }
114 EXPECT_EQ(ipa_claim_consistency, true);
115
116 // Check that Z_H(X) * Q(X) = batched_polynomial
117 std::vector<FF> Z_H(SUBGROUP_SIZE + 1);
118 Z_H[0] = -FF(1);
119 Z_H[SUBGROUP_SIZE] = FF(1);
120 Polynomial<FF> product(batched_polynomial.size());
121
122 for (size_t i = 0; i < Z_H.size(); i++) {
123 for (size_t j = 0; j < batched_quotient.size(); j++) {
124 product.at(i + j) += Z_H[i] * batched_quotient.at(j);
125 }
126 }
127 bool quotient_is_correct = true;
128 for (const auto& [coeff_expected, coeff] : zip_view(product.coeffs(), batched_polynomial.coeffs())) {
129 quotient_is_correct = (coeff_expected == coeff) && quotient_is_correct;
130 }
131 EXPECT_EQ(quotient_is_correct, true);
132}
133
134// Check the correctness of the evaluations of the challenge_polynomial, Lagrange first, and Lagrange last that the
135// verifier has to compute on its own. Compare the values against the evaluations obtaned by applying Lagrange
136// interpolation method used by Polynomial class constructor.
137TYPED_TEST(SmallSubgroupIPATest, VerifierEvaluations)
138{
139 using FF = typename TypeParam::FF;
140 using Curve = typename TypeParam::Curve;
141 using SmallSubgroupIPA = SmallSubgroupIPAVerifier<Curve>;
142
143 // Extract the constants
144 static constexpr size_t SUBGROUP_SIZE = TypeParam::SUBGROUP_SIZE;
145 const FF subgroup_generator_inverse = Curve::subgroup_generator_inverse;
146 const FF subgroup_generator = subgroup_generator_inverse.invert();
147
148 // Sample random Lagrange coefficients over H
149 std::vector<FF> challenge_poly_lagrange = this->generate_random_vector(SUBGROUP_SIZE);
150
151 // Evaluate Verifier's polynomials at the challenge
152 const FF vanishing_poly_eval = this->evaluation_challenge.pow(SUBGROUP_SIZE) - 1;
153
154 // Compute required evaluations using efficient batch evaluation
155 const auto [challenge_poly_eval, lagrange_first, lagrange_last] =
156 SmallSubgroupIPA::compute_batched_barycentric_evaluations(
157 challenge_poly_lagrange, this->evaluation_challenge, vanishing_poly_eval);
158
159 // Compute the evaluations differently, namely, using Lagrange interpolation
160 std::array<FF, SUBGROUP_SIZE> interpolation_domain;
161 interpolation_domain[0] = FF(1);
162 for (size_t idx = 1; idx < SUBGROUP_SIZE; idx++) {
163 interpolation_domain[idx] = interpolation_domain[idx - 1] * subgroup_generator;
164 }
165 Polynomial<FF> challenge_poly_monomial =
166 Polynomial<FF>(interpolation_domain, challenge_poly_lagrange, SUBGROUP_SIZE);
167
168 // Evaluate at the challenge
169 const FF challenge_poly_expected_eval = challenge_poly_monomial.evaluate(this->evaluation_challenge);
170
171 EXPECT_EQ(challenge_poly_eval, challenge_poly_expected_eval);
172
173 // Compute Lagrange polynomials using interpolation
174 std::vector<FF> lagrange_poly(SUBGROUP_SIZE);
175 lagrange_poly.at(0) = FF(1);
176 Polynomial<FF> lagrange_first_monomial = Polynomial<FF>(interpolation_domain, lagrange_poly, SUBGROUP_SIZE);
177 EXPECT_EQ(lagrange_first, lagrange_first_monomial.evaluate(this->evaluation_challenge));
178
179 lagrange_poly.at(0) = FF(0);
180 lagrange_poly.at(SUBGROUP_SIZE - 1) = FF(1);
181 Polynomial<FF> lagrange_last_monomial = Polynomial<FF>(interpolation_domain, lagrange_poly, SUBGROUP_SIZE);
182 EXPECT_EQ(lagrange_last, lagrange_last_monomial.evaluate(this->evaluation_challenge));
183}
184
185// Simulate the interaction between the prover and the verifier leading to the consistency check performed by the
186// verifier.
187TYPED_TEST(SmallSubgroupIPATest, LibraEvaluationsConsistency)
188{
189 using FF = typename TypeParam::FF;
190 using Curve = typename TypeParam::Curve;
191 using Verifier = SmallSubgroupIPAVerifier<Curve>;
193 using ZKData = ZKSumcheckData<TypeParam>;
194 using CK = typename TypeParam::CommitmentKey;
195
196 auto prover_transcript = TypeParam::Transcript::test_prover_init_empty();
197
198 // SmallSubgroupIPAProver requires at least CURVE::SUBGROUP_SIZE + 3 elements in the ck.
199 static constexpr size_t log_subgroup_size = static_cast<size_t>(numeric::get_msb(Curve::SUBGROUP_SIZE));
200 CK ck = create_commitment_key<CK>(std::max<size_t>(this->circuit_size, 1ULL << (log_subgroup_size + 1)));
201
202 ZKData zk_sumcheck_data(this->log_circuit_size, prover_transcript, ck);
203
204 std::vector<FF> multivariate_challenge = this->generate_random_vector(this->num_sumcheck_challenges);
205
206 const FF claimed_inner_product =
207 Prover::compute_claimed_inner_product(zk_sumcheck_data, multivariate_challenge, this->log_circuit_size);
208
209 Prover small_subgroup_ipa_prover =
210 Prover(zk_sumcheck_data, multivariate_challenge, claimed_inner_product, prover_transcript, ck);
211
212 small_subgroup_ipa_prover.prove();
213
214 const std::array<FF, NUM_SMALL_IPA_OPENING_CLAIMS> small_ipa_evaluations =
215 this->evaluate_small_ipa_witnesses(small_subgroup_ipa_prover.get_witness_polynomials());
216
217 bool consistency_checked = Verifier::check_libra_evaluations_consistency(
218 small_ipa_evaluations, this->evaluation_challenge, multivariate_challenge, claimed_inner_product);
219
220 EXPECT_TRUE(consistency_checked);
221}
222
223// Check that consistency check fails when some of the prover's data is corrupted.
224TYPED_TEST(SmallSubgroupIPATest, LibraEvaluationsConsistencyFailure)
225{
226 using FF = typename TypeParam::FF;
227 using Curve = typename TypeParam::Curve;
228 using Verifier = SmallSubgroupIPAVerifier<Curve>;
230 using ZKData = ZKSumcheckData<TypeParam>;
231 using CK = typename TypeParam::CommitmentKey;
232
233 auto prover_transcript = TypeParam::Transcript::test_prover_init_empty();
234
235 // SmallSubgroupIPAProver requires at least CURVE::SUBGROUP_SIZE + 3 elements in the ck.
236 static constexpr size_t log_subgroup_size = static_cast<size_t>(numeric::get_msb(Curve::SUBGROUP_SIZE));
237 CK ck = create_commitment_key<CK>(std::max<size_t>(this->circuit_size, 1ULL << (log_subgroup_size + 1)));
238
239 ZKData zk_sumcheck_data(this->log_circuit_size, prover_transcript, ck);
240
241 std::vector<FF> multivariate_challenge = this->generate_random_vector(this->num_sumcheck_challenges);
242
243 const FF claimed_inner_product =
244 Prover::compute_claimed_inner_product(zk_sumcheck_data, multivariate_challenge, this->log_circuit_size);
245
246 Prover small_subgroup_ipa_prover =
247 Prover(zk_sumcheck_data, multivariate_challenge, claimed_inner_product, prover_transcript, ck);
248
249 small_subgroup_ipa_prover.prove();
250
252 small_subgroup_ipa_prover.get_witness_polynomials();
253
254 // Tamper with witness polynomials
255 witness_polynomials[0].at(0) = FF::random_element();
256
257 const std::array<FF, NUM_SMALL_IPA_OPENING_CLAIMS> small_ipa_evaluations =
258 this->evaluate_small_ipa_witnesses(witness_polynomials);
259
260 bool consistency_checked = Verifier::check_libra_evaluations_consistency(
261 small_ipa_evaluations, this->evaluation_challenge, multivariate_challenge, claimed_inner_product);
262
263 // Since witness polynomials were modified, the consistency check must fail
264 EXPECT_FALSE(consistency_checked);
265}
266
267// Simulate the interaction between the prover and the verifier leading to the consistency check performed by the
268// verifier.
269TYPED_TEST(SmallSubgroupIPATest, TranslationMaskingTermConsistency)
270{
271 // TranslationData class is Grumpkin-specific
273 GTEST_SKIP();
274 } else {
275 using Curve = typename TypeParam::Curve;
276 using FF = typename Curve::ScalarField;
277 using Verifier = SmallSubgroupIPAVerifier<Curve>;
279 using CK = typename TypeParam::CommitmentKey;
280
281 auto prover_transcript = TypeParam::Transcript::test_prover_init_empty();
282 // Must satisfy num_wires * NUM_DISABLED_ROWS_IN_SUMCHECK + 1 < SUBGROUP_SIZE
283 const size_t num_wires = 5;
284
285 // SmallSubgroupIPAProver requires at least CURVE::SUBGROUP_SIZE + 3 elements in the ck.
286 static constexpr size_t log_subgroup_size = static_cast<size_t>(numeric::get_msb(Curve::SUBGROUP_SIZE));
287 CK ck = create_commitment_key<CK>(std::max<size_t>(this->circuit_size, 1ULL << (log_subgroup_size + 1)));
288
289 // Generate transcript polynomials
290 std::vector<Polynomial<FF>> transcript_polynomials;
291
292 for (size_t idx = 0; idx < num_wires; idx++) {
293 transcript_polynomials.push_back(Polynomial<FF>::random(this->circuit_size));
294 }
295
297 RefVector<Polynomial<FF>>(transcript_polynomials), prover_transcript, ck);
298
299 const FF evaluation_challenge_x = FF::random_element();
300 const FF batching_challenge_v = FF::random_element();
301
302 Prover small_subgroup_ipa_prover(
303 translation_data, evaluation_challenge_x, batching_challenge_v, prover_transcript, ck);
304 small_subgroup_ipa_prover.prove();
305
306 const std::array<FF, NUM_SMALL_IPA_OPENING_CLAIMS> small_ipa_evaluations =
307 this->evaluate_small_ipa_witnesses(small_subgroup_ipa_prover.get_witness_polynomials());
308
309 bool consistency_checked =
310 Verifier::check_eccvm_evaluations_consistency(small_ipa_evaluations,
311 this->evaluation_challenge,
312 evaluation_challenge_x,
313 batching_challenge_v,
314 small_subgroup_ipa_prover.claimed_inner_product);
315
316 EXPECT_TRUE(consistency_checked);
317 }
318};
319// Simulate the interaction between the prover and the verifier leading to the consistency check performed by the
320// verifier.
321TYPED_TEST(SmallSubgroupIPATest, TranslationMaskingTermConsistencyFailure)
322{
323 // TranslationData class is Grumpkin-specific
325 GTEST_SKIP();
326 } else {
327 using Curve = typename TypeParam::Curve;
328 using FF = typename Curve::ScalarField;
329 using Verifier = SmallSubgroupIPAVerifier<Curve>;
331 using CK = typename TypeParam::CommitmentKey;
332
333 auto prover_transcript = TypeParam::Transcript::test_prover_init_empty();
334 // Must satisfy num_wires * NUM_DISABLED_ROWS_IN_SUMCHECK + 1 < SUBGROUP_SIZE
335 const size_t num_wires = 5;
336
337 // SmallSubgroupIPAProver requires at least CURVE::SUBGROUP_SIZE + 3 elements in the ck.
338 static constexpr size_t log_subgroup_size = static_cast<size_t>(numeric::get_msb(Curve::SUBGROUP_SIZE));
339 CK ck = create_commitment_key<CK>(std::max<size_t>(this->circuit_size, 1ULL << (log_subgroup_size + 1)));
340
341 // Generate transcript polynomials
342 std::vector<Polynomial<FF>> transcript_polynomials;
343
344 for (size_t idx = 0; idx < num_wires; idx++) {
345 transcript_polynomials.push_back(Polynomial<FF>::random(this->circuit_size));
346 }
347
349 RefVector<Polynomial<FF>>(transcript_polynomials), prover_transcript, ck);
350
351 const FF evaluation_challenge_x = FF::random_element();
352 const FF batching_challenge_v = FF::random_element();
353
354 Prover small_subgroup_ipa_prover(
355 translation_data, evaluation_challenge_x, batching_challenge_v, prover_transcript, ck);
356 small_subgroup_ipa_prover.prove();
357
358 const std::array<FF, NUM_SMALL_IPA_OPENING_CLAIMS> small_ipa_evaluations =
359 this->evaluate_small_ipa_witnesses(small_subgroup_ipa_prover.get_witness_polynomials());
360
361 bool consistency_checked =
362 Verifier::check_eccvm_evaluations_consistency(small_ipa_evaluations,
363 this->evaluation_challenge,
364 evaluation_challenge_x,
365 batching_challenge_v,
366 /*tampered claimed inner product = */ FF::random_element());
367
368 EXPECT_TRUE(!consistency_checked);
369 }
370}
371
372// Test that verification aborts when the evaluation challenge is in the small subgroup.
373// This is an edge case that should never happen in practice (probability ~1/|H|), but
374// if it does, the protocol must reject to prevent soundness issues.
375TYPED_TEST(SmallSubgroupIPATest, EvaluationChallengeInSubgroupThrows)
376{
377 using FF = typename TypeParam::FF;
378 using Curve = typename TypeParam::Curve;
379 using Verifier = SmallSubgroupIPAVerifier<Curve>;
380
381 static constexpr size_t SUBGROUP_SIZE = TypeParam::SUBGROUP_SIZE;
382
383 // Create an evaluation challenge that is IN the small subgroup
384 // Using g^k for some k gives us an element where Z_H(g^k) = (g^k)^|H| - 1 = g^{k*|H|} - 1 = 1 - 1 = 0
385 // pick a random exponent k in the size of the subgroup
386 const FF subgroup_element = Curve::subgroup_generator.pow(10); // g^10 is in the subgroup
387
388 // Verify that Z_H(subgroup_element) = 0
389 const FF vanishing_poly_eval = subgroup_element.pow(SUBGROUP_SIZE) - FF(1);
390 EXPECT_EQ(vanishing_poly_eval, FF(0));
391
392 // Create dummy evaluations - the actual values don't matter since we expect an abort
395 };
396
397 std::vector<FF> multivariate_challenge = this->generate_random_vector(this->num_sumcheck_challenges);
398 FF dummy_inner_product = FF::random_element();
399
400 // The check should throw/abort because the evaluation challenge is in the subgroup
401 EXPECT_THROW(Verifier::check_libra_evaluations_consistency(
402 dummy_evaluations, subgroup_element, multivariate_challenge, dummy_inner_product),
403 std::runtime_error);
404}
405} // namespace bb
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:24
curve::Grumpkin Curve
BaseTranscript< Codec, HashFunction > Transcript
std::span< Fr > coeffs(size_t offset=0)
Strictly iterates the defined region of the polynomial. We keep this explicit, instead of having an i...
Fr evaluate(const Fr &z) 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
A template class for a reference vector. Behaves as if std::vector<T&> was possible.
A Curve-agnostic ZK protocol to prove inner products of small vectors.
static constexpr size_t num_sumcheck_challenges
typename Flavor::Transcript Transcript
static std::vector< FF > generate_random_vector(const size_t size)
static constexpr size_t circuit_size
static constexpr size_t log_circuit_size
typename Curve::ScalarField FF
std::array< FF, NUM_SMALL_IPA_OPENING_CLAIMS > evaluate_small_ipa_witnesses(const std::array< Polynomial< FF >, NUM_SMALL_IPA_COMMITMENTS > &witness_polynomials)
Verifies the consistency of polynomial evaluations provided by the the prover.
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 ScalarField subgroup_generator_inverse
Definition grumpkin.hpp:81
static constexpr ScalarField subgroup_generator
Definition grumpkin.hpp:79
constexpr T get_msb(const T in)
Definition get_msb.hpp:50
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
constexpr size_t NUM_SMALL_IPA_COMMITMENTS
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
::testing::Types< BN254Settings, GrumpkinSettings > TestFlavors
constexpr auto SMALL_IPA_CLAIMS
The five SmallSubgroupIPA opening claims, in transcript order.
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
CommitmentKey< Curve > ck
constexpr size_t NUM_SMALL_IPA_OPENING_CLAIMS
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
This structure is created to contain various polynomials and constants required by ZK Sumcheck.
BB_INLINE constexpr field pow(const uint256_t &exponent) const noexcept
constexpr field invert() const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_VF_LOAD_LIMBS * this
VectorField result