Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
recursive_verifier.test.cpp
Go to the documentation of this file.
2#include "barretenberg/aztec/aztec_constants.hpp"
16
17#include <gtest/gtest.h>
18
19namespace bb::avm2::constraining {
20
21class AvmRecursiveTests : public ::testing::Test {
22 public:
27
29
34
35 // Helper function to create and verify native proof. Due to the way ASSERT_TRUE
36 // works, this routine needs to return void and therefore we feed proof_result
37 // by reference.
39 {
40 static auto [cached_verified, cached_proof_result] = []() {
41 auto [trace, public_inputs] = testing::get_minimal_trace_with_pi();
42
43 const auto public_inputs_cols = public_inputs.to_columns();
44
45 InnerProver prover;
46 const auto proof = prover.prove(std::move(trace));
47 InnerVerifier verifier;
48
49 const bool verified = verifier.verify_proof(proof, public_inputs_cols);
50
51 return std::pair<bool, NativeProofResult>{ verified, NativeProofResult{ proof, public_inputs_cols } };
52 }();
53
54 ASSERT_TRUE(cached_verified) << "native proof verification failed";
55 proof_result = cached_proof_result;
56 }
57};
58
65TEST_F(AvmRecursiveTests, TwoLayerAvmRecursion)
66{
68 GTEST_SKIP() << "Skipping slow test";
69 }
70
71 // Type aliases specific to TwoLayerAvmRecursion test
72 using OuterBuilder = typename UltraFlavor::CircuitBuilder;
74 using UltraRollupProver = UltraProver_<UltraFlavor>;
75
76 NativeProofResult proof_result;
77 std::cout << "Creating and verifying native proof..." << std::endl;
78 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
79 ASSERT_NO_FATAL_FAILURE({ create_and_verify_native_proof(proof_result); });
80 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
81 std::cout << "Time taken (native proof): " << std::chrono::duration_cast<std::chrono::seconds>(end - start).count()
82 << "s" << std::endl;
83
84 auto [proof, public_inputs_cols] = proof_result;
85
86 // Construct stdlib representations of the proof, public inputs and verification key
87 OuterBuilder outer_circuit;
88 stdlib::Proof<OuterBuilder> stdlib_proof(outer_circuit, proof);
89
90 std::vector<std::vector<UltraFF>> public_inputs_ct;
91 public_inputs_ct.reserve(public_inputs_cols.size());
92 for (const auto& vec : public_inputs_cols) {
94 vec_ct.reserve(vec.size());
95 for (const auto& val : vec) {
96 vec_ct.push_back(UltraFF::from_witness(&outer_circuit, val));
97 }
98 public_inputs_ct.push_back(vec_ct);
99 }
100
101 // Construct the AVM recursive verifier and verify the proof
102 // Scoped to free memory of AvmRecursiveVerifier.
103 auto verifier_output = [&]() {
104 std::cout << "Constructing AvmRecursiveVerifier and verifying proof..." << std::endl;
105 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
106 TwoLayerAvmRecursiveVerifier avm_rec_verifier(outer_circuit);
107 auto result = avm_rec_verifier.verify_proof(stdlib_proof, public_inputs_ct);
108 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
109 std::cout << "Time taken (recursive verification): "
110 << std::chrono::duration_cast<std::chrono::seconds>(end - start).count() << "s" << std::endl;
111 return result;
112 }();
113
114 // Ensure that the pairing check is satisfied on the outputs of the recursive verifier.
115 ASSERT_TRUE(verifier_output.points_accumulator.check()) << "Pairing points (aggregation state) are not valid.";
116
118 recursion_output.update_triple_ipa_opening(verifier_output.points_accumulator,
119 std::move(verifier_output.triple_ipa_opening));
120 recursion_output.finalize(outer_circuit, /*is_hn_recursion_constraints=*/false, /*has_ipa_claim=*/true);
121
122 ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed.";
123
124 vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates());
125
126 // Construct and verify an Ultra Rollup proof of the AVM recursive verifier circuit. This proof carries an IPA claim
127 // from ECCVM recursive verification in its public inputs that will be verified as part of the UltraRollupVerifier.
128 auto outer_proving_key = std::make_shared<ProverInstance_<UltraFlavor>>(outer_circuit);
129
130 // Scoped to free memory of UltraRollupProver.
131 auto outer_proof = [&]() {
132 auto verification_key = std::make_shared<UltraFlavor::VerificationKey>(outer_proving_key->get_precomputed());
133 UltraRollupProver outer_prover(outer_proving_key, verification_key);
134 return outer_prover.construct_proof();
135 }();
136
137 // Verify the proof of the Ultra circuit that verified the AVM recursive verifier circuit
138 auto outer_verification_key = std::make_shared<UltraFlavor::VerificationKey>(outer_proving_key->get_precomputed());
139 auto outer_vk_and_hash = std::make_shared<UltraFlavor::VKAndHash>(outer_verification_key);
140 UltraRollupVerifier final_verifier(outer_vk_and_hash);
141
142 bool result = final_verifier.verify_proof(outer_proof).result;
143 EXPECT_TRUE(result);
144}
145
146// Test that the transcript operations performed during AVM recursive verification match the ones performed by the
147// function defined in the AvmRecursiveFlavor::Transcript class
148TEST_F(AvmRecursiveTests, TranscriptOperations)
149{
151 GTEST_SKIP() << "Skipping slow test";
152 }
153
155
156 NativeProofResult proof_result;
157 std::cout << "Creating and verifying native proof..." << std::endl;
158 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
159 ASSERT_NO_FATAL_FAILURE({ create_and_verify_native_proof(proof_result); });
160 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
161 std::cout << "Time taken (native proof): " << std::chrono::duration_cast<std::chrono::seconds>(end - start).count()
162 << "s" << std::endl;
163
164 auto [proof, public_inputs_cols] = proof_result;
165
166 // Construct stdlib representations of the proof, public inputs and verification key
168 stdlib::Proof<MegaCircuitBuilder> stdlib_proof(builder, proof);
169
170 std::vector<std::vector<FF>> public_inputs_ct;
171 public_inputs_ct.reserve(public_inputs_cols.size());
172 for (const auto& vec : public_inputs_cols) {
173 std::vector<FF> vec_ct;
174 vec_ct.reserve(vec.size());
175 for (const auto& val : vec) {
176 vec_ct.push_back(FF::from_witness(&builder, val));
177 }
178 public_inputs_ct.push_back(vec_ct);
179 }
180
181 // Construct the AVM recursive verifier and verify the proof
182 // Scoped to free memory of AvmRecursiveVerifier.
183 FF final_state_full_verification;
185 transcript->enable_manifest();
186 {
187 std::cout << "Constructing AvmRecursiveVerifier and verifying proof..." << std::endl;
188 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
189 AvmRecursiveVerifier avm_rec_verifier(builder, transcript);
190 [[maybe_unused]] auto _result = avm_rec_verifier.verify_proof(stdlib_proof, public_inputs_ct);
191 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
192 std::cout << "Time taken (recursive verification): "
193 << std::chrono::duration_cast<std::chrono::seconds>(end - start).count() << "s" << std::endl;
194 final_state_full_verification = avm_rec_verifier.hash_avm_transcript();
195 };
196
197 // Perform only transcript operations
198 FF final_state_transcript_operations_only;
199 auto mocked_transcript = std::make_shared<AvmRecursiveFlavor::Transcript>();
200 {
201 std::tie(final_state_transcript_operations_only, mocked_transcript) =
203 }
204
205 // Check that the native values underlying the final states match
206 EXPECT_EQ(final_state_full_verification.get_value(), final_state_transcript_operations_only.get_value());
207
208 // Check consistency of the transcripts
209 auto manifest = transcript->get_manifest();
210 auto mocked_manifest = mocked_transcript->get_manifest();
211
212 BB_ASSERT_GT(manifest.size(), 0U);
213 BB_ASSERT_EQ(manifest.size(), mocked_manifest.size());
214 for (size_t round = 0; round < manifest.size(); ++round) {
215 ASSERT_EQ(manifest[round], mocked_manifest[round])
216 << std::format("Real/Mocked manifest discrepancy in round {}", round);
217 }
218
219 // Check that the circuit is satisfied
220 final_state_transcript_operations_only.assert_equal(final_state_full_verification);
221 EXPECT_TRUE(CircuitChecker::check(builder));
222 EXPECT_TRUE(!builder.failed());
223}
224
225// Ensures that the recursive verifier fails with wrong PIs.
226TEST_F(AvmRecursiveTests, TwoLayerAvmRecursionFailsWithWrongPIs)
227{
229 GTEST_SKIP() << "Skipping slow test";
230 }
231
232 // Type aliases specific to TwoLayerAvmRecursion test
233 using OuterBuilder = typename UltraFlavor::CircuitBuilder;
235
236 NativeProofResult proof_result;
237 std::cout << "Creating and verifying native proof..." << std::endl;
238 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
239 ASSERT_NO_FATAL_FAILURE({ create_and_verify_native_proof(proof_result); });
240 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
241 std::cout << "Time taken (native proof): " << std::chrono::duration_cast<std::chrono::seconds>(end - start).count()
242 << "s" << std::endl;
243
244 auto [proof, public_inputs_cols] = proof_result;
245
246 // Construct stdlib representations of the proof, public inputs and verification key
247 OuterBuilder outer_circuit;
248 stdlib::Proof<OuterBuilder> stdlib_proof(outer_circuit, proof);
249
250 std::vector<std::vector<UltraFF>> public_inputs_ct;
251 public_inputs_ct.reserve(public_inputs_cols.size());
252 for (const auto& vec : public_inputs_cols) {
254 vec_ct.reserve(vec.size());
255 for (const auto& val : vec) {
256 vec_ct.push_back(UltraFF::from_witness(&outer_circuit, val));
257 }
258 public_inputs_ct.push_back(vec_ct);
259 }
260 // Mutate a PI entry to verify that validation correctly fails with incorrect public inputs
261 public_inputs_ct[1][5] += 1;
262
263 // Construct the AVM recursive verifier and verify the proof
264 // Scoped to free memory of AvmRecursiveVerifier.
265 {
266 std::cout << "Constructing AvmRecursiveVerifier and verifying proof..." << std::endl;
267 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
268 TwoLayerAvmRecursiveVerifier avm_rec_verifier(outer_circuit);
269 auto result = avm_rec_verifier.verify_proof(stdlib_proof, public_inputs_ct);
270 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
271 std::cout << "Time taken (recursive verification): "
272 << std::chrono::duration_cast<std::chrono::seconds>(end - start).count() << "s" << std::endl;
273 };
274
275 ASSERT_TRUE(outer_circuit.failed()) << "Outer circuit SHOULD fail with bad PIs.";
276}
277
278} // namespace bb::avm2::constraining
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:113
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
UltraCircuitBuilder CircuitBuilder
typename Curve::ScalarField FF
Output verify_proof(const Proof &proof)
Perform ultra verification.
Proof prove(tracegen::TraceContainer &&trace)
static std::pair< stdlib::field_t< Builder >, std::shared_ptr< TemplatedTranscript< Builder > > > hash_avm_transcript_for_testing(Builder &builder, const stdlib::Proof< Builder > &stdlib_proof, const std::vector< std::vector< stdlib::field_t< Builder > > > &public_inputs)
Testing method to hash the transcript after having replicated the operations performed on the AVM tra...
FF hash_avm_transcript()
Hash the transcript after verification is complete to produce a hash of the public inputs and proofs ...
PairingPoints verify_proof(const StdlibProof &stdlib_proof, const std::vector< std::vector< typename Flavor::FF > > &public_inputs)
Verify an AVM proof and return PairingPoints whose validity bears witness to successful verification ...
bool verify_proof(const HonkProof &proof, const std::vector< std::vector< FF > > &public_inputs)
Verify an AVM proof.
Definition verifier.cpp:53
Recursive verifier of AVM2 proofs that utilizes the Goblin mechanism for efficient EC operations.
TwoLayerAvmRecursiveVerifierOutput verify_proof(const stdlib::Proof< UltraCircuitBuilder > &stdlib_proof, const std::vector< std::vector< UltraFF > > &public_inputs) const
Recursively verify an AVM proof using Goblin and two layers of recursive verification.
typename RecursiveFlavor::CircuitBuilder OuterBuilder
static void create_and_verify_native_proof(NativeProofResult &proof_result)
A simple wrapper around a vector of stdlib field elements representing a proof.
Definition proof.hpp:20
#define vinfo(...)
Definition log.hpp:94
AluTraceBuilder builder
Definition alu.test.cpp:124
TestTraceContainer trace
TEST_F(AvmRecursiveTests, TwoLayerAvmRecursion)
A test of the Two Layer AVM recursive verifier.
bool skip_slow_tests()
Check if slow tests should be skipped.
Definition fixtures.cpp:244
std::pair< tracegen::TraceContainer, PublicInputs > get_minimal_trace_with_pi()
Definition fixtures.cpp:230
AvmFlavorSettings::FF FF
Definition field.hpp:10
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Container for the output of multiple recursive verifications.
void finalize(Builder &builder, bool is_hn_recursion_constraints=false, bool has_ipa_claim=false)
Finalize the output by accumulating IPA claims/proofs, performing full IPA verification,...
void update_triple_ipa_opening(const stdlib::recursion::PairingPoints< stdlib::bn254< Builder > > &pairing_points, TripleIpaOpening triple_ipa_opening)
VectorField result