diff --git a/Cargo.lock b/Cargo.lock index 89e2cd91e..a6f9284f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6297,6 +6297,7 @@ dependencies = [ "reth-primitives", "reth-provider", "reth-revm-inspectors", + "reth-trie", "revm", "tracing", ] diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 1d850933a..f869eb485 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -104,8 +104,8 @@ impl BlockchainTree { externals.fetch_latest_canonical_hashes(config.num_of_canonical_hashes() as usize)?; // TODO(rakita) save last finalized block inside database but for now just take - // tip-max_reorg_depth - // task: https://github.com/paradigmxyz/reth/issues/1712 + // `tip - max_reorg_depth` + // https://github.com/paradigmxyz/reth/issues/1712 let last_finalized_block_number = if last_canonical_hashes.len() > max_reorg_depth { // we pick `Highest - max_reorg_depth` block as last finalized block. last_canonical_hashes.keys().nth_back(max_reorg_depth) diff --git a/crates/blockchain-tree/src/chain.rs b/crates/blockchain-tree/src/chain.rs index 252b93606..c52ccb304 100644 --- a/crates/blockchain-tree/src/chain.rs +++ b/crates/blockchain-tree/src/chain.rs @@ -158,15 +158,19 @@ impl AppendableChain { state.revert_to(parent.number); // Revert changesets to get the state of the parent that we need to apply the change. - let post_state_data = BundleStateDataRef { + let bundle_state_data = BundleStateDataRef { state: &state, sidechain_block_hashes: &side_chain_block_hashes, canonical_block_hashes, canonical_fork, }; - let block_state = - Self::validate_and_execute_sidechain(block.clone(), parent, post_state_data, externals) - .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; + let block_state = Self::validate_and_execute_sidechain( + block.clone(), + parent, + bundle_state_data, + externals, + ) + .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; state.extend(block_state); // If all is okay, return new chain back. Present chain is not modified. @@ -185,7 +189,7 @@ impl AppendableChain { fn validate_and_execute( block: SealedBlockWithSenders, parent_block: &SealedHeader, - post_state_data_provider: BSDP, + bundle_state_data_provider: BSDP, externals: &TreeExternals, block_kind: BlockKind, block_validation_kind: BlockValidationKind, @@ -203,10 +207,10 @@ impl AppendableChain { // get the state provider. let db = externals.database(); - let canonical_fork = post_state_data_provider.canonical_fork(); + let canonical_fork = bundle_state_data_provider.canonical_fork(); let state_provider = db.history_by_block_number(canonical_fork.number)?; - let provider = BundleStateProvider::new(state_provider, post_state_data_provider); + let provider = BundleStateProvider::new(state_provider, bundle_state_data_provider); let mut executor = externals.executor_factory.with_state(&provider); executor.execute_and_verify_receipt(&block, U256::MAX, Some(senders))?; @@ -232,7 +236,7 @@ impl AppendableChain { fn validate_and_execute_sidechain( block: SealedBlockWithSenders, parent_block: &SealedHeader, - post_state_data_provider: BSDP, + bundle_state_data_provider: BSDP, externals: &TreeExternals, ) -> RethResult where @@ -243,7 +247,7 @@ impl AppendableChain { Self::validate_and_execute( block, parent_block, - post_state_data_provider, + bundle_state_data_provider, externals, BlockKind::ForksHistoricalBlock, BlockValidationKind::SkipStateRootValidation, @@ -279,7 +283,7 @@ impl AppendableChain { { let parent_block = self.chain.tip(); - let post_state_data = BundleStateDataRef { + let bundle_state_data = BundleStateDataRef { state: self.state(), sidechain_block_hashes: &side_chain_block_hashes, canonical_block_hashes, @@ -289,7 +293,7 @@ impl AppendableChain { let block_state = Self::validate_and_execute( block.clone(), parent_block, - post_state_data, + bundle_state_data, externals, block_kind, block_validation_kind, diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 30c6c351c..18f74ecf2 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -22,6 +22,9 @@ revm.workspace = true # common tracing.workspace = true +[dev-dependencies] +reth-trie.workspace = true + [features] optimism = [ "revm/optimism", diff --git a/crates/revm/src/processor.rs b/crates/revm/src/processor.rs index f78c04117..1f30ef4dd 100644 --- a/crates/revm/src/processor.rs +++ b/crates/revm/src/processor.rs @@ -568,6 +568,7 @@ mod tests { use reth_provider::{ AccountReader, BlockHashReader, BundleStateWithReceipts, StateRootProvider, }; + use reth_trie::updates::TrieUpdates; use revm::{Database, TransitionState}; use std::collections::HashMap; @@ -627,6 +628,13 @@ mod tests { fn state_root(&self, _bundle_state: &BundleStateWithReceipts) -> ProviderResult { unimplemented!("state root computation is not supported") } + + fn state_root_with_updates( + &self, + _bundle_state: &BundleStateWithReceipts, + ) -> ProviderResult<(B256, TrieUpdates)> { + unimplemented!("state root computation is not supported") + } } impl StateProvider for StateProviderTest { diff --git a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs index c1ee9b65f..a28581429 100644 --- a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs +++ b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs @@ -13,6 +13,7 @@ use reth_primitives::{ }; use reth_trie::{ hashed_cursor::{HashedPostState, HashedPostStateCursorFactory, HashedStorage}, + updates::TrieUpdates, StateRoot, StateRootError, }; use revm::{db::states::BundleState, primitives::AccountInfo}; @@ -154,6 +155,20 @@ impl BundleStateWithReceipts { hashed_state.sorted() } + /// Returns [StateRoot] calculator. + fn state_root_calculator<'a, 'b, TX: DbTx>( + &self, + tx: &'a TX, + hashed_post_state: &'b HashedPostState, + ) -> StateRoot<'a, TX, HashedPostStateCursorFactory<'a, 'b, TX>> { + let (account_prefix_set, storage_prefix_set) = hashed_post_state.construct_prefix_sets(); + let hashed_cursor_factory = HashedPostStateCursorFactory::new(tx, hashed_post_state); + StateRoot::new(tx) + .with_hashed_cursor_factory(hashed_cursor_factory) + .with_changed_account_prefixes(account_prefix_set) + .with_changed_storage_prefixes(storage_prefix_set) + } + /// Calculate the state root for this [BundleState]. /// Internally, function calls [Self::hash_state_slow] to obtain the [HashedPostState]. /// Afterwards, it retrieves the prefixsets from the [HashedPostState] and uses them to @@ -196,13 +211,17 @@ impl BundleStateWithReceipts { /// The state root for this [BundleState]. pub fn state_root_slow(&self, tx: &TX) -> Result { let hashed_post_state = self.hash_state_slow(); - let (account_prefix_set, storage_prefix_set) = hashed_post_state.construct_prefix_sets(); - let hashed_cursor_factory = HashedPostStateCursorFactory::new(tx, &hashed_post_state); - StateRoot::new(tx) - .with_hashed_cursor_factory(hashed_cursor_factory) - .with_changed_account_prefixes(account_prefix_set) - .with_changed_storage_prefixes(storage_prefix_set) - .root() + self.state_root_calculator(tx, &hashed_post_state).root() + } + + /// Calculates the state root for this [BundleState] and returns it alongside trie updates. + /// See [Self::state_root_slow] for more info. + pub fn state_root_slow_with_updates( + &self, + tx: &TX, + ) -> Result<(B256, TrieUpdates), StateRootError> { + let hashed_post_state = self.hash_state_slow(); + self.state_root_calculator(tx, &hashed_post_state).root_with_updates() } /// Transform block number to the index of block. diff --git a/crates/storage/provider/src/bundle_state/state_changes.rs b/crates/storage/provider/src/bundle_state/state_changes.rs index 765fc0ee2..a62606ded 100644 --- a/crates/storage/provider/src/bundle_state/state_changes.rs +++ b/crates/storage/provider/src/bundle_state/state_changes.rs @@ -19,7 +19,7 @@ impl From for StateChanges { } impl StateChanges { - /// Write the post state to the database. + /// Write the bundle state to the database. pub fn write_to_db(mut self, tx: &TX) -> Result<(), DatabaseError> { // sort all entries so they can be written to database in more performant way. // and take smaller memory footprint. @@ -28,28 +28,28 @@ impl StateChanges { self.0.contracts.par_sort_by_key(|a| a.0); // Write new account state - tracing::trace!(target: "provider::post_state", len = self.0.accounts.len(), "Writing new account state"); + tracing::trace!(target: "provider::bundle_state", len = self.0.accounts.len(), "Writing new account state"); let mut accounts_cursor = tx.cursor_write::()?; // write account to database. for (address, account) in self.0.accounts.into_iter() { if let Some(account) = account { - tracing::trace!(target: "provider::post_state", ?address, "Updating plain state account"); + tracing::trace!(target: "provider::bundle_state", ?address, "Updating plain state account"); accounts_cursor.upsert(address, into_reth_acc(account))?; } else if accounts_cursor.seek_exact(address)?.is_some() { - tracing::trace!(target: "provider::post_state", ?address, "Deleting plain state account"); + tracing::trace!(target: "provider::bundle_state", ?address, "Deleting plain state account"); accounts_cursor.delete_current()?; } } // Write bytecode - tracing::trace!(target: "provider::post_state", len = self.0.contracts.len(), "Writing bytecodes"); + tracing::trace!(target: "provider::bundle_state", len = self.0.contracts.len(), "Writing bytecodes"); let mut bytecodes_cursor = tx.cursor_write::()?; for (hash, bytecode) in self.0.contracts.into_iter() { bytecodes_cursor.upsert(hash, Bytecode(bytecode))?; } // Write new storage state and wipe storage if needed. - tracing::trace!(target: "provider::post_state", len = self.0.storage.len(), "Writing new storage state"); + tracing::trace!(target: "provider::bundle_state", len = self.0.storage.len(), "Writing new storage state"); let mut storages_cursor = tx.cursor_dup_write::()?; for PlainStorageChangeset { address, wipe_storage, storage } in self.0.storage.into_iter() { // Wiping of storage. @@ -65,7 +65,7 @@ impl StateChanges { storage.par_sort_unstable_by_key(|a| a.key); for entry in storage.into_iter() { - tracing::trace!(target: "provider::post_state", ?address, ?entry.key, "Updating plain state storage"); + tracing::trace!(target: "provider::bundle_state", ?address, ?entry.key, "Updating plain state storage"); if let Some(db_entry) = storages_cursor.seek_by_key_subkey(address, entry.key)? { if db_entry.key == entry.key { storages_cursor.delete_current()?; diff --git a/crates/storage/provider/src/providers/bundle_state_provider.rs b/crates/storage/provider/src/providers/bundle_state_provider.rs index 46d9ae702..f2b0e5fdf 100644 --- a/crates/storage/provider/src/providers/bundle_state_provider.rs +++ b/crates/storage/provider/src/providers/bundle_state_provider.rs @@ -4,6 +4,7 @@ use crate::{ }; use reth_interfaces::provider::{ProviderError, ProviderResult}; use reth_primitives::{trie::AccountProof, Account, Address, BlockNumber, Bytecode, B256}; +use reth_trie::updates::TrieUpdates; /// A state provider that either resolves to data in a wrapped [`crate::BundleStateWithReceipts`], /// or an underlying state provider. @@ -11,14 +12,14 @@ use reth_primitives::{trie::AccountProof, Account, Address, BlockNumber, Bytecod pub struct BundleStateProvider { /// The inner state provider. pub(crate) state_provider: SP, - /// Post state data, - pub(crate) post_state_data_provider: BSDP, + /// Bundle state data, + pub(crate) bundle_state_data_provider: BSDP, } impl BundleStateProvider { - /// Create new post-state provider - pub fn new(state_provider: SP, post_state_data_provider: BSDP) -> Self { - Self { state_provider, post_state_data_provider } + /// Create new bundle state provider + pub fn new(state_provider: SP, bundle_state_data_provider: BSDP) -> Self { + Self { state_provider, bundle_state_data_provider } } } @@ -28,7 +29,7 @@ impl BlockHashReader for BundleStateProvider { fn block_hash(&self, block_number: BlockNumber) -> ProviderResult> { - let block_hash = self.post_state_data_provider.block_hash(block_number); + let block_hash = self.bundle_state_data_provider.block_hash(block_number); if block_hash.is_some() { return Ok(block_hash) } @@ -48,7 +49,7 @@ impl AccountReader for BundleStateProvider { fn basic_account(&self, address: Address) -> ProviderResult> { - if let Some(account) = self.post_state_data_provider.state().account(&address) { + if let Some(account) = self.bundle_state_data_provider.state().account(&address) { Ok(account) } else { self.state_provider.basic_account(address) @@ -59,11 +60,20 @@ impl AccountReader impl StateRootProvider for BundleStateProvider { - fn state_root(&self, post_state: &BundleStateWithReceipts) -> ProviderResult { - let mut state = self.post_state_data_provider.state().clone(); - state.extend(post_state.clone()); + fn state_root(&self, bundle_state: &BundleStateWithReceipts) -> ProviderResult { + let mut state = self.bundle_state_data_provider.state().clone(); + state.extend(bundle_state.clone()); self.state_provider.state_root(&state) } + + fn state_root_with_updates( + &self, + bundle_state: &BundleStateWithReceipts, + ) -> ProviderResult<(B256, TrieUpdates)> { + let mut state = self.bundle_state_data_provider.state().clone(); + state.extend(bundle_state.clone()); + self.state_provider.state_root_with_updates(&state) + } } impl StateProvider @@ -76,7 +86,7 @@ impl StateProvider ) -> ProviderResult> { let u256_storage_key = storage_key.into(); if let Some(value) = - self.post_state_data_provider.state().storage(&account, u256_storage_key) + self.bundle_state_data_provider.state().storage(&account, u256_storage_key) { return Ok(Some(value)) } @@ -85,7 +95,7 @@ impl StateProvider } fn bytecode_by_hash(&self, code_hash: B256) -> ProviderResult> { - if let Some(bytecode) = self.post_state_data_provider.state().bytecode(&code_hash) { + if let Some(bytecode) = self.bundle_state_data_provider.state().bytecode(&code_hash) { return Ok(Some(bytecode)) } diff --git a/crates/storage/provider/src/providers/mod.rs b/crates/storage/provider/src/providers/mod.rs index 898b5a39c..528577963 100644 --- a/crates/storage/provider/src/providers/mod.rs +++ b/crates/storage/provider/src/providers/mod.rs @@ -571,14 +571,14 @@ where fn pending_with_provider( &self, - post_state_data: Box, + bundle_state_data: Box, ) -> ProviderResult> { - let canonical_fork = post_state_data.canonical_fork(); + let canonical_fork = bundle_state_data.canonical_fork(); trace!(target: "providers::blockchain", ?canonical_fork, "Returning post state provider"); let state_provider = self.history_by_block_hash(canonical_fork.hash)?; - let post_state_provider = BundleStateProvider::new(state_provider, post_state_data); - Ok(Box::new(post_state_provider)) + let bundle_state_provider = BundleStateProvider::new(state_provider, bundle_state_data); + Ok(Box::new(bundle_state_provider)) } } diff --git a/crates/storage/provider/src/providers/state/historical.rs b/crates/storage/provider/src/providers/state/historical.rs index dbdba8f98..7d7fabe20 100644 --- a/crates/storage/provider/src/providers/state/historical.rs +++ b/crates/storage/provider/src/providers/state/historical.rs @@ -14,6 +14,7 @@ use reth_interfaces::provider::ProviderResult; use reth_primitives::{ trie::AccountProof, Account, Address, BlockNumber, Bytecode, StorageKey, StorageValue, B256, }; +use reth_trie::updates::TrieUpdates; /// State provider for a given block number which takes a tx reference. /// @@ -198,7 +199,14 @@ impl<'b, TX: DbTx> BlockHashReader for HistoricalStateProviderRef<'b, TX> { } impl<'b, TX: DbTx> StateRootProvider for HistoricalStateProviderRef<'b, TX> { - fn state_root(&self, _post_state: &BundleStateWithReceipts) -> ProviderResult { + fn state_root(&self, _bundle_state: &BundleStateWithReceipts) -> ProviderResult { + Err(ProviderError::StateRootNotAvailableForHistoricalBlock) + } + + fn state_root_with_updates( + &self, + _bundle_state: &BundleStateWithReceipts, + ) -> ProviderResult<(B256, TrieUpdates)> { Err(ProviderError::StateRootNotAvailableForHistoricalBlock) } } diff --git a/crates/storage/provider/src/providers/state/latest.rs b/crates/storage/provider/src/providers/state/latest.rs index 1b45555fc..df515f78e 100644 --- a/crates/storage/provider/src/providers/state/latest.rs +++ b/crates/storage/provider/src/providers/state/latest.rs @@ -12,6 +12,7 @@ use reth_primitives::{ keccak256, trie::AccountProof, Account, Address, BlockNumber, Bytecode, StorageKey, StorageValue, B256, }; +use reth_trie::updates::TrieUpdates; /// State provider over latest state that takes tx reference. #[derive(Debug)] @@ -62,6 +63,15 @@ impl<'b, TX: DbTx> StateRootProvider for LatestStateProviderRef<'b, TX> { fn state_root(&self, bundle_state: &BundleStateWithReceipts) -> ProviderResult { bundle_state.state_root_slow(self.db).map_err(|err| ProviderError::Database(err.into())) } + + fn state_root_with_updates( + &self, + bundle_state: &BundleStateWithReceipts, + ) -> ProviderResult<(B256, TrieUpdates)> { + bundle_state + .state_root_slow_with_updates(self.db) + .map_err(|err| ProviderError::Database(err.into())) + } } impl<'b, TX: DbTx> StateProvider for LatestStateProviderRef<'b, TX> { diff --git a/crates/storage/provider/src/providers/state/macros.rs b/crates/storage/provider/src/providers/state/macros.rs index 67b3c33f5..300b2c2ec 100644 --- a/crates/storage/provider/src/providers/state/macros.rs +++ b/crates/storage/provider/src/providers/state/macros.rs @@ -32,6 +32,7 @@ macro_rules! delegate_provider_impls { for $target => StateRootProvider $(where [$($generics)*])? { fn state_root(&self, state: &crate::BundleStateWithReceipts) -> reth_interfaces::provider::ProviderResult; + fn state_root_with_updates(&self, state: &crate::BundleStateWithReceipts) -> reth_interfaces::provider::ProviderResult<(reth_primitives::B256, reth_trie::updates::TrieUpdates)>; } AccountReader $(where [$($generics)*])? { fn basic_account(&self, address: reth_primitives::Address) -> reth_interfaces::provider::ProviderResult>; diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index c4689ac57..f32b4fd81 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -15,6 +15,7 @@ use reth_primitives::{ SealedBlock, SealedHeader, StorageKey, StorageValue, TransactionMeta, TransactionSigned, TransactionSignedNoHash, TxHash, TxNumber, B256, U256, }; +use reth_trie::updates::TrieUpdates; use revm::primitives::{BlockEnv, CfgEnv}; use std::{ collections::{BTreeMap, HashMap}, @@ -496,7 +497,14 @@ impl AccountReader for MockEthProvider { } impl StateRootProvider for MockEthProvider { - fn state_root(&self, _state: &BundleStateWithReceipts) -> ProviderResult { + fn state_root(&self, _bundle_state: &BundleStateWithReceipts) -> ProviderResult { + todo!() + } + + fn state_root_with_updates( + &self, + _bundle_state: &BundleStateWithReceipts, + ) -> ProviderResult<(B256, TrieUpdates)> { todo!() } } @@ -602,7 +610,7 @@ impl StateProviderFactory for MockEthProvider { fn pending_with_provider<'a>( &'a self, - _post_state_data: Box, + _bundle_state_data: Box, ) -> ProviderResult> { Ok(Box::new(self.clone())) } @@ -638,7 +646,7 @@ impl StateProviderFactory for Arc { fn pending_with_provider<'a>( &'a self, - _post_state_data: Box, + _bundle_state_data: Box, ) -> ProviderResult> { Ok(Box::new(self.clone())) } diff --git a/crates/storage/provider/src/test_utils/noop.rs b/crates/storage/provider/src/test_utils/noop.rs index 45258bc69..7c2b761ce 100644 --- a/crates/storage/provider/src/test_utils/noop.rs +++ b/crates/storage/provider/src/test_utils/noop.rs @@ -17,6 +17,7 @@ use reth_primitives::{ SealedHeader, StorageKey, StorageValue, TransactionMeta, TransactionSigned, TransactionSignedNoHash, TxHash, TxNumber, B256, MAINNET, U256, }; +use reth_trie::updates::TrieUpdates; use revm::primitives::{BlockEnv, CfgEnv}; use std::{ ops::{RangeBounds, RangeInclusive}, @@ -274,6 +275,13 @@ impl StateRootProvider for NoopProvider { fn state_root(&self, _state: &BundleStateWithReceipts) -> ProviderResult { Ok(B256::default()) } + + fn state_root_with_updates( + &self, + _bundle_state: &BundleStateWithReceipts, + ) -> ProviderResult<(B256, TrieUpdates)> { + Ok((B256::default(), TrieUpdates::default())) + } } impl StateProvider for NoopProvider { @@ -368,7 +376,7 @@ impl StateProviderFactory for NoopProvider { fn pending_with_provider<'a>( &'a self, - _post_state_data: Box, + _bundle_state_data: Box, ) -> ProviderResult> { Ok(Box::new(*self)) } diff --git a/crates/storage/provider/src/traits/state.rs b/crates/storage/provider/src/traits/state.rs index 617550441..158ec2bb4 100644 --- a/crates/storage/provider/src/traits/state.rs +++ b/crates/storage/provider/src/traits/state.rs @@ -6,6 +6,7 @@ use reth_primitives::{ trie::AccountProof, Address, BlockHash, BlockId, BlockNumHash, BlockNumber, BlockNumberOrTag, Bytecode, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256, }; +use reth_trie::updates::TrieUpdates; /// Type alias of boxed [StateProvider]. pub type StateProviderBox<'a> = Box; @@ -180,11 +181,11 @@ pub trait StateProviderFactory: BlockIdReader + Send + Sync { block_hash: B256, ) -> ProviderResult>>; - /// Return a [StateProvider] that contains post state data provider. + /// Return a [StateProvider] that contains bundle state data provider. /// Used to inspect or execute transaction on the pending state. fn pending_with_provider( &self, - post_state_data: Box, + bundle_state_data: Box, ) -> ProviderResult>; } @@ -232,6 +233,17 @@ pub trait BundleStateDataProvider: Send + Sync { /// A type that can compute the state root of a given post state. #[auto_impl[Box,&, Arc]] pub trait StateRootProvider: Send + Sync { - /// Returns the state root of the BundleState on top of the current state. - fn state_root(&self, post_state: &BundleStateWithReceipts) -> ProviderResult; + /// Returns the state root of the `BundleState` on top of the current state. + /// + /// NOTE: It is recommended to provide a different implementation from + /// `state_root_with_updates` since it affects the memory usage during state root + /// computation. + fn state_root(&self, bundle_state: &BundleStateWithReceipts) -> ProviderResult; + + /// Returns the state root of the BundleState on top of the current state with trie + /// updates to be committed to the database. + fn state_root_with_updates( + &self, + bundle_state: &BundleStateWithReceipts, + ) -> ProviderResult<(B256, TrieUpdates)>; }