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,
304 vk.get_g1_identity(),
305 verifier_transcript)
306 .batch_opening_claim;
307
308 auto result = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, vk, verifier_transcript);
309
310 EXPECT_EQ(result, true);
311}
312// Shplemini + IPA. Five polynomials, one of which is shifted.
313TEST_F(IPATest, ShpleminiIPAWithShift)
314{
315 // Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random
316 // point.
317 auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u'
318 MockClaimGenerator mock_claims(n,
319 /*num_polynomials*/ 4,
320 /*num_to_be_shifted*/ 1,
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,
343 vk.get_g1_identity(),
344 verifier_transcript)
345 .batch_opening_claim;
346
347 auto result = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, vk, verifier_transcript);
348 // auto result = PCS::reduce_verify(vk, shplonk_verifier_claim, verifier_transcript);
349
350 EXPECT_EQ(result, true);
351}
352// Test `ShpleminiVerifier::remove_shifted_commitments`. Four polynomials, two of which are shifted.
353TEST_F(IPATest, ShpleminiIPAShiftsRemoval)
354{
355 // Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random
356 // point.
357 auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u'
358 MockClaimGenerator mock_claims(n,
359 /*num_polynomials*/ 4,
360 /*num_to_be_shifted*/ 2,
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,
398 vk.get_g1_identity(),
399 verifier_transcript,
400 repeated_commitments)
401 .batch_opening_claim;
402
403 auto result = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, vk, verifier_transcript);
404 EXPECT_EQ(result, true);
405}
406typename IPATest::CK IPATest::ck;
407typename 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[...
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, shifted and interleaved polynomials in Shplem...
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()