From 775a5f6d15f72259edd9af2c12cbb6ebd8eab5d4 Mon Sep 17 00:00:00 2001 From: Yoav Gross Date: Wed, 27 May 2026 10:29:44 +0300 Subject: [PATCH] starknet_committer: split commit_block into read and compute phases Extract read_original_forest (IO-bound) and compute_updated_forest (CPU-bound) so the DB is free for parallel reads during computation. Decouple the storage/classes update lifetimes from the original forest's lifetime in the read path, since the forest only borrows the sorted indices; this lets the compute phase take ownership of the update maps. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/block_committer/commit.rs | 78 +++++++++++++++---- .../starknet_committer/src/db/facts_db/db.rs | 4 +- .../starknet_committer/src/db/forest_trait.rs | 10 +-- .../starknet_committer/src/db/index_db/db.rs | 4 +- .../src/db/trie_traversal.rs | 22 ++++-- 5 files changed, 87 insertions(+), 31 deletions(-) diff --git a/crates/starknet_committer/src/block_committer/commit.rs b/crates/starknet_committer/src/block_committer/commit.rs index e0446507947..570802f78f5 100644 --- a/crates/starknet_committer/src/block_committer/commit.rs +++ b/crates/starknet_committer/src/block_committer/commit.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use starknet_api::core::{ClassHash, ContractAddress, Nonce}; +use starknet_patricia::patricia_merkle_tree::node_data::leaf::LeafModifications; use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices}; use starknet_types_core::felt::Felt; use tracing::{debug, warn}; @@ -21,11 +22,11 @@ use crate::db::forest_trait::ForestReader; use crate::forest::deleted_nodes::{find_deleted_nodes, DeletedNodes}; use crate::forest::filled_forest::FilledForest; use crate::forest::forest_errors::ForestError; -use crate::forest::original_skeleton_forest::ForestSortedIndices; +use crate::forest::original_skeleton_forest::{ForestSortedIndices, OriginalSkeletonForest}; use crate::forest::updated_skeleton_forest::UpdatedSkeletonForest; use crate::hash_function::hash::TreeHashFunctionImpl; use crate::patricia_merkle_tree::leaf::leaf_impl::ContractState; -use crate::patricia_merkle_tree::types::class_hash_into_node_index; +use crate::patricia_merkle_tree::types::{class_hash_into_node_index, CompiledClassHash}; pub type BlockCommitmentResult = Result; @@ -54,16 +55,50 @@ pub async fn commit_block( + input: &Input, + actual_storage_updates: &HashMap>, + actual_classes_updates: &LeafModifications, + forest_sorted_indices: &'a ForestSortedIndices<'a>, + trie_reader: &mut Reader, + measurements: &mut M, +) -> BlockCommitmentResult<(OriginalSkeletonForest<'a>, HashMap)> { measurements.start_measurement(Action::Read); - let roots = - trie_reader.read_roots(input.initial_read_context).await.map_err(ForestError::from)?; + let roots = trie_reader + .read_roots(input.initial_read_context.clone()) + .await + .map_err(ForestError::from)?; let (original_forest, original_contracts_trie_leaves) = trie_reader .read( roots, - &actual_storage_updates, - &actual_classes_updates, - &forest_sorted_indices, + actual_storage_updates, + actual_classes_updates, + forest_sorted_indices, input.config.clone(), ) .await?; @@ -79,15 +114,28 @@ pub async fn commit_block( + original_forest: OriginalSkeletonForest<'_>, + original_contracts_trie_leaves: HashMap, + actual_storage_updates: HashMap>, + actual_classes_updates: LeafModifications, + state_diff: &StateDiff, + measurements: &mut M, +) -> BlockCommitmentResult<(FilledForest, DeletedNodes)> { measurements.start_measurement(Action::Compute); + + // Compute the new topology. let updated_forest = UpdatedSkeletonForest::create( &original_forest, - &input.state_diff.skeleton_classes_updates(), - &input.state_diff.skeleton_storage_updates(), + &state_diff.skeleton_classes_updates(), + &state_diff.skeleton_storage_updates(), &original_contracts_trie_leaves, - &input.state_diff.address_to_class_hash, - &input.state_diff.address_to_nonce, + &state_diff.address_to_class_hash, + &state_diff.address_to_nonce, )?; debug!("Updated skeleton forest created successfully."); @@ -106,8 +154,8 @@ pub async fn commit_block ForestReader for FactsDb { async fn read<'a>( &mut self, roots: StateRoots, - storage_updates: &'a HashMap>, - classes_updates: &'a LeafModifications, + storage_updates: &HashMap>, + classes_updates: &LeafModifications, forest_sorted_indices: &'a ForestSortedIndices<'a>, config: ReaderConfig, ) -> ForestResult<(OriginalSkeletonForest<'a>, HashMap)> { diff --git a/crates/starknet_committer/src/db/forest_trait.rs b/crates/starknet_committer/src/db/forest_trait.rs index 702a9c8de6f..1da7db964bf 100644 --- a/crates/starknet_committer/src/db/forest_trait.rs +++ b/crates/starknet_committer/src/db/forest_trait.rs @@ -70,13 +70,13 @@ pub trait ForestMetadata { #[async_trait] pub trait ForestReader { /// Input required to start reading the storage trie. - type InitialReadContext: InputContext + Send; + type InitialReadContext: InputContext + Send + Sync; async fn read<'a>( &mut self, roots: StateRoots, - storage_updates: &'a HashMap>, - classes_updates: &'a LeafModifications, + storage_updates: &HashMap>, + classes_updates: &LeafModifications, forest_sorted_indices: &'a ForestSortedIndices<'a>, config: ReaderConfig, ) -> ForestResult<(OriginalSkeletonForest<'a>, HashMap)>; @@ -91,8 +91,8 @@ pub trait ForestReader { pub(crate) async fn read_forest<'a, S, Layout>( storage: &mut S, roots: StateRoots, - storage_updates: &'a HashMap>, - classes_updates: &'a LeafModifications, + storage_updates: &HashMap>, + classes_updates: &LeafModifications, forest_sorted_indices: &'a ForestSortedIndices<'a>, config: ReaderConfig, ) -> ForestResult<(OriginalSkeletonForest<'a>, HashMap)> diff --git a/crates/starknet_committer/src/db/index_db/db.rs b/crates/starknet_committer/src/db/index_db/db.rs index ef600a296ab..69b0bdeba6a 100644 --- a/crates/starknet_committer/src/db/index_db/db.rs +++ b/crates/starknet_committer/src/db/index_db/db.rs @@ -212,8 +212,8 @@ where async fn read<'a>( &mut self, roots: StateRoots, - storage_updates: &'a HashMap>, - classes_updates: &'a LeafModifications, + storage_updates: &HashMap>, + classes_updates: &LeafModifications, forest_sorted_indices: &'a ForestSortedIndices<'a>, config: ReaderConfig, ) -> ForestResult<(OriginalSkeletonForest<'a>, HashMap)> { diff --git a/crates/starknet_committer/src/db/trie_traversal.rs b/crates/starknet_committer/src/db/trie_traversal.rs index 13a1e0a2e35..fd331e48b17 100644 --- a/crates/starknet_committer/src/db/trie_traversal.rs +++ b/crates/starknet_committer/src/db/trie_traversal.rs @@ -662,7 +662,7 @@ pub async fn create_original_skeleton_tree<'a, L: Leaf, Layout: NodeLayout<'a, L /// sequentially. pub async fn create_storage_tries<'a, Layout>( storage: &mut impl Storage, - actual_storage_updates: &'a HashMap>, + actual_storage_updates: &HashMap>, original_contracts_trie_leaves: &HashMap, config: &ReaderConfig, storage_tries_sorted_indices: &'a HashMap>, @@ -796,17 +796,25 @@ where } /// Holds all data needed to build one storage trie concurrently. -/// Implements [StorageTask] so that [GatherableStorage::gather] can drive it. -struct TrieReadTask<'a, Layout: NodeLayoutFor> { +/// +/// Two distinct lifetimes are needed: +/// - `'a` is the lifetime of the caller-owned index vectors that produced `sorted_leaf_indices`. +/// - `'u` is the borrow of `updates`, used only during construction of the original skeleton tree, +/// but does not retain a reference into the output `OriginalSkeletonTreeImpl<'a>`. +/// +/// Unifying the lifetimes would make the returned `OriginalSkeletonForest<'a>` to +/// borrow the storage update maps too, which would prevent `commit_block` from later moving those +/// maps by value into the compute phase. +struct TrieReadTask<'a, 'u, Layout: NodeLayoutFor> { address: ContractAddress, - updates: &'a LeafModifications, + updates: &'u LeafModifications, storage_root_hash: HashOutput, sorted_leaf_indices: SortedLeafIndices<'a>, warn_on_trivial_modifications: bool, _layout: PhantomData, } -impl<'a, S, Layout> StorageTaskOutput for TrieReadTask<'a, Layout> +impl<'a, 'u, S, Layout> StorageTaskOutput for TrieReadTask<'a, 'u, Layout> where S: ImmutableReadOnlyStorage, Layout: NodeLayoutFor + Send + 'static, @@ -815,7 +823,7 @@ where } #[async_trait] -impl<'a, 's, S, Layout> StorageTask<'s, S> for TrieReadTask<'a, Layout> +impl<'a, 'u, 's, S, Layout> StorageTask<'s, S> for TrieReadTask<'a, 'u, Layout> where S: ImmutableReadOnlyStorage + 's, Layout: NodeLayoutFor + Send + 'static, @@ -838,7 +846,7 @@ where async fn create_storage_tries_concurrently<'a, S, Layout>( storage: &mut S, - actual_storage_updates: &'a HashMap>, + actual_storage_updates: &HashMap>, original_contracts_trie_leaves: &HashMap, warn_on_trivial_modifications: bool, storage_tries_sorted_indices: &'a HashMap>,