Skip to content

Commit 43c13f0

Browse files
dbanks12jeanmon
andauthored
chore: more tests of AVM recursive verifier (#17048)
Tests that when you mangle AVM public inputs, recursive verification: 1. Passes if PI validation is disabled (fallback) 2. Fails if PI validation is enabled --------- Co-authored-by: jeanmon <[email protected]>
1 parent 4004e1d commit 43c13f0

File tree

4 files changed

+174
-13
lines changed

4 files changed

+174
-13
lines changed

barretenberg/cpp/src/barretenberg/vm2/constraining/prover.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ HonkProof AvmProver::construct_proof()
169169
// Add circuit size public input size and public inputs to transcript.
170170
execute_preamble_round();
171171

172-
// TODO: Make secure at some point
172+
// TODO(https://github.com/AztecProtocol/aztec-packages/pull/17045): make the protocols secure at some point
173173
// // Add public inputs to transcript.
174174
// AVM_TRACK_TIME("prove/public_inputs_round", execute_public_inputs_round());
175175

barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ AvmRecursiveVerifier::PairingPoints AvmRecursiveVerifier::verify_proof(
113113
RelationParams relation_parameters;
114114
VerifierCommitments commitments{ key };
115115

116-
// TODO (make the protocols secure at some point)
116+
// TODO(https://github.com/AztecProtocol/aztec-packages/pull/17045): make the protocols secure at some point
117117
// // Add public inputs to transcript
118118
// for (size_t i = 0; i < AVM_NUM_PUBLIC_INPUT_COLUMNS; i++) {
119119
// for (size_t j = 0; j < public_inputs[i].size(); j++) {
@@ -124,6 +124,9 @@ AvmRecursiveVerifier::PairingPoints AvmRecursiveVerifier::verify_proof(
124124

125125
for (size_t i = 0; i < AVM_NUM_PUBLIC_INPUT_COLUMNS; i++) {
126126
for (size_t j = 0; j < public_inputs[i].size(); j++) {
127+
// TODO(https://github.com/AztecProtocol/aztec-packages/pull/17045): make the protocols secure at some point
128+
// transcript->add_to_hash_buffer("public_input_" + std::to_string(i) + "_" + std::to_string(j),
129+
// public_inputs[i][j]);
127130
public_inputs[i][j].unset_free_witness_tag();
128131
}
129132
}

barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.test.cpp

Lines changed: 168 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,25 @@ class AvmRecursiveTests : public ::testing::Test {
3939
// by reference.
4040
static void create_and_verify_native_proof(NativeProofResult& proof_result)
4141
{
42-
auto [trace, public_inputs] = testing::get_minimal_trace_with_pi();
42+
static auto [cached_verified, cached_proof_result] = []() {
43+
auto [trace, public_inputs] = testing::get_minimal_trace_with_pi();
4344

44-
const auto public_inputs_cols = public_inputs.to_columns();
45+
const auto public_inputs_cols = public_inputs.to_columns();
4546

46-
InnerProver prover;
47-
const auto [proof, vk_data] = prover.prove(std::move(trace));
48-
const auto verification_key = InnerProver::create_verification_key(vk_data);
49-
InnerVerifier verifier(verification_key);
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);
5051

51-
const bool verified = verifier.verify_proof(proof, public_inputs_cols);
52+
const bool verified = verifier.verify_proof(proof, public_inputs_cols);
5253

53-
// Should be in principle ASSERT_TRUE, but compiler does not like it.
54-
EXPECT_TRUE(verified) << "native proof verification failed";
54+
return std::pair<bool, NativeProofResult>{
55+
verified, NativeProofResult{ proof, verification_key, public_inputs_cols }
56+
};
57+
}();
5558

56-
proof_result = { proof, verification_key, public_inputs_cols };
59+
ASSERT_TRUE(cached_verified) << "native proof verification failed";
60+
proof_result = cached_proof_result;
5761
}
5862
};
5963

@@ -154,4 +158,158 @@ TEST_F(AvmRecursiveTests, GoblinRecursion)
154158
EXPECT_TRUE(result);
155159
}
156160

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.
163+
TEST_F(AvmRecursiveTests, GoblinRecursionWithoutPIValidation)
164+
{
165+
// Type aliases specific to GoblinRecursion test
166+
using AvmRecursiveVerifier = AvmGoblinRecursiveVerifier;
167+
using OuterBuilder = typename UltraRollupFlavor::CircuitBuilder;
168+
using UltraFF = UltraRecursiveFlavor_<OuterBuilder>::FF;
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) {
193+
std::vector<UltraFF> vec_ct;
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<DeciderProvingKey_<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.
257+
TEST_F(AvmRecursiveTests, GoblinRecursionFailsWithWrongPIs)
258+
{
259+
// Type aliases specific to GoblinRecursion test
260+
using AvmRecursiveVerifier = AvmGoblinRecursiveVerifier;
261+
using OuterBuilder = typename UltraRollupFlavor::CircuitBuilder;
262+
using UltraFF = UltraRecursiveFlavor_<OuterBuilder>::FF;
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) {
283+
std::vector<UltraFF> vec_ct;
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+
157315
} // namespace bb::avm2::constraining

barretenberg/cpp/src/barretenberg/vm2/constraining/verifier.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ bool AvmVerifier::verify_proof(const HonkProof& proof, const std::vector<std::ve
7171
vinfo("Public input size mismatch");
7272
return false;
7373
}
74-
// TODO: make secure at some point
74+
// TODO(https://github.com/AztecProtocol/aztec-packages/pull/17045): make the protocols secure at some point
7575
// for (size_t j = 0; j < public_inputs[i].size(); j++) {
7676
// transcript->add_to_hash_buffer("public_input_" + std::to_string(i) + "_" + std::to_string(j),
7777
// public_inputs[i][j]);

0 commit comments

Comments
 (0)