Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ class ContractDBInterface {
virtual std::optional<ContractClass> get_contract_class(const ContractClassId& class_id) const = 0;
};

// The sibling path and root after the insertion.
struct AppendLeafResult {
FF root;
crypto::merkle_tree::fr_sibling_path path;
};

// Low level access to a merkle db. In general these will not be constrained.
class LowLevelMerkleDBInterface {
public:
Expand All @@ -45,7 +51,9 @@ class LowLevelMerkleDBInterface {
insert_indexed_leaves_public_data_tree(const crypto::merkle_tree::PublicDataLeafValue& leaf_value) = 0;
virtual world_state::SequentialInsertionResult<crypto::merkle_tree::NullifierLeafValue>
insert_indexed_leaves_nullifier_tree(const crypto::merkle_tree::NullifierLeafValue& leaf_value) = 0;
virtual void append_leaves(world_state::MerkleTreeId tree_id, std::span<const FF> leaves) = 0;

virtual std::vector<AppendLeafResult> append_leaves(world_state::MerkleTreeId tree_id,
std::span<const FF> leaves) = 0;

virtual void create_checkpoint() = 0;
virtual void commit_checkpoint() = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "barretenberg/common/log.hpp"
#include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp"
#include "barretenberg/vm2/simulation/lib/contract_crypto.hpp"
#include "barretenberg/vm2/simulation/lib/db_interfaces.hpp"

namespace bb::avm2::simulation {

Expand All @@ -26,6 +27,24 @@ std::string to_string(const TreeSnapshots& snapshots)
snapshots.l1ToL2MessageTree);
}

std::string get_tree_name(world_state::MerkleTreeId tree_id)
{
switch (tree_id) {
case world_state::MerkleTreeId::PUBLIC_DATA_TREE:
return "PUBLIC_DATA_TREE";
case world_state::MerkleTreeId::NULLIFIER_TREE:
return "NULLIFIER_TREE";
case world_state::MerkleTreeId::NOTE_HASH_TREE:
return "NOTE_HASH_TREE";
case world_state::MerkleTreeId::L1_TO_L2_MESSAGE_TREE:
return "L1_TO_L2_MESSAGE_TREE";
case world_state::MerkleTreeId::ARCHIVE:
return "ARCHIVE";
}

return "UNKNOWN"; // To make GCC happy.
}

} // namespace

// HintedRawContractDB starts.
Expand Down Expand Up @@ -232,8 +251,8 @@ crypto::merkle_tree::fr_sibling_path HintedRawMerkleDB::get_sibling_path(world_s
tree_info.root,
", size: ",
tree_info.nextAvailableLeafIndex,
", tree_id: ",
static_cast<uint32_t>(tree_id),
", tree: ",
get_tree_name(tree_id),
", leaf_index: ",
leaf_index,
")"));
Expand All @@ -252,8 +271,8 @@ crypto::merkle_tree::GetLowIndexedLeafResponse HintedRawMerkleDB::get_low_indexe
tree_info.root,
", size: ",
tree_info.nextAvailableLeafIndex,
", tree_id: ",
static_cast<uint32_t>(tree_id),
", tree: ",
get_tree_name(tree_id),
", value: ",
value,
")"));
Expand Down Expand Up @@ -507,31 +526,42 @@ void HintedRawMerkleDB::revert_checkpoint()
checkpoint_action_counter++;
}

void HintedRawMerkleDB::append_leaves(world_state::MerkleTreeId tree_id, std::span<const FF> leaves)
std::vector<AppendLeafResult> HintedRawMerkleDB::append_leaves(world_state::MerkleTreeId tree_id,
std::span<const FF> leaves)
{
if (leaves.empty()) {
return;
std::vector<AppendLeafResult> results;
results.reserve(leaves.size());

// We need to process each leaf individually because we need the sibling path after insertion, to be able to
// constraint the insertion.
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/13380): This can be changed if the world state
// appendLeaves returns the sibling paths.
for (const auto& leaf : leaves) {
results.push_back(appendLeafInternal(tree_id, leaf));
}

return results;
}

