Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ipa.test.cpp
Go to the documentation of this file.
1
7using namespace bb;
8
9namespace {
11
12class IPATest : public CommitmentTest<Curve> {
13 public:
14 using Fr = typename Curve::ScalarField;
15 using GroupElement = typename Curve::Element;
16 using CK = CommitmentKey<Curve>;
19 using Commitment = typename Curve::AffineElement;
20
21 using ShplonkProver = ShplonkProver_<Curve>;
22 using ShplonkVerifier = ShplonkVerifier_<Curve>;
23 using GeminiProver = GeminiProver_<Curve>;
24 using GeminiVerifier = GeminiVerifier_<Curve>;
25 using ShpleminiVerifier = ShpleminiVerifier_<Curve>;
26 using ClaimBatcher = ClaimBatcher_<Curve>;
27 using ClaimBatch = ClaimBatcher::Batch;
28
29 static CK ck;
30 static VK vk;
31
32 static constexpr size_t log_n = 7;
33
35
36 static constexpr size_t n = 1UL << log_n;
37
38 static void SetUpTestSuite()
39 {
40 ck = create_commitment_key<CK>(n);
41 vk = create_verifier_commitment_key<VK>();
42 }
43
44 struct ResultOfProveVerify {
45 bool result;
46 std::shared_ptr<NativeTranscript> prover_transcript;
47 std::shared_ptr<NativeTranscript> verifier_transcript;
48 };
49
50 static ResultOfProveVerify run_native_prove_verify(const Polynomial& poly, const Fr x)
51 {
52 Commitment commitment = ck.commit(poly);
53 auto eval = poly.evaluate(x);
54 const OpeningPair<Curve> opening_pair = { x, eval };
55 const OpeningClaim<Curve> opening_claim{ opening_pair, commitment };
56
57 // initialize empty prover transcript
58 auto prover_transcript = std::make_shared<NativeTranscript>();
59 PCS::compute_opening_proof(ck, { poly, opening_pair }, prover_transcript);
60
61 // initialize verifier transcript from proof data
62 auto verifier_transcript = std::make_shared<NativeTranscript>();
63 verifier_transcript->load_proof(prover_transcript->export_proof());
64 // the native reduce_verify does a _complete_ IPA proof and returns whether or not the checks pass.
65 bool result = PCS::reduce_verify(vk, opening_claim, verifier_transcript);
66 return { result, prover_transcript, verifier_transcript };
67 }
68};
69} // namespace
70
71#define IPA_TEST
72#include "ipa.hpp"
73
74// Commit Tests: below are several tests to make sure commitment is correct.
75//
76// Commit to a polynomial that is non-zero but has many zero coefficients
77TEST_F(IPATest, CommitOnManyZeroCoeffPolyWorks)
78{
79 constexpr size_t n = 16;
80 Polynomial p(n);
81 for (size_t i = 0; i < n - 1; i++) {
82 p.at(i) = Fr::zero();
83 }
84 p.at(3) = this->random_element();
85 GroupElement commitment = ck.commit(p);
86 auto srs_elements = ck.srs->get_monomial_points();
87 GroupElement expected = srs_elements[0] * p[0];
88 for (size_t i = 1; i < n; i += 1) {
89 expected += srs_elements[i] * p[i];
90 }
91 EXPECT_EQ(expected.normalize(), commitment.normalize());
92}
93// Commit to zero poly
94TEST_F(IPATest, CommitToZeroPoly)
95{
96 Polynomial poly(n);
97 Commitment commitment = ck.commit(poly);
98 EXPECT_TRUE(commitment.is_point_at_infinity());
99
100 auto x = this->random_element();
101 auto eval = poly.evaluate(x);
102 EXPECT_EQ(eval, Fr::zero());
103}
104// Commit to a random poly
105TEST_F(IPATest, Commit)
106{
107 auto poly = Polynomial::random(n);
108 const GroupElement commitment = ck.commit(poly);
109 auto srs_elements = ck.srs->get_monomial_points();
110 GroupElement expected = srs_elements[0] * poly[0];
111 for (size_t i = 1; i < n; ++i) {
112 expected += srs_elements[i] * poly[i];
113 }
114 EXPECT_EQ(expected.normalize(), commitment.normalize());
115}
116
117// Opening tests, i.e., check completeness for prove-and-verify.
118//
119// poly is zero, point is random
120TEST_F(IPATest, OpenZeroPolynomial)
121{
122 Polynomial poly(n);
123 auto x = this->random_element();
124 bool result = run_native_prove_verify(poly, x).result;
125 EXPECT_TRUE(result);
126}
127
128TEST_F(IPATest, OpenManyZerosPolynomial)
129{
130 // polynomial with zero odd coefficients and random even coefficients
131 Polynomial poly_even(n);
132 // polynomial with zero even coefficients and random odd coefficients
133 Polynomial poly_odd(n);
134 for (size_t i = 0; i < n / 2; ++i) {
135 poly_even.at(2 * i) = this->random_element();
136 poly_odd.at(2 * i + 1) = this->random_element();
137 }
138 auto x = this->random_element();
139 bool result_even = run_native_prove_verify(poly_even, x).result;
140 bool result_odd = run_native_prove_verify(poly_odd, x).result;
141 EXPECT_TRUE(result_even && result_odd);
142}
143
144// poly is random, point is zero
145TEST_F(IPATest, OpenAtZero)
146{
147 // generate a random polynomial, degree needs to be a power of two
148 auto poly = Polynomial::random(n);
149 const Fr x = Fr::zero();
150 bool result = run_native_prove_verify(poly, x).result;
151 EXPECT_TRUE(result);
152}
153
154// poly and point are random
155TEST_F(IPATest, Open)
156{
157 // generate a random polynomial, degree needs to be a power of two
158 auto poly = Polynomial::random(n);
159 auto x = this->random_element();
160 auto result_of_prove_verify = run_native_prove_verify(poly, x);
161 EXPECT_TRUE(result_of_prove_verify.result);
162
163 EXPECT_EQ(result_of_prove_verify.prover_transcript->get_manifest(),
164 result_of_prove_verify.verifier_transcript->get_manifest());
165}
166
167// poly and point are random, condition on the fact that the evaluation is zero.
168TEST_F(IPATest, OpeningValueZero)
169{
170 // generate random polynomial
171 auto poly = Polynomial::random(n);
172 auto x = this->random_element();
173 auto initial_evaluation = poly.evaluate(x);
174 auto change_in_linear_coefficient = initial_evaluation / x;
175 // change linear coefficient so that poly(x) == 0.
176 poly.at(1) -= change_in_linear_coefficient;
177
178 EXPECT_EQ(poly.evaluate(x), Fr::zero());
179 bool result = run_native_prove_verify(poly, x).result;
180 EXPECT_TRUE(result);
181}
182
183// Tests that "artificially" mutate the Transcript. This uses the type `MockTranscript`.
184
185namespace bb {
186#if !defined(__wasm__)
187// This test ensures that IPA throws or aborts when a challenge is zero, since it breaks the logic of the argument
188TEST_F(IPATest, ChallengesAreZero)
189{
190 // generate a random polynomial, degree needs to be a power of two
191 auto poly = Polynomial::random(n);
192 auto [x, eval] = this->random_eval(poly);
193 auto commitment = ck.commit(poly);
194 const OpeningPair<Curve> opening_pair = { x, eval };
195 const OpeningClaim<Curve> opening_claim{ opening_pair, commitment };
196
197 // initialize an empty mock transcript
198 auto transcript = std::make_shared<MockTranscript>();
199 const size_t num_challenges = numeric::get_msb(n) + 1;
200 std::vector<uint256_t> random_vector(num_challenges);
201
202 // Generate a random element vector with challenges
203 for (size_t i = 0; i < num_challenges; i++) {
204 random_vector[i] = Fr::random_element();
205 }
206
207 // Compute opening proofs several times, where each time a different challenge is equal to zero. Should cause
208 // exceptions
209 for (size_t i = 0; i < num_challenges; i++) {
210 auto new_random_vector = random_vector;
211 new_random_vector[i] = Fr::zero();
212 transcript->initialize(new_random_vector);
213 EXPECT_ANY_THROW(PCS::compute_opening_proof<MockTranscript>(ck, { poly, opening_pair }, transcript));
214 }
215 // Fill out a vector of affine elements that the verifier receives from the prover with generators (we don't care
216 // about them right now)
217 std::vector<Curve::AffineElement> lrs(num_challenges * 2);
218 for (size_t i = 0; i < num_challenges * 2; i++) {
219 lrs[i] = Curve::AffineElement::one();
220 }
221 // Verify proofs several times, where each time a different challenge is equal to zero. Should cause
222 // exceptions
223 for (size_t i = 0; i < num_challenges; i++) {
224 auto new_random_vector = random_vector;
225 new_random_vector[i] = Fr::zero();
226 transcript->initialize(new_random_vector, lrs, { uint256_t(n) });
227 EXPECT_ANY_THROW(PCS::reduce_verify(vk, opening_claim, transcript));
228 }
229}
230
231// This test checks that if the vector \vec{a_new} becomes zero after one round, it doesn't break IPA.
232TEST_F(IPATest, AIsZeroAfterOneRound)
233{
234 // generate a random polynomial of degree < n / 2
235 auto poly = Polynomial(n);
236 for (size_t i = 0; i < n / 2; i++) {
237 poly.at(i) = Fr::random_element();
238 poly.at(i + (n / 2)) = poly[i];
239 }
240 auto [x, eval] = this->random_eval(poly);
241 auto commitment = ck.commit(poly);
242 const OpeningPair<Curve> opening_pair = { x, eval };
243 const OpeningClaim<Curve> opening_claim{ opening_pair, commitment };
244
245 // initialize an empty mock transcript
246 auto transcript = std::make_shared<MockTranscript>();
247 const size_t num_challenges = log_n + 1;
248 std::vector<uint256_t> random_vector(num_challenges);
249
250 // Generate a random element vector with challenges
251 for (size_t i = 0; i < num_challenges; i++) {
252 random_vector[i] = Fr::random_element();
253 }
254 // Substitute the first folding challenge with -1
255 random_vector[1] = -Fr::one();
256
257 // Put the challenges in the transcript
258 transcript->initialize(random_vector);
259
260 // Compute opening proof
261 PCS::compute_opening_proof<MockTranscript>(ck, { poly, opening_pair }, transcript);
262
263 // Reset indices
264 transcript->reset_indices();
265
266 // Verify
267 EXPECT_TRUE(PCS::reduce_verify(vk, opening_claim, transcript));
268}
269#endif
270} // namespace bb
271
272// Tests of batched MLPCS, where IPA is the final univariate commitment scheme.
273
274// Gemini + Shplonk + IPA. Two random polynomials, no shifts.
275TEST_F(IPATest, GeminiShplonkIPAWithoutShift)
276{
277 // Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random
278 // point.
279 auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u'
280
281 MockClaimGenerator mock_claims(n,
282 /*num_polynomials*/ 2,
283 /*num_to_be_shifted*/ 0,
284 /*num_to_be_right_shifted_by_k*/ 0,
285 mle_opening_point,
286 ck);
287
288 auto prover_transcript = NativeTranscript::prover_init_empty();
289
290 // Run the full prover PCS protocol:
291 // Compute:
292 // - (d+1) opening pairs: {r, \hat{a}_0}, {-r^{2^i}, a_i}, i = 0, ..., d-1
293 // - (d+1) Fold polynomials Fold_{r}^(0), Fold_{-r}^(0), and Fold^(i), i = 0, ..., d-1
294 auto prover_opening_claims =
295 GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript);
296
297 const auto opening_claim = ShplonkProver::prove(ck, prover_opening_claims, prover_transcript);
298 PCS::compute_opening_proof(ck, opening_claim, prover_transcript);
299
300 auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);
301
302 auto gemini_verifier_claim =
303 GeminiVerifier::reduce_verification(mle_opening_point, mock_claims.claim_batcher, verifier_transcript);
304
305 const auto shplonk_verifier_claim =
306 ShplonkVerifier::reduce_verification(vk.get_g1_identity(), gemini_verifier_claim, verifier_transcript);
307 auto result = PCS::reduce_verify(vk, shplonk_verifier_claim, verifier_transcript);
308
309 EXPECT_EQ(result, true);
310}
311// Shplemini + IPA. Five polynomials, one of which is shifted.
312TEST_F(IPATest, ShpleminiIPAWithShift)
313{
314 // Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random
315 // point.
316 auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u'
317 MockClaimGenerator mock_claims(n,
318 /*num_polynomials*/ 4,
319 /*num_to_be_shifted*/ 1,
320 /*num_to_be_right_shifted_by_k*/ 0,
321 mle_opening_point,
322 ck);
323 auto prover_transcript = NativeTranscript::prover_init_empty();
324
325 // Run the full prover PCS protocol:
326
327 // Compute:
328 // - (d+1) opening pairs: {r, \hat{a}_0}, {-r^{2^i}, a_i}, i = 0, ..., d-1
329 // - (d+1) Fold polynomials Fold_{r}^(0), Fold_{-r}^(0), and Fold^(i), i = 0, ..., d-1
330 auto prover_opening_claims =
331 GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript);
332 const auto opening_claim = ShplonkProver::prove(ck, prover_opening_claims, prover_transcript);
333 PCS::compute_opening_proof(ck, opening_claim, prover_transcript);
334
335 auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);
336
337 std::array<Fr, log_n> padding_indicator_array;
338 std::ranges::fill(padding_indicator_array, Fr{ 1 });
339
340 const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array,
341 mock_claims.claim_batcher,
342 mle_opening_point,
344 verifier_transcript);
345
346 auto result = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, vk, verifier_transcript);
347 // auto result = PCS::reduce_verify(vk, shplonk_verifier_claim, verifier_transcript);
348
349 EXPECT_EQ(result, true);
350}
351// Test `ShpleminiVerifier::remove_shifted_commitments`. Four polynomials, two of which are shifted.
352TEST_F(IPATest, ShpleminiIPAShiftsRemoval)
353{
354 // Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random
355 // point.
356 auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u'
357 MockClaimGenerator mock_claims(n,
358 /*num_polynomials*/ 4,
359 /*num_to_be_shifted*/ 2,
360 /*num_to_be_right_shifted_by_k*/ 0,
361 mle_opening_point,
362 ck);
363
364 auto prover_transcript = NativeTranscript::prover_init_empty();
365
366 // Run the full prover PCS protocol:
367
368 // Compute:
369 // - (d+1) opening pairs: {r, \hat{a}_0}, {-r^{2^i}, a_i}, i = 0, ..., d-1
370 // - (d+1) Fold polynomials Fold_{r}^(0), Fold_{-r}^(0), and Fold^(i), i = 0, ..., d-1
371 auto prover_opening_claims =
372 GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript);
373
374 const auto opening_claim = ShplonkProver::prove(ck, prover_opening_claims, prover_transcript);
375 PCS::compute_opening_proof(ck, opening_claim, prover_transcript);
376
377 // the index of the first commitment to a polynomial to be shifted in the union of unshifted_commitments and
378 // shifted_commitments. in our case, it is poly2
379 const size_t to_be_shifted_commitments_start = 2;
380 // the index of the first commitment to a shifted polynomial in the union of unshifted_commitments and
381 // shifted_commitments. in our case, it is the second occurence of poly2
382 const size_t shifted_commitments_start = 4;
383 // number of shifted polynomials
384 const size_t num_shifted_commitments = 2;
385 const RepeatedCommitmentsData repeated_commitments =
386 RepeatedCommitmentsData(to_be_shifted_commitments_start, shifted_commitments_start, num_shifted_commitments);
387 // since commitments to poly2, poly3 and their shifts are the same group elements, we simply combine the scalar
388 // multipliers of commitment2 and commitment3 in one place and remove the entries of the commitments and scalars
389 // vectors corresponding to the "shifted" commitment
390 auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);
391
392 std::array<Fr, log_n> padding_indicator_array;
393 std::ranges::fill(padding_indicator_array, Fr{ 1 });
394
395 const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array,
396 mock_claims.claim_batcher,
397 mle_opening_point,
399 verifier_transcript,
400 repeated_commitments);
401
402 auto result = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, vk, verifier_transcript);
403 EXPECT_EQ(result, true);
404}
405typename IPATest::CK IPATest::ck;
406typename IPATest::VK IPATest::vk;
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...
static std::shared_ptr< BaseTranscript > prover_init_empty()
For testing: initializes transcript with some arbitrary data so that a challenge can be generated aft...
CommitmentKey object over a pairing group 𝔾₁.
IPA (inner product argument) commitment scheme class.
Definition ipa.hpp:93
Unverified claim (C,r,v) for some witness polynomial p(X) such that.
Definition claim.hpp:53
Opening pair (r,v) for some witness polynomial p(X) such that p(r) = v.
Definition claim.hpp:19
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
Fr evaluate(const Fr &z, size_t target_size) const
Fr & at(size_t index)
Our mutable accessor, unlike operator[]. We abuse precedent a bit to differentiate at() and operator[...
An efficient verifier for the evaluation proofs of multilinear polynomials and their shifts.
Shplonk Prover.
Definition shplonk.hpp:36
Shplonk Verifier.
Definition shplonk.hpp:343
Representation of the Grumpkin Verifier Commitment Key inside a bn254 circuit.
typename Group::element Element
Definition grumpkin.hpp:55
typename Group::affine_element AffineElement
Definition grumpkin.hpp:56
TEST_F(IPATest, CommitOnManyZeroCoeffPolyWorks)
Definition ipa.test.cpp:77
constexpr T get_msb(const T in)
Definition get_msb.hpp:47
Entry point for Barretenberg command-line interface.
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:188
CommitmentKey< Curve > ck
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Logic to support batching opening claims for unshifted and shifted polynomials in Shplemini.
Constructs random polynomials, computes commitments and corresponding evaluations.
static constexpr field one()
static field random_element(numeric::RNG *engine=nullptr) noexcept
static constexpr field zero()