Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ipa_recursive.test.cpp
Go to the documentation of this file.
1
12
13using namespace bb;
14
15namespace {
16using NativeCurve = curve::Grumpkin;
19
20class IPARecursiveTests : public CommitmentTest<NativeCurve> {
21 public:
22 using Fr = typename NativeCurve::ScalarField;
23 using GroupElement = typename NativeCurve::Element;
27 using Commitment = typename NativeCurve::AffineElement;
29
31 // `FailureMode::None` corresponds to a normal, completeness test. The other cases are legitimate failure modes,
32 // where the test should fail. As neither `a_0` nor `G_0` are hashed, the corresponding variants will not fail for
33 // Fiat-Shamir reasons. The last failure mode is: we send an OpeningClaim to the hash buffer, then
34 // we have the prover run the IPA process with a _different polynomial_.
35 enum class FailureMode : std::uint8_t { None, A_Zero, G_Zero, ChangePoly };
36
52 template <size_t log_poly_length>
54 Builder& builder, Polynomial& poly, Fr x, FailureMode failure_mode = FailureMode::None)
55 {
56 using NativeIPA = IPA<NativeCurve, log_poly_length>;
57 EXPECT_EQ(1UL << log_poly_length, poly.size());
58 Commitment commitment = this->commit(poly);
59 auto eval = poly.evaluate(x);
60
61 const OpeningPair<NativeCurve> opening_pair = { x, eval };
62 const OpeningClaim<NativeCurve> opening_claim{ opening_pair, commitment };
63 const ProverOpeningClaim<NativeCurve> prover_claim{ poly, opening_pair };
64 // initialize empty prover transcript
65 auto prover_transcript = std::make_shared<NativeTranscript>();
66 using DataType = NativeTranscript::DataType;
67 std::vector<DataType> proof;
68 // Export proof
69 switch (failure_mode) {
70 case FailureMode::None:
71 // Normal operation
72 NativeIPA::compute_opening_proof(this->ck(), prover_claim, prover_transcript);
73 proof = prover_transcript->export_proof();
74 break;
75 case FailureMode::A_Zero:
76 NativeIPA::compute_opening_proof(this->ck(), prover_claim, prover_transcript);
77 proof = prover_transcript->export_proof();
78 // Multiply the last element of the proof, what the prover sends as a_0, by 3
79 proof.back() *= 3;
80 break;
81 case FailureMode::G_Zero: {
82 NativeIPA::compute_opening_proof(this->ck(), prover_claim, prover_transcript);
83 proof = prover_transcript->export_proof();
84 // Multiply the second to last element of the proof, what the prover sends as G_0, by 2.
85 const size_t comm_frs = 2; // an affine Grumpkin point requires 2 Fr elements to represent.
86 const size_t offset = log_poly_length * 2 * comm_frs; // we first send the L_i and R_i, then G_0.
87 auto element_frs = std::span{ proof }.subspan(offset, comm_frs);
88
89 Commitment op_commitment = NativeTranscript::template deserialize<Commitment>(element_frs);
90 Commitment new_op_commitment = op_commitment + op_commitment;
91 auto new_op_commitment_reserialized = NativeTranscript::serialize(new_op_commitment);
92 std::copy(new_op_commitment_reserialized.begin(),
93 new_op_commitment_reserialized.end(),
94 proof.begin() + static_cast<std::ptrdiff_t>(offset));
95 break;
96 }
97 case FailureMode::ChangePoly:
98 // instead of calling compute_opening_proof, we first add the prover claim to the hash buffer, then we run
99 // IPA with a _new_ polynomial.
100 NativeIPA::add_claim_to_hash_buffer(this->ck(), prover_claim, prover_transcript);
101 // generate a new polynomial evaluation claim.
102 auto [new_poly, new_x] = generate_poly_and_challenge<log_poly_length>();
103 auto new_eval = new_poly.evaluate(new_x);
104
105 const OpeningPair<NativeCurve> new_opening_pair = { new_x, new_eval };
106 const ProverOpeningClaim<NativeCurve> new_prover_claim{ new_poly, new_opening_pair };
107 NativeIPA::compute_opening_proof_internal(this->ck(), new_prover_claim, prover_transcript);
108 proof = prover_transcript->export_proof();
109 break;
110 }
111
112 // initialize verifier transcript from proof data
113 auto verifier_transcript = std::make_shared<NativeTranscript>();
114 verifier_transcript->load_proof(proof);
115 // run the native proof
116 auto result = NativeIPA::reduce_verify(this->vk(), opening_claim, verifier_transcript);
117
118 if (failure_mode == FailureMode::None) {
119 EXPECT_TRUE(result);
120 }
121
122 // Recursively verify the proof
123 auto stdlib_comm = Curve::Group::from_witness(&builder, commitment);
124 auto stdlib_x = Curve::ScalarField::from_witness(&builder, x);
125 auto stdlib_eval = Curve::ScalarField::from_witness(&builder, eval);
126 OpeningClaim<Curve> stdlib_opening_claim{ { stdlib_x, stdlib_eval }, stdlib_comm };
127
128 // Construct stdlib verifier transcript
129 auto recursive_verifier_transcript = std::make_shared<StdlibTranscript>();
130 recursive_verifier_transcript->load_proof(StdlibProof(builder, proof));
131 return { recursive_verifier_transcript, stdlib_opening_claim };
132 }
141 template <size_t log_poly_length>
142 Builder build_ipa_recursive_verifier_circuit(Polynomial& poly, Fr x, FailureMode failure_mode = FailureMode::None)
143 {
144 using RecursiveIPA = IPA<Curve, log_poly_length>;
145
147 auto [stdlib_transcript, stdlib_claim] = create_ipa_claim<log_poly_length>(builder, poly, x, failure_mode);
148
149 RecursiveIPA::reduce_verify(stdlib_claim, stdlib_transcript);
151 builder.finalize_circuit(/*ensure_nonzero=*/true);
152 return builder;
153 }
154 // flag to determine what type of polynomial to generate
155 enum class PolyType : std::uint8_t { Random, ManyZeros, Sparse, Zero };
156
157 template <size_t log_poly_length>
158 std::tuple<Polynomial, Fr> generate_poly_and_challenge(PolyType poly_type = PolyType::Random)
159 {
160
161 static constexpr size_t poly_length = 1UL << log_poly_length;
162 Polynomial poly(poly_length);
163 switch (poly_type) {
164 case PolyType::Random:
165 poly = Polynomial::random(poly_length);
166 break;
167 case PolyType::ManyZeros:
168 poly = Polynomial::random(poly_length);
169 for (size_t i = 0; i < poly_length / 2; ++i) {
170 poly.at(i) = Fr::zero();
171 }
172 case PolyType::Sparse:
173 // set a few coefficients to be non-zero
174 for (size_t i = 0; i < std::min<size_t>(100, poly_length / 2); ++i) {
175 size_t idx = static_cast<size_t>(this->engine->get_random_uint64() % poly_length);
176 poly.at(idx) = this->random_element();
177 }
178 break;
179 case PolyType::Zero:
180 break;
181 }
182 auto x = this->random_element();
183 return { poly, x };
184 }
185
190 template <size_t log_poly_length>
191 void test_recursive_ipa(Polynomial& poly, Fr x, FailureMode failure_mode = FailureMode::None)
192 {
194 Builder builder(build_ipa_recursive_verifier_circuit<log_poly_length>(poly, x, failure_mode));
195 info("IPA Recursive Verifier num finalized gates = ", builder.get_num_finalized_gates());
196 if (failure_mode == FailureMode::None) {
197 EXPECT_TRUE(CircuitChecker::check(builder));
198 } else {
199 EXPECT_FALSE(CircuitChecker::check(builder));
200 }
201 }
202
209 template <size_t log_poly_length> void test_accumulation(Polynomial& poly1, Polynomial& poly2, Fr x1, Fr x2)
210 {
211 using NativeIPA = IPA<NativeCurve, log_poly_length>;
212 using RecursiveIPA = IPA<Curve, log_poly_length>;
213
214 // We create a circuit that does two IPA verifications. However, we don't do the full verifications and instead
215 // accumulate the claims into one claim. This accumulation is done in circuit. Create two accumulators, which
216 // contain the commitment and an opening claim.
218 auto [transcript_1, claim_1] = create_ipa_claim<log_poly_length>(builder, poly1, x1);
219 auto [transcript_2, claim_2] = create_ipa_claim<log_poly_length>(builder, poly2, x2);
220
221 // Creates two IPA accumulators and accumulators from the two claims. Also constructs the accumulated h
222 // polynomial.
223 auto [output_claim, ipa_proof] =
224 RecursiveIPA::accumulate(this->ck(), transcript_1, claim_1, transcript_2, claim_2);
225 output_claim.set_public();
226 builder.ipa_proof = ipa_proof;
227 builder.finalize_circuit(/*ensure_nonzero=*/false);
228 info("Circuit with 2 IPA Recursive Verifiers and IPA Accumulation num finalized gates = ",
229 builder.get_num_finalized_gates());
230
231 EXPECT_TRUE(CircuitChecker::check(builder));
232
233 const OpeningPair<NativeCurve> opening_pair{ bb::fq(output_claim.opening_pair.challenge.get_value()),
234 bb::fq(output_claim.opening_pair.evaluation.get_value()) };
235 Commitment native_comm = output_claim.commitment.get_value();
236 const OpeningClaim<NativeCurve> opening_claim{ opening_pair, native_comm };
237
238 // Natively verify this proof to check it.
239 auto verifier_transcript = std::make_shared<NativeTranscript>();
240 verifier_transcript->load_proof(ipa_proof);
241
242 auto result = NativeIPA::reduce_verify(this->vk(), opening_claim, verifier_transcript);
243 EXPECT_TRUE(result);
244 }
245};
246} // namespace
247
248#define IPA_TEST
249
253TEST_F(IPARecursiveTests, RecursiveSmallSparse)
254{
255 static constexpr size_t log_poly_length = 2;
256 auto [poly, x] = generate_poly_and_challenge<log_poly_length>(PolyType::ManyZeros);
257 test_recursive_ipa<log_poly_length>(poly, x);
258}
259
263TEST_F(IPARecursiveTests, RecursiveMediumManyZeros)
264{
265 static constexpr size_t log_poly_length = 10;
266 auto [poly, x] = generate_poly_and_challenge<log_poly_length>(PolyType::Sparse);
267 test_recursive_ipa<log_poly_length>(poly, x);
268}
269
270TEST_F(IPARecursiveTests, RecursiveMediumZeroPoly)
271{
272 static constexpr size_t log_poly_length = 10;
273 auto [poly, x] = generate_poly_and_challenge<log_poly_length>(PolyType::Zero);
274 test_recursive_ipa<log_poly_length>(poly, x);
275}
276
277TEST_F(IPARecursiveTests, RecursiveMediumZeroChallenge)
278{
279 static constexpr size_t log_poly_length = 10;
280 auto [poly, x] = generate_poly_and_challenge<log_poly_length>(PolyType::Random);
281 test_recursive_ipa<log_poly_length>(poly, Fr::zero());
282}
283
284TEST_F(IPARecursiveTests, RecursiveMediumZeroEvaluation)
285{
286 static constexpr size_t log_poly_length = 10;
287 auto [poly, x] = generate_poly_and_challenge<log_poly_length>(PolyType::Random);
288 auto initial_evaluation = poly.evaluate(x);
289 poly.at(1) -= initial_evaluation / x;
290 test_recursive_ipa<log_poly_length>(poly, x);
291}
292
296TEST_F(IPARecursiveTests, RecursiveLargeRandom)
297{
298 static constexpr size_t log_poly_length = CONST_ECCVM_LOG_N;
299 auto [poly, x] = generate_poly_and_challenge<log_poly_length>(PolyType::Random);
300 test_recursive_ipa<log_poly_length>(poly, x);
301}
302
307TEST_F(IPARecursiveTests, RecursiveMediumRandomFailure)
308{
309 static constexpr size_t log_poly_length = 10;
310 auto [poly, x] = generate_poly_and_challenge<log_poly_length>(PolyType::Random);
311 test_recursive_ipa<log_poly_length>(poly, x, FailureMode::A_Zero);
312 test_recursive_ipa<log_poly_length>(poly, x, FailureMode::G_Zero);
313 test_recursive_ipa<log_poly_length>(poly, x, FailureMode::ChangePoly);
314}
315
319TEST_F(IPARecursiveTests, AccumulateSmallRandom)
320{
321 static constexpr size_t log_poly_length = 2;
322 auto [poly1, x1] = generate_poly_and_challenge<log_poly_length>(PolyType::Random);
323 auto [poly2, x2] = generate_poly_and_challenge<log_poly_length>(PolyType::Random);
324 test_accumulation<log_poly_length>(poly1, poly2, x1, x2);
325}
326
330TEST_F(IPARecursiveTests, AccumulateMediumRandom)
331{
332 static constexpr size_t log_poly_length = 10;
333 auto [poly1, x1] = generate_poly_and_challenge<log_poly_length>();
334 auto [poly2, x2] = generate_poly_and_challenge<log_poly_length>();
335 test_accumulation<log_poly_length>(poly1, poly2, x1, x2);
336}
337TEST_F(IPARecursiveTests, AccumulateMediumFirstZeroPoly)
338{
339 static constexpr size_t log_poly_length = 10;
340 static constexpr size_t poly_length = 1UL << log_poly_length;
341 Polynomial poly1(poly_length);
342 auto x1 = this->random_element();
343 auto [poly2, x2] = generate_poly_and_challenge<log_poly_length>();
344 test_accumulation<log_poly_length>(poly1, poly2, x1, x2);
345}
346TEST_F(IPARecursiveTests, AccumulateMediumBothZeroPoly)
347{
348 static constexpr size_t log_poly_length = 10;
349 static constexpr size_t poly_length = 1UL << log_poly_length;
350 Polynomial poly1(poly_length);
351 Polynomial poly2(poly_length);
352 auto x1 = this->random_element();
353 auto x2 = this->random_element();
354 test_accumulation<log_poly_length>(poly1, poly2, x1, x2);
355}
356TEST_F(IPARecursiveTests, AccumulateMediumSparseManyZeros)
357{
358 static constexpr size_t log_poly_length = 10;
359 auto [poly1, x1] = generate_poly_and_challenge<log_poly_length>(PolyType::Sparse);
360 auto [poly2, x2] = generate_poly_and_challenge<log_poly_length>(PolyType::ManyZeros);
361 test_accumulation<log_poly_length>(poly1, poly2, x1, x2);
362}
363
364TEST_F(IPARecursiveTests, FullRecursiveVerifierMediumZeroPoly)
365{
366
367 static constexpr size_t log_poly_length = 10;
368 static constexpr size_t poly_length = 1UL << log_poly_length;
369 using RecursiveIPA = IPA<Curve, log_poly_length>;
370
372 Polynomial poly(poly_length);
373 auto x = this->random_element();
374 auto [stdlib_transcript, stdlib_claim] = create_ipa_claim<log_poly_length>(builder, poly, x);
375
376 VerifierCommitmentKey<Curve> stdlib_pcs_vkey(&builder, poly_length, this->vk());
377 auto result = RecursiveIPA::full_verify_recursive(stdlib_pcs_vkey, stdlib_claim, stdlib_transcript);
378 EXPECT_TRUE(result);
379 builder.finalize_circuit(/*ensure_nonzero=*/true);
380 info("Full IPA Recursive Verifier num finalized gates for length ",
381 1UL << log_poly_length,
382 " = ",
383 builder.get_num_finalized_gates());
384 EXPECT_TRUE(CircuitChecker::check(builder));
385}
386
387TEST_F(IPARecursiveTests, FullRecursiveVerifierMediumRandom)
388{
389
390 static constexpr size_t log_poly_length = 10;
391 static constexpr size_t poly_length = 1UL << log_poly_length;
392 using RecursiveIPA = IPA<Curve, log_poly_length>;
393
395 auto [poly, x] = generate_poly_and_challenge<log_poly_length>();
396 auto [stdlib_transcript, stdlib_claim] = create_ipa_claim<log_poly_length>(builder, poly, x);
397
398 VerifierCommitmentKey<Curve> stdlib_pcs_vkey(&builder, poly_length, this->vk());
399 auto result = RecursiveIPA::full_verify_recursive(stdlib_pcs_vkey, stdlib_claim, stdlib_transcript);
400 EXPECT_TRUE(result);
401 builder.finalize_circuit(/*ensure_nonzero=*/true);
402 info("Full IPA Recursive Verifier num finalized gates for length ",
403 1UL << log_poly_length,
404 " = ",
405 builder.get_num_finalized_gates());
406 EXPECT_TRUE(CircuitChecker::check(builder));
407}
408
409TEST_F(IPARecursiveTests, AccumulationAndFullRecursiveVerifierMediumRandom)
410{
411 static constexpr size_t log_poly_length = 10;
412
413 using RecursiveIPA = IPA<Curve, log_poly_length>;
414
415 // We create a circuit that does two IPA verifications. However, we don't do the full verifications and instead
416 // accumulate the claims into one claim. This accumulation is done in circuit. Create two accumulators, which
417 // contain the commitment and an opening claim.
419
420 auto [poly1, x1] = generate_poly_and_challenge<log_poly_length>();
421 auto [poly2, x2] = generate_poly_and_challenge<log_poly_length>();
422
423 auto [transcript_1, claim_1] = create_ipa_claim<log_poly_length>(builder, poly1, x1);
424 auto [transcript_2, claim_2] = create_ipa_claim<log_poly_length>(builder, poly2, x2);
425
426 // Creates two IPA accumulators and accumulators from the two claims. Also constructs the accumulated h
427 // polynomial.
428 auto [output_claim, ipa_proof] = RecursiveIPA::accumulate(this->ck(), transcript_1, claim_1, transcript_2, claim_2);
429 output_claim.set_public();
430 builder.ipa_proof = ipa_proof;
431 builder.finalize_circuit(/*ensure_nonzero=*/false);
432 info("Circuit with 2 IPA Recursive Verifiers and IPA Accumulation num finalized gates = ",
433 builder.get_num_finalized_gates());
434
435 EXPECT_TRUE(CircuitChecker::check(builder));
436
437 Builder root_rollup;
438 // Fully recursively verify this proof to check it.
439 VerifierCommitmentKey<Curve> stdlib_pcs_vkey(&root_rollup, 1UL << log_poly_length, this->vk());
440 auto stdlib_verifier_transcript = std::make_shared<StdlibTranscript>();
441 stdlib_verifier_transcript->load_proof(StdlibProof(root_rollup, ipa_proof));
442 OpeningClaim<Curve> ipa_claim;
443 ipa_claim.opening_pair.challenge =
444 Curve::ScalarField::create_from_u512_as_witness(&root_rollup, output_claim.opening_pair.challenge.get_value());
445 ipa_claim.opening_pair.evaluation =
446 Curve::ScalarField::create_from_u512_as_witness(&root_rollup, output_claim.opening_pair.evaluation.get_value());
447 ipa_claim.commitment = Curve::AffineElement::from_witness(&root_rollup, output_claim.commitment.get_value());
448 auto result = RecursiveIPA::full_verify_recursive(stdlib_pcs_vkey, ipa_claim, stdlib_verifier_transcript);
449 root_rollup.finalize_circuit(/*ensure_nonzero=*/true);
450 EXPECT_TRUE(result);
451 info("Full IPA Recursive Verifier num finalized gates for length ",
452 1UL << log_poly_length,
453 " = ",
454 root_rollup.get_num_finalized_gates());
455}
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:32
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
typename Codec::DataType DataType
static std::vector< DataType > serialize(const T &element)
Serialize a size_t to a vector of field elements.
CommitmentKey object over a pairing group 𝔾₁.
Commitment commit(const Polynomial &polynomial)
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
OpeningPair< Curve > opening_pair
Definition claim.hpp:62
Commitment commitment
Definition claim.hpp:64
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[...
std::size_t size() const
Polynomial p and an opening pair (r,v) such that p(r) = v.
Definition claim.hpp:34
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
Representation of the Grumpkin Verifier Commitment Key inside a bn254 circuit.
A simple wrapper around a vector of stdlib field elements representing a proof.
Definition proof.hpp:19
void info(Args... args)
Definition log.hpp:74
AluTraceBuilder builder
Definition alu.test.cpp:123
ssize_t offset
Definition engine.cpp:36
stdlib::Proof< Builder > StdlibProof
Entry point for Barretenberg command-line interface.
field< Bn254FqParams > fq
Definition fq.hpp:169
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:188
BaseTranscript< stdlib::StdlibCodec< stdlib::field_t< UltraCircuitBuilder > >, stdlib::poseidon2< UltraCircuitBuilder > > UltraStdlibTranscript
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
CommitmentKey< Curve > ck
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr field zero()
Curve grumpkin in circuit setting.
Definition grumpkin.hpp:21
static void add_default_to_public_inputs(Builder &builder)
Adds default public inputs to the builder.