Skip to content

Commit c8819e6

Browse files
authored
feat: merge-train/avm (#17076)
BEGIN_COMMIT_OVERRIDE fix(avm)!: unconstrained reads in debuglog (#17061) chore: more tests of AVM recursive verifier (#17048) END_COMMIT_OVERRIDE
2 parents 25c8b04 + 5230141 commit c8819e6

File tree

12 files changed

+302
-68
lines changed

12 files changed

+302
-68
lines changed

barretenberg/cpp/pil/vm2/memory.pil

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,6 @@ sel_to_radix_write * (1 - sel_to_radix_write) = 0;
135135
// Permutation consistency.
136136
// TODO(#16759): Enable the following relation ensuring that each memory entry is associated with a valid permutation selector.
137137
// DebugLog opcode is generating some memory entries which should not be triggered.
138-
/*
139138
#[ACTIVE_ROW_NEEDS_PERM_SELECTOR]
140139
sel = // Addressing.
141140
sel_addressing_base
@@ -165,7 +164,6 @@ sel = // Addressing.
165164
+ sel_ecc_write[0] + sel_ecc_write[1] + sel_ecc_write[2]
166165
// To Radix.
167166
+ sel_to_radix_write;
168-
*/
169167

170168
// Other boolean constraints.
171169
sel * (1 - sel) = 0; // Ensure mutual exclusivity of the permutation selectors.

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/relations/memory.test.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ TEST(MemoryConstrainingTest, MultipleEventsWithTraceGen)
140140
range_check_trace_builder.process(range_check_events, trace);
141141
memory_trace_builder.process(mem_events, trace);
142142

143+
// For the selector consistency, we need to make the read/write come from some trace.
144+
trace.visit_column(Column::memory_sel,
145+
[&](uint32_t row, const FF&) { trace.set(Column::memory_sel_register_op_0_, row, 1); });
146+
143147
check_relation<memory>(trace);
144148
check_all_interactions<MemoryTraceBuilder>(trace);
145149
}

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]);

barretenberg/cpp/src/barretenberg/vm2/generated/flavor_variables.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ struct AvmFlavorVariables {
148148
// Need to be templated for recursive verifier
149149
template <typename FF_>
150150
using MainRelations_ = flat_tuple::tuple<
151-
152151
// Optimized Relations
153152
avm2::optimized_poseidon2_perm<FF_>,
154153
// Relations

barretenberg/cpp/src/barretenberg/vm2/generated/relations/memory.hpp

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ template <typename FF_> class memoryImpl {
1414
public:
1515
using FF = FF_;
1616

17-
static constexpr std::array<size_t, 59> SUBRELATION_PARTIAL_LENGTHS = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
18-
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
19-
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
20-
3, 4, 3, 2, 2, 5, 5, 2, 4, 4, 4, 4, 5, 3 };
17+
static constexpr std::array<size_t, 60> SUBRELATION_PARTIAL_LENGTHS = {
18+
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
19+
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 4, 3, 2, 2, 5, 5, 2, 4, 4, 4, 4, 5, 3
20+
};
2121

2222
template <typename AllEntities> inline static bool skip(const AllEntities& in)
2323
{
@@ -38,23 +38,26 @@ template <typename FF> class memory : public Relation<memoryImpl<FF>> {
3838
static constexpr const std::string_view NAME = "memory";
3939

4040
// Subrelation indices constants, to be used in tests.
41-
static constexpr size_t SR_MEM_CONTIGUOUS = 46;
42-
static constexpr size_t SR_SEL_RNG_CHK = 47;
43-
static constexpr size_t SR_GLOBAL_ADDR = 48;
44-
static constexpr size_t SR_TIMESTAMP = 49;
45-
static constexpr size_t SR_LAST_ACCESS = 50;
46-
static constexpr size_t SR_DIFF = 51;
47-
static constexpr size_t SR_DIFF_DECOMP = 52;
48-
static constexpr size_t SR_MEMORY_INIT_VALUE = 53;
49-
static constexpr size_t SR_MEMORY_INIT_TAG = 54;
50-
static constexpr size_t SR_READ_WRITE_CONSISTENCY_VALUE = 55;
51-
static constexpr size_t SR_READ_WRITE_CONSISTENCY_TAG = 56;
52-
static constexpr size_t SR_TAG_IS_FF = 57;
53-
static constexpr size_t SR_SEL_RNG_WRITE = 58;
41+
static constexpr size_t SR_ACTIVE_ROW_NEEDS_PERM_SELECTOR = 42;
42+
static constexpr size_t SR_MEM_CONTIGUOUS = 47;
43+
static constexpr size_t SR_SEL_RNG_CHK = 48;
44+
static constexpr size_t SR_GLOBAL_ADDR = 49;
45+
static constexpr size_t SR_TIMESTAMP = 50;
46+
static constexpr size_t SR_LAST_ACCESS = 51;
47+
static constexpr size_t SR_DIFF = 52;
48+
static constexpr size_t SR_DIFF_DECOMP = 53;
49+
static constexpr size_t SR_MEMORY_INIT_VALUE = 54;
50+
static constexpr size_t SR_MEMORY_INIT_TAG = 55;
51+
static constexpr size_t SR_READ_WRITE_CONSISTENCY_VALUE = 56;
52+
static constexpr size_t SR_READ_WRITE_CONSISTENCY_TAG = 57;
53+
static constexpr size_t SR_TAG_IS_FF = 58;
54+
static constexpr size_t SR_SEL_RNG_WRITE = 59;
5455

5556
static std::string get_subrelation_label(size_t index)
5657
{
5758
switch (index) {
59+
case SR_ACTIVE_ROW_NEEDS_PERM_SELECTOR:
60+
return "ACTIVE_ROW_NEEDS_PERM_SELECTOR";
5861
case SR_MEM_CONTIGUOUS:
5962
return "MEM_CONTIGUOUS";
6063
case SR_SEL_RNG_CHK:

0 commit comments

Comments
 (0)