From 1673817a7df522b08bbde436bd5436b9355942ee Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Fri, 18 Oct 2024 22:04:13 +0900 Subject: [PATCH] feat(provider): add `BlockchainProvider3` with only one view (#11706) --- crates/chain-state/src/in_memory.rs | 69 +- crates/chain-state/src/lib.rs | 2 +- crates/chain-state/src/memory_overlay.rs | 349 +++++----- ...in_provider.rs => blockchain_provider3.rs} | 619 ++++++------------ .../factories/blockchain_provider.rs | 144 +++- crates/storage/provider/src/providers/mod.rs | 4 +- 6 files changed, 560 insertions(+), 627 deletions(-) rename crates/storage/provider/src/providers/{blockchain_provider.rs => blockchain_provider3.rs} (87%) diff --git a/crates/chain-state/src/in_memory.rs b/crates/chain-state/src/in_memory.rs index f157da5ff450..7e4d4b5a5fab 100644 --- a/crates/chain-state/src/in_memory.rs +++ b/crates/chain-state/src/in_memory.rs @@ -4,7 +4,7 @@ use crate::{ CanonStateNotification, CanonStateNotificationSender, CanonStateNotifications, ChainInfoTracker, MemoryOverlayStateProvider, }; -use alloy_eips::BlockNumHash; +use alloy_eips::{BlockHashOrNumber, BlockNumHash}; use alloy_primitives::{map::HashMap, Address, TxHash, B256}; use parking_lot::RwLock; use reth_chainspec::ChainInfo; @@ -514,7 +514,7 @@ impl CanonicalInMemoryState { historical: StateProviderBox, ) -> MemoryOverlayStateProvider { let in_memory = if let Some(state) = self.state_by_hash(hash) { - state.chain().into_iter().map(|block_state| block_state.block()).collect() + state.chain().map(|block_state| block_state.block()).collect() } else { Vec::new() }; @@ -692,10 +692,8 @@ impl BlockState { /// Returns a vector of `BlockStates` representing the entire in memory chain. /// The block state order in the output vector is newest to oldest (highest to lowest), /// including self as the first element. - pub fn chain(&self) -> Vec<&Self> { - let mut chain = vec![self]; - self.append_parent_chain(&mut chain); - chain + pub fn chain(&self) -> impl Iterator { + std::iter::successors(Some(self), |state| state.parent.as_deref()) } /// Appends the parent chain of this [`BlockState`] to the given vector. @@ -715,10 +713,59 @@ impl BlockState { /// This merges the state of all blocks that are part of the chain that the this block is /// the head of. This includes all blocks that connect back to the canonical block on disk. pub fn state_provider(&self, historical: StateProviderBox) -> MemoryOverlayStateProvider { - let in_memory = self.chain().into_iter().map(|block_state| block_state.block()).collect(); + let in_memory = self.chain().map(|block_state| block_state.block()).collect(); MemoryOverlayStateProvider::new(historical, in_memory) } + + /// Tries to find a block by [`BlockHashOrNumber`] in the chain ending at this block. + pub fn block_on_chain(&self, hash_or_num: BlockHashOrNumber) -> Option<&Self> { + self.chain().find(|block| match hash_or_num { + BlockHashOrNumber::Hash(hash) => block.hash() == hash, + BlockHashOrNumber::Number(number) => block.number() == number, + }) + } + + /// Tries to find a transaction by [`TxHash`] in the chain ending at this block. + pub fn transaction_on_chain(&self, hash: TxHash) -> Option { + self.chain().find_map(|block_state| { + block_state + .block_ref() + .block() + .body + .transactions() + .find(|tx| tx.hash() == hash) + .cloned() + }) + } + + /// Tries to find a transaction with meta by [`TxHash`] in the chain ending at this block. + pub fn transaction_meta_on_chain( + &self, + tx_hash: TxHash, + ) -> Option<(TransactionSigned, TransactionMeta)> { + self.chain().find_map(|block_state| { + block_state + .block_ref() + .block() + .body + .transactions() + .enumerate() + .find(|(_, tx)| tx.hash() == tx_hash) + .map(|(index, tx)| { + let meta = TransactionMeta { + tx_hash, + index: index as u64, + block_hash: block_state.hash(), + block_number: block_state.block_ref().block.number, + base_fee: block_state.block_ref().block.header.base_fee_per_gas, + timestamp: block_state.block_ref().block.timestamp, + excess_blob_gas: block_state.block_ref().block.excess_blob_gas, + }; + (tx.clone(), meta) + }) + }) + } } /// Represents an executed block stored in-memory. @@ -1381,7 +1428,7 @@ mod tests { let parents = single_block.parent_state_chain(); assert_eq!(parents.len(), 0); - let block_state_chain = single_block.chain(); + let block_state_chain = single_block.chain().collect::>(); assert_eq!(block_state_chain.len(), 1); assert_eq!(block_state_chain[0].block().block.number, single_block_number); assert_eq!(block_state_chain[0].block().block.hash(), single_block_hash); @@ -1392,18 +1439,18 @@ mod tests { let mut test_block_builder = TestBlockBuilder::default(); let chain = create_mock_state_chain(&mut test_block_builder, 3); - let block_state_chain = chain[2].chain(); + let block_state_chain = chain[2].chain().collect::>(); assert_eq!(block_state_chain.len(), 3); assert_eq!(block_state_chain[0].block().block.number, 3); assert_eq!(block_state_chain[1].block().block.number, 2); assert_eq!(block_state_chain[2].block().block.number, 1); - let block_state_chain = chain[1].chain(); + let block_state_chain = chain[1].chain().collect::>(); assert_eq!(block_state_chain.len(), 2); assert_eq!(block_state_chain[0].block().block.number, 2); assert_eq!(block_state_chain[1].block().block.number, 1); - let block_state_chain = chain[0].chain(); + let block_state_chain = chain[0].chain().collect::>(); assert_eq!(block_state_chain.len(), 1); assert_eq!(block_state_chain[0].block().block.number, 1); } diff --git a/crates/chain-state/src/lib.rs b/crates/chain-state/src/lib.rs index 50a103111071..bd9b43a59eae 100644 --- a/crates/chain-state/src/lib.rs +++ b/crates/chain-state/src/lib.rs @@ -22,7 +22,7 @@ pub use notifications::{ }; mod memory_overlay; -pub use memory_overlay::MemoryOverlayStateProvider; +pub use memory_overlay::{MemoryOverlayStateProvider, MemoryOverlayStateProviderRef}; #[cfg(any(test, feature = "test-utils"))] /// Common test helpers diff --git a/crates/chain-state/src/memory_overlay.rs b/crates/chain-state/src/memory_overlay.rs index eb125dad115e..ada0faee4907 100644 --- a/crates/chain-state/src/memory_overlay.rs +++ b/crates/chain-state/src/memory_overlay.rs @@ -7,14 +7,26 @@ use alloy_primitives::{ use reth_errors::ProviderResult; use reth_primitives::{Account, Bytecode}; use reth_storage_api::{ - AccountReader, BlockHashReader, StateProofProvider, StateProvider, StateProviderBox, - StateRootProvider, StorageRootProvider, + AccountReader, BlockHashReader, StateProofProvider, StateProvider, StateRootProvider, + StorageRootProvider, }; use reth_trie::{ updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof, TrieInput, }; use std::sync::OnceLock; +/// A state provider that stores references to in-memory blocks along with their state as well as a +/// reference of the historical state provider for fallback lookups. +#[allow(missing_debug_implementations)] +pub struct MemoryOverlayStateProviderRef<'a> { + /// Historical state provider for state lookups that are not found in in-memory blocks. + pub(crate) historical: Box, + /// The collection of executed parent blocks. Expected order is newest to oldest. + pub(crate) in_memory: Vec, + /// Lazy-loaded in-memory trie data. + pub(crate) trie_state: OnceLock, +} + /// A state provider that stores references to in-memory blocks along with their state as well as /// the historical state provider for fallback lookups. #[allow(missing_debug_implementations)] @@ -27,193 +39,200 @@ pub struct MemoryOverlayStateProvider { pub(crate) trie_state: OnceLock, } -impl MemoryOverlayStateProvider { - /// Create new memory overlay state provider. - /// - /// ## Arguments - /// - /// - `in_memory` - the collection of executed ancestor blocks in reverse. - /// - `historical` - a historical state provider for the latest ancestor block stored in the - /// database. - pub fn new(historical: Box, in_memory: Vec) -> Self { - Self { historical, in_memory, trie_state: OnceLock::new() } - } - - /// Turn this state provider into a [`StateProviderBox`] - pub fn boxed(self) -> StateProviderBox { - Box::new(self) - } - - /// Return lazy-loaded trie state aggregated from in-memory blocks. - fn trie_state(&self) -> &MemoryOverlayTrieState { - self.trie_state.get_or_init(|| { - let mut trie_state = MemoryOverlayTrieState::default(); - for block in self.in_memory.iter().rev() { - trie_state.state.extend_ref(block.hashed_state.as_ref()); - trie_state.nodes.extend_ref(block.trie.as_ref()); - } - trie_state - }) - } -} +macro_rules! impl_state_provider { + ([$($tokens:tt)*],$type:ty, $historical_type:ty) => { + impl $($tokens)* $type { + /// Create new memory overlay state provider. + /// + /// ## Arguments + /// + /// - `in_memory` - the collection of executed ancestor blocks in reverse. + /// - `historical` - a historical state provider for the latest ancestor block stored in the + /// database. + pub fn new(historical: $historical_type, in_memory: Vec) -> Self { + Self { historical, in_memory, trie_state: OnceLock::new() } + } + + /// Turn this state provider into a state provider + pub fn boxed(self) -> $historical_type { + Box::new(self) + } -impl BlockHashReader for MemoryOverlayStateProvider { - fn block_hash(&self, number: BlockNumber) -> ProviderResult> { - for block in &self.in_memory { - if block.block.number == number { - return Ok(Some(block.block.hash())) + /// Return lazy-loaded trie state aggregated from in-memory blocks. + fn trie_state(&self) -> &MemoryOverlayTrieState { + self.trie_state.get_or_init(|| { + let mut trie_state = MemoryOverlayTrieState::default(); + for block in self.in_memory.iter().rev() { + trie_state.state.extend_ref(block.hashed_state.as_ref()); + trie_state.nodes.extend_ref(block.trie.as_ref()); + } + trie_state + }) } } - self.historical.block_hash(number) - } - - fn canonical_hashes_range( - &self, - start: BlockNumber, - end: BlockNumber, - ) -> ProviderResult> { - let range = start..end; - let mut earliest_block_number = None; - let mut in_memory_hashes = Vec::new(); - for block in &self.in_memory { - if range.contains(&block.block.number) { - in_memory_hashes.insert(0, block.block.hash()); - earliest_block_number = Some(block.block.number); + impl $($tokens)* BlockHashReader for $type { + fn block_hash(&self, number: BlockNumber) -> ProviderResult> { + for block in &self.in_memory { + if block.block.number == number { + return Ok(Some(block.block.hash())) + } + } + + self.historical.block_hash(number) + } + + fn canonical_hashes_range( + &self, + start: BlockNumber, + end: BlockNumber, + ) -> ProviderResult> { + let range = start..end; + let mut earliest_block_number = None; + let mut in_memory_hashes = Vec::new(); + for block in &self.in_memory { + if range.contains(&block.block.number) { + in_memory_hashes.insert(0, block.block.hash()); + earliest_block_number = Some(block.block.number); + } + } + + let mut hashes = + self.historical.canonical_hashes_range(start, earliest_block_number.unwrap_or(end))?; + hashes.append(&mut in_memory_hashes); + Ok(hashes) } } - let mut hashes = - self.historical.canonical_hashes_range(start, earliest_block_number.unwrap_or(end))?; - hashes.append(&mut in_memory_hashes); - Ok(hashes) - } -} + impl $($tokens)* AccountReader for $type { + fn basic_account(&self, address: Address) -> ProviderResult> { + for block in &self.in_memory { + if let Some(account) = block.execution_output.account(&address) { + return Ok(account) + } + } -impl AccountReader for MemoryOverlayStateProvider { - fn basic_account(&self, address: Address) -> ProviderResult> { - for block in &self.in_memory { - if let Some(account) = block.execution_output.account(&address) { - return Ok(account) + self.historical.basic_account(address) } } - self.historical.basic_account(address) - } -} + impl $($tokens)* StateRootProvider for $type { + fn state_root(&self, state: HashedPostState) -> ProviderResult { + self.state_root_from_nodes(TrieInput::from_state(state)) + } -impl StateRootProvider for MemoryOverlayStateProvider { - fn state_root(&self, state: HashedPostState) -> ProviderResult { - self.state_root_from_nodes(TrieInput::from_state(state)) - } - - fn state_root_from_nodes(&self, mut input: TrieInput) -> ProviderResult { - let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone(); - input.prepend_cached(nodes, state); - self.historical.state_root_from_nodes(input) - } - - fn state_root_with_updates( - &self, - state: HashedPostState, - ) -> ProviderResult<(B256, TrieUpdates)> { - self.state_root_from_nodes_with_updates(TrieInput::from_state(state)) - } - - fn state_root_from_nodes_with_updates( - &self, - mut input: TrieInput, - ) -> ProviderResult<(B256, TrieUpdates)> { - let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone(); - input.prepend_cached(nodes, state); - self.historical.state_root_from_nodes_with_updates(input) - } -} + fn state_root_from_nodes(&self, mut input: TrieInput) -> ProviderResult { + let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone(); + input.prepend_cached(nodes, state); + self.historical.state_root_from_nodes(input) + } -impl StorageRootProvider for MemoryOverlayStateProvider { - // TODO: Currently this does not reuse available in-memory trie nodes. - fn storage_root(&self, address: Address, storage: HashedStorage) -> ProviderResult { - let state = &self.trie_state().state; - let mut hashed_storage = - state.storages.get(&keccak256(address)).cloned().unwrap_or_default(); - hashed_storage.extend(&storage); - self.historical.storage_root(address, hashed_storage) - } - - // TODO: Currently this does not reuse available in-memory trie nodes. - fn storage_proof( - &self, - address: Address, - slot: B256, - storage: HashedStorage, - ) -> ProviderResult { - let state = &self.trie_state().state; - let mut hashed_storage = - state.storages.get(&keccak256(address)).cloned().unwrap_or_default(); - hashed_storage.extend(&storage); - self.historical.storage_proof(address, slot, hashed_storage) - } -} + fn state_root_with_updates( + &self, + state: HashedPostState, + ) -> ProviderResult<(B256, TrieUpdates)> { + self.state_root_from_nodes_with_updates(TrieInput::from_state(state)) + } -impl StateProofProvider for MemoryOverlayStateProvider { - fn proof( - &self, - mut input: TrieInput, - address: Address, - slots: &[B256], - ) -> ProviderResult { - let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone(); - input.prepend_cached(nodes, state); - self.historical.proof(input, address, slots) - } - - fn multiproof( - &self, - mut input: TrieInput, - targets: HashMap>, - ) -> ProviderResult { - let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone(); - input.prepend_cached(nodes, state); - self.historical.multiproof(input, targets) - } - - fn witness( - &self, - mut input: TrieInput, - target: HashedPostState, - ) -> ProviderResult> { - let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone(); - input.prepend_cached(nodes, state); - self.historical.witness(input, target) - } -} + fn state_root_from_nodes_with_updates( + &self, + mut input: TrieInput, + ) -> ProviderResult<(B256, TrieUpdates)> { + let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone(); + input.prepend_cached(nodes, state); + self.historical.state_root_from_nodes_with_updates(input) + } + } + + impl $($tokens)* StorageRootProvider for $type { + // TODO: Currently this does not reuse available in-memory trie nodes. + fn storage_root(&self, address: Address, storage: HashedStorage) -> ProviderResult { + let state = &self.trie_state().state; + let mut hashed_storage = + state.storages.get(&keccak256(address)).cloned().unwrap_or_default(); + hashed_storage.extend(&storage); + self.historical.storage_root(address, hashed_storage) + } -impl StateProvider for MemoryOverlayStateProvider { - fn storage( - &self, - address: Address, - storage_key: StorageKey, - ) -> ProviderResult> { - for block in &self.in_memory { - if let Some(value) = block.execution_output.storage(&address, storage_key.into()) { - return Ok(Some(value)) + // TODO: Currently this does not reuse available in-memory trie nodes. + fn storage_proof( + &self, + address: Address, + slot: B256, + storage: HashedStorage, + ) -> ProviderResult { + let state = &self.trie_state().state; + let mut hashed_storage = + state.storages.get(&keccak256(address)).cloned().unwrap_or_default(); + hashed_storage.extend(&storage); + self.historical.storage_proof(address, slot, hashed_storage) } } - self.historical.storage(address, storage_key) - } + impl $($tokens)* StateProofProvider for $type { + fn proof( + &self, + mut input: TrieInput, + address: Address, + slots: &[B256], + ) -> ProviderResult { + let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone(); + input.prepend_cached(nodes, state); + self.historical.proof(input, address, slots) + } + + fn multiproof( + &self, + mut input: TrieInput, + targets: HashMap>, + ) -> ProviderResult { + let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone(); + input.prepend_cached(nodes, state); + self.historical.multiproof(input, targets) + } - fn bytecode_by_hash(&self, code_hash: B256) -> ProviderResult> { - for block in &self.in_memory { - if let Some(contract) = block.execution_output.bytecode(&code_hash) { - return Ok(Some(contract)) + fn witness( + &self, + mut input: TrieInput, + target: HashedPostState, + ) -> ProviderResult> { + let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone(); + input.prepend_cached(nodes, state); + self.historical.witness(input, target) } } - self.historical.bytecode_by_hash(code_hash) - } + impl $($tokens)* StateProvider for $type { + fn storage( + &self, + address: Address, + storage_key: StorageKey, + ) -> ProviderResult> { + for block in &self.in_memory { + if let Some(value) = block.execution_output.storage(&address, storage_key.into()) { + return Ok(Some(value)) + } + } + + self.historical.storage(address, storage_key) + } + + fn bytecode_by_hash(&self, code_hash: B256) -> ProviderResult> { + for block in &self.in_memory { + if let Some(contract) = block.execution_output.bytecode(&code_hash) { + return Ok(Some(contract)) + } + } + + self.historical.bytecode_by_hash(code_hash) + } + } + }; } +impl_state_provider!([], MemoryOverlayStateProvider, Box); +impl_state_provider!([<'a>], MemoryOverlayStateProviderRef<'a>, Box); + /// The collection of data necessary for trie-related operations for [`MemoryOverlayStateProvider`]. #[derive(Clone, Default, Debug)] pub(crate) struct MemoryOverlayTrieState { diff --git a/crates/storage/provider/src/providers/blockchain_provider.rs b/crates/storage/provider/src/providers/blockchain_provider3.rs similarity index 87% rename from crates/storage/provider/src/providers/blockchain_provider.rs rename to crates/storage/provider/src/providers/blockchain_provider3.rs index 9e6f32b33a3b..931ae668d3d5 100644 --- a/crates/storage/provider/src/providers/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/blockchain_provider3.rs @@ -1,25 +1,20 @@ +use super::{DatabaseProviderRO, ProviderFactory, ProviderNodeTypes}; use crate::{ providers::StaticFileProvider, AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, - BlockReader, BlockReaderIdExt, BlockSource, CanonChainTracker, CanonStateNotifications, - CanonStateSubscriptions, ChainSpecProvider, ChainStateBlockReader, ChangeSetReader, - DatabaseProviderFactory, DatabaseProviderRO, EvmEnvProvider, HeaderProvider, ProviderError, - ProviderFactory, PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, - RequestsProvider, StageCheckpointReader, StateProviderBox, StateProviderFactory, StateReader, - StaticFileProviderFactory, TransactionVariant, TransactionsProvider, WithdrawalsProvider, + BlockReader, BlockReaderIdExt, BlockSource, ChainSpecProvider, ChangeSetReader, EvmEnvProvider, + HeaderProvider, ProviderError, PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, + RequestsProvider, StageCheckpointReader, StateReader, StaticFileProviderFactory, + TransactionVariant, TransactionsProvider, WithdrawalsProvider, }; use alloy_eips::{BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag, HashOrNumber}; use alloy_primitives::{Address, BlockHash, BlockNumber, Sealable, TxHash, TxNumber, B256, U256}; -use alloy_rpc_types_engine::ForkchoiceState; -use reth_chain_state::{ - BlockState, CanonicalInMemoryState, ForkChoiceNotifications, ForkChoiceSubscriptions, - MemoryOverlayStateProvider, -}; +use parking_lot::RwLock; +use reth_chain_state::{BlockState, CanonicalInMemoryState, MemoryOverlayStateProviderRef}; use reth_chainspec::{ChainInfo, EthereumHardforks}; use reth_db::models::BlockNumberAddress; use reth_db_api::models::{AccountBeforeTx, StoredBlockBodyIndices}; use reth_evm::ConfigureEvmEnv; use reth_execution_types::{BundleStateInit, ExecutionOutcome, RevertsInit}; -use reth_node_types::NodeTypesWithDB; use reth_primitives::{ Account, Block, BlockWithSenders, Header, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, StorageEntry, TransactionMeta, TransactionSigned, TransactionSignedNoHash, @@ -27,7 +22,7 @@ use reth_primitives::{ }; use reth_prune_types::{PruneCheckpoint, PruneSegment}; use reth_stages_types::{StageCheckpoint, StageId}; -use reth_storage_api::StorageChangeSetReader; +use reth_storage_api::{DatabaseProviderFactory, StateProvider, StorageChangeSetReader}; use reth_storage_errors::provider::ProviderResult; use revm::{ db::states::PlainStorageRevert, @@ -37,85 +32,67 @@ use std::{ collections::{hash_map, HashMap}, ops::{Add, Bound, RangeBounds, RangeInclusive, Sub}, sync::Arc, - time::Instant, }; use tracing::trace; -use super::ProviderNodeTypes; - -/// The main type for interacting with the blockchain. -/// -/// This type serves as the main entry point for interacting with the blockchain and provides data -/// from database storage and from the blockchain tree (pending state etc.) It is a simple wrapper -/// type that holds an instance of the database and the blockchain tree. +/// Type that interacts with a snapshot view of the blockchain (storage and in-memory) at time of +/// instantiation. #[derive(Debug)] -pub struct BlockchainProvider2 { - /// Provider type used to access the database. - database: ProviderFactory, - /// Tracks the chain info wrt forkchoice updates and in memory canonical - /// state. - pub(super) canonical_in_memory_state: CanonicalInMemoryState, -} - -impl Clone for BlockchainProvider2 { - fn clone(&self) -> Self { - Self { - database: self.database.clone(), - canonical_in_memory_state: self.canonical_in_memory_state.clone(), - } - } +pub struct BlockchainProvider3 { + /// Storage provider. + storage_provider: as DatabaseProviderFactory>::Provider, + /// Head block at time of [`Self`] creation + head_block: Option>, + /// Snapshotted chain. It starts as empty, requiring [`Self::in_memory_chain()`] to be called. + /// Use `self.head_block.chain()` instead if you don't care about iteration order. + memory_chain: RwLock>>>, + /// In-memory canonical state. This is not a snapshot, and can change! Use with caution. + pub canonical_in_memory_state: CanonicalInMemoryState, } -impl BlockchainProvider2 { - /// Create a new provider using only the database, fetching the latest header from - /// the database to initialize the provider. - pub fn new(database: ProviderFactory) -> ProviderResult { - let provider = database.provider()?; - let best: ChainInfo = provider.chain_info()?; - match provider.header_by_number(best.best_number)? { - Some(header) => { - drop(provider); - Ok(Self::with_latest(database, SealedHeader::new(header, best.best_hash))?) - } - None => Err(ProviderError::HeaderNotFound(best.best_number.into())), - } - } - - /// Create new provider instance that wraps the database and the blockchain tree, using the - /// provided latest header to initialize the chain info tracker. +impl BlockchainProvider3 { + /// Create a new provider using [`ProviderFactory`] and [`CanonicalInMemoryState`], /// - /// This returns a `ProviderResult` since it tries the retrieve the last finalized header from - /// `database`. - pub fn with_latest(database: ProviderFactory, latest: SealedHeader) -> ProviderResult { - let provider = database.provider()?; - let finalized_header = provider - .last_finalized_block_number()? - .map(|num| provider.sealed_header(num)) - .transpose()? - .flatten(); - let safe_header = provider - .last_safe_block_number()? - .or_else(|| { - // for the purpose of this we can also use the finalized block if we don't have the - // safe block - provider.last_finalized_block_number().ok().flatten() - }) - .map(|num| provider.sealed_header(num)) - .transpose()? - .flatten(); + /// Underneath it will take a snapshot by fetching [`CanonicalInMemoryState::head_state`] and + /// [`ProviderFactory::database_provider_ro`] effectively maintaining one single snapshotted + /// view of memory and database. + pub fn new( + storage_provider_factory: ProviderFactory, + state: CanonicalInMemoryState, + ) -> ProviderResult { + // Each one provides a snapshot at the time of instantiation, but its order matters. + // + // If we acquire first the database provider, it's possible that before the in-memory chain + // snapshot is instantiated, it will flush blocks to disk. This would + // mean that our database provider would not have access to the flushed blocks (since it's + // working under an older view), while the in-memory state may have deleted them + // entirely. Resulting in gaps on the range. + let head_block = state.head_state(); + let storage_provider = storage_provider_factory.database_provider_ro()?; Ok(Self { - database, - canonical_in_memory_state: CanonicalInMemoryState::with_head( - latest, - finalized_header, - safe_header, - ), + storage_provider, + head_block, + canonical_in_memory_state: state, + memory_chain: RwLock::new(Arc::new(vec![])), }) } - /// Gets a clone of `canonical_in_memory_state`. - pub fn canonical_in_memory_state(&self) -> CanonicalInMemoryState { - self.canonical_in_memory_state.clone() + /// Returns a vector of in-memory blocks. + /// + /// If hasn't been requested yet in this provider, it also stores it in [`Self`]. + /// + /// The blocks are ordered from newest to oldest (highest to lowest). + fn in_memory_chain(&self) -> Arc>> { + let chain = self.memory_chain.read(); + if let Some(head_block) = &self.head_block { + if chain.is_empty() { + drop(chain); + let mut chain = self.memory_chain.write(); + *chain = Arc::new(head_block.clone().iter().collect::>()); + return chain.clone() + } + } + chain.clone() } // Helper function to convert range bounds @@ -142,6 +119,46 @@ impl BlockchainProvider2 { (start, end) } + /// Storage provider for latest block + fn latest_ref<'a>(&'a self) -> ProviderResult> { + trace!(target: "providers::blockchain", "Getting latest block state provider"); + + // use latest state provider if the head state exists + if let Some(state) = &self.head_block { + trace!(target: "providers::blockchain", "Using head state for latest state provider"); + Ok(self.block_state_provider_ref(state)?.boxed()) + } else { + trace!(target: "providers::blockchain", "Using database state for latest state provider"); + self.storage_provider.latest() + } + } + + fn history_by_block_hash_ref<'a>( + &'a self, + block_hash: BlockHash, + ) -> ProviderResult> { + trace!(target: "providers::blockchain", ?block_hash, "Getting history by block hash"); + + self.get_in_memory_or_storage_by_block( + block_hash.into(), + |_| self.storage_provider.history_by_block_hash(block_hash), + |block_state| { + let state_provider = self.block_state_provider_ref(block_state)?; + Ok(Box::new(state_provider)) + }, + ) + } + + /// Returns a state provider indexed by the given block number or tag. + fn state_by_block_number_ref<'a>( + &'a self, + number: BlockNumber, + ) -> ProviderResult> { + let hash = + self.block_hash(number)?.ok_or_else(|| ProviderError::HeaderNotFound(number.into()))?; + self.history_by_block_hash_ref(hash) + } + /// Return the last N blocks of state, recreating the [`ExecutionOutcome`]. /// /// If the range is empty, or there are no blocks for the given range, then this returns `None`. @@ -227,7 +244,7 @@ impl BlockchainProvider2 { ) -> ProviderResult<(BundleStateInit, RevertsInit)> { let mut state: BundleStateInit = HashMap::new(); let mut reverts: RevertsInit = HashMap::new(); - let state_provider = self.state_by_block_number_or_tag(block_range_end.into())?; + let state_provider = self.state_by_block_number_ref(block_range_end)?; // add account changeset changes for (block_number, account_before) in account_changeset.into_iter().rev() { @@ -306,7 +323,7 @@ impl BlockchainProvider2 { RangeInclusive, &mut P, ) -> ProviderResult>, - G: Fn(Arc, &mut P) -> Option, + G: Fn(&BlockState, &mut P) -> Option, P: FnMut(&T) -> bool, { // Each one provides a snapshot at the time of instantiation, but its order matters. @@ -317,8 +334,8 @@ impl BlockchainProvider2 { // working under an older view), while the in-memory state may have deleted them // entirely. Resulting in gaps on the range. let mut in_memory_chain = - self.canonical_in_memory_state.canonical_chain().collect::>(); - let db_provider = self.database_provider_ro()?; + self.head_block.as_ref().map(|b| b.chain().collect::>()).unwrap_or_default(); + let db_provider = &self.storage_provider; let (start, end) = self.convert_range_bounds(range, || { // the first block is the highest one. @@ -371,7 +388,7 @@ impl BlockchainProvider2 { let mut items = Vec::with_capacity((end - start + 1) as usize); if let Some(storage_range) = storage_range { - let mut db_items = fetch_db_range(&db_provider, storage_range.clone(), &mut predicate)?; + let mut db_items = fetch_db_range(db_provider, storage_range.clone(), &mut predicate)?; items.append(&mut db_items); // The predicate was not met, if the number of items differs from the expected. So, we @@ -396,18 +413,19 @@ impl BlockchainProvider2 { } /// This uses a given [`BlockState`] to initialize a state provider for that block. - fn block_state_provider( + fn block_state_provider_ref( &self, state: &BlockState, - ) -> ProviderResult { + ) -> ProviderResult> { let anchor_hash = state.anchor().hash; - let latest_historical = self.database.history_by_block_hash(anchor_hash)?; - Ok(state.state_provider(latest_historical)) + let latest_historical = self.history_by_block_hash_ref(anchor_hash)?; + let in_memory = state.chain().map(|block_state| block_state.block()).collect(); + Ok(MemoryOverlayStateProviderRef::new(latest_historical, in_memory)) } /// Fetches data from either in-memory state or persistent storage for a range of transactions. /// - /// * `fetch_from_db`: has a [`DatabaseProviderRO`] and the storage specific range. + /// * `fetch_from_db`: has a `DatabaseProviderRO` and the storage specific range. /// * `fetch_from_block_state`: has a [`RangeInclusive`] of elements that should be fetched from /// [`BlockState`]. [`RangeInclusive`] is necessary to handle partial look-ups of a block. fn get_in_memory_or_storage_by_tx_range( @@ -418,13 +436,13 @@ impl BlockchainProvider2 { ) -> ProviderResult> where S: FnOnce( - DatabaseProviderRO, + &DatabaseProviderRO, RangeInclusive, ) -> ProviderResult>, - M: Fn(RangeInclusive, Arc) -> ProviderResult>, + M: Fn(RangeInclusive, &Arc) -> ProviderResult>, { - let in_mem_chain = self.canonical_in_memory_state.canonical_chain().collect::>(); - let provider = self.database.provider()?; + let in_mem_chain = self.in_memory_chain(); + let provider = &self.storage_provider; // Get the last block number stored in the storage which does NOT overlap with in-memory // chain. @@ -474,7 +492,7 @@ impl BlockchainProvider2 { } // Iterate from the lowest block to the highest in-memory chain - for block_state in in_mem_chain.into_iter().rev() { + for block_state in in_mem_chain.iter().rev() { let block_tx_count = block_state.block_ref().block().body.transactions.len(); let remaining = (tx_range.end() - tx_range.start() + 1) as usize; @@ -516,13 +534,11 @@ impl BlockchainProvider2 { fetch_from_block_state: M, ) -> ProviderResult> where - S: FnOnce(DatabaseProviderRO) -> ProviderResult>, - M: Fn(usize, TxNumber, Arc) -> ProviderResult>, + S: FnOnce(&DatabaseProviderRO) -> ProviderResult>, + M: Fn(usize, TxNumber, &BlockState) -> ProviderResult>, { - // Order of instantiation matters. More information on: - // `get_in_memory_or_storage_by_block_range_while`. - let in_mem_chain = self.canonical_in_memory_state.canonical_chain().collect::>(); - let provider = self.database.provider()?; + let in_mem_chain = self.in_memory_chain(); + let provider = &self.storage_provider; // Get the last block number stored in the database which does NOT overlap with in-memory // chain. @@ -547,7 +563,7 @@ impl BlockchainProvider2 { } // Iterate from the lowest block to the highest - for block_state in in_mem_chain.into_iter().rev() { + for block_state in in_mem_chain.iter().rev() { let executed_block = block_state.block_ref(); let block = executed_block.block(); @@ -578,33 +594,24 @@ impl BlockchainProvider2 { } /// Fetches data from either in-memory state or persistent storage by [`BlockHashOrNumber`]. - fn get_in_memory_or_storage_by_block( + pub(crate) fn get_in_memory_or_storage_by_block( &self, id: BlockHashOrNumber, fetch_from_db: S, fetch_from_block_state: M, ) -> ProviderResult where - S: FnOnce(DatabaseProviderRO) -> ProviderResult, - M: Fn(Arc) -> ProviderResult, + S: FnOnce(&DatabaseProviderRO) -> ProviderResult, + M: Fn(&BlockState) -> ProviderResult, { - let block_state = match id { - BlockHashOrNumber::Hash(block_hash) => { - self.canonical_in_memory_state.state_by_hash(block_hash) - } - BlockHashOrNumber::Number(block_number) => { - self.canonical_in_memory_state.state_by_number(block_number) - } - }; - - if let Some(block_state) = block_state { + if let Some(Some(block_state)) = self.head_block.as_ref().map(|b| b.block_on_chain(id)) { return fetch_from_block_state(block_state) } - fetch_from_db(self.database_provider_ro()?) + fetch_from_db(&self.storage_provider) } } -impl BlockchainProvider2 { +impl BlockchainProvider3 { /// Ensures that the given block number is canonical (synced) /// /// This is a helper for guarding the `HistoricalStateProvider` against block numbers that are @@ -614,7 +621,7 @@ impl BlockchainProvider2 { /// Instead, we ensure that the `block_number` is within the range of the /// [`Self::best_block_number`] which is updated when a block is synced. #[inline] - fn ensure_canonical_block(&self, block_number: BlockNumber) -> ProviderResult<()> { + pub(crate) fn ensure_canonical_block(&self, block_number: BlockNumber) -> ProviderResult<()> { let latest = self.best_block_number()?; if block_number > latest { Err(ProviderError::HeaderNotFound(block_number.into())) @@ -624,27 +631,13 @@ impl BlockchainProvider2 { } } -impl DatabaseProviderFactory for BlockchainProvider2 { - type DB = N::DB; - type Provider = as DatabaseProviderFactory>::Provider; - type ProviderRW = as DatabaseProviderFactory>::ProviderRW; - - fn database_provider_ro(&self) -> ProviderResult { - self.database.database_provider_ro() - } - - fn database_provider_rw(&self) -> ProviderResult { - self.database.database_provider_rw() - } -} - -impl StaticFileProviderFactory for BlockchainProvider2 { +impl StaticFileProviderFactory for BlockchainProvider3 { fn static_file_provider(&self) -> StaticFileProvider { - self.database.static_file_provider() + self.storage_provider.static_file_provider() } } -impl HeaderProvider for BlockchainProvider2 { +impl HeaderProvider for BlockchainProvider3 { fn header(&self, block_hash: &BlockHash) -> ProviderResult> { self.get_in_memory_or_storage_by_block( (*block_hash).into(), @@ -670,7 +663,8 @@ impl HeaderProvider for BlockchainProvider2 { } fn header_td_by_number(&self, number: BlockNumber) -> ProviderResult> { - let number = if self.canonical_in_memory_state.hash_by_number(number).is_some() { + let number = if self.head_block.as_ref().map(|b| b.block_on_chain(number.into())).is_some() + { // If the block exists in memory, we should return a TD for it. // // The canonical in memory state should only store post-merge blocks. Post-merge blocks @@ -688,7 +682,7 @@ impl HeaderProvider for BlockchainProvider2 { // Otherwise, return what we have on disk for the input block number }; - self.database.header_td_by_number(number) + self.storage_provider.header_td_by_number(number) } fn headers_range(&self, range: impl RangeBounds) -> ProviderResult> { @@ -737,7 +731,7 @@ impl HeaderProvider for BlockchainProvider2 { } } -impl BlockHashReader for BlockchainProvider2 { +impl BlockHashReader for BlockchainProvider3 { fn block_hash(&self, number: u64) -> ProviderResult> { self.get_in_memory_or_storage_by_block( number.into(), @@ -763,17 +757,18 @@ impl BlockHashReader for BlockchainProvider2 { } } -impl BlockNumReader for BlockchainProvider2 { +impl BlockNumReader for BlockchainProvider3 { fn chain_info(&self) -> ProviderResult { - Ok(self.canonical_in_memory_state.chain_info()) + let best_number = self.best_block_number()?; + Ok(ChainInfo { best_hash: self.block_hash(best_number)?.unwrap_or_default(), best_number }) } fn best_block_number(&self) -> ProviderResult { - Ok(self.canonical_in_memory_state.get_canonical_block_number()) + self.head_block.as_ref().map(|b| Ok(b.number())).unwrap_or_else(|| self.last_block_number()) } fn last_block_number(&self) -> ProviderResult { - self.database.last_block_number() + self.storage_provider.last_block_number() } fn block_number(&self, hash: B256) -> ProviderResult> { @@ -785,7 +780,7 @@ impl BlockNumReader for BlockchainProvider2 { } } -impl BlockIdReader for BlockchainProvider2 { +impl BlockIdReader for BlockchainProvider3 { fn pending_block_num_hash(&self) -> ProviderResult> { Ok(self.canonical_in_memory_state.pending_block_num_hash()) } @@ -799,7 +794,7 @@ impl BlockIdReader for BlockchainProvider2 { } } -impl BlockReader for BlockchainProvider2 { +impl BlockReader for BlockchainProvider3 { fn find_block_by_hash(&self, hash: B256, source: BlockSource) -> ProviderResult> { match source { BlockSource::Any | BlockSource::Canonical => { @@ -862,7 +857,7 @@ impl BlockReader for BlockchainProvider2 { // Find the last block indices on database let last_storage_block_number = block_state.anchor().number; let mut stored_indices = self - .database + .storage_provider .block_body_indices(last_storage_block_number)? .ok_or(ProviderError::BlockBodyIndicesNotFound(last_storage_block_number))?; @@ -871,7 +866,7 @@ impl BlockReader for BlockchainProvider2 { stored_indices.tx_count = 0; // Iterate from the lowest block in memory until our target block - for state in block_state.chain().into_iter().rev() { + for state in block_state.chain().collect::>().into_iter().rev() { let block_tx_count = state.block_ref().block.body.transactions.len() as u64; if state.block_ref().block().number == number { stored_indices.tx_count = block_tx_count; @@ -949,7 +944,7 @@ impl BlockReader for BlockchainProvider2 { } } -impl TransactionsProvider for BlockchainProvider2 { +impl TransactionsProvider for BlockchainProvider3 { fn transaction_id(&self, tx_hash: TxHash) -> ProviderResult> { self.get_in_memory_or_storage_by_tx( tx_hash.into(), @@ -989,11 +984,11 @@ impl TransactionsProvider for BlockchainProvider2 { } fn transaction_by_hash(&self, hash: TxHash) -> ProviderResult> { - if let Some(tx) = self.canonical_in_memory_state.transaction_by_hash(hash) { + if let Some(tx) = self.head_block.as_ref().and_then(|b| b.transaction_on_chain(hash)) { return Ok(Some(tx)) } - self.database.transaction_by_hash(hash) + self.storage_provider.transaction_by_hash(hash) } fn transaction_by_hash_with_meta( @@ -1001,12 +996,12 @@ impl TransactionsProvider for BlockchainProvider2 { tx_hash: TxHash, ) -> ProviderResult> { if let Some((tx, meta)) = - self.canonical_in_memory_state.transaction_by_hash_with_meta(tx_hash) + self.head_block.as_ref().and_then(|b| b.transaction_meta_on_chain(tx_hash)) { return Ok(Some((tx, meta))) } - self.database.transaction_by_hash_with_meta(tx_hash) + self.storage_provider.transaction_by_hash_with_meta(tx_hash) } fn transaction_block(&self, id: TxNumber) -> ProviderResult> { @@ -1077,7 +1072,7 @@ impl TransactionsProvider for BlockchainProvider2 { } } -impl ReceiptProvider for BlockchainProvider2 { +impl ReceiptProvider for BlockchainProvider3 { fn receipt(&self, id: TxNumber) -> ProviderResult> { self.get_in_memory_or_storage_by_tx( id.into(), @@ -1089,7 +1084,7 @@ impl ReceiptProvider for BlockchainProvider2 { } fn receipt_by_hash(&self, hash: TxHash) -> ProviderResult> { - for block_state in self.canonical_in_memory_state.canonical_chain() { + for block_state in self.head_block.iter().flat_map(|b| b.chain()) { let executed_block = block_state.block_ref(); let block = executed_block.block(); let receipts = block_state.executed_block_receipts(); @@ -1108,7 +1103,7 @@ impl ReceiptProvider for BlockchainProvider2 { } } - self.database.receipt_by_hash(hash) + self.storage_provider.receipt_by_hash(hash) } fn receipts_by_block(&self, block: BlockHashOrNumber) -> ProviderResult>> { @@ -1133,7 +1128,7 @@ impl ReceiptProvider for BlockchainProvider2 { } } -impl ReceiptProviderIdExt for BlockchainProvider2 { +impl ReceiptProviderIdExt for BlockchainProvider3 { fn receipts_by_block_id(&self, block: BlockId) -> ProviderResult>> { match block { BlockId::Hash(rpc_block_hash) => { @@ -1164,7 +1159,7 @@ impl ReceiptProviderIdExt for BlockchainProvider2 { } } -impl WithdrawalsProvider for BlockchainProvider2 { +impl WithdrawalsProvider for BlockchainProvider3 { fn withdrawals_by_block( &self, id: BlockHashOrNumber, @@ -1200,7 +1195,7 @@ impl WithdrawalsProvider for BlockchainProvider2 { } } -impl RequestsProvider for BlockchainProvider2 { +impl RequestsProvider for BlockchainProvider3 { fn requests_by_block( &self, id: BlockHashOrNumber, @@ -1218,21 +1213,21 @@ impl RequestsProvider for BlockchainProvider2 { } } -impl StageCheckpointReader for BlockchainProvider2 { +impl StageCheckpointReader for BlockchainProvider3 { fn get_stage_checkpoint(&self, id: StageId) -> ProviderResult> { - self.database.provider()?.get_stage_checkpoint(id) + self.storage_provider.get_stage_checkpoint(id) } fn get_stage_checkpoint_progress(&self, id: StageId) -> ProviderResult>> { - self.database.provider()?.get_stage_checkpoint_progress(id) + self.storage_provider.get_stage_checkpoint_progress(id) } fn get_all_checkpoints(&self) -> ProviderResult> { - self.database.provider()?.get_all_checkpoints() + self.storage_provider.get_all_checkpoints() } } -impl EvmEnvProvider for BlockchainProvider2 { +impl EvmEnvProvider for BlockchainProvider3 { fn fill_env_at( &self, cfg: &mut CfgEnvWithHandlerCfg, @@ -1296,176 +1291,28 @@ impl EvmEnvProvider for BlockchainProvider2 { } } -impl PruneCheckpointReader for BlockchainProvider2 { +impl PruneCheckpointReader for BlockchainProvider3 { fn get_prune_checkpoint( &self, segment: PruneSegment, ) -> ProviderResult> { - self.database.provider()?.get_prune_checkpoint(segment) + self.storage_provider.get_prune_checkpoint(segment) } fn get_prune_checkpoints(&self) -> ProviderResult> { - self.database.provider()?.get_prune_checkpoints() + self.storage_provider.get_prune_checkpoints() } } -impl ChainSpecProvider for BlockchainProvider2 { +impl ChainSpecProvider for BlockchainProvider3 { type ChainSpec = N::ChainSpec; fn chain_spec(&self) -> Arc { - self.database.chain_spec() + ChainSpecProvider::chain_spec(&self.storage_provider) } } -impl StateProviderFactory for BlockchainProvider2 { - /// Storage provider for latest block - fn latest(&self) -> ProviderResult { - trace!(target: "providers::blockchain", "Getting latest block state provider"); - // use latest state provider if the head state exists - if let Some(state) = self.canonical_in_memory_state.head_state() { - trace!(target: "providers::blockchain", "Using head state for latest state provider"); - Ok(self.block_state_provider(&state)?.boxed()) - } else { - trace!(target: "providers::blockchain", "Using database state for latest state provider"); - self.database.latest() - } - } - - fn history_by_block_number( - &self, - block_number: BlockNumber, - ) -> ProviderResult { - trace!(target: "providers::blockchain", ?block_number, "Getting history by block number"); - self.ensure_canonical_block(block_number)?; - let hash = self - .block_hash(block_number)? - .ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?; - self.history_by_block_hash(hash) - } - - fn history_by_block_hash(&self, block_hash: BlockHash) -> ProviderResult { - trace!(target: "providers::blockchain", ?block_hash, "Getting history by block hash"); - - self.get_in_memory_or_storage_by_block( - block_hash.into(), - |_| { - // TODO(joshie): port history_by_block_hash to DatabaseProvider and use db_provider - self.database.history_by_block_hash(block_hash) - }, - |block_state| { - let state_provider = self.block_state_provider(&block_state)?; - Ok(Box::new(state_provider)) - }, - ) - } - - fn state_by_block_hash(&self, hash: BlockHash) -> ProviderResult { - trace!(target: "providers::blockchain", ?hash, "Getting state by block hash"); - if let Ok(state) = self.history_by_block_hash(hash) { - // This could be tracked by a historical block - Ok(state) - } else if let Ok(Some(pending)) = self.pending_state_by_hash(hash) { - // .. or this could be the pending state - Ok(pending) - } else { - // if we couldn't find it anywhere, then we should return an error - Err(ProviderError::StateForHashNotFound(hash)) - } - } - - /// Returns the state provider for pending state. - /// - /// If there's no pending block available then the latest state provider is returned: - /// [`Self::latest`] - fn pending(&self) -> ProviderResult { - trace!(target: "providers::blockchain", "Getting provider for pending state"); - - if let Some(pending) = self.canonical_in_memory_state.pending_state() { - // we have a pending block - return Ok(Box::new(self.block_state_provider(&pending)?)); - } - - // fallback to latest state if the pending block is not available - self.latest() - } - - fn pending_state_by_hash(&self, block_hash: B256) -> ProviderResult> { - if let Some(pending) = self.canonical_in_memory_state.pending_state() { - if pending.hash() == block_hash { - return Ok(Some(Box::new(self.block_state_provider(&pending)?))); - } - } - Ok(None) - } - - /// Returns a [`StateProviderBox`] indexed by the given block number or tag. - fn state_by_block_number_or_tag( - &self, - number_or_tag: BlockNumberOrTag, - ) -> ProviderResult { - match number_or_tag { - BlockNumberOrTag::Latest => self.latest(), - BlockNumberOrTag::Finalized => { - // we can only get the finalized state by hash, not by num - let hash = - self.finalized_block_hash()?.ok_or(ProviderError::FinalizedBlockNotFound)?; - self.state_by_block_hash(hash) - } - BlockNumberOrTag::Safe => { - // we can only get the safe state by hash, not by num - let hash = self.safe_block_hash()?.ok_or(ProviderError::SafeBlockNotFound)?; - self.state_by_block_hash(hash) - } - BlockNumberOrTag::Earliest => self.history_by_block_number(0), - BlockNumberOrTag::Pending => self.pending(), - BlockNumberOrTag::Number(num) => { - let hash = self - .block_hash(num)? - .ok_or_else(|| ProviderError::HeaderNotFound(num.into()))?; - self.state_by_block_hash(hash) - } - } - } -} - -impl CanonChainTracker for BlockchainProvider2 -where - Self: BlockReader, -{ - fn on_forkchoice_update_received(&self, _update: &ForkchoiceState) { - // update timestamp - self.canonical_in_memory_state.on_forkchoice_update_received(); - } - - fn last_received_update_timestamp(&self) -> Option { - self.canonical_in_memory_state.last_received_update_timestamp() - } - - fn on_transition_configuration_exchanged(&self) { - self.canonical_in_memory_state.on_transition_configuration_exchanged(); - } - - fn last_exchanged_transition_configuration_timestamp(&self) -> Option { - self.canonical_in_memory_state.last_exchanged_transition_configuration_timestamp() - } - - fn set_canonical_head(&self, header: SealedHeader) { - self.canonical_in_memory_state.set_canonical_head(header); - } - - fn set_safe(&self, header: SealedHeader) { - self.canonical_in_memory_state.set_safe(header); - } - - fn set_finalized(&self, header: SealedHeader) { - self.canonical_in_memory_state.set_finalized(header); - } -} - -impl BlockReaderIdExt for BlockchainProvider2 -where - Self: BlockReader + ReceiptProviderIdExt, -{ +impl BlockReaderIdExt for BlockchainProvider3 { fn block_by_id(&self, id: BlockId) -> ProviderResult> { match id { BlockId::Number(num) => self.block_by_number_or_tag(num), @@ -1564,30 +1411,14 @@ where } } -impl CanonStateSubscriptions for BlockchainProvider2 { - fn subscribe_to_canonical_state(&self) -> CanonStateNotifications { - self.canonical_in_memory_state.subscribe_canon_state() - } -} - -impl ForkChoiceSubscriptions for BlockchainProvider2 { - fn subscribe_safe_block(&self) -> ForkChoiceNotifications { - let receiver = self.canonical_in_memory_state.subscribe_safe_block(); - ForkChoiceNotifications(receiver) - } - - fn subscribe_finalized_block(&self) -> ForkChoiceNotifications { - let receiver = self.canonical_in_memory_state.subscribe_finalized_block(); - ForkChoiceNotifications(receiver) - } -} - -impl StorageChangeSetReader for BlockchainProvider2 { +impl StorageChangeSetReader for BlockchainProvider3 { fn storage_changeset( &self, block_number: BlockNumber, ) -> ProviderResult> { - if let Some(state) = self.canonical_in_memory_state.state_by_number(block_number) { + if let Some(state) = + self.head_block.as_ref().and_then(|b| b.block_on_chain(block_number.into())) + { let changesets = state .block() .execution_output @@ -1610,10 +1441,10 @@ impl StorageChangeSetReader for BlockchainProvider2 { Ok(changesets) } else { // Perform checks on whether or not changesets exist for the block. - let provider = self.database.provider()?; // No prune checkpoint means history should exist and we should `unwrap_or(true)` - let storage_history_exists = provider + let storage_history_exists = self + .storage_provider .get_prune_checkpoint(PruneSegment::StorageHistory)? .and_then(|checkpoint| { // return true if the block number is ahead of the prune checkpoint. @@ -1628,17 +1459,19 @@ impl StorageChangeSetReader for BlockchainProvider2 { return Err(ProviderError::StateAtBlockPruned(block_number)) } - provider.storage_changeset(block_number) + self.storage_provider.storage_changeset(block_number) } } } -impl ChangeSetReader for BlockchainProvider2 { +impl ChangeSetReader for BlockchainProvider3 { fn account_block_changeset( &self, block_number: BlockNumber, ) -> ProviderResult> { - if let Some(state) = self.canonical_in_memory_state.state_by_number(block_number) { + if let Some(state) = + self.head_block.as_ref().and_then(|b| b.block_on_chain(block_number.into())) + { let changesets = state .block_ref() .execution_output @@ -1654,9 +1487,10 @@ impl ChangeSetReader for BlockchainProvider2 { Ok(changesets) } else { // Perform checks on whether or not changesets exist for the block. - let provider = self.database.provider()?; + // No prune checkpoint means history should exist and we should `unwrap_or(true)` - let account_history_exists = provider + let account_history_exists = self + .storage_provider .get_prune_checkpoint(PruneSegment::AccountHistory)? .and_then(|checkpoint| { // return true if the block number is ahead of the prune checkpoint. @@ -1671,21 +1505,21 @@ impl ChangeSetReader for BlockchainProvider2 { return Err(ProviderError::StateAtBlockPruned(block_number)) } - provider.account_block_changeset(block_number) + self.storage_provider.account_block_changeset(block_number) } } } -impl AccountReader for BlockchainProvider2 { +impl AccountReader for BlockchainProvider3 { /// Get basic account information. fn basic_account(&self, address: Address) -> ProviderResult> { // use latest state provider - let state_provider = self.latest()?; + let state_provider = self.latest_ref()?; state_provider.basic_account(address) } } -impl StateReader for BlockchainProvider2 { +impl StateReader for BlockchainProvider3 { /// Re-constructs the [`ExecutionOutcome`] from in-memory and database state, if necessary. /// /// If data for the block does not exist, this will return [`None`]. @@ -1696,11 +1530,11 @@ impl StateReader for BlockchainProvider2 { /// because the tree thread is responsible for modifying the [`CanonicalInMemoryState`] in the /// first place. fn get_state(&self, block: BlockNumber) -> ProviderResult> { - if let Some(state) = self.canonical_in_memory_state.state_by_number(block) { + if let Some(state) = self.head_block.as_ref().and_then(|b| b.block_on_chain(block.into())) { let state = state.block_ref().execution_outcome().clone(); Ok(Some(state)) } else { - self.get_state(block..=block) + Self::get_state(self, block..=block) } } } @@ -1714,7 +1548,7 @@ mod tests { }; use crate::{ - providers::BlockchainProvider2, + providers::factories::BlockchainProviderFactory, test_utils::{ create_test_provider_factory, create_test_provider_factory_with_chain_spec, MockNodeTypesWithDB, @@ -1727,10 +1561,7 @@ mod tests { use alloy_primitives::{BlockNumber, TxNumber, B256}; use itertools::Itertools; use rand::Rng; - use reth_chain_state::{ - test_utils::TestBlockBuilder, CanonStateNotification, CanonStateSubscriptions, - CanonicalInMemoryState, ExecutedBlock, NewCanonicalChain, - }; + use reth_chain_state::{CanonicalInMemoryState, ExecutedBlock, NewCanonicalChain}; use reth_chainspec::{ ChainSpec, ChainSpecBuilder, ChainSpecProvider, EthereumHardfork, MAINNET, }; @@ -1740,7 +1571,7 @@ mod tests { }; use reth_db_api::{cursor::DbCursorRO, transaction::DbTx}; use reth_errors::ProviderError; - use reth_execution_types::{Chain, ExecutionOutcome}; + use reth_execution_types::ExecutionOutcome; use reth_primitives::{ Receipt, SealedBlock, StaticFileSegment, TransactionSignedNoHash, Withdrawals, }; @@ -1802,7 +1633,7 @@ mod tests { in_memory_blocks: usize, block_range_params: BlockRangeParams, ) -> eyre::Result<( - BlockchainProvider2, + BlockchainProviderFactory, Vec, Vec, Vec>, @@ -1867,7 +1698,7 @@ mod tests { // Commit to both storages: database and static files UnifiedStorageWriter::commit(provider_rw, factory.static_file_provider())?; - let provider = BlockchainProvider2::new(factory)?; + let provider_factory = BlockchainProviderFactory::new(factory)?; // Insert the rest of the blocks and receipts into the in-memory state let chain = NewCanonicalChain::Commit { @@ -1889,7 +1720,7 @@ mod tests { }) .collect(), }; - provider.canonical_in_memory_state.update_chain(chain); + provider_factory.canonical_in_memory_state.update_chain(chain); // Get canonical, safe, and finalized blocks let blocks = database_blocks.iter().chain(in_memory_blocks.iter()).collect::>(); @@ -1899,11 +1730,11 @@ mod tests { let finalized_block = blocks.get(block_count - 3).unwrap(); // Set the canonical head, safe, and finalized blocks - provider.set_canonical_head(canonical_block.header.clone()); - provider.set_safe(safe_block.header.clone()); - provider.set_finalized(finalized_block.header.clone()); + provider_factory.set_canonical_head(canonical_block.header.clone()); + provider_factory.set_safe(safe_block.header.clone()); + provider_factory.set_finalized(finalized_block.header.clone()); - Ok((provider, database_blocks.clone(), in_memory_blocks.clone(), receipts)) + Ok((provider_factory, database_blocks.clone(), in_memory_blocks.clone(), receipts)) } #[allow(clippy::type_complexity)] @@ -1913,7 +1744,7 @@ mod tests { in_memory_blocks: usize, block_range_params: BlockRangeParams, ) -> eyre::Result<( - BlockchainProvider2, + BlockchainProviderFactory, Vec, Vec, Vec>, @@ -1933,7 +1764,7 @@ mod tests { /// This simulates a RPC method having a different view than when its database transaction was /// created. fn persist_block_after_db_tx_creation( - provider: BlockchainProvider2, + provider: BlockchainProviderFactory, block_number: BlockNumber, ) { let hook_provider = provider.clone(); @@ -1987,7 +1818,8 @@ mod tests { provider_rw.commit()?; // Create a new provider - let provider = BlockchainProvider2::new(factory)?; + let provider_factory = BlockchainProviderFactory::new(factory)?; + let provider = provider_factory.provider()?; // Useful blocks let first_db_block = database_blocks.first().unwrap(); @@ -2019,6 +1851,7 @@ mod tests { )], }; provider.canonical_in_memory_state.update_chain(chain); + let provider = provider_factory.provider()?; // Now the block should be found in memory assert_eq!( @@ -2085,7 +1918,8 @@ mod tests { provider_rw.commit()?; // Create a new provider - let provider = BlockchainProvider2::new(factory)?; + let provider_factory = BlockchainProviderFactory::new(factory)?; + let provider = provider_factory.provider()?; // First in memory block let first_in_mem_block = in_memory_blocks.first().unwrap(); @@ -2110,6 +1944,8 @@ mod tests { }; provider.canonical_in_memory_state.update_chain(chain); + let provider = provider_factory.provider()?; + // First in memory block should be found assert_eq!( provider.block(BlockHashOrNumber::Hash(first_in_mem_block.hash()))?, @@ -2335,50 +2171,6 @@ mod tests { Ok(()) } - #[tokio::test] - async fn test_canon_state_subscriptions() -> eyre::Result<()> { - let factory = create_test_provider_factory(); - - // Generate a random block to initialise the blockchain provider. - let mut test_block_builder = TestBlockBuilder::default(); - let block_1 = test_block_builder.generate_random_block(0, B256::ZERO); - let block_hash_1 = block_1.hash(); - - // Insert and commit the block. - let provider_rw = factory.provider_rw()?; - provider_rw.insert_historical_block(block_1)?; - provider_rw.commit()?; - - let provider = BlockchainProvider2::new(factory)?; - - // Subscribe twice for canonical state updates. - let in_memory_state = provider.canonical_in_memory_state(); - let mut rx_1 = provider.subscribe_to_canonical_state(); - let mut rx_2 = provider.subscribe_to_canonical_state(); - - // Send and receive commit notifications. - let block_2 = test_block_builder.generate_random_block(1, block_hash_1); - let chain = Chain::new(vec![block_2], ExecutionOutcome::default(), None); - let commit = CanonStateNotification::Commit { new: Arc::new(chain.clone()) }; - in_memory_state.notify_canon_state(commit.clone()); - let (notification_1, notification_2) = tokio::join!(rx_1.recv(), rx_2.recv()); - assert_eq!(notification_1, Ok(commit.clone())); - assert_eq!(notification_2, Ok(commit.clone())); - - // Send and receive re-org notifications. - let block_3 = test_block_builder.generate_random_block(1, block_hash_1); - let block_4 = test_block_builder.generate_random_block(2, block_3.hash()); - let new_chain = Chain::new(vec![block_3, block_4], ExecutionOutcome::default(), None); - let re_org = - CanonStateNotification::Reorg { old: Arc::new(chain), new: Arc::new(new_chain) }; - in_memory_state.notify_canon_state(re_org.clone()); - let (notification_1, notification_2) = tokio::join!(rx_1.recv(), rx_2.recv()); - assert_eq!(notification_1, Ok(re_org.clone())); - assert_eq!(notification_2, Ok(re_org.clone())); - - Ok(()) - } - #[test] fn test_withdrawals_provider() -> eyre::Result<()> { let mut rng = generators::rng(); @@ -2794,7 +2586,8 @@ mod tests { )?; provider_rw.commit()?; - let provider = BlockchainProvider2::new(factory)?; + let provider_factory = BlockchainProviderFactory::new(factory)?; + let provider = provider_factory.provider()?; let in_memory_changesets = in_memory_changesets.into_iter().next().unwrap(); let chain = NewCanonicalChain::Commit { @@ -2826,6 +2619,8 @@ mod tests { }; provider.canonical_in_memory_state.update_chain(chain); + let provider = provider_factory.provider()?; + assert_eq!( provider.account_block_changeset(last_database_block).unwrap(), database_changesets diff --git a/crates/storage/provider/src/providers/factories/blockchain_provider.rs b/crates/storage/provider/src/providers/factories/blockchain_provider.rs index a913eae6c6cc..8c99a1018876 100644 --- a/crates/storage/provider/src/providers/factories/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/factories/blockchain_provider.rs @@ -1,20 +1,23 @@ #![allow(unused)] use crate::{ - providers::{BlockchainProvider2, StaticFileProvider}, + providers::{BlockchainProvider3, StaticFileProvider}, AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, CanonChainTracker, CanonStateNotifications, CanonStateSubscriptions, - ChainSpecProvider, ChainStateBlockReader, ChangeSetReader, DatabaseProviderFactory, - EvmEnvProvider, HeaderProvider, ProviderError, ProviderFactory, PruneCheckpointReader, - ReceiptProvider, ReceiptProviderIdExt, RequestsProvider, StageCheckpointReader, - StateProviderBox, StateProviderFactory, StateReader, StaticFileProviderFactory, - TransactionVariant, TransactionsProvider, WithdrawalsProvider, + ChainSpecProvider, ChainStateBlockReader, ChangeSetReader, DatabaseProvider, + DatabaseProviderFactory, EvmEnvProvider, FullProvider, HeaderProvider, ProviderError, + ProviderFactory, PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, + RequestsProvider, StageCheckpointReader, StateProviderBox, StateProviderFactory, StateReader, + StaticFileProviderFactory, TransactionVariant, TransactionsProvider, WithdrawalsProvider, }; use alloy_eips::{BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag}; use alloy_primitives::{Address, BlockHash, BlockNumber, Sealable, TxHash, TxNumber, B256, U256}; use alloy_rpc_types_engine::ForkchoiceState; -use reth_chain_state::{CanonicalInMemoryState, ForkChoiceNotifications, ForkChoiceSubscriptions}; +use reth_chain_state::{ + BlockState, CanonicalInMemoryState, ForkChoiceNotifications, ForkChoiceSubscriptions, + MemoryOverlayStateProvider, +}; use reth_chainspec::{ChainInfo, EthereumHardforks}; -use reth_db::models::BlockNumberAddress; +use reth_db::{models::BlockNumberAddress, transaction::DbTx, Database}; use reth_db_api::models::{AccountBeforeTx, StoredBlockBodyIndices}; use reth_evm::ConfigureEvmEnv; use reth_execution_types::ExecutionOutcome; @@ -26,7 +29,7 @@ use reth_primitives::{ }; use reth_prune_types::{PruneCheckpoint, PruneSegment}; use reth_stages_types::{StageCheckpoint, StageId}; -use reth_storage_api::StorageChangeSetReader; +use reth_storage_api::{DBProvider, StorageChangeSetReader}; use reth_storage_errors::provider::ProviderResult; use revm::primitives::{BlockEnv, CfgEnvWithHandlerCfg}; use std::{ @@ -34,6 +37,7 @@ use std::{ sync::Arc, time::Instant, }; +use tracing::trace; use crate::providers::ProviderNodeTypes; @@ -47,10 +51,10 @@ use crate::providers::ProviderNodeTypes; #[derive(Debug)] pub struct BlockchainProviderFactory { /// Provider factory used to access the database. - database: ProviderFactory, + pub(crate) database: ProviderFactory, /// Tracks the chain info wrt forkchoice updates and in memory canonical /// state. - pub(super) canonical_in_memory_state: CanonicalInMemoryState, + pub(crate) canonical_in_memory_state: CanonicalInMemoryState, } impl Clone for BlockchainProviderFactory { @@ -118,10 +122,18 @@ impl BlockchainProviderFactory { /// database using different types of providers. Example: [`HeaderProvider`] /// [`BlockHashReader`]. This may fail if the inner read database transaction fails to open. #[track_caller] - pub fn provider(&self) -> ProviderResult> { - let mut provider = BlockchainProvider2::new(self.database.clone())?; - provider.canonical_in_memory_state = self.canonical_in_memory_state(); - Ok(provider) + pub fn provider(&self) -> ProviderResult> { + BlockchainProvider3::new(self.database.clone(), self.canonical_in_memory_state()) + } + + /// This uses a given [`BlockState`] to initialize a state provider for that block. + fn block_state_provider( + &self, + state: &BlockState, + ) -> ProviderResult { + let anchor_hash = state.anchor().hash; + let latest_historical = self.database.history_by_block_hash(anchor_hash)?; + Ok(state.state_provider(latest_historical)) } /// Return the last N blocks of state, recreating the [`ExecutionOutcome`]. @@ -520,22 +532,55 @@ impl ChainSpecProvider for BlockchainProviderFactory { impl StateProviderFactory for BlockchainProviderFactory { /// Storage provider for latest block fn latest(&self) -> ProviderResult { - self.provider()?.latest() + trace!(target: "providers::blockchain", "Getting latest block state provider"); + // use latest state provider if the head state exists + if let Some(state) = self.canonical_in_memory_state.head_state() { + trace!(target: "providers::blockchain", "Using head state for latest state provider"); + Ok(self.block_state_provider(&state)?.boxed()) + } else { + trace!(target: "providers::blockchain", "Using database state for latest state provider"); + self.database.latest() + } } fn history_by_block_number( &self, block_number: BlockNumber, ) -> ProviderResult { - self.provider()?.history_by_block_number(block_number) + trace!(target: "providers::blockchain", ?block_number, "Getting history by block number"); + let provider = self.provider()?; + provider.ensure_canonical_block(block_number)?; + let hash = provider + .block_hash(block_number)? + .ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?; + self.history_by_block_hash(hash) } fn history_by_block_hash(&self, block_hash: BlockHash) -> ProviderResult { - self.provider()?.history_by_block_hash(block_hash) + trace!(target: "providers::blockchain", ?block_hash, "Getting history by block hash"); + + self.provider()?.get_in_memory_or_storage_by_block( + block_hash.into(), + |_| self.database.history_by_block_hash(block_hash), + |block_state| { + let state_provider = self.block_state_provider(block_state)?; + Ok(Box::new(state_provider)) + }, + ) } fn state_by_block_hash(&self, hash: BlockHash) -> ProviderResult { - self.provider()?.state_by_block_hash(hash) + trace!(target: "providers::blockchain", ?hash, "Getting state by block hash"); + if let Ok(state) = self.history_by_block_hash(hash) { + // This could be tracked by a historical block + Ok(state) + } else if let Ok(Some(pending)) = self.pending_state_by_hash(hash) { + // .. or this could be the pending state + Ok(pending) + } else { + // if we couldn't find it anywhere, then we should return an error + Err(ProviderError::StateForHashNotFound(hash)) + } } /// Returns the state provider for pending state. @@ -543,11 +588,24 @@ impl StateProviderFactory for BlockchainProviderFactory /// If there's no pending block available then the latest state provider is returned: /// [`Self::latest`] fn pending(&self) -> ProviderResult { - self.provider()?.pending() + trace!(target: "providers::blockchain", "Getting provider for pending state"); + + if let Some(pending) = self.canonical_in_memory_state.pending_state() { + // we have a pending block + return Ok(Box::new(self.block_state_provider(&pending)?)); + } + + // fallback to latest state if the pending block is not available + self.latest() } fn pending_state_by_hash(&self, block_hash: B256) -> ProviderResult> { - self.provider()?.pending_state_by_hash(block_hash) + if let Some(pending) = self.canonical_in_memory_state.pending_state() { + if pending.hash() == block_hash { + return Ok(Some(Box::new(self.block_state_provider(&pending)?))); + } + } + Ok(None) } /// Returns a [`StateProviderBox`] indexed by the given block number or tag. @@ -555,7 +613,28 @@ impl StateProviderFactory for BlockchainProviderFactory &self, number_or_tag: BlockNumberOrTag, ) -> ProviderResult { - self.provider()?.state_by_block_number_or_tag(number_or_tag) + match number_or_tag { + BlockNumberOrTag::Latest => self.latest(), + BlockNumberOrTag::Finalized => { + // we can only get the finalized state by hash, not by num + let hash = + self.finalized_block_hash()?.ok_or(ProviderError::FinalizedBlockNotFound)?; + self.state_by_block_hash(hash) + } + BlockNumberOrTag::Safe => { + // we can only get the safe state by hash, not by num + let hash = self.safe_block_hash()?.ok_or(ProviderError::SafeBlockNotFound)?; + self.state_by_block_hash(hash) + } + BlockNumberOrTag::Earliest => self.history_by_block_number(0), + BlockNumberOrTag::Pending => self.pending(), + BlockNumberOrTag::Number(num) => { + let hash = self + .block_hash(num)? + .ok_or_else(|| ProviderError::HeaderNotFound(num.into()))?; + self.state_by_block_hash(hash) + } + } } } @@ -692,7 +771,7 @@ mod tests { }; use crate::{ - providers::BlockchainProvider2, + providers::BlockchainProviderFactory, test_utils::{ create_test_provider_factory, create_test_provider_factory_with_chain_spec, MockNodeTypesWithDB, @@ -735,8 +814,6 @@ mod tests { use revm::db::BundleState; use std::ops::Bound; - use super::BlockchainProviderFactory; - const TEST_BLOCKS_COUNT: usize = 5; const TEST_TRANSACTIONS_COUNT: u8 = 4; @@ -913,7 +990,7 @@ mod tests { /// This simulates a RPC method having a different view than when its database transaction was /// created. fn persist_block_after_db_tx_creation( - provider: Arc>, + provider: BlockchainProviderFactory, block_number: BlockNumber, ) { let hook_provider = provider.clone(); @@ -967,7 +1044,7 @@ mod tests { provider_rw.commit()?; // Create a new provider - let provider = BlockchainProvider2::new(factory)?; + let provider = BlockchainProviderFactory::new(factory)?; // Useful blocks let first_db_block = database_blocks.first().unwrap(); @@ -1065,7 +1142,7 @@ mod tests { provider_rw.commit()?; // Create a new provider - let provider = BlockchainProvider2::new(factory)?; + let provider = BlockchainProviderFactory::new(factory)?; // First in memory block let first_in_mem_block = in_memory_blocks.first().unwrap(); @@ -1329,7 +1406,7 @@ mod tests { provider_rw.insert_historical_block(block_1)?; provider_rw.commit()?; - let provider = BlockchainProvider2::new(factory)?; + let provider = BlockchainProviderFactory::new(factory)?; // Subscribe twice for canonical state updates. let in_memory_state = provider.canonical_in_memory_state(); @@ -1774,7 +1851,7 @@ mod tests { )?; provider_rw.commit()?; - let provider = BlockchainProvider2::new(factory)?; + let provider = BlockchainProviderFactory::new(factory)?; let in_memory_changesets = in_memory_changesets.into_iter().next().unwrap(); let chain = NewCanonicalChain::Commit { @@ -2122,7 +2199,6 @@ mod tests { ..Default::default() }, )?; - let provider = Arc::new(provider); $( // Since data moves for each tried method, need to recalculate everything @@ -2237,7 +2313,6 @@ mod tests { ..Default::default() }, )?; - let provider = Arc::new(provider); $( // Since data moves for each tried method, need to recalculate everything @@ -2363,7 +2438,6 @@ mod tests { ..Default::default() }, )?; - let provider = Arc::new(provider); let mut in_memory_blocks: std::collections::VecDeque<_> = in_memory_blocks.into(); @@ -2665,8 +2739,6 @@ mod tests { }, )?; - let provider = Arc::new(provider); - // Old implementation was querying the database first. This is problematic, if there are // changes AFTER the database transaction is created. let old_transaction_hash_fn = @@ -2719,7 +2791,7 @@ mod tests { correct_transaction_hash_fn( to_be_persisted_tx.hash(), provider.canonical_in_memory_state(), - provider.database.clone() + provider.database ), Ok(Some(to_be_persisted_tx)) ); diff --git a/crates/storage/provider/src/providers/mod.rs b/crates/storage/provider/src/providers/mod.rs index 392b9cbb1b1d..7a0738521405 100644 --- a/crates/storage/provider/src/providers/mod.rs +++ b/crates/storage/provider/src/providers/mod.rs @@ -58,8 +58,8 @@ mod consistent_view; use alloy_rpc_types_engine::ForkchoiceState; pub use consistent_view::{ConsistentDbView, ConsistentViewError}; -mod blockchain_provider; -pub use blockchain_provider::BlockchainProvider2; +mod blockchain_provider3; +pub use blockchain_provider3::BlockchainProvider3; mod factories; pub use factories::BlockchainProviderFactory;