diff --git a/crates/starknet_committer/src/db/forest_trait.rs b/crates/starknet_committer/src/db/forest_trait.rs index e64eba2b51b..1806f106c5e 100644 --- a/crates/starknet_committer/src/db/forest_trait.rs +++ b/crates/starknet_committer/src/db/forest_trait.rs @@ -185,6 +185,24 @@ pub trait ForestWriterWithMetadata: ForestWriter + ForestMetadata { /// Serializes deleted nodes into a vector of database keys. fn serialize_deleted_nodes(deleted_nodes: DeletedNodes) -> Vec; + /// Appends serialized forest nodes, metadata entries, and deleted nodes to + /// `operations`. + fn append_forest_and_metadata( + operations: &mut DbOperationMap, + filled_forest: &FilledForest, + metadata: HashMap, + deleted_nodes: DeletedNodes, + ) -> SerializationResult<()> { + for key in Self::serialize_deleted_nodes(deleted_nodes) { + operations.insert(key, DbOperation::Delete); + } + operations.extend(updates_to_set_operations(Self::serialize_forest(filled_forest)?)); + for (metadata_type, value) in metadata { + operations.insert(Self::metadata_key(metadata_type), DbOperation::Set(value)); + } + Ok(()) + } + /// Writes only metadata entries to storage, without a filled forest. /// Returns an error if any of the metadata keys are already set. /// May overwrite existing metadata in case of a write race (existence check and writing are not @@ -212,16 +230,8 @@ pub trait ForestWriterWithMetadata: ForestWriter + ForestMetadata { metadata: HashMap, deleted_nodes: DeletedNodes, ) -> SerializationResult { - let mut updates = Self::serialize_forest(filled_forest)?; - for (metadata_type, value) in metadata { - Self::insert_metadata(&mut updates, metadata_type, value); - } - let keys_to_delete = Self::serialize_deleted_nodes(deleted_nodes); - let operations = keys_to_delete - .into_iter() - .map(|key| (key, DbOperation::Delete)) - .chain(updates_to_set_operations(updates)) - .collect(); + let mut operations = DbOperationMap::new(); + Self::append_forest_and_metadata(&mut operations, filled_forest, metadata, deleted_nodes)?; Ok(self.write_updates(operations).await) } } diff --git a/crates/starknet_committer/src/db/index_db/db.rs b/crates/starknet_committer/src/db/index_db/db.rs index 67d4aa09dac..21ad8066a70 100644 --- a/crates/starknet_committer/src/db/index_db/db.rs +++ b/crates/starknet_committer/src/db/index_db/db.rs @@ -3,17 +3,29 @@ use std::marker::PhantomData; use std::sync::LazyLock; use async_trait::async_trait; +#[cfg(feature = "os_input")] +use starknet_api::block::BlockNumber; use starknet_api::core::{ContractAddress, PATRICIA_KEY_UPPER_BOUND_FELT}; use starknet_api::hash::{HashOutput, StateRoots}; use starknet_patricia::db_layout::{NodeLayout, NodeLayoutFor}; use starknet_patricia::patricia_merkle_tree::filled_tree::node::FilledNode; use starknet_patricia::patricia_merkle_tree::node_data::leaf::{Leaf, LeafModifications}; +#[cfg(feature = "os_input")] +use starknet_patricia::patricia_merkle_tree::traversal::TraversalResult; use starknet_patricia::patricia_merkle_tree::types::NodeIndex; use starknet_patricia::patricia_merkle_tree::updated_skeleton_tree::hash_function::TreeHashFunction; use starknet_patricia_storage::db_object::{DBObject, EmptyKeyContext, HasStaticPrefix}; use starknet_patricia_storage::errors::{DeserializationError, SerializationResult}; +#[cfg(feature = "os_input")] +use starknet_patricia_storage::map_storage::MapStorage; #[cfg(any(feature = "testing", test))] use starknet_patricia_storage::storage_trait::AsyncStorage; +#[cfg(feature = "os_input")] +use starknet_patricia_storage::storage_trait::DbOperation; +#[cfg(feature = "os_input")] +use starknet_patricia_storage::storage_trait::ImmutableReadOnlyStorage; +#[cfg(feature = "os_input")] +use starknet_patricia_storage::storage_trait::PatriciaStorageError; use starknet_patricia_storage::storage_trait::{ DbHashMap, DbKey, @@ -22,10 +34,19 @@ use starknet_patricia_storage::storage_trait::{ PatriciaStorageResult, Storage, }; +#[cfg(feature = "os_input")] +use starknet_patricia_storage::two_layer_storage::TwoLayerStorage; use starknet_types_core::felt::Felt; use crate::block_committer::input::{InputContext, ReaderConfig, StarknetStorageValue}; use crate::db::db_layout::DbLayout; +#[cfg(feature = "os_input")] +use crate::db::forest_trait::forest_trait_witnesses::{ + ForestReaderWithWitnesses, + ForestWriterWithMetadataAndWitnesses, + PatriciaProofsUpdate, + PatriciaProofsWrite, +}; use crate::db::forest_trait::{ read_forest, serialize_forest, @@ -53,11 +74,17 @@ use crate::db::index_db::types::{ use crate::db::serde_db_utils::DbBlockNumber; use crate::forest::deleted_nodes::DeletedNodes; use crate::forest::filled_forest::FilledForest; +#[cfg(feature = "os_input")] +use crate::forest::forest_errors::ForestError; use crate::forest::forest_errors::ForestResult; use crate::forest::original_skeleton_forest::{ForestSortedIndices, OriginalSkeletonForest}; use crate::hash_function::hash::TreeHashFunctionImpl; use crate::patricia_merkle_tree::leaf::leaf_impl::ContractState; +#[cfg(feature = "os_input")] +use crate::patricia_merkle_tree::tree::{fetch_all_patricia_paths, SortedLeafIndices}; use crate::patricia_merkle_tree::types::CompiledClassHash; +#[cfg(feature = "os_input")] +use crate::patricia_merkle_tree::types::StarknetForestProofs; /// Set to 2^251 + 1 to avoid collisions with contract addresses prefixes. pub(crate) static FIRST_AVAILABLE_PREFIX_FELT: LazyLock = @@ -90,7 +117,7 @@ pub(crate) static ACCESSED_KEYS_DIGEST_METADATA_PREFIX: LazyLock<[u8; 32]> = LazyLock::new(|| (Felt::from_bytes_be(&STATE_ROOT_METADATA_PREFIX) + Felt::ONE).to_bytes_be()); /// Prefix for Patricia proofs payload (per block). -#[allow(dead_code)] +#[cfg_attr(not(feature = "os_input"), expect(dead_code))] pub(crate) static PATRICIA_PATHS_PREFIX: LazyLock<[u8; 32]> = LazyLock::new(|| { (Felt::from_bytes_be(&ACCESSED_KEYS_DIGEST_METADATA_PREFIX) + Felt::ONE).to_bytes_be() }); @@ -288,14 +315,14 @@ impl ForestMetadata for IndexDb { key } ForestMetadataType::StateDiffHash(block_number) => { - metadata_block_number_key(&STATE_DIFF_HASH_METADATA_PREFIX, block_number) + block_number_based_key(&STATE_DIFF_HASH_METADATA_PREFIX, block_number) } ForestMetadataType::StateRoot(block_number) => { - metadata_block_number_key(&STATE_ROOT_METADATA_PREFIX, block_number) + block_number_based_key(&STATE_ROOT_METADATA_PREFIX, block_number) } #[cfg(feature = "os_input")] ForestMetadataType::AccessedKeysDigest(block_number) => { - metadata_block_number_key(&ACCESSED_KEYS_DIGEST_METADATA_PREFIX, block_number) + block_number_based_key(&ACCESSED_KEYS_DIGEST_METADATA_PREFIX, block_number) } }) } @@ -329,7 +356,7 @@ impl ForestWriterWithMetadata for IndexDb { } } -fn metadata_block_number_key(prefix: &[u8; 32], block_number: DbBlockNumber) -> Vec { +fn block_number_based_key(prefix: &[u8; 32], block_number: DbBlockNumber) -> Vec { let mut key = Vec::with_capacity(64); key.extend_from_slice(prefix); key.extend_from_slice(&block_number.serialize()); @@ -337,6 +364,119 @@ fn metadata_block_number_key(prefix: &[u8; 32], block_number: DbBlockNumber) -> key } +#[cfg(feature = "os_input")] +#[async_trait] +impl ForestReaderWithWitnesses + for IndexDb +{ + async fn read_witnesses( + &mut self, + height: BlockNumber, + ) -> ForestResult> { + let db_key = DbKey(block_number_based_key(&PATRICIA_PATHS_PREFIX, DbBlockNumber(height))); + + Ok(match self.get_from_storage(db_key).await? { + None => None, + Some(DbValue(bytes)) => { + Some(StarknetForestProofs::deserialize(&DbValue(bytes)).map_err(|e| { + ForestError::PatriciaStorage(PatriciaStorageError::Deserialization(e)) + })?) + } + }) + } + + async fn fetch_patricia_witnesses( + &mut self, + classes_trie_root_hash: HashOutput, + contracts_trie_root_hash: HashOutput, + class_sorted_leaf_indices: SortedLeafIndices<'_>, + contract_sorted_leaf_indices: SortedLeafIndices<'_>, + contract_storage_sorted_leaf_indices: &HashMap>, + staged_serialized_forest: Option, + ) -> TraversalResult { + match staged_serialized_forest { + None => { + fetch_all_patricia_paths::( + &mut self.storage, + classes_trie_root_hash, + contracts_trie_root_hash, + class_sorted_leaf_indices, + contract_sorted_leaf_indices, + contract_storage_sorted_leaf_indices, + ) + .await + } + Some(modifications) => { + let mut overlay = MapStorage::default(); + overlay.mset(modifications).await?; + let mut layered = TwoLayerStorage::new(overlay, &self.storage); + fetch_all_patricia_paths::( + &mut layered, + classes_trie_root_hash, + contracts_trie_root_hash, + class_sorted_leaf_indices, + contract_sorted_leaf_indices, + contract_storage_sorted_leaf_indices, + ) + .await + } + } + } +} + +#[cfg(feature = "os_input")] +#[async_trait] +impl ForestWriterWithMetadataAndWitnesses for IndexDb { + async fn write_with_metadata_and_witnesses( + &mut self, + filled_forest: &FilledForest, + metadata: HashMap, + deleted_nodes: DeletedNodes, + patricia_proofs_update: PatriciaProofsUpdate, + ) -> SerializationResult { + let mut operations = DbOperationMap::new(); + Self::append_forest_and_metadata(&mut operations, filled_forest, metadata, deleted_nodes)?; + match patricia_proofs_update { + PatriciaProofsUpdate::Delete(block_number) => { + operations.insert( + Self::metadata_key(ForestMetadataType::AccessedKeysDigest(DbBlockNumber( + block_number, + ))), + DbOperation::Delete, + ); + operations.insert( + DbKey(block_number_based_key( + &PATRICIA_PATHS_PREFIX, + DbBlockNumber(block_number), + )), + DbOperation::Delete, + ); + } + PatriciaProofsUpdate::Write(PatriciaProofsWrite { + block_number, + keys_digest, + witnesses, + }) => { + let encoded = witnesses.serialize()?; + operations.insert( + Self::metadata_key(ForestMetadataType::AccessedKeysDigest(DbBlockNumber( + block_number, + ))), + DbOperation::Set(DbValue(keys_digest.to_vec())), + ); + operations.insert( + DbKey(block_number_based_key( + &PATRICIA_PATHS_PREFIX, + DbBlockNumber(block_number), + )), + DbOperation::Set(encoded), + ); + } + } + Ok(self.write_updates(operations).await) + } +} + fn extract_root_hash(root: &Option) -> Result where TreeHashFunctionImpl: TreeHashFunction,