Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
shplemini.test.cpp
Go to the documentation of this file.
1
2#include "shplemini.hpp"
3#include "../commitment_key.test.hpp"
4#include "../gemini/gemini.hpp"
5#include "../kzg/kzg.hpp"
6#include "../shplonk/shplonk.hpp"
7#include "../utils/batch_mul_native.hpp"
14
15#include <gtest/gtest.h>
16#include <vector>
17
18namespace bb {
19
20template <class Flavor> class ShpleminiTest : public CommitmentTest<typename Flavor::Curve> {
21 public:
22 // Size of the test polynomials
23 static constexpr size_t log_n = 9;
24 static constexpr size_t n = 1UL << log_n;
25 // Total number of random polynomials in each test
26 static constexpr size_t num_polynomials = 7;
27 // Number of shiftable polynomials
28 static constexpr size_t num_shiftable = 2;
29
30 // The length of the mock sumcheck univariates.
31 static constexpr size_t sumcheck_univariate_length = 24;
32
33 using Fr = typename Flavor::Curve::ScalarField;
34 using GroupElement = typename Flavor::Curve::Element;
35 using Commitment = typename Flavor::Curve::AffineElement;
36 using CK = typename Flavor::CommitmentKey;
38
39 // Witness polynomials array: [0]=Concatenated(G), [1]=GrandSum(A), [2]=unused, [3]=Quotient(Q)
40 enum class TamperedPolynomial : size_t { None = SIZE_MAX, Concatenated = 0, GrandSum = 1, Quotient = 3 };
41
42 // libra_commitments array: [0]=Concatenated, [1]=GrandSum, [2]=Quotient
43 enum class TamperedCommitment : size_t { None = SIZE_MAX, Concatenated = 0, GrandSum = 1, Quotient = 2 };
44};
45
46using TestSettings = ::testing::Types<BN254Settings, GrumpkinSettings>;
47
49
50// Non-template test fixture for KZG-specific tests
51class ShpleminiKZGTest : public CommitmentTest<curve::BN254> {
52 public:
53 static constexpr size_t log_n = 9;
54 static constexpr size_t n = 1UL << log_n;
55};
56
57// This test checks that batch_multivariate_opening_claims method operates correctly
58TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
59{
60 using Curve = typename TypeParam::Curve;
61 using Fr = typename Curve::ScalarField;
62 using GroupElement = typename Curve::Element;
63 using Commitment = typename Curve::AffineElement;
64 using CK = typename TypeParam::CommitmentKey;
65
66 CK ck = create_commitment_key<CK>(this->n);
67
68 // Generate mock challenges
69 Fr rho = Fr::random_element();
70 Fr gemini_eval_challenge = Fr::random_element();
71 Fr shplonk_batching_challenge = Fr::random_element();
72 Fr shplonk_eval_challenge = Fr::random_element();
73
74 // Generate multilinear polynomials and compute their commitments
75 auto mle_opening_point = this->random_evaluation_point(this->log_n);
76
77 MockClaimGenerator<Curve> mock_claims(this->n,
78 /*num_polynomials*/ this->num_polynomials,
79 /*num_to_be_shifted*/ this->num_shiftable,
80 mle_opening_point,
81 ck);
82
83 // Collect multilinear evaluations
84 std::vector<Fr> rhos = gemini::powers_of_rho(rho, this->num_polynomials + this->num_shiftable);
85
86 // Lambda to compute batched multivariate evaluation
87 auto update_batched_eval = [&](Fr& batched_eval, const std::vector<Fr>& evaluations, Fr& rho_power) {
88 for (auto& eval : evaluations) {
89 batched_eval += eval * rho_power;
90 rho_power *= rho;
91 }
92 };
93
94 Fr rho_power(1);
95 Fr batched_evaluation(0);
96 update_batched_eval(batched_evaluation, mock_claims.unshifted.evals, rho_power);
97 update_batched_eval(batched_evaluation, mock_claims.to_be_shifted.evals, rho_power);
98
99 // Lambda to compute batched commitment
100 auto compute_batched_commitment = [&](const std::vector<Commitment>& commitments, Fr& rho_power) {
101 GroupElement batched = GroupElement::zero();
102 for (auto& comm : commitments) {
103 batched += comm * rho_power;
104 rho_power *= rho;
105 }
106 return batched;
107 };
108
109 // Compute batched commitments manually
110 rho_power = Fr(1);
111 GroupElement batched_commitment_unshifted =
112 compute_batched_commitment(mock_claims.unshifted.commitments, rho_power);
113 GroupElement batched_commitment_to_be_shifted =
114 compute_batched_commitment(mock_claims.to_be_shifted.commitments, rho_power);
115
116 // Compute expected result manually
117 GroupElement to_be_shifted_contribution = batched_commitment_to_be_shifted * gemini_eval_challenge.invert();
118
119 GroupElement commitment_to_univariate_pos = batched_commitment_unshifted + to_be_shifted_contribution;
120
121 GroupElement commitment_to_univariate_neg = batched_commitment_unshifted - to_be_shifted_contribution;
122
123 GroupElement expected_result =
124 commitment_to_univariate_pos * (shplonk_eval_challenge - gemini_eval_challenge).invert() +
125 commitment_to_univariate_neg *
126 (shplonk_batching_challenge * (shplonk_eval_challenge + gemini_eval_challenge).invert());
127
128 // Run the ShepliminiVerifier batching method
129 std::vector<Commitment> commitments;
130 std::vector<Fr> scalars;
131 Fr verifier_batched_evaluation{ 0 };
132
133 Fr inverted_vanishing_eval_pos = (shplonk_eval_challenge - gemini_eval_challenge).invert();
134 Fr inverted_vanishing_eval_neg = (shplonk_eval_challenge + gemini_eval_challenge).invert();
135
136 std::vector<Fr> inverted_vanishing_evals = { inverted_vanishing_eval_pos, inverted_vanishing_eval_neg };
137
139 inverted_vanishing_evals, shplonk_batching_challenge, gemini_eval_challenge);
140
142 commitments, scalars, verifier_batched_evaluation, rho);
143
144 // Final pairing check
145 GroupElement shplemini_result = batch_mul_native<Curve>(commitments, scalars);
146
147 EXPECT_EQ(commitments.size(),
148 mock_claims.unshifted.commitments.size() + mock_claims.to_be_shifted.commitments.size());
149 EXPECT_EQ(batched_evaluation, verifier_batched_evaluation);
150 EXPECT_EQ(-expected_result, shplemini_result);
151}
152TYPED_TEST(ShpleminiTest, CorrectnessOfGeminiClaimBatching)
153{
154 using Curve = TypeParam::Curve;
155 using GeminiProver = GeminiProver_<Curve>;
156 using ShpleminiVerifier = ShpleminiVerifier_<Curve>;
157 using ShplonkVerifier = ShplonkVerifier_<Curve>;
158 using Fr = typename Curve::ScalarField;
159 using GroupElement = typename Curve::Element;
160 using Commitment = typename Curve::AffineElement;
161 using Polynomial = typename bb::Polynomial<Fr>;
162 using CK = typename TypeParam::CommitmentKey;
163
164 CK ck = create_commitment_key<CK>(this->n);
165
166 // Generate mock challenges
167 Fr rho = Fr::random_element();
168 Fr gemini_eval_challenge = Fr::random_element();
169 Fr shplonk_batching_challenge = Fr::random_element();
170
171 std::vector<Fr> shplonk_batching_challenge_powers =
172 compute_shplonk_batching_challenge_powers(shplonk_batching_challenge, this->log_n);
173
174 Fr shplonk_eval_challenge = Fr::random_element();
175
176 std::vector<Fr> mle_opening_point = this->random_evaluation_point(this->log_n);
177
178 MockClaimGenerator<Curve> mock_claims(this->n,
179 /*num_polynomials*/ this->num_polynomials,
180 /*num_to_be_shifted*/ this->num_shiftable,
181 mle_opening_point,
182 ck);
183
184 // Collect multilinear evaluations
185 std::vector<Fr> rhos = gemini::powers_of_rho(rho, this->num_polynomials + this->num_shiftable);
186
187 Polynomial batched = mock_claims.polynomial_batcher.compute_batched(rho);
188
189 // Compute:
190 // - (d+1) opening pairs: {r, \hat{a}_0}, {-r^{2^i}, a_i}, i = 0, ..., d-1
191 // - (d+1) Fold polynomials Fold_{r}^(0), Fold_{-r}^(0), and Fold^(i), i = 0, ..., d-1
192 auto fold_polynomials = GeminiProver::compute_fold_polynomials(this->log_n, mle_opening_point, batched);
193
194 std::vector<Commitment> prover_commitments;
195 for (size_t l = 0; l < this->log_n - 1; ++l) {
196 auto commitment = ck.commit(fold_polynomials[l]);
197 prover_commitments.emplace_back(commitment);
198 }
199
200 auto [A_0_pos, A_0_neg] =
201 mock_claims.polynomial_batcher.compute_partially_evaluated_batch_polynomials(gemini_eval_challenge);
202
203 const auto opening_claims = GeminiProver::construct_univariate_opening_claims(
204 this->log_n, std::move(A_0_pos), std::move(A_0_neg), std::move(fold_polynomials), gemini_eval_challenge);
205
206 std::vector<Fr> prover_evaluations;
207 for (size_t l = 0; l < this->log_n; ++l) {
208 const auto& evaluation = opening_claims[l + 1].opening_pair.evaluation;
209 prover_evaluations.emplace_back(evaluation);
210 }
211
212 std::vector<Fr> r_squares = gemini::powers_of_evaluation_challenge(gemini_eval_challenge, this->log_n);
213
214 GroupElement expected_result = GroupElement::zero();
215 std::vector<Fr> expected_inverse_vanishing_evals;
216 expected_inverse_vanishing_evals.reserve(2 * this->log_n);
217 // Compute expected inverses
218 for (size_t idx = 0; idx < this->log_n; idx++) {
219 expected_inverse_vanishing_evals.emplace_back((shplonk_eval_challenge - r_squares[idx]).invert());
220 expected_inverse_vanishing_evals.emplace_back((shplonk_eval_challenge + r_squares[idx]).invert());
221 }
222
223 Fr current_challenge{ shplonk_batching_challenge * shplonk_batching_challenge };
224 for (size_t idx = 0; idx < prover_commitments.size(); ++idx) {
225 expected_result -= prover_commitments[idx] * current_challenge * expected_inverse_vanishing_evals[2 * idx + 2];
226 current_challenge *= shplonk_batching_challenge;
227 expected_result -= prover_commitments[idx] * current_challenge * expected_inverse_vanishing_evals[2 * idx + 3];
228 current_challenge *= shplonk_batching_challenge;
229 }
230
231 // Run the ShepliminiVerifier batching method
232 std::vector<Fr> inverse_vanishing_evals =
233 ShplonkVerifier::compute_inverted_gemini_denominators(shplonk_eval_challenge, r_squares);
234
235 Fr expected_constant_term_accumulator{ 0 };
236 std::vector<Fr> padding_indicator_array(this->log_n, Fr{ 1 });
237
238 std::vector<Fr> gemini_fold_pos_evaluations =
240 expected_constant_term_accumulator,
241 mle_opening_point,
242 r_squares,
243 prover_evaluations,
244 expected_constant_term_accumulator);
245 std::vector<Commitment> commitments;
246 std::vector<Fr> scalars;
247
248 ShpleminiVerifier::batch_gemini_claims_received_from_prover(padding_indicator_array,
249 prover_commitments,
250 prover_evaluations,
251 gemini_fold_pos_evaluations,
252 inverse_vanishing_evals,
253 shplonk_batching_challenge_powers,
254 commitments,
255 scalars,
256 expected_constant_term_accumulator);
257
258 // Compute the group element using the output of Shplemini method
259 GroupElement shplemini_result = batch_mul_native<Curve>(commitments, scalars);
260
261 EXPECT_EQ(shplemini_result, expected_result);
262}
263
269TYPED_TEST(ShpleminiTest, ShpleminiZKNoSumcheckOpenings)
270{
271 using ZKData = ZKSumcheckData<TypeParam>;
272 using Curve = TypeParam::Curve;
273 using ShpleminiProver = ShpleminiProver_<Curve>;
274 constexpr bool HasZK = true;
275 using ShpleminiVerifier = ShpleminiVerifier_<Curve, HasZK>;
276 using Fr = typename Curve::ScalarField;
277 using Commitment = typename Curve::AffineElement;
278 using CK = typename TypeParam::CommitmentKey;
279
280 // Initialize transcript and commitment key
281 auto prover_transcript = TypeParam::Transcript::prover_init_empty();
282
283 // SmallSubgroupIPAProver requires at least CURVE::SUBGROUP_SIZE + 3 elements in the ck.
284 static constexpr size_t log_subgroup_size = static_cast<size_t>(numeric::get_msb(Curve::SUBGROUP_SIZE));
285 CK ck = create_commitment_key<CK>(std::max<size_t>(this->n, 1ULL << (log_subgroup_size + 1)));
286
287 // Generate Libra polynomials, compute masked concatenated Libra polynomial, commit to it
288 ZKData zk_sumcheck_data(this->log_n, prover_transcript, ck);
289
290 // Generate multivariate challenge
291 std::vector<Fr> mle_opening_point = this->random_evaluation_point(this->log_n);
292
293 // Generate random prover polynomials, compute their evaluations and commitments
294 MockClaimGenerator<Curve> mock_claims(this->n,
295 /*num_polynomials*/ this->num_polynomials,
296 /*num_to_be_shifted*/ this->num_shiftable,
297 mle_opening_point,
298 ck);
299
300 // Compute the sum of the Libra constant term and Libra univariates evaluated at Sumcheck challenges
302 zk_sumcheck_data, mle_opening_point, this->log_n);
303
304 prover_transcript->send_to_verifier("Libra:claimed_evaluation", claimed_inner_product);
305
306 // Instantiate SmallSubgroupIPAProver, this prover sends commitments to Big Sum and Quotient polynomials
307 SmallSubgroupIPAProver<TypeParam> small_subgroup_ipa_prover(
308 zk_sumcheck_data, mle_opening_point, claimed_inner_product, prover_transcript, ck);
309 small_subgroup_ipa_prover.prove();
310
311 // Reduce to KZG or IPA based on the curve used in the test Flavor
312 const auto opening_claim = ShpleminiProver::prove(this->n,
313 mock_claims.polynomial_batcher,
314 mle_opening_point,
315 ck,
316 prover_transcript,
317 small_subgroup_ipa_prover.get_witness_polynomials());
318
320 TestFixture::IPA::compute_opening_proof(this->ck(), opening_claim, prover_transcript);
321 } else {
322 KZG<Curve>::compute_opening_proof(this->ck(), opening_claim, prover_transcript);
323 }
324
325 // Initialize verifier's transcript
326 auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);
327
328 // Start populating Verifier's array of Libra commitments
330 libra_commitments[0] =
331 verifier_transcript->template receive_from_prover<Commitment>("Libra:concatenation_commitment");
332
333 // Place Libra data to the transcript
334 const Fr libra_total_sum = verifier_transcript->template receive_from_prover<Fr>("Libra:Sum");
335 const Fr libra_challenge = verifier_transcript->template get_challenge<Fr>("Libra:Challenge");
336 const Fr libra_evaluation = verifier_transcript->template receive_from_prover<Fr>("Libra:claimed_evaluation");
337
338 // Check that transcript is consistent
339 EXPECT_EQ(libra_total_sum, zk_sumcheck_data.libra_total_sum);
340 EXPECT_EQ(libra_challenge, zk_sumcheck_data.libra_challenge);
341 EXPECT_EQ(libra_evaluation, claimed_inner_product);
342
343 // Finalize the array of Libra/SmallSubgroupIpa commitments
344 libra_commitments[1] = verifier_transcript->template receive_from_prover<Commitment>("Libra:grand_sum_commitment");
345 libra_commitments[2] = verifier_transcript->template receive_from_prover<Commitment>("Libra:quotient_commitment");
346
347 // Run Shplemini
348 std::vector<Fr> padding_indicator_array(this->log_n, Fr{ 1 });
349
350 auto [batch_opening_claim, consistency_checked] =
351 ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array,
352 mock_claims.claim_batcher,
353 mle_opening_point,
354 this->vk().get_g1_identity(),
355 verifier_transcript,
356 {},
357 libra_commitments,
358 libra_evaluation);
359 // Verify claim using KZG or IPA
361 auto result =
362 TestFixture::IPA::reduce_verify_batch_opening_claim(batch_opening_claim, this->vk(), verifier_transcript);
363 EXPECT_EQ(result, true);
364 } else {
365 const auto pairing_points =
366 KZG<Curve>::reduce_verify_batch_opening_claim(std::move(batch_opening_claim), verifier_transcript);
367 // Final pairing check: e([Q] - [Q_z] + z[W], [1]_2) = e([W], [x]_2)
368 EXPECT_EQ(this->vk().pairing_check(pairing_points[0], pairing_points[1]), true);
369 }
370 EXPECT_EQ(consistency_checked, true);
371}
372
379TYPED_TEST(ShpleminiTest, ShpleminiZKWithSumcheckOpenings)
380{
381 using Curve = TypeParam::Curve;
382 using Fr = typename Curve::ScalarField;
383 using Commitment = typename Curve::AffineElement;
384 using CK = typename TypeParam::CommitmentKey;
385
386 using ShpleminiProver = ShpleminiProver_<Curve>;
387 constexpr bool HasZK = true;
388 using ShpleminiVerifier = ShpleminiVerifier_<Curve, HasZK>;
389
390 CK ck = create_commitment_key<CK>(4096);
391
392 // Generate Sumcheck challenge
393 std::vector<Fr> challenge = this->random_evaluation_point(this->log_n);
394
395 auto prover_transcript = TypeParam::Transcript::prover_init_empty();
396
397 // Generate masking polynomials for Sumcheck Round Univariates
398 ZKSumcheckData<TypeParam> zk_sumcheck_data(this->log_n, prover_transcript, ck);
399 // Generate mock witness
400 MockClaimGenerator<Curve> mock_claims(this->n, 1);
401
402 // Generate valid sumcheck polynomials of given length
403 mock_claims.template compute_sumcheck_opening_data<TypeParam>(
404 this->log_n, this->sumcheck_univariate_length, challenge, ck);
405
406 // Compute the sum of the Libra constant term and Libra univariates evaluated at Sumcheck challenges
407 const Fr claimed_inner_product =
408 SmallSubgroupIPAProver<TypeParam>::compute_claimed_inner_product(zk_sumcheck_data, challenge, this->log_n);
409
410 prover_transcript->send_to_verifier("Libra:claimed_evaluation", claimed_inner_product);
411
412 // Instantiate SmallSubgroupIPAProver, this prover sends commitments to Big Sum and Quotient polynomials
413 SmallSubgroupIPAProver<TypeParam> small_subgroup_ipa_prover(
414 zk_sumcheck_data, challenge, claimed_inner_product, prover_transcript, ck);
415 small_subgroup_ipa_prover.prove();
416
417 // Reduce proving to a single claimed fed to KZG or IPA
418 const auto opening_claim = ShpleminiProver::prove(this->n,
419 mock_claims.polynomial_batcher,
420 challenge,
421 ck,
422 prover_transcript,
423 small_subgroup_ipa_prover.get_witness_polynomials(),
424 mock_claims.round_univariates,
425 mock_claims.sumcheck_evaluations);
426
428 TestFixture::IPA::compute_opening_proof(this->ck(), opening_claim, prover_transcript);
429 } else {
430 KZG<Curve>::compute_opening_proof(this->ck(), opening_claim, prover_transcript);
431 }
432
433 // Initialize verifier's transcript
434 auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);
435
437 libra_commitments[0] =
438 verifier_transcript->template receive_from_prover<Commitment>("Libra:concatenation_commitment");
439
440 // Place Libra data to the transcript
441 const Fr libra_total_sum = verifier_transcript->template receive_from_prover<Fr>("Libra:Sum");
442 const Fr libra_challenge = verifier_transcript->template get_challenge<Fr>("Libra:Challenge");
443 const Fr libra_evaluation = verifier_transcript->template receive_from_prover<Fr>("Libra:claimed_evaluation");
444
445 // Check that transcript is consistent
446 EXPECT_EQ(libra_total_sum, zk_sumcheck_data.libra_total_sum);
447 EXPECT_EQ(libra_challenge, zk_sumcheck_data.libra_challenge);
448 EXPECT_EQ(libra_evaluation, claimed_inner_product);
449
450 // Finalize the array of Libra/SmallSubgroupIpa commitments
451 libra_commitments[1] = verifier_transcript->template receive_from_prover<Commitment>("Libra:grand_sum_commitment");
452 libra_commitments[2] = verifier_transcript->template receive_from_prover<Commitment>("Libra:quotient_commitment");
453
454 // Run Shplemini
455 std::vector<Fr> padding_indicator_array(this->log_n, Fr{ 1 });
456
457 auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array,
458 mock_claims.claim_batcher,
459 challenge,
460 this->vk().get_g1_identity(),
461 verifier_transcript,
462 {},
463 libra_commitments,
464 libra_evaluation,
465 mock_claims.sumcheck_commitments,
466 mock_claims.sumcheck_evaluations)
467 .batch_opening_claim;
468 // Verify claim using KZG or IPA
470 auto result =
471 TestFixture::IPA::reduce_verify_batch_opening_claim(batch_opening_claim, this->vk(), verifier_transcript);
472 EXPECT_EQ(result, true);
473 } else {
474 const auto pairing_points =
475 KZG<Curve>::reduce_verify_batch_opening_claim(std::move(batch_opening_claim), verifier_transcript);
476 // Final pairing check: e([Q] - [Q_z] + z[W], [1]_2) = e([W], [x]_2)
477 EXPECT_EQ(this->vk().pairing_check(pairing_points[0], pairing_points[1]), true);
478 }
479}
480
487TYPED_TEST(ShpleminiTest, HighDegreeAttackAccept)
488{
489 using Curve = typename TypeParam::Curve;
490 using Fr = typename Curve::ScalarField;
491 using CK = typename TypeParam::CommitmentKey;
492 using ShpleminiProver = ShpleminiProver_<Curve>;
493 using ShpleminiVerifier = ShpleminiVerifier_<Curve>;
495
496 // Use the fixture's n (1 << 9 = 512) as the polynomial size
497 // small_log_n = 3 means we fold to a constant after 3 rounds
498 static constexpr size_t small_log_n = 3;
499 CK ck = create_commitment_key<CK>(this->n);
500
501 // Sample public opening point (u_0, u_1, u_2)
502 auto u = this->random_evaluation_point(small_log_n);
503
504 // Choose a claimed eval at `u`
505 Fr claimed_multilinear_eval = Fr::random_element();
506
507 // poly is of high degrees (up to n), as the SRS allows for it
508 Polynomial poly(this->n);
509
510 // Define poly to be of a specific form such that after small_log_n folds with u, it becomes a constant equal to
511 // claimed_multilinear_eval. The non-zero coefficients are at indices that fold correctly.
512 // For n = 512, small_log_n = 3: indices 4, 504, 508 work (instead of 4, 4088, 4092 for n = 4096)
513 const Fr tail = ((Fr(1) - u[0]) * (Fr(1) - u[1])).invert();
514 poly.at(4) = claimed_multilinear_eval * tail / u[2];
515 poly.at(this->n - 8) = tail; // 504 for n=512
516 poly.at(this->n - 4) = -tail * (Fr(1) - u[2]) / u[2]; // 508 for n=512
517
518 MockClaimGenerator<Curve> mock_claims(
519 this->n, std::vector{ std::move(poly) }, std::vector<Fr>{ claimed_multilinear_eval }, ck);
520
521 auto prover_transcript = NativeTranscript::prover_init_empty();
522
523 // Run Shplemini prover
524 const auto opening_claim =
525 ShpleminiProver::prove(this->n, mock_claims.polynomial_batcher, u, ck, prover_transcript);
526
527 // Run KZG/IPA prover
529 TestFixture::IPA::compute_opening_proof(ck, opening_claim, prover_transcript);
530 } else {
531 KZG<Curve>::compute_opening_proof(ck, opening_claim, prover_transcript);
532 }
533
534 // Verifier side
535 auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);
536
537 std::vector<Fr> padding_indicator_array(small_log_n, Fr{ 1 });
538
539 auto batch_opening_claim =
540 ShpleminiVerifier::compute_batch_opening_claim(
541 padding_indicator_array, mock_claims.claim_batcher, u, this->vk().get_g1_identity(), verifier_transcript)
542 .batch_opening_claim;
543
544 // Verify claim - should succeed because the polynomial was crafted to fold correctly
546 auto result =
547 TestFixture::IPA::reduce_verify_batch_opening_claim(batch_opening_claim, this->vk(), verifier_transcript);
548 EXPECT_EQ(result, true);
549 } else {
550 const auto pairing_points =
551 KZG<Curve>::reduce_verify_batch_opening_claim(std::move(batch_opening_claim), verifier_transcript);
552 EXPECT_EQ(this->vk().pairing_check(pairing_points[0], pairing_points[1]), true);
553 }
554}
555
561TYPED_TEST(ShpleminiTest, HighDegreeAttackReject)
562{
563 using Curve = typename TypeParam::Curve;
564 using Fr = typename Curve::ScalarField;
565 using CK = typename TypeParam::CommitmentKey;
566 using ShpleminiProver = ShpleminiProver_<Curve>;
567 using ShpleminiVerifier = ShpleminiVerifier_<Curve>;
569
570 // Use a larger SRS size to allow committing to high degree polynomials
571 static constexpr size_t big_n = 1UL << 12;
572 static constexpr size_t small_log_n = 3;
573 static constexpr size_t big_ck_size = 1 << 14;
574 CK ck = create_commitment_key<CK>(big_ck_size);
575
576 // Random high degree polynomial
577 Polynomial poly = Polynomial::random(big_n);
578
579 // Sample public opening point (u_0, u_1, u_2)
580 auto u = this->random_evaluation_point(small_log_n);
581
582 // Choose a random claimed eval at `u` (likely wrong)
583 Fr claimed_multilinear_eval = Fr::random_element();
584
585 MockClaimGenerator<Curve> mock_claims(
586 big_n, std::vector{ std::move(poly) }, std::vector<Fr>{ claimed_multilinear_eval }, ck);
587
588 auto prover_transcript = NativeTranscript::prover_init_empty();
589
590 // Run Shplemini prover
591 const auto opening_claim = ShpleminiProver::prove(big_n, mock_claims.polynomial_batcher, u, ck, prover_transcript);
592
593 // Run KZG/IPA prover
595 TestFixture::IPA::compute_opening_proof(ck, opening_claim, prover_transcript);
596 } else {
597 KZG<Curve>::compute_opening_proof(ck, opening_claim, prover_transcript);
598 }
599
600 // Verifier side
601 auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);
602
603 std::vector<Fr> padding_indicator_array(small_log_n, Fr{ 1 });
604
605 auto batch_opening_claim =
606 ShpleminiVerifier::compute_batch_opening_claim(
607 padding_indicator_array, mock_claims.claim_batcher, u, this->vk().get_g1_identity(), verifier_transcript)
608 .batch_opening_claim;
609
610 // Verify claim - should fail because the random polynomial doesn't fold correctly
612 // IPA throws an exception on verification failure
613 EXPECT_THROW(
614 TestFixture::IPA::reduce_verify_batch_opening_claim(batch_opening_claim, this->vk(), verifier_transcript),
615 std::runtime_error);
616 } else {
617 const auto pairing_points =
618 KZG<Curve>::reduce_verify_batch_opening_claim(std::move(batch_opening_claim), verifier_transcript);
619 EXPECT_EQ(this->vk().pairing_check(pairing_points[0], pairing_points[1]), false);
620 }
621}
622
628TYPED_TEST(ShpleminiTest, LibraConsistencyCheckFailsOnCorruptedEvaluation)
629{
630 using ZKData = ZKSumcheckData<TypeParam>;
631 using Curve = typename TypeParam::Curve;
632 using ShpleminiProver = ShpleminiProver_<Curve>;
633 constexpr bool HasZK = true;
634 using ShpleminiVerifier = ShpleminiVerifier_<Curve, HasZK>;
635 using Fr = typename Curve::ScalarField;
636 using Commitment = typename Curve::AffineElement;
637 using CK = typename TypeParam::CommitmentKey;
638
639 // Initialize transcript and commitment key
640 auto prover_transcript = TypeParam::Transcript::prover_init_empty();
641
642 // SmallSubgroupIPAProver requires at least CURVE::SUBGROUP_SIZE + 3 elements in the ck.
643 static constexpr size_t log_subgroup_size = static_cast<size_t>(numeric::get_msb(Curve::SUBGROUP_SIZE));
644 CK ck = create_commitment_key<CK>(std::max<size_t>(this->n, 1ULL << (log_subgroup_size + 1)));
645
646 // Generate Libra polynomials, compute masked concatenated Libra polynomial, commit to it
647 ZKData zk_sumcheck_data(this->log_n, prover_transcript, ck);
648
649 // Generate multivariate challenge
650 std::vector<Fr> mle_opening_point = this->random_evaluation_point(this->log_n);
651
652 // Generate random prover polynomials, compute their evaluations and commitments
653 MockClaimGenerator<Curve> mock_claims(this->n,
654 /*num_polynomials*/ this->num_polynomials,
655 /*num_to_be_shifted*/ this->num_shiftable,
656 mle_opening_point,
657 ck);
658
659 // Compute the correct sum of the Libra constant term and Libra univariates evaluated at Sumcheck challenges
661 zk_sumcheck_data, mle_opening_point, this->log_n);
662
663 // CORRUPT: Malicious prover sends a corrupted evaluation via the transcript
664 const Fr corrupted_inner_product = claimed_inner_product + Fr::random_element();
665 prover_transcript->send_to_verifier("Libra:claimed_evaluation", corrupted_inner_product);
666
667 // Instantiate SmallSubgroupIPAProver with the CORRECT value (prover's internal state is correct,
668 // but the value sent to verifier is corrupted - simulating a cheating prover)
669 SmallSubgroupIPAProver<TypeParam> small_subgroup_ipa_prover(
670 zk_sumcheck_data, mle_opening_point, corrupted_inner_product, prover_transcript, ck);
671 small_subgroup_ipa_prover.prove();
672
673 // Reduce to KZG or IPA based on the curve used in the test Flavor
674 const auto opening_claim = ShpleminiProver::prove(this->n,
675 mock_claims.polynomial_batcher,
676 mle_opening_point,
677 ck,
678 prover_transcript,
679 small_subgroup_ipa_prover.get_witness_polynomials());
680
682 TestFixture::IPA::compute_opening_proof(this->ck(), opening_claim, prover_transcript);
683 } else {
684 KZG<Curve>::compute_opening_proof(this->ck(), opening_claim, prover_transcript);
685 }
686
687 // Initialize verifier's transcript
688 auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);
689
690 // Start populating Verifier's array of Libra commitments
692 libra_commitments[0] =
693 verifier_transcript->template receive_from_prover<Commitment>("Libra:concatenation_commitment");
694
695 // Place Libra data to the transcript
696 [[maybe_unused]] const Fr libra_total_sum = verifier_transcript->template receive_from_prover<Fr>("Libra:Sum");
697 [[maybe_unused]] const Fr libra_challenge = verifier_transcript->template get_challenge<Fr>("Libra:Challenge");
698 // Verifier receives the CORRUPTED evaluation from the transcript
699 const Fr libra_evaluation = verifier_transcript->template receive_from_prover<Fr>("Libra:claimed_evaluation");
700
701 // Finalize the array of Libra/SmallSubgroupIpa commitments
702 libra_commitments[1] = verifier_transcript->template receive_from_prover<Commitment>("Libra:grand_sum_commitment");
703 libra_commitments[2] = verifier_transcript->template receive_from_prover<Commitment>("Libra:quotient_commitment");
704
705 // Run Shplemini - verifier uses the corrupted evaluation received from the transcript
706 std::vector<Fr> padding_indicator_array(this->log_n, Fr{ 1 });
707
708 auto shplemini_output = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array,
709 mock_claims.claim_batcher,
710 mle_opening_point,
711 this->vk().get_g1_identity(),
712 verifier_transcript,
713 {},
714 libra_commitments,
715 libra_evaluation);
716
717 // Verify that consistency_checked is false due to corrupted Libra evaluation
718 EXPECT_FALSE(shplemini_output.consistency_checked);
719}
720
728template <typename TypeParam>
730 typename ShpleminiTest<TypeParam>::TamperedPolynomial tamper_polynomial,
731 typename ShpleminiTest<TypeParam>::TamperedCommitment tamper_commitment,
732 bool expected_consistency_checked)
733{
734 using TamperedPolynomial = typename ShpleminiTest<TypeParam>::TamperedPolynomial;
735 using TamperedCommitment = typename ShpleminiTest<TypeParam>::TamperedCommitment;
736 using ZKData = ZKSumcheckData<TypeParam>;
737 using Curve = typename TypeParam::Curve;
738 using ShpleminiProver = ShpleminiProver_<Curve>;
739 constexpr bool HasZK = true;
740 using ShpleminiVerifier = ShpleminiVerifier_<Curve, HasZK>;
741 using Fr = typename Curve::ScalarField;
742 using Commitment = typename Curve::AffineElement;
743 using CK = typename TypeParam::CommitmentKey;
744
745 auto prover_transcript = TypeParam::Transcript::prover_init_empty();
746
747 static constexpr size_t log_subgroup_size = static_cast<size_t>(numeric::get_msb(Curve::SUBGROUP_SIZE));
748 CK ck = create_commitment_key<CK>(std::max<size_t>(test->n, 1ULL << (log_subgroup_size + 1)));
749
750 ZKData zk_sumcheck_data(test->log_n, prover_transcript, ck);
751 std::vector<Fr> mle_opening_point = test->random_evaluation_point(test->log_n);
752
753 MockClaimGenerator<Curve> mock_claims(test->n, test->num_polynomials, test->num_shiftable, mle_opening_point, ck);
754
756 zk_sumcheck_data, mle_opening_point, test->log_n);
757
758 prover_transcript->send_to_verifier("Libra:claimed_evaluation", claimed_inner_product);
759
760 SmallSubgroupIPAProver<TypeParam> small_subgroup_ipa_prover(
761 zk_sumcheck_data, mle_opening_point, claimed_inner_product, prover_transcript, ck);
762 small_subgroup_ipa_prover.prove();
763
764 auto witness_polynomials = small_subgroup_ipa_prover.get_witness_polynomials();
765
766 // Optionally tamper with a witness polynomial
767 if (tamper_polynomial != TamperedPolynomial::None) {
768 witness_polynomials[static_cast<size_t>(tamper_polynomial)].at(0) += Fr::random_element();
769 }
770
771 const auto opening_claim = ShpleminiProver::prove(
772 test->n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript, witness_polynomials);
773
775 ShpleminiTest<TypeParam>::IPA::compute_opening_proof(test->ck(), opening_claim, prover_transcript);
776 } else {
777 KZG<Curve>::compute_opening_proof(test->ck(), opening_claim, prover_transcript);
778 }
779
780 auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);
781
783 libra_commitments[0] =
784 verifier_transcript->template receive_from_prover<Commitment>("Libra:concatenation_commitment");
785
786 [[maybe_unused]] const Fr libra_total_sum = verifier_transcript->template receive_from_prover<Fr>("Libra:Sum");
787 [[maybe_unused]] const Fr libra_challenge = verifier_transcript->template get_challenge<Fr>("Libra:Challenge");
788 const Fr libra_evaluation = verifier_transcript->template receive_from_prover<Fr>("Libra:claimed_evaluation");
789
790 libra_commitments[1] = verifier_transcript->template receive_from_prover<Commitment>("Libra:grand_sum_commitment");
791 libra_commitments[2] = verifier_transcript->template receive_from_prover<Commitment>("Libra:quotient_commitment");
792
793 // Optionally tamper with a commitment
794 if (tamper_commitment != TamperedCommitment::None) {
795 auto idx = static_cast<size_t>(tamper_commitment);
796 libra_commitments[idx] = libra_commitments[idx] + Commitment::one();
797 }
798
799 std::vector<Fr> padding_indicator_array(test->log_n, Fr{ 1 });
800
801 auto [batch_opening_claim, consistency_checked] =
802 ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array,
803 mock_claims.claim_batcher,
804 mle_opening_point,
805 test->vk().get_g1_identity(),
806 verifier_transcript,
807 {},
808 libra_commitments,
809 libra_evaluation);
810
811 EXPECT_EQ(consistency_checked, expected_consistency_checked);
812
813 // PCS verification should always fail when tampering occurred
816 batch_opening_claim, test->vk(), verifier_transcript),
817 std::runtime_error);
818 } else {
819 const auto pairing_points =
820 KZG<Curve>::reduce_verify_batch_opening_claim(std::move(batch_opening_claim), verifier_transcript);
821 EXPECT_FALSE(test->vk().pairing_check(pairing_points[0], pairing_points[1]));
822 }
823}
824
828TYPED_TEST(ShpleminiTest, LibraQuotientPolynomialTamperingCausesVerificationFailure)
829{
830 using TamperedPolynomial = typename TestFixture::TamperedPolynomial;
831 using TamperedCommitment = typename TestFixture::TamperedCommitment;
832 // Consistency check fails because Q(r) is wrong
834 this, TamperedPolynomial::Quotient, TamperedCommitment::None, /*expected_consistency_checked=*/false);
835}
836
840TYPED_TEST(ShpleminiTest, LibraQuotientCommitmentTamperingCausesVerificationFailure)
841{
842 using TamperedPolynomial = typename TestFixture::TamperedPolynomial;
843 using TamperedCommitment = typename TestFixture::TamperedCommitment;
844 // Consistency check passes because evaluations are honest
846 this, TamperedPolynomial::None, TamperedCommitment::Quotient, /*expected_consistency_checked=*/true);
847}
848
852TYPED_TEST(ShpleminiTest, LibraGrandSumPolynomialTamperingCausesVerificationFailure)
853{
854 using TamperedPolynomial = typename TestFixture::TamperedPolynomial;
855 using TamperedCommitment = typename TestFixture::TamperedCommitment;
856 // Consistency check fails because A(r) and A(g*r) are wrong
858 this, TamperedPolynomial::GrandSum, TamperedCommitment::None, /*expected_consistency_checked=*/false);
859}
860
864TYPED_TEST(ShpleminiTest, LibraGrandSumCommitmentTamperingCausesVerificationFailure)
865{
866 using TamperedPolynomial = typename TestFixture::TamperedPolynomial;
867 using TamperedCommitment = typename TestFixture::TamperedCommitment;
868 // Consistency check passes because evaluations are honest
870 this, TamperedPolynomial::None, TamperedCommitment::GrandSum, /*expected_consistency_checked=*/true);
871}
872
876TYPED_TEST(ShpleminiTest, LibraConcatenatedPolynomialTamperingCausesVerificationFailure)
877{
878 using TamperedPolynomial = typename TestFixture::TamperedPolynomial;
879 using TamperedCommitment = typename TestFixture::TamperedCommitment;
880 // Consistency check fails because G(r) is wrong
882 this, TamperedPolynomial::Concatenated, TamperedCommitment::None, /*expected_consistency_checked=*/false);
883}
884
888TYPED_TEST(ShpleminiTest, LibraConcatenatedCommitmentTamperingCausesVerificationFailure)
889{
890 using TamperedPolynomial = typename TestFixture::TamperedPolynomial;
891 using TamperedCommitment = typename TestFixture::TamperedCommitment;
892 // Consistency check passes because evaluations are honest
894 this, TamperedPolynomial::None, TamperedCommitment::Concatenated, /*expected_consistency_checked=*/true);
895}
896
897} // namespace bb
static std::shared_ptr< BaseTranscript > prover_init_empty()
For testing: initializes transcript with some arbitrary data so that a challenge can be generated aft...
static std::shared_ptr< BaseTranscript > verifier_init_empty(const std::shared_ptr< BaseTranscript > &transcript)
For testing: initializes transcript based on proof data then receives junk data produced by BaseTrans...
std::vector< Fr > random_evaluation_point(const size_t num_variables)
bb::CommitmentKey< Curve > CommitmentKey
Polynomial compute_batched(const Fr &challenge)
Compute batched polynomial A₀ = F + G/X as the linear combination of all polynomials to be opened,...
Definition gemini.hpp:175
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:231
static std::vector< Fr > compute_fold_pos_evaluations(std::span< const Fr > padding_indicator_array, const Fr &batched_evaluation, std::span< const Fr > evaluation_point, std::span< const Fr > challenge_powers, std::span< const Fr > fold_neg_evals, Fr p_neg=Fr(0))
Compute .
Definition gemini.hpp:397
static PairingPointsType reduce_verify_batch_opening_claim(BatchOpeningClaim< Curve > &&batch_opening_claim, const std::shared_ptr< Transcript > &transcript, const size_t expected_final_msm_size=0)
Computes the input points for the pairing check needed to verify a KZG opening claim obtained from a ...
Definition kzg.hpp:131
static void compute_opening_proof(const CK &ck, const ProverOpeningClaim< Curve > &opening_claim, const std::shared_ptr< Transcript > &prover_trancript)
Computes the KZG commitment to an opening proof polynomial at a single evaluation point.
Definition kzg.hpp:43
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
static Polynomial random(size_t size, size_t start_index=0)
Fr & at(size_t index)
Our mutable accessor, unlike operator[]. We abuse precedent a bit to differentiate at() and operator[...
static constexpr size_t n
static constexpr size_t log_n
static constexpr size_t n
typename Flavor::Curve::ScalarField Fr
static constexpr size_t num_polynomials
typename Flavor::CommitmentKey CK
typename Flavor::Curve::AffineElement Commitment
static constexpr size_t log_n
static constexpr size_t sumcheck_univariate_length
static constexpr size_t num_shiftable
typename Flavor::Curve::Element GroupElement
IPA< typename Flavor::Curve, log_n > IPA
Shplonk Verifier.
Definition shplonk.hpp:343
A Curve-agnostic ZK protocol to prove inner products of small vectors.
std::array< bb::Polynomial< FF >, NUM_SMALL_IPA_EVALUATIONS > get_witness_polynomials() const
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...
void prove()
Compute the derived witnesses and and commit to them.
typename Group::element Element
Definition grumpkin.hpp:62
static constexpr size_t SUBGROUP_SIZE
Definition grumpkin.hpp:74
typename Group::affine_element AffineElement
Definition grumpkin.hpp:63
bool expected_result
std::vector< Fr > powers_of_evaluation_challenge(const Fr &r, const size_t num_squares)
Compute squares of folding challenge r.
Definition gemini.hpp:94
std::vector< Fr > powers_of_rho(const Fr &rho, const size_t num_powers)
Compute powers of challenge ρ
Definition gemini.hpp:77
constexpr T get_msb(const T in)
Definition get_msb.hpp:47
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
::testing::Types< BN254Settings, GrumpkinSettings > TestSettings
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
void run_libra_tampering_test(ShpleminiTest< TypeParam > *test, typename ShpleminiTest< TypeParam >::TamperedPolynomial tamper_polynomial, typename ShpleminiTest< TypeParam >::TamperedCommitment tamper_commitment, bool expected_consistency_checked)
Helper to run a Libra tampering test with configurable tampering options.
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
CommitmentKey< Curve > ck
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Curve::ScalarField Fr
void compute_scalars_for_each_batch(std::span< const Fr > inverted_vanishing_evals, const Fr &nu_challenge, const Fr &r_challenge)
Compute scalars used to batch each set of claims, excluding contribution from batching challenge \rho...
void update_batch_mul_inputs_and_batched_evaluation(std::vector< Commitment > &commitments, std::vector< Fr > &scalars, Fr &batched_evaluation, const Fr &rho, Fr shplonk_batching_pos={ 0 }, Fr shplonk_batching_neg={ 0 })
Append the commitments and scalars from each batch of claims to the Shplemini, vectors which subseque...
Constructs random polynomials, computes commitments and corresponding evaluations.
std::vector< bb::Polynomial< Fr > > round_univariates
std::vector< Commitment > sumcheck_commitments
std::vector< std::array< Fr, 3 > > sumcheck_evaluations
This structure is created to contain various polynomials and constants required by ZK Sumcheck.
constexpr field invert() const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept