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// Opening tests, i.e., check completeness for prove-and-verify.
72//
73// poly is zero, point is random
74TEST_F(IPATest, OpenZeroPolynomial)
75{
76 Polynomial poly(n);
77 auto x = this->random_element();
78 bool result = run_native_prove_verify(poly, x).result;
79 EXPECT_TRUE(result);
80}
81
82TEST_F(IPATest, OpenManyZerosPolynomial)
83{
84 // polynomial with zero odd coefficients and random even coefficients
85 Polynomial poly_even(n);
86 // polynomial with zero even coefficients and random odd coefficients
87 Polynomial poly_odd(n);
88 for (size_t i = 0; i < n / 2; ++i) {
89 poly_even.at(2 * i) = this->random_element();
90 poly_odd.at(2 * i + 1) = this->random_element();
91 }
92 auto x = this->random_element();
93 bool result_even = run_native_prove_verify(poly_even, x).result;
94 bool result_odd = run_native_prove_verify(poly_odd, x).result;
95 EXPECT_TRUE(result_even && result_odd);
96}
97
98// poly is random, point is zero
99TEST_F(IPATest, OpenAtZero)
100{
101 // generate a random polynomial, degree needs to be a power of two
102 auto poly = Polynomial::random(n);
103 const Fr x = Fr::zero();
104 bool result = run_native_prove_verify(poly, x).result;
105 EXPECT_TRUE(result);
106}
107
108// poly and point are random
109TEST_F(IPATest, Open)
110{
111 // generate a random polynomial, degree needs to be a power of two
112 auto poly = Polynomial::random(n);
113 auto x = this->random_element();
114 auto result_of_prove_verify = run_native_prove_verify(poly, x);
115 EXPECT_TRUE(result_of_prove_verify.result);
116
117 EXPECT_EQ(result_of_prove_verify.prover_transcript->get_manifest(),
118 result_of_prove_verify.verifier_transcript->get_manifest());
119}
120
121// poly and point are random, condition on the fact that the evaluation is zero.
122TEST_F(IPATest, OpeningValueZero)
123{
124 // generate random polynomial
125 auto poly = Polynomial::random(n);
126 auto x = this->random_element();
127 auto initial_evaluation = poly.evaluate(x);
128 auto change_in_linear_coefficient = initial_evaluation / x;
129 // change linear coefficient so that poly(x) == 0.
130 poly.at(1) -= change_in_linear_coefficient;
131
132 EXPECT_EQ(poly.evaluate(x), Fr::zero());
133 bool result = run_native_prove_verify(poly, x).result;
134 EXPECT_TRUE(result);
135}
136
137// Tests that "artificially" mutate the Transcript. This uses the type `MockTranscript`.
138
139namespace bb {
140#if !defined(__wasm__)
141// This test ensures that IPA throws or aborts when a challenge is zero, since it breaks the logic of the argument
142TEST_F(IPATest, ChallengesAreZero)
143{
144 // generate a random polynomial, degree needs to be a power of two
145 auto poly = Polynomial::random(n);
146 auto [x, eval] = this->random_eval(poly);
147 auto commitment = ck.commit(poly);
148 const OpeningPair<Curve> opening_pair = { x, eval };
149 const OpeningClaim<Curve> opening_claim{ opening_pair, commitment };
150
151 // initialize an empty mock transcript
152 auto transcript = std::make_shared<MockTranscript>();
153 const size_t num_challenges = numeric::get_msb(n) + 1;
154 std::vector<uint256_t> random_vector(num_challenges);
155
156 // Generate a random element vector with challenges
157 for (size_t i = 0; i < num_challenges; i++) {
158 random_vector[i] = Fr::random_element();
159 }
160
161 // Compute opening proofs several times, where each time a different challenge is equal to zero. Should cause
162 // exceptions
163 for (size_t i = 0; i < num_challenges; i++) {
164 auto new_random_vector = random_vector;
165 new_random_vector[i] = Fr::zero();
166 transcript->initialize(new_random_vector);
167 EXPECT_ANY_THROW(PCS::compute_opening_proof<MockTranscript>(ck, { poly, opening_pair }, transcript));
168 }
169 // Fill out a vector of affine elements that the verifier receives from the prover with generators (we don't care
170 // about them right now)
171 std::vector<Curve::AffineElement> lrs(num_challenges * 2);
172 for (size_t i = 0; i < num_challenges * 2; i++) {
173 lrs[i] = Curve::AffineElement::one();
174 }
175 // Verify proofs several times, where each time a different challenge is equal to zero. Should cause
176 // exceptions
177 for (size_t i = 0; i < num_challenges; i++) {
178 auto new_random_vector = random_vector;
179 new_random_vector[i] = Fr::zero();
180 transcript->initialize(new_random_vector, lrs, { uint256_t(n) });
181 EXPECT_ANY_THROW(PCS::reduce_verify(vk, opening_claim, transcript));
182 }
183}
184
185// This test checks that if the vector \vec{a_new} becomes zero after one round, it doesn't break IPA.
186TEST_F(IPATest, AIsZeroAfterOneRound)
187{
188 // generate a random polynomial of degree < n / 2
189 auto poly = Polynomial(n);
190 for (size_t i = 0; i < n / 2; i++) {
191 poly.at(i) = Fr::random_element();
192 poly.at(i + (n / 2)) = poly[i];
193 }
194 auto [x, eval] = this->random_eval(poly);
195 auto commitment = ck.commit(poly);
196 const OpeningPair<Curve> opening_pair = { x, eval };
197 const OpeningClaim<Curve> opening_claim{ opening_pair, commitment };
198
199 // initialize an empty mock transcript
200 auto transcript = std::make_shared<MockTranscript>();
201 const size_t num_challenges = log_n + 1;
202 std::vector<uint256_t> random_vector(num_challenges);
203
204 // Generate a random element vector with challenges
205 for (size_t i = 0; i < num_challenges; i++) {
206 random_vector[i] = Fr::random_element();
207 }
208 // Substitute the first folding challenge with -1
209 random_vector[1] = -Fr::one();
210
211 // Put the challenges in the transcript
212 transcript->initialize(random_vector);
213
214 // Compute opening proof
215 PCS::compute_opening_proof<MockTranscript>(ck, { poly, opening_pair }, transcript);
216
217 // Reset indices
218 transcript->reset_indices();
219
220 // Verify
221 EXPECT_TRUE(PCS::reduce_verify(vk, opening_claim, transcript));
222}
223#endif
224} // namespace bb
225
226// Tests of batched MLPCS, where IPA is the final univariate commitment scheme.
227
228// Shplemini + IPA. Two random polynomials, no shifts.
229TEST_F(IPATest, ShpleminiIPAWithoutShift)
230{
231 // Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random
232 // point.
233 auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u'
234
235 MockClaimGenerator mock_claims(n,
236 /*num_polynomials*/ 2,
237 /*num_to_be_shifted*/ 0,
238 mle_opening_point,
239 ck);
240
241 auto prover_transcript = NativeTranscript::prover_init_empty();
242
243 // Run the full prover PCS protocol:
244 // Compute:
245 // - (d+1) opening pairs: {r, \hat{a}_0}, {-r^{2^i}, a_i}, i = 0, ..., d-1
246 // - (d+1) Fold polynomials Fold_{r}^(0), Fold_{-r}^(0), and Fold^(i), i = 0, ..., d-1
247 auto prover_opening_claims =
248 GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript);
249
250 const auto opening_claim = ShplonkProver::prove(ck, prover_opening_claims, prover_transcript);
251 PCS::compute_opening_proof(ck, opening_claim, prover_transcript);
252
253 auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);
254
255 std::array<Fr, log_n> padding_indicator_array;
256 std::ranges::fill(padding_indicator_array, Fr{ 1 });
257
258 const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array,
259 mock_claims.claim_batcher,
260 mle_opening_point,
261 vk.get_g1_identity(),
262 verifier_transcript)
263 .batch_opening_claim;
264
265 auto result = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, vk, verifier_transcript);
266
267 EXPECT_EQ(result, true);
268}
269// Shplemini + IPA. Five polynomials, one of which is shifted.
270TEST_F(IPATest, ShpleminiIPAWithShift)
271{
272 // Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random
273 // point.
274 auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u'
275 MockClaimGenerator mock_claims(n,
276 /*num_polynomials*/ 4,
277 /*num_to_be_shifted*/ 1,
278 mle_opening_point,
279 ck);
280 auto prover_transcript = NativeTranscript::prover_init_empty();
281
282 // Run the full prover PCS protocol:
283
284 // Compute:
285 // - (d+1) opening pairs: {r, \hat{a}_0}, {-r^{2^i}, a_i}, i = 0, ..., d-1
286 // - (d+1) Fold polynomials Fold_{r}^(0), Fold_{-r}^(0), and Fold^(i), i = 0, ..., d-1
287 auto prover_opening_claims =
288 GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript);
289 const auto opening_claim = ShplonkProver::prove(ck, prover_opening_claims, prover_transcript);
290 PCS::compute_opening_proof(ck, opening_claim, prover_transcript);
291
292 auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);
293
294 std::array<Fr, log_n> padding_indicator_array;
295 std::ranges::fill(padding_indicator_array, Fr{ 1 });
296
297 const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array,
298 mock_claims.claim_batcher,
299 mle_opening_point,
300 vk.get_g1_identity(),
301 verifier_transcript)
302 .batch_opening_claim;
303
304 auto result = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, vk, verifier_transcript);
305 // auto result = PCS::reduce_verify(vk, shplonk_verifier_claim, verifier_transcript);
306
307 EXPECT_EQ(result, true);
308}
309// Test `ShpleminiVerifier::remove_shifted_commitments`. Four polynomials, two of which are shifted.
310TEST_F(IPATest, ShpleminiIPAShiftsRemoval)
311{
312 // Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random
313 // point.
314 auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u'
315 MockClaimGenerator mock_claims(n,
316 /*num_polynomials*/ 4,
317 /*num_to_be_shifted*/ 2,
318 mle_opening_point,
319 ck);
320
321 auto prover_transcript = NativeTranscript::prover_init_empty();
322
323 // Run the full prover PCS protocol:
324
325 // Compute:
326 // - (d+1) opening pairs: {r, \hat{a}_0}, {-r^{2^i}, a_i}, i = 0, ..., d-1
327 // - (d+1) Fold polynomials Fold_{r}^(0), Fold_{-r}^(0), and Fold^(i), i = 0, ..., d-1
328 auto prover_opening_claims =
329 GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript);
330
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 // the index of the first commitment to a polynomial to be shifted in the union of unshifted_commitments and
335 // shifted_commitments. in our case, it is poly2
336 const size_t to_be_shifted_commitments_start = 2;
337 // the index of the first commitment to a shifted polynomial in the union of unshifted_commitments and
338 // shifted_commitments. in our case, it is the second occurence of poly2
339 const size_t shifted_commitments_start = 4;
340 // number of shifted polynomials
341 const size_t num_shifted_commitments = 2;
342 const RepeatedCommitmentsData repeated_commitments =
343 RepeatedCommitmentsData(to_be_shifted_commitments_start, shifted_commitments_start, num_shifted_commitments);
344 // since commitments to poly2, poly3 and their shifts are the same group elements, we simply combine the scalar
345 // multipliers of commitment2 and commitment3 in one place and remove the entries of the commitments and scalars
346 // vectors corresponding to the "shifted" commitment
347 auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);
348
349 std::array<Fr, log_n> padding_indicator_array;
350 std::ranges::fill(padding_indicator_array, Fr{ 1 });
351
352 const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array,
353 mock_claims.claim_batcher,
354 mle_opening_point,
355 vk.get_g1_identity(),
356 verifier_transcript,
357 repeated_commitments)
358 .batch_opening_claim;
359
360 auto result = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, vk, verifier_transcript);
361 EXPECT_EQ(result, true);
362}
363typename IPATest::CK IPATest::ck;
364typename 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 𝔾₁.
static void SetUpTestSuite()
IPA (inner product argument) commitment scheme class.
Definition ipa.hpp:92
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, OpenZeroPolynomial)
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:142
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()