diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp index 3f82c1b63eba..b9524a2edf27 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp @@ -22,6 +22,12 @@ class ContractDBInterface { virtual std::optional 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: @@ -45,7 +51,9 @@ class LowLevelMerkleDBInterface { insert_indexed_leaves_public_data_tree(const crypto::merkle_tree::PublicDataLeafValue& leaf_value) = 0; virtual world_state::SequentialInsertionResult insert_indexed_leaves_nullifier_tree(const crypto::merkle_tree::NullifierLeafValue& leaf_value) = 0; - virtual void append_leaves(world_state::MerkleTreeId tree_id, std::span leaves) = 0; + + virtual std::vector append_leaves(world_state::MerkleTreeId tree_id, + std::span leaves) = 0; virtual void create_checkpoint() = 0; virtual void commit_checkpoint() = 0; diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.cpp index 127ff06dd608..0c1345adf0b0 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.cpp @@ -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 { @@ -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. @@ -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(tree_id), + ", tree: ", + get_tree_name(tree_id), ", leaf_index: ", leaf_index, ")")); @@ -252,8 +271,8 @@ crypto::merkle_tree::GetLowIndexedLeafResponse HintedRawMerkleDB::get_low_indexe tree_info.root, ", size: ", tree_info.nextAvailableLeafIndex, - ", tree_id: ", - static_cast(tree_id), + ", tree: ", + get_tree_name(tree_id), ", value: ", value, ")")); @@ -507,31 +526,42 @@ void HintedRawMerkleDB::revert_checkpoint() checkpoint_action_counter++; } -void HintedRawMerkleDB::append_leaves(world_state::MerkleTreeId tree_id, std::span leaves) +std::vector HintedRawMerkleDB::append_leaves(world_state::MerkleTreeId tree_id, + std::span leaves) { - if (leaves.empty()) { - return; + std::vector 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(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(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; @@ -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 diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.hpp index 210d3baeaec5..5497b8987e2e 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.hpp @@ -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 insert_indexed_leaves_nullifier_tree(const crypto::merkle_tree::NullifierLeafValue& leaf_value) override; - void append_leaves(world_state::MerkleTreeId tree_id, std::span leaves) override; + std::vector append_leaves(world_state::MerkleTreeId tree_id, std::span leaves) override; void create_checkpoint() override; void commit_checkpoint() override; @@ -102,7 +102,9 @@ class HintedRawMerkleDB final : public LowLevelMerkleDBInterface { unordered_flat_map commit_checkpoint_hints; unordered_flat_map 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 diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp index 571f60df04d7..638c51598a6a 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp @@ -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 leaves), (override)); + MOCK_METHOD(std::vector, + append_leaves, + (world_state::MerkleTreeId tree_id, std::span leaves), + (override)); MOCK_METHOD(void, create_checkpoint, (), (override)); MOCK_METHOD(void, commit_checkpoint, (), (override)); MOCK_METHOD(void, revert_checkpoint, (), (override)); diff --git a/yarn-project/simulator/src/public/hinting_db_sources.ts b/yarn-project/simulator/src/public/hinting_db_sources.ts index d3732889ffd1..4d21fbb4ce67 100644 --- a/yarn-project/simulator/src/public/hinting_db_sources.ts +++ b/yarn-project/simulator/src/public/hinting_db_sources.ts @@ -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(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 { @@ -383,4 +374,25 @@ export class HintingPublicTreesDB extends PublicTreesDB { `[${treeName}] Evolved tree state: ${beforeState.root}, ${beforeState.nextAvailableLeafIndex} -> ${afterState.root}, ${afterState.nextAvailableLeafIndex}.`, ); } + + private async appendLeafInternal( + treeId: ID, + leaf: MerkleTreeLeafType, + ): Promise> { + // 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(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(treeId, BigInt(beforeState.nextAvailableLeafIndex)); + } }