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