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{
73 // Type aliases specific to GoblinRecursion test
75 using OuterBuilder = typename UltraRollupFlavor::CircuitBuilder;
77 using UltraRollupProver = UltraProver_<UltraRollupFlavor>;
78 using NativeVerifierCommitmentKey = typename AvmFlavor::VerifierCommitmentKey;
79
80 NativeProofResult proof_result;
81 std::cout << "Creating and verifying native proof..." << std::endl;
82 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
83 ASSERT_NO_FATAL_FAILURE({ create_and_verify_native_proof(proof_result); });
84 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
85 std::cout << "Time taken (native proof): " << std::chrono::duration_cast<std::chrono::seconds>(end - start).count()
86 << "s" << std::endl;
87
88 auto [proof, verification_key, public_inputs_cols] = proof_result;
89 proof.insert(proof.begin(), 0); // TODO(#14234)[Unconditional PIs validation]: remove this
90
91 // Construct stdlib representations of the proof, public inputs and verification key
92 OuterBuilder outer_circuit;
93 stdlib::Proof<OuterBuilder> stdlib_proof(outer_circuit, proof);
94
95 std::vector<std::vector<UltraFF>> public_inputs_ct;
96 public_inputs_ct.reserve(public_inputs_cols.size());
97 for (const auto& vec : public_inputs_cols) {
99 vec_ct.reserve(vec.size());
100 for (const auto& val : vec) {
101 vec_ct.push_back(UltraFF::from_witness(&outer_circuit, val));
102 }
103 public_inputs_ct.push_back(vec_ct);
104 }
105
106 auto key_fields_native = verification_key->to_field_elements();
107 std::vector<UltraFF> outer_key_fields;
108 for (const auto& f : key_fields_native) {
109 UltraFF val = UltraFF::from_witness(&outer_circuit, f);
110 outer_key_fields.push_back(val);
111 }
112
113 // Construct the AVM recursive verifier and verify the proof
114 // Scoped to free memory of AvmRecursiveVerifier.
115 auto verifier_output = [&]() {
116 std::cout << "Constructing AvmRecursiveVerifier and verifying proof..." << std::endl;
117 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
118 AvmRecursiveVerifier avm_rec_verifier(outer_circuit, outer_key_fields);
119 auto result = avm_rec_verifier.verify_proof(stdlib_proof, public_inputs_ct);
120 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
121 std::cout << "Time taken (recursive verification): "
122 << std::chrono::duration_cast<std::chrono::seconds>(end - start).count() << "s" << std::endl;
123 return result;
124 }();
125
126 verifier_output.points_accumulator.set_public();
127 verifier_output.ipa_claim.set_public();
128 outer_circuit.ipa_proof = verifier_output.ipa_proof.get_value();
129
130 // Ensure that the pairing check is satisfied on the outputs of the recursive verifier
131 NativeVerifierCommitmentKey pcs_vkey{};
132 bool agg_output_valid = pcs_vkey.pairing_check(verifier_output.points_accumulator.P0.get_value(),
133 verifier_output.points_accumulator.P1.get_value());
134 ASSERT_TRUE(agg_output_valid) << "Pairing points (aggregation state) are not valid.";
135 ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed.";
136
137 vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates);
138
139 // Construct and verify an Ultra Rollup proof of the AVM recursive verifier circuit. This proof carries an IPA claim
140 // from ECCVM recursive verification in its public inputs that will be verified as part of the UltraRollupVerifier.
141 auto outer_proving_key = std::make_shared<ProverInstance_<UltraRollupFlavor>>(outer_circuit);
142
143 // Scoped to free memory of UltraRollupProver.
144 auto outer_proof = [&]() {
145 auto verification_key =
146 std::make_shared<UltraRollupFlavor::VerificationKey>(outer_proving_key->get_precomputed());
147 UltraRollupProver outer_prover(outer_proving_key, verification_key);
148 return outer_prover.construct_proof();
149 }();
150
151 // Verify the proof of the Ultra circuit that verified the AVM recursive verifier circuit
152 auto outer_verification_key =
153 std::make_shared<UltraRollupFlavor::VerificationKey>(outer_proving_key->get_precomputed());
154 VerifierCommitmentKey<curve::Grumpkin> ipa_verification_key(1 << CONST_ECCVM_LOG_N);
155 UltraRollupVerifier final_verifier(outer_verification_key, ipa_verification_key);
156
157 bool result = final_verifier.template verify_proof<bb::RollupIO>(outer_proof, outer_proving_key->ipa_proof).result;
158 EXPECT_TRUE(result);
159}
160
161// Similar to GoblinRecursion, but with PI validation disabled and garbage PIs in the public inputs.
162// This is important as long as we use a fallback mechanism for the AVM proofs.
163TEST_F(AvmRecursiveTests, GoblinRecursionWithoutPIValidation)
164{
165 // Type aliases specific to GoblinRecursion test
167 using OuterBuilder = typename UltraRollupFlavor::CircuitBuilder;
169 using UltraRollupProver = UltraProver_<UltraRollupFlavor>;
170 using NativeVerifierCommitmentKey = typename AvmFlavor::VerifierCommitmentKey;
171
172 NativeProofResult proof_result;
173 std::cout << "Creating and verifying native proof..." << std::endl;
174 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
175 ASSERT_NO_FATAL_FAILURE({ create_and_verify_native_proof(proof_result); });
176 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
177 std::cout << "Time taken (native proof): " << std::chrono::duration_cast<std::chrono::seconds>(end - start).count()
178 << "s" << std::endl;
179
180 auto [proof, verification_key, public_inputs_cols] = proof_result;
181 // Set fallback / disable PI validation
182 proof.insert(proof.begin(),
183 1); // TODO(#14234)[Unconditional PIs validation]: PI validation is disabled for this test.
184
185 // Construct stdlib representations of the proof, public inputs and verification key
186 OuterBuilder outer_circuit;
187 stdlib::Proof<OuterBuilder> stdlib_proof(outer_circuit, proof);
188
189 std::vector<std::vector<UltraFF>> public_inputs_ct;
190 public_inputs_ct.reserve(public_inputs_cols.size());
191 // Use GARBAGE in public inputs and confirm that PI validation is disabled!
192 for (const auto& vec : public_inputs_cols) {
194 vec_ct.reserve(vec.size());
195 for (const auto& _ : vec) {
196 vec_ct.push_back(UltraFF::from_witness(&outer_circuit, FF::random_element()));
197 }
198 public_inputs_ct.push_back(vec_ct);
199 }
200
201 auto key_fields_native = verification_key->to_field_elements();
202 std::vector<UltraFF> outer_key_fields;
203 for (const auto& f : key_fields_native) {
204 UltraFF val = UltraFF::from_witness(&outer_circuit, f);
205 outer_key_fields.push_back(val);
206 }
207
208 // Construct the AVM recursive verifier and verify the proof
209 // Scoped to free memory of AvmRecursiveVerifier.
210 auto verifier_output = [&]() {
211 std::cout << "Constructing AvmRecursiveVerifier and verifying proof..." << std::endl;
212 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
213 AvmRecursiveVerifier avm_rec_verifier(outer_circuit, outer_key_fields);
214 auto result = avm_rec_verifier.verify_proof(stdlib_proof, public_inputs_ct);
215 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
216 std::cout << "Time taken (recursive verification): "
217 << std::chrono::duration_cast<std::chrono::seconds>(end - start).count() << "s" << std::endl;
218 return result;
219 }();
220
221 verifier_output.points_accumulator.set_public();
222 verifier_output.ipa_claim.set_public();
223 outer_circuit.ipa_proof = verifier_output.ipa_proof.get_value();
224
225 // Ensure that the pairing check is satisfied on the outputs of the recursive verifier
226 NativeVerifierCommitmentKey pcs_vkey{};
227 bool agg_output_valid = pcs_vkey.pairing_check(verifier_output.points_accumulator.P0.get_value(),
228 verifier_output.points_accumulator.P1.get_value());
229 ASSERT_TRUE(agg_output_valid) << "Pairing points (aggregation state) are not valid.";
230 ASSERT_FALSE(outer_circuit.failed()) << "Outer circuit has failed.";
231
232 vinfo("Recursive verifier: finalized num gates = ", outer_circuit.num_gates);
233
234 // Construct and verify an Ultra Rollup proof of the AVM recursive verifier circuit. This proof carries an IPA claim
235 // from ECCVM recursive verification in its public inputs that will be verified as part of the UltraRollupVerifier.
236 auto outer_proving_key = std::make_shared<ProverInstance_<UltraRollupFlavor>>(outer_circuit);
237
238 // Scoped to free memory of UltraRollupProver.
239 auto outer_proof = [&]() {
240 auto verification_key =
241 std::make_shared<UltraRollupFlavor::VerificationKey>(outer_proving_key->get_precomputed());
242 UltraRollupProver outer_prover(outer_proving_key, verification_key);
243 return outer_prover.construct_proof();
244 }();
245
246 // Verify the proof of the Ultra circuit that verified the AVM recursive verifier circuit
247 auto outer_verification_key =
248 std::make_shared<UltraRollupFlavor::VerificationKey>(outer_proving_key->get_precomputed());
249 VerifierCommitmentKey<curve::Grumpkin> ipa_verification_key(1 << CONST_ECCVM_LOG_N);
250 UltraRollupVerifier final_verifier(outer_verification_key, ipa_verification_key);
251
252 bool result = final_verifier.template verify_proof<bb::RollupIO>(outer_proof, outer_proving_key->ipa_proof).result;
253 EXPECT_TRUE(result);
254}
255
256// Ensures that the recursive verifier fails with wrong PIs.
257TEST_F(AvmRecursiveTests, GoblinRecursionFailsWithWrongPIs)
258{
259 // Type aliases specific to GoblinRecursion test
261 using OuterBuilder = typename UltraRollupFlavor::CircuitBuilder;
263
264 NativeProofResult proof_result;
265 std::cout << "Creating and verifying native proof..." << std::endl;
266 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
267 ASSERT_NO_FATAL_FAILURE({ create_and_verify_native_proof(proof_result); });
268 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
269 std::cout << "Time taken (native proof): " << std::chrono::duration_cast<std::chrono::seconds>(end - start).count()
270 << "s" << std::endl;
271
272 auto [proof, verification_key, public_inputs_cols] = proof_result;
273 // PI validation is enabled.
274 proof.insert(proof.begin(), 0); // TODO(#14234)[Unconditional PIs validation]: remove this
275
276 // Construct stdlib representations of the proof, public inputs and verification key
277 OuterBuilder outer_circuit;
278 stdlib::Proof<OuterBuilder> stdlib_proof(outer_circuit, proof);
279
280 std::vector<std::vector<UltraFF>> public_inputs_ct;
281 public_inputs_ct.reserve(public_inputs_cols.size());
282 for (const auto& vec : public_inputs_cols) {
284 vec_ct.reserve(vec.size());
285 for (const auto& val : vec) {
286 vec_ct.push_back(UltraFF::from_witness(&outer_circuit, val));
287 }
288 public_inputs_ct.push_back(vec_ct);
289 }
290 // Mutate some PI entry so that we can confirm that PI validation is enabled and fails!
291 public_inputs_ct[1][5] += 1;
292
293 auto key_fields_native = verification_key->to_field_elements();
294 std::vector<UltraFF> outer_key_fields;
295 for (const auto& f : key_fields_native) {
296 UltraFF val = UltraFF::from_witness(&outer_circuit, f);
297 outer_key_fields.push_back(val);
298 }
299
300 // Construct the AVM recursive verifier and verify the proof
301 // Scoped to free memory of AvmRecursiveVerifier.
302 {
303 std::cout << "Constructing AvmRecursiveVerifier and verifying proof..." << std::endl;
304 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
305 AvmRecursiveVerifier avm_rec_verifier(outer_circuit, outer_key_fields);
306 auto result = avm_rec_verifier.verify_proof(stdlib_proof, public_inputs_ct);
307 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
308 std::cout << "Time taken (recursive verification): "
309 << std::chrono::duration_cast<std::chrono::seconds>(end - start).count() << "s" << std::endl;
310 };
311
312 ASSERT_TRUE(outer_circuit.failed()) << "Outer circuit SHOULD fail with bad PIs.";
313}
314
315} // 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:41
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
#define vinfo(...)
Definition log.hpp:79
TestTraceContainer trace
TEST_F(AvmRecursiveTests, GoblinRecursion)
A test of the Goblinized AVM recursive verifier.
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