AppendLeafResult HintedRawMerkleDB::appendLeafInternal(world_state::MerkleTreeId tree_id, const FF& leaf)
{
auto tree_info = get_tree_info(tree_id);
AppendLeavesHintKey key = { tree_info, tree_id, std::vector<FF>(leaves.begin(), leaves.end()) };
AppendLeavesHintKey key = { tree_info, tree_id, { leaf } };
auto it = append_leaves_hints.find(key);
if (it == append_leaves_hints.end()) {
throw std::runtime_error(format("Append leaves hint not found for key (root: ",
tree_info.root,
", size: ",
tree_info.nextAvailableLeafIndex,
", tree_id: ",
static_cast<uint32_t>(tree_id),
", leaves: ",
leaves[0],
"..., leaves count: ",
leaves.size(),
", tree: ",
get_tree_name(tree_id),
", leaf: ",
leaf,
")"));
}
const auto& stateAfter = it->second;

// Update the tree state based on the hint
// Update the tree state based on the hint.
switch (tree_id) {
case world_state::MerkleTreeId::NOTE_HASH_TREE:
tree_roots.noteHashTree = stateAfter;
Expand All @@ -552,6 +582,9 @@ void HintedRawMerkleDB::append_leaves(world_state::MerkleTreeId tree_id, std::sp
default:
throw std::runtime_error("append_leaves is only supported for NOTE_HASH_TREE and L1_TO_L2_MESSAGE_TREE");
}

// Get the sibling path for the newly inserted leaf.
return { .root = tree_info.root, .path = get_sibling_path(tree_id, tree_info.nextAvailableLeafIndex) };
}

} // namespace bb::avm2::simulation
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class HintedRawMerkleDB final : public LowLevelMerkleDBInterface {
insert_indexed_leaves_public_data_tree(const crypto::merkle_tree::PublicDataLeafValue& leaf_value) override;
world_state::SequentialInsertionResult<crypto::merkle_tree::NullifierLeafValue>
insert_indexed_leaves_nullifier_tree(const crypto::merkle_tree::NullifierLeafValue& leaf_value) override;
void append_leaves(world_state::MerkleTreeId tree_id, std::span<const FF> leaves) override;
std::vector<AppendLeafResult> append_leaves(world_state::MerkleTreeId tree_id, std::span<const FF> leaves) override;

void create_checkpoint() override;
void commit_checkpoint() override;
Expand Down Expand Up @@ -102,7 +102,9 @@ class HintedRawMerkleDB final : public LowLevelMerkleDBInterface {
unordered_flat_map</*action_counter*/ uint32_t, CommitCheckpointHint> commit_checkpoint_hints;
unordered_flat_map</*action_counter*/ uint32_t, RevertCheckpointHint> revert_checkpoint_hints;

// Private helper methods.
const AppendOnlyTreeSnapshot& get_tree_info(world_state::MerkleTreeId tree_id) const;
AppendLeafResult appendLeafInternal(world_state::MerkleTreeId tree_id, const FF& leaf);
};

} // namespace bb::avm2::simulation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ class MockLowLevelMerkleDB : public LowLevelMerkleDBInterface {
insert_indexed_leaves_nullifier_tree,
(const crypto::merkle_tree::NullifierLeafValue& leaf_value),
(override));
MOCK_METHOD(void, append_leaves, (world_state::MerkleTreeId tree_id, std::span<const FF> leaves), (override));
MOCK_METHOD(std::vector<AppendLeafResult>,
append_leaves,
(world_state::MerkleTreeId tree_id, std::span<const FF> leaves),
(override));
MOCK_METHOD(void, create_checkpoint, (), (override));
MOCK_METHOD(void, commit_checkpoint, (), (override));
MOCK_METHOD(void, revert_checkpoint, (), (override));
Expand Down
38 changes: 25 additions & 13 deletions yarn-project/simulator/src/public/hinting_db_sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,20 +272,11 @@ export class HintingPublicTreesDB extends PublicTreesDB {
// Use sequentialInsert for PublicDataTree and NullifierTree.
assert(treeId == MerkleTreeId.NOTE_HASH_TREE || treeId == MerkleTreeId.L1_TO_L2_MESSAGE_TREE);

if (leaves.length === 0) {
return;
// We need to process each leaf individually because we need the sibling path after insertion, to be able to constraint the insertion.
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/13380): This can be changed if the world state appendLeaves returns the sibling paths.
for (const leaf of leaves) {
await this.appendLeafInternal(treeId, leaf);
}

const beforeState = await this.getHintKey(treeId);

await super.appendLeaves<ID>(treeId, leaves);

const afterState = await this.getHintKey(treeId);

HintingPublicTreesDB.log.debug('[appendLeaves] Evolved tree state.');
HintingPublicTreesDB.logTreeChange(beforeState, afterState, treeId);

this.hints.appendLeavesHints.push(new AvmAppendLeavesHint(beforeState, afterState, treeId, leaves as Fr[]));
}

public override async createCheckpoint(): Promise<void> {
Expand Down Expand Up @@ -383,4 +374,25 @@ export class HintingPublicTreesDB extends PublicTreesDB {
`[${treeName}] Evolved tree state: ${beforeState.root}, ${beforeState.nextAvailableLeafIndex} -> ${afterState.root}, ${afterState.nextAvailableLeafIndex}.`,
);
}

private async appendLeafInternal<ID extends MerkleTreeId, N extends number>(
treeId: ID,
leaf: MerkleTreeLeafType<ID>,
): Promise<SiblingPath<N>> {
// Use sequentialInsert for PublicDataTree and NullifierTree.
assert(treeId == MerkleTreeId.NOTE_HASH_TREE || treeId == MerkleTreeId.L1_TO_L2_MESSAGE_TREE);

const beforeState = await this.getHintKey(treeId);

await super.appendLeaves<ID>(treeId, [leaf]);

const afterState = await this.getHintKey(treeId);

HintingPublicTreesDB.log.debug('[appendLeaves] Evolved tree state.');
HintingPublicTreesDB.logTreeChange(beforeState, afterState, treeId);

this.hints.appendLeavesHints.push(new AvmAppendLeavesHint(beforeState, afterState, treeId, [leaf as Fr]));

return await this.getSiblingPath<N>(treeId, BigInt(beforeState.nextAvailableLeafIndex));
}
}