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