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.
15
16#include <gtest/gtest.h>
17
18namespace bb::avm2::constraining {
19
20class AvmRecursiveTests : public ::testing::Test {
21 public:
26
28
29 // Helper function to create and verify native proof
36
37 // Helper function to create and verify native proof. Due to the way ASSERT_TRUE
38 // works, this routine needs to return void and therefore we feed proof_result
39 // by reference.
41 {
42 static auto [cached_verified, cached_proof_result] = []() {
43 auto [trace, public_inputs] = testing::get_minimal_trace_with_pi();
44
45 const auto public_inputs_cols = public_inputs.to_columns();
46
47 InnerProver prover;
48 const auto [proof, vk_data] = prover.prove(std::move(trace));
49 const auto verification_key = InnerProver::create_verification_key(vk_data);
50 InnerVerifier verifier(verification_key);
51
52 const bool verified = verifier.verify_proof(proof, public_inputs_cols);
53
55 verified, NativeProofResult{ proof, verification_key, public_inputs_cols }
56 };
57 }();
58
59 ASSERT_TRUE(cached_verified) << "native proof verification failed";
60 proof_result = cached_proof_result;
61 }
62};
63
71TEST_F(AvmRecursiveTests, GoblinRecursion)
72{
74 GTEST_SKIP() << "Skipping slow test";
75 }
76
77 // Type aliases specific to GoblinRecursion test
79 using OuterBuilder = typename UltraRollupFlavor::CircuitBuilder;
81 using UltraRollupProver = UltraProver_<UltraRollupFlavor>;
82 using NativeVerifierCommitmentKey = typename AvmFlavor::VerifierCommitmentKey;
83
84 NativeProofResult proof_result;
85 std::cout << "Creating and verifying native proof..." << std::endl;
86 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
87 ASSERT_NO_FATAL_FAILURE({ create_and_verify_native_proof(proof_result); });
88 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
89 std::cout << "Time taken (native proof): " << std::chrono::duration_cast<std::chrono::seconds>(end - start).count()
90 << "s" << std::endl;
91
92 auto [proof, verification_key, public_inputs_cols] = proof_result;
93 proof.insert(proof.begin(), 0); // TODO(#14234)[Unconditional PIs validation]: remove this
94
95 // Construct stdlib representations of the proof, public inputs and verification key
96 OuterBuilder outer_circuit;
97 stdlib::Proof<OuterBuilder> stdlib_proof(outer_circuit, proof);
98
99 std::vector<std::vector<UltraFF>> public_inputs_ct;
100 public_inputs_ct.reserve(public_inputs_cols.size());
101 for (const auto& vec : public_inputs_cols) {
103 vec_ct.reserve(vec.size());
104 for (const auto& val : vec) {
105 vec_ct.push_back(UltraFF::from_witness(&outer_circuit, val));
106 }
107 public_inputs_ct.push_back(vec_ct);
108 }
109
110 auto key_fields_native = verification_key->to_field_elements();
111 std::vector<UltraFF> outer_key_fields;
112 for (const auto& f : key_fields_native) {
113 UltraFF val = UltraFF::from_witness(&outer_circuit, f);
114 outer_key_fields.push_back(val);
115 }
116
117 // Construct the AVM recursive verifier and verify the proof
118 // Scoped to free memory of AvmRecursiveVerifier.
119 auto verifier_output = [&]() {
120 std::cout << "Constructing AvmRecursiveVerifier and verifying proof..." << std::endl;
121 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
122 AvmRecursiveVerifier avm_rec_verifier(outer_circuit, outer_key_fields);
123 auto result = avm_rec_verifier.verify_proof(stdlib_proof, public_inputs_ct);
124 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
125 std::cout << "Time taken (recursive verification): "
126 << std::chrono::duration_cast<std::chrono::seconds>(end - start).count() << "s" << std::endl;
127 return result;
128 }();
129
131 inputs.pairing_inputs = verifier_output.points_accumulator;
132 inputs.ipa_claim = verifier_output.ipa_claim;
133 inputs.set_public();
134 outer_circuit.ipa_proof = verifier_output.ipa_proof.get_value();
135
136 // Ensure that the pairing check is satisfied on the outputs of the recursive verifier
137 NativeVerifierCommitmentKey pcs_vkey{};
138 bool agg_output_valid = pcs_vkey.pairing_check(verifier_output.points_accumulator.P0.get_value(),
139 verifier_output.points_accumulator.P1.get_value());
140 ASSERT_TRUE(agg_output_valid) << "Pairing points (aggregation state) are not valid.";
141 ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed.";
142
143 vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates());
144
145 // Construct and verify an Ultra Rollup proof of the AVM recursive verifier circuit. This proof carries an IPA claim
146 // from ECCVM recursive verification in its public inputs that will be verified as part of the UltraRollupVerifier.
147 auto outer_proving_key = std::make_shared<ProverInstance_<UltraRollupFlavor>>(outer_circuit);
148
149 // Scoped to free memory of UltraRollupProver.
150 auto outer_proof = [&]() {
151 auto verification_key =
152 std::make_shared<UltraRollupFlavor::VerificationKey>(outer_proving_key->get_precomputed());
153 UltraRollupProver outer_prover(outer_proving_key, verification_key);
154 return outer_prover.construct_proof();
155 }();
156
157 // Verify the proof of the Ultra circuit that verified the AVM recursive verifier circuit
158 auto outer_verification_key =
159 std::make_shared<UltraRollupFlavor::VerificationKey>(outer_proving_key->get_precomputed());
160 VerifierCommitmentKey<curve::Grumpkin> ipa_verification_key(1 << CONST_ECCVM_LOG_N);
161 UltraRollupVerifier final_verifier(outer_verification_key, ipa_verification_key);
162
163 bool result = final_verifier.template verify_proof<bb::RollupIO>(outer_proof, outer_proving_key->ipa_proof).result;
164 EXPECT_TRUE(result);
165}
166
167// Similar to GoblinRecursion, but with PI validation disabled and garbage PIs in the public inputs.
168// This is important as long as we use a fallback mechanism for the AVM proofs.
169TEST_F(AvmRecursiveTests, GoblinRecursionWithoutPIValidation)
170{
172 GTEST_SKIP() << "Skipping slow test";
173 }
174
175 // Type aliases specific to GoblinRecursion test
177 using OuterBuilder = typename UltraRollupFlavor::CircuitBuilder;
179 using UltraRollupProver = UltraProver_<UltraRollupFlavor>;
180 using NativeVerifierCommitmentKey = typename AvmFlavor::VerifierCommitmentKey;
181
182 NativeProofResult proof_result;
183 std::cout << "Creating and verifying native proof..." << std::endl;
184 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
185 ASSERT_NO_FATAL_FAILURE({ create_and_verify_native_proof(proof_result); });
186 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
187 std::cout << "Time taken (native proof): " << std::chrono::duration_cast<std::chrono::seconds>(end - start).count()
188 << "s" << std::endl;
189
190 auto [proof, verification_key, public_inputs_cols] = proof_result;
191 // Set fallback / disable PI validation
192 proof.insert(proof.begin(),
193 1); // TODO(#14234)[Unconditional PIs validation]: PI validation is disabled for this test.
194
195 // Construct stdlib representations of the proof, public inputs and verification key
196 OuterBuilder outer_circuit;
197 stdlib::Proof<OuterBuilder> stdlib_proof(outer_circuit, proof);
198
199 std::vector<std::vector<UltraFF>> public_inputs_ct;
200 public_inputs_ct.reserve(public_inputs_cols.size());
201 // Use GARBAGE in public inputs and confirm that PI validation is disabled!
202 for (const auto& vec : public_inputs_cols) {
204 vec_ct.reserve(vec.size());
205 for (const auto& _ : vec) {
206 vec_ct.push_back(UltraFF::from_witness(&outer_circuit, FF::random_element()));
207 }
208 public_inputs_ct.push_back(vec_ct);
209 }
210
211 auto key_fields_native = verification_key->to_field_elements();
212 std::vector<UltraFF> outer_key_fields;
213 for (const auto& f : key_fields_native) {
214 UltraFF val = UltraFF::from_witness(&outer_circuit, f);
215 outer_key_fields.push_back(val);
216 }
217
218 // Construct the AVM recursive verifier and verify the proof
219 // Scoped to free memory of AvmRecursiveVerifier.
220 auto verifier_output = [&]() {
221 std::cout << "Constructing AvmRecursiveVerifier and verifying proof..." << std::endl;
222 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
223 AvmRecursiveVerifier avm_rec_verifier(outer_circuit, outer_key_fields);
224 auto result = avm_rec_verifier.verify_proof(stdlib_proof, public_inputs_ct);
225 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
226 std::cout << "Time taken (recursive verification): "
227 << std::chrono::duration_cast<std::chrono::seconds>(end - start).count() << "s" << std::endl;
228 return result;
229 }();
230
232 inputs.pairing_inputs = verifier_output.points_accumulator;
233 inputs.ipa_claim = verifier_output.ipa_claim;
234 inputs.set_public();
235 outer_circuit.ipa_proof = verifier_output.ipa_proof.get_value();
236
237 // Ensure that the pairing check is satisfied on the outputs of the recursive verifier
238 NativeVerifierCommitmentKey pcs_vkey{};
239 bool agg_output_valid = pcs_vkey.pairing_check(verifier_output.points_accumulator.P0.get_value(),
240 verifier_output.points_accumulator.P1.get_value());
241 ASSERT_TRUE(agg_output_valid) << "Pairing points (aggregation state) are not valid.";
242 ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed.";
243
244 vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates());
245
246 // Construct and verify an Ultra Rollup proof of the AVM recursive verifier circuit. This proof carries an IPA claim
247 // from ECCVM recursive verification in its public inputs that will be verified as part of the UltraRollupVerifier.
248 auto outer_proving_key = std::make_shared<ProverInstance_<UltraRollupFlavor>>(outer_circuit);
249
250 // Scoped to free memory of UltraRollupProver.
251 auto outer_proof = [&]() {
252 auto verification_key =
253 std::make_shared<UltraRollupFlavor::VerificationKey>(outer_proving_key->get_precomputed());
254 UltraRollupProver outer_prover(outer_proving_key, verification_key);
255 return outer_prover.construct_proof();
256 }();
257
258 // Verify the proof of the Ultra circuit that verified the AVM recursive verifier circuit
259 auto outer_verification_key =
260 std::make_shared<UltraRollupFlavor::VerificationKey>(outer_proving_key->get_precomputed());
261 VerifierCommitmentKey<curve::Grumpkin> ipa_verification_key(1 << CONST_ECCVM_LOG_N);
262 UltraRollupVerifier final_verifier(outer_verification_key, ipa_verification_key);
263
264 bool result = final_verifier.template verify_proof<bb::RollupIO>(outer_proof, outer_proving_key->ipa_proof).result;
265 EXPECT_TRUE(result);
266}
267
268// Ensures that the recursive verifier fails with wrong PIs.
269TEST_F(AvmRecursiveTests, GoblinRecursionFailsWithWrongPIs)
270{
272 GTEST_SKIP() << "Skipping slow test";
273 }
274
275 // Type aliases specific to GoblinRecursion test
277 using OuterBuilder = typename UltraRollupFlavor::CircuitBuilder;
279
280 NativeProofResult proof_result;
281 std::cout << "Creating and verifying native proof..." << std::endl;
282 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
283 ASSERT_NO_FATAL_FAILURE({ create_and_verify_native_proof(proof_result); });
284 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
285 std::cout << "Time taken (native proof): " << std::chrono::duration_cast<std::chrono::seconds>(end - start).count()
286 << "s" << std::endl;
287
288 auto [proof, verification_key, public_inputs_cols] = proof_result;
289 // PI validation is enabled.
290 proof.insert(proof.begin(), 0); // TODO(#14234)[Unconditional PIs validation]: remove this
291
292 // Construct stdlib representations of the proof, public inputs and verification key
293 OuterBuilder outer_circuit;
294 stdlib::Proof<OuterBuilder> stdlib_proof(outer_circuit, proof);
295
296 std::vector<std::vector<UltraFF>> public_inputs_ct;
297 public_inputs_ct.reserve(public_inputs_cols.size());
298 for (const auto& vec : public_inputs_cols) {
300 vec_ct.reserve(vec.size());
301 for (const auto& val : vec) {
302 vec_ct.push_back(UltraFF::from_witness(&outer_circuit, val));
303 }
304 public_inputs_ct.push_back(vec_ct);
305 }
306 // Mutate some PI entry so that we can confirm that PI validation is enabled and fails!
307 public_inputs_ct[1][5] += 1;
308
309 auto key_fields_native = verification_key->to_field_elements();
310 std::vector<UltraFF> outer_key_fields;
311 for (const auto& f : key_fields_native) {
312 UltraFF val = UltraFF::from_witness(&outer_circuit, f);
313 outer_key_fields.push_back(val);
314 }
315
316 // Construct the AVM recursive verifier and verify the proof
317 // Scoped to free memory of AvmRecursiveVerifier.
318 {
319 std::cout << "Constructing AvmRecursiveVerifier and verifying proof..." << std::endl;
320 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
321 AvmRecursiveVerifier avm_rec_verifier(outer_circuit, outer_key_fields);
322 auto result = avm_rec_verifier.verify_proof(stdlib_proof, public_inputs_ct);
323 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
324 std::cout << "Time taken (recursive verification): "
325 << std::chrono::duration_cast<std::chrono::seconds>(end - start).count() << "s" << std::endl;
326 };
327
328 ASSERT_TRUE(outer_circuit.failed()) << "Outer circuit SHOULD fail with bad PIs.";
329}
330
331} // namespace bb::avm2::constraining
UltraCircuitBuilder CircuitBuilder
typename Curve::ScalarField FF
Representation of the Grumpkin Verifier Commitment Key inside a bn254 circuit.
AvmFlavorSettings::VerifierCommitmentKey VerifierCommitmentKey
Definition flavor.hpp:43
Recursive verifier of AVM2 proofs that utilizes the Goblin mechanism for efficient EC operations.
std::pair< Proof, VkData > prove(tracegen::TraceContainer &&trace)
static std::shared_ptr< AvmVerifier::VerificationKey > create_verification_key(const VkData &vk_data)
AvmRecursiveFlavorSettings::CircuitBuilder CircuitBuilder
PairingPoints verify_proof(const HonkProof &proof, const std::vector< std::vector< fr > > &public_inputs_vec_nt)
virtual bool verify_proof(const HonkProof &proof, const std::vector< std::vector< FF > > &public_inputs)
This function verifies an Avm Honk proof for given program settings.
Definition verifier.cpp:42
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:19
The data that is propagated on the public inputs of a rollup circuit.
#define vinfo(...)
Definition log.hpp:80
TestTraceContainer trace
AvmProvingInputs inputs
TEST_F(AvmRecursiveTests, GoblinRecursion)
A test of the Goblinized AVM recursive verifier.
bool skip_slow_tests()
Check if slow tests should be skipped.
Definition fixtures.cpp:199
std::pair< tracegen::TraceContainer, PublicInputs > get_minimal_trace_with_pi()
Definition fixtures.cpp:183
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
static field random_element(numeric::RNG *engine=nullptr) noexcept