diff --git a/crates/starknet_committer/src/db.rs b/crates/starknet_committer/src/db.rs index 97722798166..6f60b0ded6a 100644 --- a/crates/starknet_committer/src/db.rs +++ b/crates/starknet_committer/src/db.rs @@ -4,3 +4,4 @@ pub mod external_test_utils; pub mod facts_db; pub mod forest_trait; pub mod index_db; +pub mod trie_traversal; diff --git a/crates/starknet_committer/src/db/db_layout.rs b/crates/starknet_committer/src/db/db_layout.rs index 1a0544e10c4..1a42ef4f8b7 100644 --- a/crates/starknet_committer/src/db/db_layout.rs +++ b/crates/starknet_committer/src/db/db_layout.rs @@ -1,13 +1,26 @@ +use starknet_api::hash::HashOutput; use starknet_patricia::patricia_merkle_tree::filled_tree::node::FilledNode; use starknet_patricia::patricia_merkle_tree::node_data::leaf::Leaf; +use starknet_patricia::patricia_merkle_tree::traversal::SubTreeTrait; +use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices}; +use starknet_patricia_storage::db_object::HasStaticPrefix; use starknet_patricia_storage::errors::DeserializationError; use starknet_patricia_storage::storage_trait::DbValue; -pub trait NodeLayout { +use crate::db::index_db::leaves::TrieType; + +pub trait NodeLayout<'a, L: Leaf> { type ChildData: Copy; type DeserializationContext; + type SubTree: SubTreeTrait<'a, NodeData = Self::ChildData, NodeContext = Self::DeserializationContext>; fn deserialize_node( value: &DbValue, deserialize_context: &Self::DeserializationContext, ) -> Result, DeserializationError>; + fn create_subtree( + sorted_leaf_indices: SortedLeafIndices<'a>, + root_index: NodeIndex, + root_hash: HashOutput, + ) -> Self::SubTree; + fn generate_key_context(trie_type: TrieType) -> ::KeyContext; } diff --git a/crates/starknet_committer/src/db/external_test_utils.rs b/crates/starknet_committer/src/db/external_test_utils.rs index 5b97bfb0c79..a840a25b81c 100644 --- a/crates/starknet_committer/src/db/external_test_utils.rs +++ b/crates/starknet_committer/src/db/external_test_utils.rs @@ -18,7 +18,8 @@ use starknet_patricia::patricia_merkle_tree::updated_skeleton_tree::tree::{ use starknet_patricia_storage::db_object::{EmptyKeyContext, HasStaticPrefix}; use starknet_patricia_storage::map_storage::MapStorage; -use crate::db::facts_db::create_facts_tree::create_original_skeleton_tree; +use crate::db::facts_db::db::FactsNodeLayout; +use crate::db::trie_traversal::create_original_skeleton_tree; // TODO(Ariel, 14/12/2025): make this generic over the layout. pub async fn tree_computation_flow( @@ -33,7 +34,7 @@ where { let mut sorted_leaf_indices: Vec = leaf_modifications.keys().copied().collect(); let sorted_leaf_indices = SortedLeafIndices::new(&mut sorted_leaf_indices); - let mut original_skeleton = create_original_skeleton_tree( + let mut original_skeleton = create_original_skeleton_tree::( storage, root_hash, sorted_leaf_indices, diff --git a/crates/starknet_committer/src/db/facts_db/create_facts_tree.rs b/crates/starknet_committer/src/db/facts_db/create_facts_tree.rs index d8b2eb4a3e8..c3451c03cc7 100644 --- a/crates/starknet_committer/src/db/facts_db/create_facts_tree.rs +++ b/crates/starknet_committer/src/db/facts_db/create_facts_tree.rs @@ -1,32 +1,18 @@ -use std::borrow::Borrow; use std::collections::HashMap; -use std::fmt::Debug; use starknet_api::hash::HashOutput; -use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{ - BinaryData, - EdgeData, - NodeData, -}; use starknet_patricia::patricia_merkle_tree::node_data::leaf::{Leaf, LeafModifications}; -use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::config::{ - NoCompareOriginalSkeletonTrieConfig, - OriginalSkeletonTreeConfig, -}; -use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::node::OriginalSkeletonNode; +use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::config::OriginalSkeletonTreeConfig; use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::tree::{ OriginalSkeletonTreeImpl, OriginalSkeletonTreeResult, }; use starknet_patricia::patricia_merkle_tree::traversal::SubTreeTrait; use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices}; -use starknet_patricia_storage::db_object::HasStaticPrefix; +use starknet_patricia_storage::db_object::{EmptyKeyContext, HasStaticPrefix}; use starknet_patricia_storage::storage_trait::Storage; -use tracing::warn; -use crate::db::db_layout::NodeLayout; use crate::db::facts_db::db::FactsNodeLayout; -use crate::db::facts_db::traversal::get_roots_from_storage; use crate::db::facts_db::types::FactsSubTree; use crate::db::trie_traversal::fetch_nodes; use crate::patricia_merkle_tree::tree::OriginalSkeletonTrieDontCompareConfig; @@ -35,188 +21,15 @@ use crate::patricia_merkle_tree::tree::OriginalSkeletonTrieDontCompareConfig; #[path = "create_facts_tree_test.rs"] pub mod create_facts_tree_test; -/// Logs out a warning of a trivial modification. -macro_rules! log_trivial_modification { - ($index:expr, $value:expr) => { - warn!("Encountered a trivial modification at index {:?}, with value {:?}", $index, $value); - }; -} - -/// Fetches the Patricia witnesses, required to build the original skeleton tree from storage. -/// Given a list of subtrees, traverses towards their leaves and fetches all non-empty, -/// unmodified nodes. If `compare_modified_leaves` is set, function logs out a warning when -/// encountering a trivial modification. Fills the previous leaf values if it is not none. -async fn fetch_nodes<'a, L, Layout, SubTree>( - skeleton_tree: &mut OriginalSkeletonTreeImpl<'a>, - subtrees: Vec, - storage: &mut impl Storage, - leaf_modifications: &LeafModifications, - config: &impl OriginalSkeletonTreeConfig, - mut previous_leaves: Option<&mut HashMap>, - key_context: &::KeyContext, -) -> OriginalSkeletonTreeResult<()> -where - L: Leaf, - Layout: NodeLayout, - SubTree: SubTreeTrait<'a, NodeData = Layout::ChildData, NodeContext = Layout::DeserializationContext>, -{ - let mut current_subtrees = subtrees; - let mut next_subtrees = Vec::new(); - while !current_subtrees.is_empty() { - let should_fetch_modified_leaves = - config.compare_modified_leaves() || previous_leaves.is_some(); - let filled_roots = - get_roots_from_storage::(¤t_subtrees, storage, key_context).await?; - for (filled_root, subtree) in filled_roots.into_iter().zip(current_subtrees.iter()) { - match filled_root.data { - // Binary node. - NodeData::Binary(BinaryData { left_data, right_data }) => { - if subtree.is_unmodified() { - skeleton_tree.nodes.insert( - subtree.get_root_index(), - OriginalSkeletonNode::UnmodifiedSubTree(filled_root.hash), - ); - continue; - } - skeleton_tree - .nodes - .insert(subtree.get_root_index(), OriginalSkeletonNode::Binary); - let (left_subtree, right_subtree) = - subtree.get_children_subtrees(left_data, right_data); - - handle_subtree( - skeleton_tree, - &mut next_subtrees, - left_subtree, - left_data, - should_fetch_modified_leaves, - ); - handle_subtree( - skeleton_tree, - &mut next_subtrees, - right_subtree, - right_data, - should_fetch_modified_leaves, - ) - } - // Edge node. - NodeData::Edge(EdgeData { bottom_data, path_to_bottom }) => { - skeleton_tree.nodes.insert( - subtree.get_root_index(), - OriginalSkeletonNode::Edge(path_to_bottom), - ); - - // Parse bottom. - let (bottom_subtree, previously_empty_leaves_indices) = - subtree.get_bottom_subtree(&path_to_bottom, bottom_data); - - if subtree.is_unmodified() { - if let Some(unmodified_child_hash) = - SubTree::unmodified_child_hash(bottom_data) - { - skeleton_tree.nodes.insert( - path_to_bottom.bottom_index(subtree.get_root_index()), - OriginalSkeletonNode::UnmodifiedSubTree(unmodified_child_hash), - ); - } else { - // With index layout we need to traverse an unmodified bottom node. - next_subtrees.push(bottom_subtree) - } - continue; - } - - if let Some(ref mut leaves) = previous_leaves { - leaves.extend( - previously_empty_leaves_indices - .iter() - .map(|idx| (**idx, L::default())) - .collect::>(), - ); - } - log_warning_for_empty_leaves( - &previously_empty_leaves_indices, - leaf_modifications, - config, - )?; - - handle_subtree( - skeleton_tree, - &mut next_subtrees, - bottom_subtree, - bottom_data, - should_fetch_modified_leaves, - ); - } - // Leaf node. - NodeData::Leaf(previous_leaf) => { - if subtree.is_unmodified() { - skeleton_tree.nodes.insert( - subtree.get_root_index(), - OriginalSkeletonNode::UnmodifiedSubTree(filled_root.hash), - ); - } else { - let root_index = subtree.get_root_index(); - // Modified leaf. - if config.compare_modified_leaves() - && L::compare(leaf_modifications, &root_index, &previous_leaf)? - { - log_trivial_modification!(root_index, previous_leaf); - } - // If previous values of modified leaves are requested, add this leaf. - if let Some(ref mut leaves) = previous_leaves { - leaves.insert(root_index, previous_leaf); - } - } - } - } - } - current_subtrees = next_subtrees; - next_subtrees = Vec::new(); - } - Ok(()) -} - -pub async fn create_original_skeleton_tree<'a, L: Leaf>( - storage: &mut impl Storage, - root_hash: HashOutput, - sorted_leaf_indices: SortedLeafIndices<'a>, - config: &impl OriginalSkeletonTreeConfig, - leaf_modifications: &LeafModifications, - key_context: &::KeyContext, -) -> OriginalSkeletonTreeResult> { - if sorted_leaf_indices.is_empty() { - return Ok(OriginalSkeletonTreeImpl::create_unmodified(root_hash)); - } - if root_hash == HashOutput::ROOT_OF_EMPTY_TREE { - log_warning_for_empty_leaves( - sorted_leaf_indices.get_indices(), - leaf_modifications, - config, - )?; - return Ok(OriginalSkeletonTreeImpl::create_empty(sorted_leaf_indices)); - } - let main_subtree = FactsSubTree::create(sorted_leaf_indices, NodeIndex::ROOT, root_hash); - let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes: HashMap::new(), sorted_leaf_indices }; - fetch_nodes::>( - &mut skeleton_tree, - vec![main_subtree], - storage, - leaf_modifications, - config, - None, - key_context, - ) - .await?; - Ok(skeleton_tree) -} - -pub async fn create_original_skeleton_tree_and_get_previous_leaves<'a, L: Leaf>( +pub async fn create_original_skeleton_tree_and_get_previous_leaves< + 'a, + L: Leaf + HasStaticPrefix, +>( storage: &mut impl Storage, root_hash: HashOutput, sorted_leaf_indices: SortedLeafIndices<'a>, leaf_modifications: &LeafModifications, - config: &impl OriginalSkeletonTreeConfig, - key_context: &::KeyContext, + config: &impl OriginalSkeletonTreeConfig, ) -> OriginalSkeletonTreeResult<(OriginalSkeletonTreeImpl<'a>, HashMap)> { if sorted_leaf_indices.is_empty() { let unmodified = OriginalSkeletonTreeImpl::create_unmodified(root_hash); @@ -231,14 +44,14 @@ pub async fn create_original_skeleton_tree_and_get_previous_leaves<'a, L: Leaf>( let main_subtree = FactsSubTree::create(sorted_leaf_indices, NodeIndex::ROOT, root_hash); let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes: HashMap::new(), sorted_leaf_indices }; let mut leaves = HashMap::new(); - fetch_nodes::>( + fetch_nodes::( &mut skeleton_tree, vec![main_subtree], storage, leaf_modifications, config, Some(&mut leaves), - key_context, + &EmptyKeyContext, ) .await?; Ok((skeleton_tree, leaves)) @@ -247,13 +60,12 @@ pub async fn create_original_skeleton_tree_and_get_previous_leaves<'a, L: Leaf>( /// Prepares the OS inputs by fetching paths to the given leaves (i.e. their induced Skeleton tree). /// Note that ATM, the Rust committer does not manage history and is not used for storage proofs; /// Thus, this function assumes facts layout. -pub async fn get_leaves<'a, L: Leaf>( +pub async fn get_leaves<'a, L: Leaf + HasStaticPrefix>( storage: &mut impl Storage, root_hash: HashOutput, sorted_leaf_indices: SortedLeafIndices<'a>, - key_context: &::KeyContext, ) -> OriginalSkeletonTreeResult> { - let config = NoCompareOriginalSkeletonTrieConfig::default(); + let config = OriginalSkeletonTrieDontCompareConfig; let leaf_modifications = LeafModifications::new(); let (_, previous_leaves) = create_original_skeleton_tree_and_get_previous_leaves( storage, @@ -261,65 +73,7 @@ pub async fn get_leaves<'a, L: Leaf>( sorted_leaf_indices, &leaf_modifications, &config, - key_context, ) .await?; Ok(previous_leaves) } - -/// Handles a subtree referred by an edge or a binary node. Decides whether we deserialize the -/// referred subtree or not. -fn handle_subtree<'a, SubTree: SubTreeTrait<'a>>( - skeleton_tree: &mut OriginalSkeletonTreeImpl<'a>, - next_subtrees: &mut Vec, - subtree: SubTree, - subtree_data: SubTree::NodeData, - should_fetch_modified_leaves: bool, -) { - let is_leaf = subtree.is_leaf(); - let is_unmodified = subtree.is_unmodified(); - - // 1. Internal node → always traverse. - if !is_leaf { - next_subtrees.push(subtree); - return; - } - - // 2. Modified leaf. - if !is_unmodified { - if should_fetch_modified_leaves { - next_subtrees.push(subtree); - } - return; - } - - // 3. Unmodified leaf sibling. - if let Some(unmodified_child_hash) = SubTree::unmodified_child_hash(subtree_data) { - skeleton_tree.nodes.insert( - subtree.get_root_index(), - OriginalSkeletonNode::UnmodifiedSubTree(unmodified_child_hash), - ); - } else { - next_subtrees.push(subtree); - } -} - -/// Given leaf indices that were previously empty leaves, logs out a warning for trivial -/// modification if a leaf is modified to an empty leaf. -/// If this check is suppressed by configuration, does nothing. -fn log_warning_for_empty_leaves + Debug>( - leaf_indices: &[T], - leaf_modifications: &LeafModifications, - config: &impl OriginalSkeletonTreeConfig, -) -> OriginalSkeletonTreeResult<()> { - if !config.compare_modified_leaves() { - return Ok(()); - } - let empty_leaf = L::default(); - for leaf_index in leaf_indices { - if L::compare(leaf_modifications, leaf_index.borrow(), &empty_leaf)? { - log_trivial_modification!(leaf_index, empty_leaf); - } - } - Ok(()) -} diff --git a/crates/starknet_committer/src/db/facts_db/create_facts_tree_test.rs b/crates/starknet_committer/src/db/facts_db/create_facts_tree_test.rs index 8748c7b46ab..720c6d55abd 100644 --- a/crates/starknet_committer/src/db/facts_db/create_facts_tree_test.rs +++ b/crates/starknet_committer/src/db/facts_db/create_facts_tree_test.rs @@ -22,7 +22,8 @@ use starknet_patricia_storage::map_storage::MapStorage; use starknet_patricia_storage::storage_trait::{DbHashMap, DbKey, DbValue}; use starknet_types_core::felt::Felt; -use crate::db::facts_db::create_facts_tree::create_original_skeleton_tree; +use crate::db::facts_db::db::FactsNodeLayout; +use crate::db::trie_traversal::create_original_skeleton_tree; use crate::patricia_merkle_tree::tree::OriginalSkeletonTrieConfig; #[tokio::test] @@ -211,7 +212,7 @@ async fn test_create_tree( let config = OriginalSkeletonTrieConfig::new(compare_modified_leaves); let mut sorted_leaf_indices: Vec = leaf_modifications.keys().copied().collect(); let sorted_leaf_indices = SortedLeafIndices::new(&mut sorted_leaf_indices); - let skeleton_tree = create_original_skeleton_tree( + let skeleton_tree = create_original_skeleton_tree::( &mut storage, root_hash, sorted_leaf_indices, diff --git a/crates/starknet_committer/src/db/facts_db/db.rs b/crates/starknet_committer/src/db/facts_db/db.rs index 708d48ed97b..f4db0e9843c 100644 --- a/crates/starknet_committer/src/db/facts_db/db.rs +++ b/crates/starknet_committer/src/db/facts_db/db.rs @@ -7,9 +7,9 @@ use starknet_patricia::patricia_merkle_tree::filled_tree::node::FactDbFilledNode use starknet_patricia::patricia_merkle_tree::filled_tree::node_serde::FactNodeDeserializationContext; use starknet_patricia::patricia_merkle_tree::filled_tree::tree::FilledTree; use starknet_patricia::patricia_merkle_tree::node_data::leaf::{Leaf, LeafModifications}; -use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::tree::OriginalSkeletonTreeImpl; +use starknet_patricia::patricia_merkle_tree::traversal::SubTreeTrait; use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices}; -use starknet_patricia_storage::db_object::{DBObject, EmptyKeyContext}; +use starknet_patricia_storage::db_object::{DBObject, EmptyKeyContext, HasStaticPrefix}; use starknet_patricia_storage::errors::DeserializationError; use starknet_patricia_storage::map_storage::MapStorage; use starknet_patricia_storage::storage_trait::{ @@ -21,41 +21,46 @@ use starknet_patricia_storage::storage_trait::{ Storage, }; -use crate::block_committer::input::{ - contract_address_into_node_index, - Config, - ConfigImpl, - StarknetStorageValue, -}; +use crate::block_committer::input::{ConfigImpl, StarknetStorageValue}; use crate::db::db_layout::NodeLayout; -use crate::db::facts_db::create_facts_tree::{ - create_original_skeleton_tree, - create_original_skeleton_tree_and_get_previous_leaves, -}; +use crate::db::facts_db::types::FactsSubTree; use crate::db::forest_trait::{ForestMetadata, ForestMetadataType, ForestReader, ForestWriter}; +use crate::db::index_db::leaves::TrieType; +use crate::db::trie_traversal::{create_classes_trie, create_contracts_trie, create_storage_tries}; use crate::forest::filled_forest::FilledForest; -use crate::forest::forest_errors::{ForestError, ForestResult}; +use crate::forest::forest_errors::ForestResult; use crate::forest::original_skeleton_forest::{ForestSortedIndices, OriginalSkeletonForest}; use crate::patricia_merkle_tree::leaf::leaf_impl::ContractState; -use crate::patricia_merkle_tree::tree::{ - OriginalSkeletonClassesTrieConfig, - OriginalSkeletonContractsTrieConfig, - OriginalSkeletonStorageTrieConfig, -}; use crate::patricia_merkle_tree::types::CompiledClassHash; pub struct FactsNodeLayout {} -impl NodeLayout for FactsNodeLayout { +impl<'a, L: Leaf> NodeLayout<'a, L> for FactsNodeLayout +where + L: HasStaticPrefix, +{ type ChildData = HashOutput; type DeserializationContext = FactNodeDeserializationContext; + type SubTree = FactsSubTree<'a>; + + fn create_subtree( + sorted_leaf_indices: SortedLeafIndices<'a>, + root_index: NodeIndex, + root_hash: HashOutput, + ) -> Self::SubTree { + FactsSubTree::create(sorted_leaf_indices, root_index, root_hash) + } fn deserialize_node( value: &DbValue, deserialize_context: &Self::DeserializationContext, ) -> Result, DeserializationError> { DBObject::deserialize(value, deserialize_context) } + fn generate_key_context(_trie_type: TrieType) -> ::KeyContext { + EmptyKeyContext + } } + pub struct FactsDb { // TODO(Yoav): Define StorageStats trait and impl it here. Then, make the storage field // private. @@ -69,76 +74,6 @@ impl FactsDb { pub fn new(storage: S) -> Self { Self { storage } } - - /// Creates the contracts trie original skeleton. - /// Also returns the previous contracts state of the modified contracts. - async fn create_contracts_trie<'a>( - &mut self, - contracts_trie_root_hash: HashOutput, - contracts_trie_sorted_indices: SortedLeafIndices<'a>, - ) -> ForestResult<(OriginalSkeletonTreeImpl<'a>, HashMap)> { - Ok(create_original_skeleton_tree_and_get_previous_leaves( - &mut self.storage, - contracts_trie_root_hash, - contracts_trie_sorted_indices, - &HashMap::new(), - &OriginalSkeletonContractsTrieConfig::new(), - &EmptyKeyContext, - ) - .await?) - } - - async fn create_storage_tries<'a>( - &mut self, - actual_storage_updates: &HashMap>, - original_contracts_trie_leaves: &HashMap, - config: &impl Config, - storage_tries_sorted_indices: &HashMap>, - ) -> ForestResult>> { - let mut storage_tries = HashMap::new(); - for (address, updates) in actual_storage_updates { - let sorted_leaf_indices = storage_tries_sorted_indices - .get(address) - .ok_or(ForestError::MissingSortedLeafIndices(*address))?; - let contract_state = original_contracts_trie_leaves - .get(&contract_address_into_node_index(address)) - .ok_or(ForestError::MissingContractCurrentState(*address))?; - let config = - OriginalSkeletonStorageTrieConfig::new(config.warn_on_trivial_modifications()); - - let original_skeleton = create_original_skeleton_tree( - &mut self.storage, - contract_state.storage_root_hash, - *sorted_leaf_indices, - &config, - updates, - &EmptyKeyContext, - ) - .await?; - storage_tries.insert(*address, original_skeleton); - } - Ok(storage_tries) - } - - async fn create_classes_trie<'a>( - &mut self, - actual_classes_updates: &LeafModifications, - classes_trie_root_hash: HashOutput, - config: &impl Config, - contracts_trie_sorted_indices: SortedLeafIndices<'a>, - ) -> ForestResult> { - let config = OriginalSkeletonClassesTrieConfig::new(config.warn_on_trivial_modifications()); - - Ok(create_original_skeleton_tree( - &mut self.storage, - classes_trie_root_hash, - contracts_trie_sorted_indices, - &config, - actual_classes_updates, - &EmptyKeyContext, - ) - .await?) - } } impl FactsDb { @@ -160,28 +95,29 @@ impl<'a, S: Storage> ForestReader<'a> for FactsDb { forest_sorted_indices: &'a ForestSortedIndices<'a>, config: ConfigImpl, ) -> ForestResult<(OriginalSkeletonForest<'a>, HashMap)> { - let (contracts_trie, original_contracts_trie_leaves) = self - .create_contracts_trie( + let (contracts_trie, original_contracts_trie_leaves) = + create_contracts_trie::( + &mut self.storage, contracts_trie_root_hash, forest_sorted_indices.contracts_trie_sorted_indices, ) .await?; - let storage_tries = self - .create_storage_tries( - storage_updates, - &original_contracts_trie_leaves, - &config, - &forest_sorted_indices.storage_tries_sorted_indices, - ) - .await?; - let classes_trie = self - .create_classes_trie( - classes_updates, - classes_trie_root_hash, - &config, - forest_sorted_indices.classes_trie_sorted_indices, - ) - .await?; + let storage_tries = create_storage_tries::( + &mut self.storage, + storage_updates, + &original_contracts_trie_leaves, + &config, + &forest_sorted_indices.storage_tries_sorted_indices, + ) + .await?; + let classes_trie = create_classes_trie::( + &mut self.storage, + classes_updates, + classes_trie_root_hash, + &config, + forest_sorted_indices.classes_trie_sorted_indices, + ) + .await?; Ok(( OriginalSkeletonForest { classes_trie, contracts_trie, storage_tries }, diff --git a/crates/starknet_committer/src/db/facts_db/traversal.rs b/crates/starknet_committer/src/db/facts_db/traversal.rs index f42853f7769..39d31d53a84 100644 --- a/crates/starknet_committer/src/db/facts_db/traversal.rs +++ b/crates/starknet_committer/src/db/facts_db/traversal.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use starknet_api::hash::HashOutput; -use starknet_patricia::patricia_merkle_tree::filled_tree::node::FilledNode; use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{ NodeData, Preimage, @@ -10,55 +9,26 @@ use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{ use starknet_patricia::patricia_merkle_tree::node_data::leaf::Leaf; use starknet_patricia::patricia_merkle_tree::traversal::{SubTreeTrait, TraversalResult}; use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices}; -use starknet_patricia_storage::db_object::HasStaticPrefix; -use starknet_patricia_storage::errors::StorageError; -use starknet_patricia_storage::storage_trait::{create_db_key, DbKey, Storage}; +use starknet_patricia_storage::db_object::{EmptyKeyContext, HasStaticPrefix}; +use starknet_patricia_storage::storage_trait::Storage; -use crate::db::db_layout::NodeLayout; use crate::db::facts_db::db::FactsNodeLayout; use crate::db::facts_db::types::FactsSubTree; +use crate::db::trie_traversal::get_roots_from_storage; #[cfg(test)] #[path = "traversal_test.rs"] pub mod traversal_test; -// TODO(Aviv, 17/07/2024): Split between storage prefix implementation and function logic. -pub async fn get_roots_from_storage<'a, L: Leaf, Layout: NodeLayout>( - subtrees: &[impl SubTreeTrait< - 'a, - NodeData = Layout::ChildData, - NodeContext = Layout::DeserializationContext, - >], - storage: &mut impl Storage, - key_context: &::KeyContext, -) -> TraversalResult>> { - let mut subtrees_roots = vec![]; - let db_keys: Vec = subtrees - .iter() - .map(|subtree| { - create_db_key(subtree.get_root_prefix::(key_context), &subtree.get_root_suffix()) - }) - .collect(); - - let db_vals = storage.mget(&db_keys.iter().collect::>()).await?; - for ((subtree, optional_val), db_key) in subtrees.iter().zip(db_vals.iter()).zip(db_keys) { - let Some(val) = optional_val else { Err(StorageError::MissingKey(db_key))? }; - let filled_node = Layout::deserialize_node(val, &subtree.get_root_context())?; - subtrees_roots.push(filled_node); - } - Ok(subtrees_roots) -} - /// Returns the Patricia inner nodes ([PreimageMap]) in the paths to the given `leaf_indices` in the /// given tree according to the `root_hash`. /// If `leaves` is not `None`, it also fetches the modified leaves and inserts them into the /// provided map. -pub async fn fetch_patricia_paths( +pub async fn fetch_patricia_paths>( storage: &mut impl Storage, root_hash: HashOutput, sorted_leaf_indices: SortedLeafIndices<'_>, leaves: Option<&mut HashMap>, - key_context: &::KeyContext, ) -> TraversalResult { let mut witnesses = PreimageMap::new(); @@ -68,14 +38,7 @@ pub async fn fetch_patricia_paths( let main_subtree = FactsSubTree::create(sorted_leaf_indices, NodeIndex::ROOT, root_hash); - fetch_patricia_paths_inner::( - storage, - vec![main_subtree], - &mut witnesses, - leaves, - key_context, - ) - .await?; + fetch_patricia_paths_inner::(storage, vec![main_subtree], &mut witnesses, leaves).await?; Ok(witnesses) } @@ -86,19 +49,24 @@ pub async fn fetch_patricia_paths( /// inner nodes in their paths. /// If `leaves` is not `None`, it also fetches the modified leaves and inserts them into the /// provided map. -pub(crate) async fn fetch_patricia_paths_inner<'a, L: Leaf>( +pub(crate) async fn fetch_patricia_paths_inner< + 'a, + L: Leaf + HasStaticPrefix, +>( storage: &mut impl Storage, subtrees: Vec>, witnesses: &mut PreimageMap, mut leaves: Option<&mut HashMap>, - key_context: &::KeyContext, ) -> TraversalResult<()> { let mut current_subtrees = subtrees; let mut next_subtrees = Vec::new(); while !current_subtrees.is_empty() { - let filled_roots = - get_roots_from_storage::(¤t_subtrees, storage, key_context) - .await?; + let filled_roots = get_roots_from_storage::( + ¤t_subtrees, + storage, + &EmptyKeyContext, + ) + .await?; for (filled_root, subtree) in filled_roots.into_iter().zip(current_subtrees.iter()) { // Always insert root. // No need to insert an unmodified node (which is not the root), because its parent is diff --git a/crates/starknet_committer/src/db/facts_db/traversal_test.rs b/crates/starknet_committer/src/db/facts_db/traversal_test.rs index 62f67fd19a4..e056b1f687f 100644 --- a/crates/starknet_committer/src/db/facts_db/traversal_test.rs +++ b/crates/starknet_committer/src/db/facts_db/traversal_test.rs @@ -27,7 +27,6 @@ use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{ }; use starknet_patricia::patricia_merkle_tree::traversal::SubTreeTrait; use starknet_patricia::patricia_merkle_tree::types::{SortedLeafIndices, SubTreeHeight}; -use starknet_patricia_storage::db_object::EmptyKeyContext; use starknet_patricia_storage::map_storage::MapStorage; use starknet_patricia_storage::storage_trait::{DbHashMap, DbKey, DbValue}; use starknet_types_core::felt::Felt; @@ -88,7 +87,6 @@ async fn test_fetch_patricia_paths_inner_impl( vec![main_subtree], &mut nodes, Some(&mut fetched_leaves), - &EmptyKeyContext, ) .await .unwrap(); diff --git a/crates/starknet_committer/src/db/index_db/leaves.rs b/crates/starknet_committer/src/db/index_db/leaves.rs index 142fcc55e42..4204103736c 100644 --- a/crates/starknet_committer/src/db/index_db/leaves.rs +++ b/crates/starknet_committer/src/db/index_db/leaves.rs @@ -2,7 +2,11 @@ use starknet_api::core::{ClassHash, Nonce}; use starknet_api::hash::HashOutput; use starknet_patricia::patricia_merkle_tree::node_data::errors::LeafResult; use starknet_patricia::patricia_merkle_tree::node_data::leaf::Leaf; -use starknet_patricia_storage::db_object::{DBObject, HasStaticPrefix}; +use starknet_patricia_storage::db_object::{ + DBObject, + EmptyDeserializationContext, + HasStaticPrefix, +}; use starknet_patricia_storage::errors::DeserializationError; use starknet_patricia_storage::storage_trait::{DbKeyPrefix, DbValue}; use starknet_types_core::felt::Felt; @@ -129,7 +133,7 @@ impl std::fmt::Display for IndexLayoutLeafDeserializationError { impl std::error::Error for IndexLayoutLeafDeserializationError {} impl DBObject for IndexLayoutContractState { - type DeserializeContext = (); + type DeserializeContext = EmptyDeserializationContext; fn serialize(&self) -> DbValue { let mut buffer = Vec::new(); self.0.class_hash.0.serialize(&mut buffer).unwrap(); @@ -163,7 +167,7 @@ impl DBObject for IndexLayoutContractState { } impl DBObject for IndexLayoutCompiledClassHash { - type DeserializeContext = (); + type DeserializeContext = EmptyDeserializationContext; fn serialize(&self) -> DbValue { let mut buffer = Vec::new(); self.0.0.serialize(&mut buffer).unwrap(); @@ -183,7 +187,7 @@ impl DBObject for IndexLayoutCompiledClassHash { } impl DBObject for IndexLayoutStarknetStorageValue { - type DeserializeContext = (); + type DeserializeContext = EmptyDeserializationContext; fn serialize(&self) -> DbValue { let mut buffer = Vec::new(); self.0.0.serialize(&mut buffer).unwrap(); diff --git a/crates/starknet_committer/src/db/index_db/types.rs b/crates/starknet_committer/src/db/index_db/types.rs index a54f2890941..a5fcf39f240 100644 --- a/crates/starknet_committer/src/db/index_db/types.rs +++ b/crates/starknet_committer/src/db/index_db/types.rs @@ -13,7 +13,11 @@ use starknet_patricia::patricia_merkle_tree::node_data::leaf::Leaf; use starknet_patricia::patricia_merkle_tree::traversal::SubTreeTrait; use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices}; use starknet_patricia::patricia_merkle_tree::updated_skeleton_tree::hash_function::TreeHashFunction; -use starknet_patricia_storage::db_object::{DBObject, HasStaticPrefix}; +use starknet_patricia_storage::db_object::{ + DBObject, + EmptyDeserializationContext, + HasStaticPrefix, +}; use starknet_patricia_storage::errors::DeserializationError; use starknet_patricia_storage::storage_trait::{DbKeyPrefix, DbValue}; use starknet_types_core::felt::Felt; @@ -63,7 +67,7 @@ where deserialize_context: &Self::DeserializeContext, ) -> Result { if deserialize_context.is_leaf { - let leaf = L::deserialize(value, &())?; + let leaf = L::deserialize(value, &EmptyDeserializationContext)?; let hash = TreeHashFunctionImpl::compute_leaf_hash(&leaf); Ok(IndexFilledNode(FilledNode { hash, data: NodeData::Leaf(leaf) })) } else if value.0.len() == INDEX_LAYOUT_BINARY_BYTES { @@ -101,37 +105,40 @@ pub struct IndexLayoutSubTree<'a> { } impl<'a> SubTreeTrait<'a> for IndexLayoutSubTree<'a> { - type ChildData = (); + type NodeData = (); type NodeContext = IndexNodeContext; - fn create_child( + fn create( sorted_leaf_indices: SortedLeafIndices<'a>, root_index: NodeIndex, - _child_data: Self::ChildData, + _child_data: Self::NodeData, ) -> Self { Self { sorted_leaf_indices, root_index } } + fn get_root_index(&self) -> NodeIndex { self.root_index } + fn get_sorted_leaf_indices(&self) -> &SortedLeafIndices<'a> { &self.sorted_leaf_indices } - fn should_traverse_unmodified_children() -> bool { - true - } - fn unmodified_child_hash(_data: Self::ChildData) -> Option { + + fn unmodified_child_hash(_data: Self::NodeData) -> Option { None } + fn get_root_context(&self) -> Self::NodeContext { Self::NodeContext { is_leaf: self.is_leaf() } } + fn get_root_prefix( &self, _key_context: &::KeyContext, ) -> DbKeyPrefix { L::get_static_prefix(_key_context) } + fn get_root_suffix(&self) -> Vec { self.root_index.0.to_be_bytes().to_vec() } diff --git a/crates/starknet_committer/src/db/trie_traversal.rs b/crates/starknet_committer/src/db/trie_traversal.rs new file mode 100644 index 00000000000..b844c0e4b14 --- /dev/null +++ b/crates/starknet_committer/src/db/trie_traversal.rs @@ -0,0 +1,393 @@ +use std::borrow::Borrow; +use std::collections::HashMap; +use std::fmt::Debug; + +use starknet_api::core::ContractAddress; +use starknet_api::hash::HashOutput; +use starknet_patricia::patricia_merkle_tree::filled_tree::node::FilledNode; +use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{ + BinaryData, + EdgeData, + NodeData, +}; +use starknet_patricia::patricia_merkle_tree::node_data::leaf::{Leaf, LeafModifications}; +use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::config::OriginalSkeletonTreeConfig; +use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::node::OriginalSkeletonNode; +use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::tree::{ + OriginalSkeletonTreeImpl, + OriginalSkeletonTreeResult, +}; +use starknet_patricia::patricia_merkle_tree::traversal::{SubTreeTrait, TraversalResult}; +use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices}; +use starknet_patricia_storage::db_object::HasStaticPrefix; +use starknet_patricia_storage::errors::StorageError; +use starknet_patricia_storage::storage_trait::{create_db_key, DbKey, Storage}; +use tracing::warn; + +use crate::block_committer::input::{ + contract_address_into_node_index, + Config, + StarknetStorageValue, +}; +use crate::db::db_layout::NodeLayout; +use crate::db::index_db::leaves::TrieType; +use crate::forest::forest_errors::{ForestError, ForestResult}; +use crate::patricia_merkle_tree::leaf::leaf_impl::ContractState; +use crate::patricia_merkle_tree::tree::{ + OriginalSkeletonTrieConfig, + OriginalSkeletonTrieDontCompareConfig, +}; +use crate::patricia_merkle_tree::types::CompiledClassHash; + +/// Logs out a warning of a trivial modification. +macro_rules! log_trivial_modification { + ($index:expr, $value:expr) => { + warn!("Encountered a trivial modification at index {:?}, with value {:?}", $index, $value); + }; +} + +/// Fetches the Patricia witnesses, required to build the original skeleton tree from storage. +/// Given a list of subtrees, traverses towards their leaves and fetches all non-empty, +/// unmodified nodes. If `compare_modified_leaves` is set, function logs out a warning when +/// encountering a trivial modification. Fills the previous leaf values if it is not none. +pub(crate) async fn fetch_nodes<'a, L, Layout>( + skeleton_tree: &mut OriginalSkeletonTreeImpl<'a>, + subtrees: Vec, + storage: &mut impl Storage, + leaf_modifications: &LeafModifications, + config: &impl OriginalSkeletonTreeConfig, + mut previous_leaves: Option<&mut HashMap>, + key_context: &::KeyContext, +) -> OriginalSkeletonTreeResult<()> +where + L: Leaf, + Layout: NodeLayout<'a, L>, +{ + let mut current_subtrees = subtrees; + let mut next_subtrees = Vec::new(); + while !current_subtrees.is_empty() { + let should_fetch_modified_leaves = + config.compare_modified_leaves() || previous_leaves.is_some(); + let filled_roots = + get_roots_from_storage::(¤t_subtrees, storage, key_context).await?; + for (filled_root, subtree) in filled_roots.into_iter().zip(current_subtrees.iter()) { + match filled_root.data { + // Binary node. + NodeData::Binary(BinaryData { left_data, right_data }) => { + if subtree.is_unmodified() { + skeleton_tree.nodes.insert( + subtree.get_root_index(), + OriginalSkeletonNode::UnmodifiedSubTree(filled_root.hash), + ); + continue; + } + skeleton_tree + .nodes + .insert(subtree.get_root_index(), OriginalSkeletonNode::Binary); + let (left_subtree, right_subtree) = + subtree.get_children_subtrees(left_data, right_data); + + handle_subtree( + skeleton_tree, + &mut next_subtrees, + left_subtree, + left_data, + should_fetch_modified_leaves, + ); + handle_subtree( + skeleton_tree, + &mut next_subtrees, + right_subtree, + right_data, + should_fetch_modified_leaves, + ) + } + // Edge node. + NodeData::Edge(EdgeData { bottom_data, path_to_bottom }) => { + skeleton_tree.nodes.insert( + subtree.get_root_index(), + OriginalSkeletonNode::Edge(path_to_bottom), + ); + + // Parse bottom. + let (bottom_subtree, previously_empty_leaves_indices) = + subtree.get_bottom_subtree(&path_to_bottom, bottom_data); + + if subtree.is_unmodified() { + if let Some(unmodified_child_hash) = + Layout::SubTree::unmodified_child_hash(bottom_data) + { + skeleton_tree.nodes.insert( + path_to_bottom.bottom_index(subtree.get_root_index()), + OriginalSkeletonNode::UnmodifiedSubTree(unmodified_child_hash), + ); + } else { + // With index layout we need to traverse an unmodified bottom node. + next_subtrees.push(bottom_subtree) + } + continue; + } + + if let Some(ref mut leaves) = previous_leaves { + leaves.extend( + previously_empty_leaves_indices + .iter() + .map(|idx| (**idx, L::default())) + .collect::>(), + ); + } + log_warning_for_empty_leaves( + &previously_empty_leaves_indices, + leaf_modifications, + config, + )?; + + handle_subtree( + skeleton_tree, + &mut next_subtrees, + bottom_subtree, + bottom_data, + should_fetch_modified_leaves, + ); + } + // Leaf node. + NodeData::Leaf(previous_leaf) => { + if subtree.is_unmodified() { + skeleton_tree.nodes.insert( + subtree.get_root_index(), + OriginalSkeletonNode::UnmodifiedSubTree(filled_root.hash), + ); + } else { + let root_index = subtree.get_root_index(); + // Modified leaf. + if config.compare_modified_leaves() + && L::compare(leaf_modifications, &root_index, &previous_leaf)? + { + log_trivial_modification!(root_index, previous_leaf); + } + // If previous values of modified leaves are requested, add this leaf. + if let Some(ref mut leaves) = previous_leaves { + leaves.insert(root_index, previous_leaf); + } + } + } + } + } + current_subtrees = next_subtrees; + next_subtrees = Vec::new(); + } + Ok(()) +} + +/// Handles a subtree referred by an edge or a binary node. Decides whether we deserialize the +/// referred subtree or not. +fn handle_subtree<'a, SubTree: SubTreeTrait<'a>>( + skeleton_tree: &mut OriginalSkeletonTreeImpl<'a>, + next_subtrees: &mut Vec, + subtree: SubTree, + subtree_data: SubTree::NodeData, + should_fetch_modified_leaves: bool, +) { + let is_leaf = subtree.is_leaf(); + let is_unmodified = subtree.is_unmodified(); + + // 1. Internal node → always traverse. + if !is_leaf { + next_subtrees.push(subtree); + return; + } + + // 2. Modified leaf. + if !is_unmodified { + if should_fetch_modified_leaves { + next_subtrees.push(subtree); + } + return; + } + + // 3. Unmodified leaf sibling. + if let Some(unmodified_child_hash) = SubTree::unmodified_child_hash(subtree_data) { + skeleton_tree.nodes.insert( + subtree.get_root_index(), + OriginalSkeletonNode::UnmodifiedSubTree(unmodified_child_hash), + ); + } else { + next_subtrees.push(subtree); + } +} + +// TODO(Aviv, 17/07/2024): Split between storage prefix implementation and function logic. +pub async fn get_roots_from_storage<'a, L: Leaf, Layout: NodeLayout<'a, L>>( + subtrees: &[impl SubTreeTrait< + 'a, + NodeData = Layout::ChildData, + NodeContext = Layout::DeserializationContext, + >], + storage: &mut impl Storage, + key_context: &::KeyContext, +) -> TraversalResult>> { + let mut subtrees_roots = vec![]; + let db_keys: Vec = subtrees + .iter() + .map(|subtree| { + create_db_key(subtree.get_root_prefix::(key_context), &subtree.get_root_suffix()) + }) + .collect(); + + let db_vals = storage.mget(&db_keys.iter().collect::>()).await?; + for ((subtree, optional_val), db_key) in subtrees.iter().zip(db_vals.iter()).zip(db_keys) { + let Some(val) = optional_val else { Err(StorageError::MissingKey(db_key))? }; + let filled_node = Layout::deserialize_node(val, &subtree.get_root_context())?; + subtrees_roots.push(filled_node); + } + Ok(subtrees_roots) +} + +pub async fn create_original_skeleton_tree<'a, L: Leaf, Layout: NodeLayout<'a, L>>( + storage: &mut impl Storage, + root_hash: HashOutput, + sorted_leaf_indices: SortedLeafIndices<'a>, + config: &impl OriginalSkeletonTreeConfig, + leaf_modifications: &LeafModifications, + key_context: &::KeyContext, +) -> OriginalSkeletonTreeResult> { + if sorted_leaf_indices.is_empty() { + return Ok(OriginalSkeletonTreeImpl::create_unmodified(root_hash)); + } + if root_hash == HashOutput::ROOT_OF_EMPTY_TREE { + return Ok(handle_empty_subtree::(sorted_leaf_indices).0); + } + let main_subtree = Layout::create_subtree(sorted_leaf_indices, NodeIndex::ROOT, root_hash); + let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes: HashMap::new(), sorted_leaf_indices }; + fetch_nodes::( + &mut skeleton_tree, + vec![main_subtree], + storage, + leaf_modifications, + config, + None, + key_context, + ) + .await?; + Ok(skeleton_tree) +} + +pub async fn create_storage_tries< + 'a, + L: Leaf + From, + Layout: NodeLayout<'a, L>, +>( + storage: &mut impl Storage, + actual_storage_updates: &HashMap>, + original_contracts_trie_leaves: &HashMap, + config: &impl Config, + storage_tries_sorted_indices: &HashMap>, +) -> ForestResult>> { + let mut storage_tries = HashMap::new(); + for (address, updates) in actual_storage_updates { + let sorted_leaf_indices = storage_tries_sorted_indices + .get(address) + .ok_or(ForestError::MissingSortedLeafIndices(*address))?; + let contract_state = original_contracts_trie_leaves + .get(&contract_address_into_node_index(address)) + .ok_or(ForestError::MissingContractCurrentState(*address))?; + let config = OriginalSkeletonTrieConfig::new(config.warn_on_trivial_modifications()); + + let original_skeleton = create_original_skeleton_tree::( + storage, + contract_state.storage_root_hash, + *sorted_leaf_indices, + &config, + &updates.iter().map(|(idx, value)| (*idx, L::from(*value))).collect(), + &Layout::generate_key_context(TrieType::StorageTrie(*address.0)), + ) + .await?; + storage_tries.insert(*address, original_skeleton); + } + Ok(storage_tries) +} + +pub async fn create_contracts_trie<'a, L: Leaf + Into, Layout: NodeLayout<'a, L>>( + storage: &mut impl Storage, + root_hash: HashOutput, + sorted_leaf_indices: SortedLeafIndices<'a>, +) -> ForestResult<(OriginalSkeletonTreeImpl<'a>, HashMap)> { + if sorted_leaf_indices.is_empty() { + let unmodified = OriginalSkeletonTreeImpl::create_unmodified(root_hash); + return Ok((unmodified, HashMap::new())); + } + if root_hash == HashOutput::ROOT_OF_EMPTY_TREE { + return Ok(handle_empty_subtree(sorted_leaf_indices)); + } + let main_subtree = Layout::create_subtree(sorted_leaf_indices, NodeIndex::ROOT, root_hash); + let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes: HashMap::new(), sorted_leaf_indices }; + let mut leaves = HashMap::new(); + fetch_nodes::( + &mut skeleton_tree, + vec![main_subtree], + storage, + &HashMap::new(), + &OriginalSkeletonTrieDontCompareConfig, + Some(&mut leaves), + &Layout::generate_key_context(TrieType::ContractsTrie), + ) + .await?; + + let leaves: HashMap = + leaves.into_iter().map(|(idx, leaf)| (idx, leaf.into())).collect(); + + Ok((skeleton_tree, leaves)) +} + +pub async fn create_classes_trie< + 'a, + L: Leaf + From, + Layout: NodeLayout<'a, L>, +>( + storage: &mut impl Storage, + actual_classes_updates: &LeafModifications, + classes_trie_root_hash: HashOutput, + config: &impl Config, + contracts_trie_sorted_indices: SortedLeafIndices<'a>, +) -> ForestResult> { + let config = OriginalSkeletonTrieConfig::new(config.warn_on_trivial_modifications()); + + Ok(create_original_skeleton_tree::( + storage, + classes_trie_root_hash, + contracts_trie_sorted_indices, + &config, + &actual_classes_updates.iter().map(|(idx, value)| (*idx, L::from(*value))).collect(), + &Layout::generate_key_context(TrieType::ClassesTrie), + ) + .await?) +} + +/// Given leaf indices that were previously empty leaves, logs out a warning for trivial +/// modification if a leaf is modified to an empty leaf. +/// If this check is suppressed by configuration, does nothing. +pub(crate) fn log_warning_for_empty_leaves + Debug>( + leaf_indices: &[T], + leaf_modifications: &LeafModifications, + config: &impl OriginalSkeletonTreeConfig, +) -> OriginalSkeletonTreeResult<()> { + if !config.compare_modified_leaves() { + return Ok(()); + } + let empty_leaf = L::default(); + for leaf_index in leaf_indices { + if L::compare(leaf_modifications, leaf_index.borrow(), &empty_leaf)? { + log_trivial_modification!(leaf_index, empty_leaf); + } + } + Ok(()) +} + +pub(crate) fn handle_empty_subtree<'a, L: Leaf>( + sorted_leaf_indices: SortedLeafIndices<'a>, +) -> (OriginalSkeletonTreeImpl<'a>, HashMap) { + ( + OriginalSkeletonTreeImpl::create_empty(sorted_leaf_indices), + sorted_leaf_indices.get_indices().iter().map(|idx| (*idx, L::default())).collect(), + ) +} diff --git a/crates/starknet_committer/src/patricia_merkle_tree/tree.rs b/crates/starknet_committer/src/patricia_merkle_tree/tree.rs index 41319f99750..138be106c86 100644 --- a/crates/starknet_committer/src/patricia_merkle_tree/tree.rs +++ b/crates/starknet_committer/src/patricia_merkle_tree/tree.rs @@ -5,7 +5,6 @@ use starknet_api::hash::HashOutput; use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::config::OriginalSkeletonTreeConfig; use starknet_patricia::patricia_merkle_tree::traversal::TraversalResult; use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices}; -use starknet_patricia_storage::db_object::EmptyKeyContext; use starknet_patricia_storage::storage_trait::Storage; use crate::block_committer::input::{ @@ -83,7 +82,6 @@ async fn fetch_all_patricia_paths( classes_trie_root_hash, class_sorted_leaf_indices, leaves, - &EmptyKeyContext, ) .await?; @@ -94,7 +92,6 @@ async fn fetch_all_patricia_paths( contracts_trie_root_hash, contract_sorted_leaf_indices, Some(&mut leaves), - &EmptyKeyContext, ) .await?; @@ -114,7 +111,6 @@ async fn fetch_all_patricia_paths( storage_root_hash, *sorted_leaf_indices, leaves, - &EmptyKeyContext, ) .await?; contracts_trie_storage_proofs.insert( diff --git a/crates/starknet_os_flow_tests/src/utils.rs b/crates/starknet_os_flow_tests/src/utils.rs index 6bb066aa2a7..7fd761f286b 100644 --- a/crates/starknet_os_flow_tests/src/utils.rs +++ b/crates/starknet_os_flow_tests/src/utils.rs @@ -258,7 +258,6 @@ pub(crate) async fn create_cached_state_input_and_commitment_infos( commitments, previous_state_roots.classes_trie_root_hash, sorted_class_leaf_indices, - &EmptyKeyContext, ) .await .unwrap(); @@ -286,7 +285,6 @@ pub(crate) async fn create_cached_state_input_and_commitment_infos( commitments, address_to_previous_storage_root_hash[&address], sorted_leaf_indices, - &EmptyKeyContext, ) .await .unwrap(); @@ -375,22 +373,14 @@ pub(crate) async fn get_previous_states_and_new_storage_roots< // Get previous contract state leaves. let sorted_contract_leaf_indices = SortedLeafIndices::new(&mut contract_leaf_indices); // Get the previous and the new contract states. - let previous_contract_states = get_leaves( - commitments, - previous_contract_trie_root, - sorted_contract_leaf_indices, - &EmptyKeyContext, - ) - .await - .unwrap(); - let new_contract_states: HashMap = get_leaves( - commitments, - new_contract_trie_root, - sorted_contract_leaf_indices, - &EmptyKeyContext, - ) - .await - .unwrap(); + let previous_contract_states = + get_leaves(commitments, previous_contract_trie_root, sorted_contract_leaf_indices) + .await + .unwrap(); + let new_contract_states: HashMap = + get_leaves(commitments, new_contract_trie_root, sorted_contract_leaf_indices) + .await + .unwrap(); let new_contract_roots: HashMap = new_contract_states .into_iter() .map(|(idx, contract_state)| {