diff --git a/crates/chain-state/src/chain_info.rs b/crates/chain-state/src/chain_info.rs index 40ca08090..c9ac52366 100644 --- a/crates/chain-state/src/chain_info.rs +++ b/crates/chain-state/src/chain_info.rs @@ -17,10 +17,12 @@ pub struct ChainInfoTracker { } impl ChainInfoTracker { - /// Create a new chain info container for the given canonical head. - pub fn new(head: SealedHeader) -> Self { - let (finalized_block, _) = watch::channel(None); - let (safe_block, _) = watch::channel(None); + /// Create a new chain info container for the given canonical head and finalized header if it + /// exists. + pub fn new(head: SealedHeader, finalized: Option) -> Self { + let (finalized_block, _) = watch::channel(finalized.clone()); + let (safe_block, _) = watch::channel(finalized); + Self { inner: Arc::new(ChainInfoInner { last_forkchoice_update: RwLock::new(None), diff --git a/crates/chain-state/src/in_memory.rs b/crates/chain-state/src/in_memory.rs index 667472553..583655a23 100644 --- a/crates/chain-state/src/in_memory.rs +++ b/crates/chain-state/src/in_memory.rs @@ -112,11 +112,13 @@ pub struct CanonicalInMemoryState { } impl CanonicalInMemoryState { - /// Create a new in memory state with the given blocks, numbers, and pending state. + /// Create a new in memory state with the given blocks, numbers, pending state and finalized + /// header if it exists. pub fn new( blocks: HashMap>, numbers: HashMap, pending: Option, + finalized: Option, ) -> Self { let in_memory_state = InMemoryState::new(blocks, numbers, pending); let head_state = in_memory_state.head_state(); @@ -124,7 +126,7 @@ impl CanonicalInMemoryState { Some(state) => state.block().block().header.clone(), None => SealedHeader::default(), }; - let chain_info_tracker = ChainInfoTracker::new(header); + let chain_info_tracker = ChainInfoTracker::new(header, finalized); let (canon_state_notification_sender, _canon_state_notification_receiver) = broadcast::channel(CANON_STATE_NOTIFICATION_CHANNEL_SIZE); @@ -137,9 +139,10 @@ impl CanonicalInMemoryState { Self { inner: Arc::new(inner) } } - /// Create a new in memory state with the given local head. - pub fn with_head(head: SealedHeader) -> Self { - let chain_info_tracker = ChainInfoTracker::new(head); + /// Create a new in memory state with the given local head and finalized header + /// if it exists. + pub fn with_head(head: SealedHeader, finalized: Option) -> Self { + let chain_info_tracker = ChainInfoTracker::new(head, finalized); let in_memory_state = InMemoryState::default(); let (canon_state_notification_sender, _canon_state_notification_receiver) = broadcast::channel(CANON_STATE_NOTIFICATION_CHANNEL_SIZE); @@ -889,7 +892,7 @@ mod tests { #[test] fn test_in_memory_state_chain_update() { - let state = CanonicalInMemoryState::new(HashMap::new(), HashMap::new(), None); + let state = CanonicalInMemoryState::new(HashMap::new(), HashMap::new(), None, None); let block1 = get_executed_block_with_number(0, B256::random()); let block2 = get_executed_block_with_number(0, B256::random()); let chain = NewCanonicalChain::Commit { new: vec![block1.clone()] }; @@ -925,7 +928,7 @@ mod tests { numbers.insert(2, block2.block().hash()); numbers.insert(3, block3.block().hash()); - let canonical_state = CanonicalInMemoryState::new(blocks, numbers, None); + let canonical_state = CanonicalInMemoryState::new(blocks, numbers, None, None); let historical: StateProviderBox = Box::new(MockStateProvider); diff --git a/crates/consensus/beacon/src/engine/test_utils.rs b/crates/consensus/beacon/src/engine/test_utils.rs index 8fd6f03d4..da63be296 100644 --- a/crates/consensus/beacon/src/engine/test_utils.rs +++ b/crates/consensus/beacon/src/engine/test_utils.rs @@ -399,9 +399,10 @@ where ) .expect("failed to create tree"), )); - let latest = self.base_config.chain_spec.genesis_header().seal_slow(); + let genesis_block = self.base_config.chain_spec.genesis_header().seal_slow(); + let blockchain_provider = - BlockchainProvider::with_latest(provider_factory.clone(), tree, latest); + BlockchainProvider::with_blocks(provider_factory.clone(), tree, genesis_block, None); let pruner = Pruner::<_, ProviderFactory<_>>::new( provider_factory.clone(), diff --git a/crates/engine/tree/src/tree/mod.rs b/crates/engine/tree/src/tree/mod.rs index 614d2d119..547d7e438 100644 --- a/crates/engine/tree/src/tree/mod.rs +++ b/crates/engine/tree/src/tree/mod.rs @@ -1736,7 +1736,7 @@ mod tests { let header = chain_spec.genesis_header().seal_slow(); let engine_api_tree_state = EngineApiTreeState::new(10, 10, header.num_hash()); - let canonical_in_memory_state = CanonicalInMemoryState::with_head(header); + let canonical_in_memory_state = CanonicalInMemoryState::with_head(header, None); let (to_payload_service, payload_command_rx) = unbounded_channel(); let payload_builder = PayloadBuilderHandle::new(to_payload_service); @@ -1797,7 +1797,7 @@ mod tests { let last_executed_block = blocks.last().unwrap().clone(); let pending = Some(BlockState::new(last_executed_block)); self.tree.canonical_in_memory_state = - CanonicalInMemoryState::new(state_by_hash, hash_by_number, pending); + CanonicalInMemoryState::new(state_by_hash, hash_by_number, pending, None); self.blocks = blocks; self diff --git a/crates/ethereum/engine/src/service.rs b/crates/ethereum/engine/src/service.rs index a258c26c9..96ec30ed5 100644 --- a/crates/ethereum/engine/src/service.rs +++ b/crates/ethereum/engine/src/service.rs @@ -159,7 +159,8 @@ mod tests { let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone()); let blockchain_db = - BlockchainProvider2::with_latest(provider_factory.clone(), SealedHeader::default()); + BlockchainProvider2::with_latest(provider_factory.clone(), SealedHeader::default()) + .unwrap(); let (_tx, rx) = watch::channel(FinishedExExHeight::NoExExs); let pruner = diff --git a/crates/storage/provider/src/providers/blockchain_provider.rs b/crates/storage/provider/src/providers/blockchain_provider.rs index 01a3a7925..a0aded2ce 100644 --- a/crates/storage/provider/src/providers/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/blockchain_provider.rs @@ -2,10 +2,10 @@ use crate::{ providers::StaticFileProvider, AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, CanonChainTracker, CanonStateNotifications, CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader, DatabaseProviderFactory, - DatabaseProviderRO, EvmEnvProvider, HeaderProvider, ProviderError, ProviderFactory, - PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, RequestsProvider, - StageCheckpointReader, StateProviderBox, StateProviderFactory, StaticFileProviderFactory, - TransactionVariant, TransactionsProvider, WithdrawalsProvider, + DatabaseProviderRO, EvmEnvProvider, FinalizedBlockReader, HeaderProvider, ProviderError, + ProviderFactory, PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, + RequestsProvider, StageCheckpointReader, StateProviderBox, StateProviderFactory, + StaticFileProviderFactory, TransactionVariant, TransactionsProvider, WithdrawalsProvider, }; use alloy_rpc_types_engine::ForkchoiceState; use reth_chain_state::CanonicalInMemoryState; @@ -55,14 +55,6 @@ impl Clone for BlockchainProvider2 { } } -impl BlockchainProvider2 { - /// Create new provider instance that wraps the database and the blockchain tree, using the - /// provided latest header to initialize the chain info tracker. - pub fn with_latest(database: ProviderFactory, latest: SealedHeader) -> Self { - Self { database, canonical_in_memory_state: CanonicalInMemoryState::with_head(latest) } - } -} - impl BlockchainProvider2 where DB: Database, @@ -75,12 +67,30 @@ where match provider.header_by_number(best.best_number)? { Some(header) => { drop(provider); - Ok(Self::with_latest(database, header.seal(best.best_hash))) + Ok(Self::with_latest(database, header.seal(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. + /// + /// 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_block_number = provider.last_finalized_block_number()?; + let finalized_header = provider.sealed_header(finalized_block_number)?; + Ok(Self { + database, + canonical_in_memory_state: CanonicalInMemoryState::with_head(latest, finalized_header), + }) + } + /// Gets a clone of `canonical_in_memory_state`. pub fn canonical_in_memory_state(&self) -> CanonicalInMemoryState { self.canonical_in_memory_state.clone() diff --git a/crates/storage/provider/src/providers/mod.rs b/crates/storage/provider/src/providers/mod.rs index 1728c5e4b..f704eb15d 100644 --- a/crates/storage/provider/src/providers/mod.rs +++ b/crates/storage/provider/src/providers/mod.rs @@ -1,18 +1,18 @@ use crate::{ AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, - BlockSource, BlockchainTreePendingStateProvider, CanonChainTracker, ChainSpecProvider, - ChangeSetReader, DatabaseProviderFactory, EvmEnvProvider, FullExecutionDataProvider, - HeaderProvider, ProviderError, PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, - RequestsProvider, StageCheckpointReader, StateProviderBox, StateProviderFactory, - StaticFileProviderFactory, TransactionVariant, TransactionsProvider, TreeViewer, - WithdrawalsProvider, + BlockSource, BlockchainTreePendingStateProvider, CanonChainTracker, CanonStateNotifications, + CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader, DatabaseProviderFactory, + EvmEnvProvider, FinalizedBlockReader, FullExecutionDataProvider, HeaderProvider, ProviderError, + PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, RequestsProvider, + StageCheckpointReader, StateProviderBox, StateProviderFactory, StaticFileProviderFactory, + TransactionVariant, TransactionsProvider, TreeViewer, WithdrawalsProvider, }; use reth_blockchain_tree_api::{ error::{CanonicalError, InsertBlockError}, BlockValidationKind, BlockchainTreeEngine, BlockchainTreeViewer, CanonicalOutcome, InsertPayloadOk, }; -use reth_chain_state::{CanonStateNotifications, CanonStateSubscriptions, ChainInfoTracker}; +use reth_chain_state::ChainInfoTracker; use reth_chainspec::{ChainInfo, ChainSpec}; use reth_db_api::{ database::Database, @@ -88,16 +88,6 @@ impl Clone for BlockchainProvider { } impl BlockchainProvider { - /// Create new provider instance that wraps the database and the blockchain tree, using the - /// provided latest header to initialize the chain info tracker. - pub fn with_latest( - database: ProviderFactory, - tree: Arc, - latest: SealedHeader, - ) -> Self { - Self { database, tree, chain_info: ChainInfoTracker::new(latest) } - } - /// Sets the treeviewer for the provider. #[doc(hidden)] pub fn with_tree(mut self, tree: Arc) -> Self { @@ -110,18 +100,31 @@ impl BlockchainProvider where DB: Database, { + /// Create new provider instance that wraps the database and the blockchain tree, using the + /// provided latest header to initialize the chain info tracker, alongside the finalized header + /// if it exists. + pub fn with_blocks( + database: ProviderFactory, + tree: Arc, + latest: SealedHeader, + finalized: Option, + ) -> Self { + Self { database, tree, chain_info: ChainInfoTracker::new(latest, finalized) } + } + /// Create a new provider using only the database and the tree, fetching the latest header from /// the database to initialize the provider. pub fn new(database: ProviderFactory, tree: Arc) -> 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, tree, header.seal(best.best_hash))) - } - None => Err(ProviderError::HeaderNotFound(best.best_number.into())), - } + let latest_header = provider + .header_by_number(best.best_number)? + .ok_or(ProviderError::HeaderNotFound(best.best_number.into()))?; + + let finalized_block_number = provider.last_finalized_block_number()?; + let finalized_header = provider.sealed_header(finalized_block_number)?; + + Ok(Self::with_blocks(database, tree, latest_header.seal(best.best_hash), finalized_header)) } /// Ensures that the given block number is canonical (synced)