Skip to content

Commit c0f5b1b

Browse files
authored
feat: merge-train/avm (#19197)
BEGIN_COMMIT_OVERRIDE feat(avm): tx<>prover fuzzer (#19134) END_COMMIT_OVERRIDE
2 parents c485cb8 + d6663f8 commit c0f5b1b

File tree

16 files changed

+422
-296
lines changed

16 files changed

+422
-296
lines changed

barretenberg/cpp/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ option(OMP_MULTITHREADING "Enable OMP multi-threading" OFF)
2828
option(FUZZING "Build ONLY fuzzing harnesses" OFF)
2929
option(ENABLE_PAR_ALGOS "Enable parallel algorithms" OFF)
3030
option(COVERAGE "Enable collecting coverage from tests" OFF)
31+
option(COVERAGE_AVM "Enable coverage filtered to AVM code only" OFF)
3132
option(ENABLE_ASAN "Address sanitizer for debugging tricky memory corruption" OFF)
3233
option(ENABLE_HEAVY_TESTS "Enable heavy tests when collecting coverage" OFF)
3334
# Note: Must do 'sudo apt-get install libdw-dev' or equivalent
@@ -74,6 +75,10 @@ if(ENABLE_ASAN)
7475
set(DISABLE_ASM ON)
7576
endif()
7677

78+
if(COVERAGE_AVM)
79+
add_compile_options(-DCOVERAGE_AVM)
80+
endif()
81+
7782
if(FUZZING)
7883
add_definitions(-DULTRA_FUZZ)
7984
if(FUZZING_SHOW_INFORMATION)

barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp

Lines changed: 17 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -3,189 +3,21 @@
33
#include <cstdint>
44
#include <vector>
55

6+
#include "barretenberg/avm_fuzzer/fuzz_lib/constants.hpp"
67
#include "barretenberg/common/throw_or_abort.hpp"
78
#include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp"
89
#include "barretenberg/crypto/poseidon2/poseidon2.hpp"
910
#include "barretenberg/vm2/common/aztec_constants.hpp"
1011
#include "barretenberg/vm2/common/aztec_types.hpp"
12+
#include "barretenberg/vm2/simulation/lib/contract_crypto.hpp"
13+
#include "barretenberg/vm2/simulation/lib/merkle.hpp"
1114

1215
using namespace bb::avm2::simulation;
1316
using Poseidon2 = bb::crypto::Poseidon2<bb::crypto::Poseidon2Bn254ScalarFieldParams>;
14-
using namespace bb::crypto::merkle_tree;
1517
using namespace bb::world_state;
1618

17-
// TODO(ilyas): implement other methods as needed
1819
namespace bb::avm2::fuzzer {
1920

20-
TreeSnapshots FuzzerLowLevelDB::get_tree_roots() const
21-
{
22-
return {
23-
.l1_to_l2_message_tree = { .root = FF(0), .next_available_leaf_index = 0 },
24-
.note_hash_tree = { .root = FF(0), .next_available_leaf_index = next_available_note_hash_index },
25-
.nullifier_tree = { .root = FF(0), .next_available_leaf_index = next_available_nullifier_index },
26-
.public_data_tree = { .root = FF(0), .next_available_leaf_index = next_available_public_data_index },
27-
};
28-
}
29-
30-
SiblingPath FuzzerLowLevelDB::get_sibling_path([[maybe_unused]] MerkleTreeId tree_id,
31-
[[maybe_unused]] index_t leaf_index) const
32-
{
33-
throw_or_abort("FuzzerLowLevelDB::get_sibling_path not implemented");
34-
}
35-
36-
std::pair<FF, index_t> FuzzerLowLevelDB::get_indexed_low_leaf_helper(
37-
const std::vector<std::pair<FF, index_t>>& value_sorted_leaves, const FF& value) const
38-
{
39-
for (size_t i = 0; i < value_sorted_leaves.size(); ++i) {
40-
if (value_sorted_leaves[i].first == value) {
41-
return value_sorted_leaves[i];
42-
}
43-
if (value_sorted_leaves[i].first > value) {
44-
return value_sorted_leaves[i - 1];
45-
}
46-
}
47-
// If we reach here, the value is larger than any leaf in the tree, return the last leaf
48-
return value_sorted_leaves.back();
49-
}
50-
51-
GetLowIndexedLeafResponse FuzzerLowLevelDB::get_low_indexed_leaf(MerkleTreeId tree_id, const FF& value) const
52-
{
53-
switch (tree_id) {
54-
case MerkleTreeId::NULLIFIER_TREE: {
55-
auto [low_value, low_index] = get_indexed_low_leaf_helper(nullifier_values, value);
56-
return GetLowIndexedLeafResponse(low_value == value, low_index);
57-
break;
58-
}
59-
case MerkleTreeId::PUBLIC_DATA_TREE: {
60-
auto [low_value, low_index] = get_indexed_low_leaf_helper(public_data_slots, value);
61-
return GetLowIndexedLeafResponse(low_value == value, low_index);
62-
break;
63-
}
64-
default:
65-
break;
66-
}
67-
return GetLowIndexedLeafResponse(false, 0);
68-
}
69-
FF FuzzerLowLevelDB::get_leaf_value(MerkleTreeId tree_id, index_t leaf_index) const
70-
{
71-
switch (tree_id) {
72-
case MerkleTreeId::NULLIFIER_TREE:
73-
return nullifier_leaves.at(leaf_index).nullifier;
74-
case MerkleTreeId::PUBLIC_DATA_TREE:
75-
return public_data_leaves.at(leaf_index).value;
76-
case MerkleTreeId::NOTE_HASH_TREE:
77-
return note_hash_leaves.at(leaf_index);
78-
default:
79-
break;
80-
}
81-
return FF(0);
82-
}
83-
simulation::IndexedLeaf<PublicDataLeafValue> FuzzerLowLevelDB::get_leaf_preimage_public_data_tree(
84-
index_t leaf_index) const
85-
{
86-
PublicDataLeafValue leaf_value = public_data_leaves.at(leaf_index);
87-
std::pair<FF, index_t> value_index_pair = { leaf_value.value, leaf_index };
88-
// Find index in public_data_slots
89-
auto it = std::ranges::find_if(
90-
public_data_slots.begin(), public_data_slots.end(), [&value_index_pair](const std::pair<FF, index_t>& pair) {
91-
return pair.second == value_index_pair.second;
92-
});
93-
if (it == public_data_slots.end()) {
94-
throw_or_abort("FuzzerLowLevelDB::get_leaf_preimage_public_data_tree: leaf not found in public_data_slots");
95-
}
96-
it++; // Now iterator is at the next element
97-
if (it == public_data_slots.end()) {
98-
// If this is the last leaf, return with index 0
99-
return simulation::IndexedLeaf<PublicDataLeafValue>(leaf_value, 0, 0);
100-
}
101-
auto [next_value, next_index] = *it;
102-
return bb::crypto::merkle_tree::IndexedLeaf<PublicDataLeafValue>(leaf_value, next_index, next_value);
103-
}
104-
105-
simulation::IndexedLeaf<NullifierLeafValue> FuzzerLowLevelDB::get_leaf_preimage_nullifier_tree(index_t leaf_index) const
106-
{
107-
auto leaf_value = nullifier_leaves.at(leaf_index);
108-
std::pair<FF, index_t> value_index_pair = { leaf_value.nullifier, leaf_index };
109-
// Find index in nullifiers_values
110-
auto it = std::ranges::find_if(
111-
nullifier_values.begin(), nullifier_values.end(), [&value_index_pair](const std::pair<FF, index_t>& pair) {
112-
return pair.second == value_index_pair.second;
113-
});
114-
if (it == nullifier_values.end()) {
115-
throw_or_abort("FuzzerLowLevelDB::get_leaf_preimage_nullifier_tree: leaf not found in nullifier_values");
116-
}
117-
118-
it++; // Now iterator is at the next element
119-
120-
if (it == nullifier_values.end()) {
121-
// If this is the last leaf, return with index 0
122-
return simulation::IndexedLeaf<NullifierLeafValue>(leaf_value, 0, 0);
123-
}
124-
auto [next_value, next_index] = *it;
125-
return simulation::IndexedLeaf<NullifierLeafValue>(leaf_value, next_index, next_value);
126-
}
127-
128-
simulation::SequentialInsertionResult<PublicDataLeafValue> FuzzerLowLevelDB::insert_indexed_leaves_public_data_tree(
129-
const PublicDataLeafValue& leaf_value)
130-
{
131-
// Add to map
132-
public_data_leaves[next_available_public_data_index] = leaf_value;
133-
// Add to sorted vector
134-
public_data_slots.push_back({ leaf_value.slot, next_available_public_data_index });
135-
// Sort vector
136-
std::ranges::sort(
137-
public_data_slots.begin(),
138-
public_data_slots.end(),
139-
[](const std::pair<FF, index_t>& a, const std::pair<FF, index_t>& b) { return a.first < b.first; });
140-
141-
// Increment next available index
142-
next_available_public_data_index++;
143-
// Don't return any witness data for now, as it's not used for pure calls.
144-
return {};
145-
}
146-
147-
simulation::SequentialInsertionResult<NullifierLeafValue> FuzzerLowLevelDB::insert_indexed_leaves_nullifier_tree(
148-
const NullifierLeafValue& leaf_value)
149-
{
150-
// Add to map
151-
nullifier_leaves[next_available_nullifier_index] = leaf_value;
152-
// Add to sorted vector
153-
nullifier_values.push_back({ leaf_value.nullifier, next_available_nullifier_index });
154-
// Sort vector
155-
std::ranges::sort(
156-
nullifier_values.begin(),
157-
nullifier_values.end(),
158-
[](const std::pair<FF, index_t>& a, const std::pair<FF, index_t>& b) { return a.first < b.first; });
159-
160-
// Increment next available index
161-
next_available_nullifier_index++;
162-
// Don't return any witness data for now, as it's not used for pure calls.
163-
return {};
164-
}
165-
166-
void FuzzerLowLevelDB::append_leaves([[maybe_unused]] MerkleTreeId tree_id, std::span<const FF> leaves)
167-
{
168-
note_hash_leaves.insert(note_hash_leaves.end(), leaves.begin(), leaves.end());
169-
next_available_note_hash_index += leaves.size();
170-
}
171-
void FuzzerLowLevelDB::pad_tree([[maybe_unused]] MerkleTreeId tree_id, [[maybe_unused]] size_t num_leaves) {}
172-
173-
void FuzzerLowLevelDB::create_checkpoint() {}
174-
void FuzzerLowLevelDB::commit_checkpoint() {}
175-
void FuzzerLowLevelDB::revert_checkpoint() {}
176-
uint32_t FuzzerLowLevelDB::get_checkpoint_id() const
177-
{
178-
return 0;
179-
}
180-
181-
// Helper to insert a contract address into the nullifier tree
182-
void FuzzerLowLevelDB::insert_contract_address(const AztecAddress& contract_address)
183-
{
184-
auto contract_nullifier =
185-
simulation::unconstrained_silo_nullifier(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, contract_address);
186-
insert_indexed_leaves_nullifier_tree(contract_nullifier);
187-
}
188-
18921
////////////////////////////////
19022
/// ContractDBInterface methods
19123
////////////////////////////////
@@ -207,13 +39,12 @@ std::optional<ContractClass> FuzzerContractDB::get_contract_class(const Contract
20739

20840
std::optional<FF> FuzzerContractDB::get_bytecode_commitment(const ContractClassId& class_id) const
20941
{
210-
// Return 0 might be an issue, in the pure bytecode manager we cache based on this value
211-
// This might cause different classes to be treated as the same if they return 0 here.
212-
// For now we just return the class_id as it should be as unique as the bytecode commitment
21342
if (!contract_classes.contains(class_id)) {
21443
return std::nullopt;
21544
}
216-
return class_id;
45+
// Compute the actual bytecode commitment from the stored bytecode
46+
const auto& klass = contract_classes.at(class_id);
47+
return compute_public_bytecode_commitment(klass.packed_bytecode);
21748
}
21849
std::optional<std::string> FuzzerContractDB::get_debug_function_name(
21950
[[maybe_unused]] const AztecAddress& address, [[maybe_unused]] const FunctionSelector& selector) const
@@ -239,12 +70,21 @@ void FuzzerContractDB::add_contracts(const ContractDeploymentData& contract_depl
23970

24071
void FuzzerContractDB::add_contract_class(const ContractClassId& class_id, const ContractClass& contract_class)
24172
{
242-
contract_classes[class_id] = contract_class;
73+
// todo(ilyas): think of a nicer way without both map and vector
74+
// Only push to vector if not already present, otherwise we get duplicates sent to the TS simulator
75+
if (contract_classes.contains(class_id)) {
76+
return;
77+
}
24378
contract_classes_vector.push_back(contract_class);
79+
contract_classes[class_id] = contract_class;
24480
}
24581

24682
void FuzzerContractDB::add_contract_instance(const AztecAddress& address, const ContractInstance& contract_instance)
24783
{
84+
// todo(ilyas): think of a nicer way without both map and vector
85+
if (contract_instances.contains(address)) {
86+
return;
87+
}
24888
contract_instances[address] = contract_instance;
24989
contract_instances_vector.push_back({ address, contract_instance });
25090
}
@@ -379,6 +219,7 @@ void FuzzerWorldStateManager::register_contract_address(const AztecAddress& cont
379219
{
380220
NullifierLeafValue contract_nullifier =
381221
unconstrained_silo_nullifier(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, contract_address);
222+
fuzz_info("Registering contract address in world state: ", contract_nullifier.nullifier);
382223
auto fork_id = fork_ids.top();
383224
ws->insert_indexed_leaves<NullifierLeafValue>(MerkleTreeId::NULLIFIER_TREE, { contract_nullifier }, fork_id);
384225
}

barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.hpp

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,15 @@
11
#pragma once
22

3+
#include <memory>
34
#include <stack>
45

56
#include "barretenberg/vm2/common/aztec_types.hpp"
67
#include "barretenberg/vm2/simulation/interfaces/db.hpp"
7-
#include "barretenberg/vm2/simulation/lib/merkle.hpp"
88
#include "barretenberg/world_state/types.hpp"
99
#include "barretenberg/world_state/world_state.hpp"
10-
#include <memory>
11-
#include <stack>
1210

1311
namespace bb::avm2::fuzzer {
1412

15-
// FIXME(ilyas): This is now unused for the fuzzer, but I have a plan to experiment with this later.
16-
class FuzzerLowLevelDB : public bb::avm2::simulation::LowLevelMerkleDBInterface {
17-
public:
18-
TreeSnapshots get_tree_roots() const override;
19-
20-
simulation::SiblingPath get_sibling_path(simulation::MerkleTreeId tree_id, index_t leaf_index) const override;
21-
22-
GetLowIndexedLeafResponse get_low_indexed_leaf(simulation::MerkleTreeId tree_id, const FF& value) const override;
23-
24-
FF get_leaf_value(simulation::MerkleTreeId tree_id, index_t leaf_index) const override;
25-
26-
simulation::IndexedLeaf<PublicDataLeafValue> get_leaf_preimage_public_data_tree(index_t leaf_index) const override;
27-
28-
simulation::IndexedLeaf<NullifierLeafValue> get_leaf_preimage_nullifier_tree(index_t leaf_index) const override;
29-
30-
simulation::SequentialInsertionResult<PublicDataLeafValue> insert_indexed_leaves_public_data_tree(
31-
const PublicDataLeafValue& leaf_value) override;
32-
33-
simulation::SequentialInsertionResult<NullifierLeafValue> insert_indexed_leaves_nullifier_tree(
34-
const NullifierLeafValue& leaf_value) override;
35-
36-
void append_leaves(simulation::MerkleTreeId tree_id, std::span<const FF> leaves) override;
37-
38-
void pad_tree(simulation::MerkleTreeId tree_id, size_t num_leaves) override;
39-
void create_checkpoint() override;
40-
void commit_checkpoint() override;
41-
void revert_checkpoint() override;
42-
uint32_t get_checkpoint_id() const override;
43-
44-
// Some helper functions for the fuzzer to use
45-
void insert_contract_address(const AztecAddress& contract_address);
46-
47-
private:
48-
std::pair<FF, index_t> get_indexed_low_leaf_helper(const std::vector<std::pair<FF, index_t>>& value_sorted_leaves,
49-
const FF& value) const;
50-
51-
// Stored leaves sorted by value/slots for low-indexed leaf retrieval
52-
std::vector<std::pair<FF, index_t>> nullifier_values{ { 0, 0 } };
53-
std::vector<std::pair<FF, index_t>> public_data_slots{ { 0, 0 } };
54-
// Stored leaves with their indices
55-
std::unordered_map<index_t, NullifierLeafValue> nullifier_leaves{ { 0, NullifierLeafValue(0) } };
56-
std::unordered_map<index_t, PublicDataLeafValue> public_data_leaves{ { 0, PublicDataLeafValue(0, 0) } };
57-
std::vector<FF> note_hash_leaves;
58-
59-
// Indices
60-
uint64_t next_available_nullifier_index = 1;
61-
uint64_t next_available_public_data_index = 1;
62-
uint64_t next_available_note_hash_index = 0;
63-
};
64-
6513
class FuzzerContractDB : public simulation::ContractDBInterface {
6614
public:
6715
FuzzerContractDB() = default;

0 commit comments

Comments
 (0)