diff --git a/crates/interfaces/src/provider.rs b/crates/interfaces/src/provider.rs index c7b5f6258..e8a62e300 100644 --- a/crates/interfaces/src/provider.rs +++ b/crates/interfaces/src/provider.rs @@ -158,8 +158,11 @@ pub struct RootMismatch { #[derive(Clone, Debug, Error, PartialEq, Eq)] pub enum ConsistentViewError { /// Error thrown on attempt to initialize provider while node is still syncing. - #[error("node is syncing. best block: {0}")] - Syncing(BlockNumber), + #[error("node is syncing. best block: {best_block:?}")] + Syncing { + /// Best block diff. + best_block: GotExpected, + }, /// Error thrown on inconsistent database view. #[error("inconsistent database state: {tip:?}")] Inconsistent { diff --git a/crates/storage/provider/src/providers/consistent_view.rs b/crates/storage/provider/src/providers/consistent_view.rs index 3cad431b9..07d3614ef 100644 --- a/crates/storage/provider/src/providers/consistent_view.rs +++ b/crates/storage/provider/src/providers/consistent_view.rs @@ -1,5 +1,5 @@ -use crate::{BlockNumReader, DatabaseProviderFactory, DatabaseProviderRO, ProviderError}; -use reth_db::{cursor::DbCursorRO, database::Database, tables, transaction::DbTx}; +use crate::{BlockNumReader, DatabaseProviderFactory, DatabaseProviderRO, HeaderProvider}; +use reth_db::database::Database; use reth_interfaces::provider::ProviderResult; use reth_primitives::{GotExpected, B256}; use std::marker::PhantomData; @@ -40,24 +40,24 @@ where /// Creates new consistent database view with latest tip. pub fn new_with_latest_tip(provider: Provider) -> ProviderResult { - let tip = provider - .database_provider_ro()? - .tx_ref() - .cursor_read::()? - .last()?; - Ok(Self::new(provider, tip.map(|(_, hash)| hash))) + let provider_ro = provider.database_provider_ro()?; + let last_num = provider_ro.last_block_number()?; + let tip = provider_ro.sealed_header(last_num)?.map(|h| h.hash()); + Ok(Self::new(provider, tip)) } /// Creates new read-only provider and performs consistency checks on the current tip. pub fn provider_ro(&self) -> ProviderResult> { + // Create a new provider. let provider_ro = self.provider.database_provider_ro()?; - let last_entry = provider_ro - .tx_ref() - .cursor_read::() - .and_then(|mut cursor| cursor.last()) - .map_err(ProviderError::Database)?; - let tip = last_entry.map(|(_, hash)| hash); + // Check that the latest stored header number matches the number + // that consistent view was initialized with. + // The mismatch can happen if a new block was appended while + // the view was being used. + // We compare block hashes instead of block numbers to account for reorgs. + let last_num = provider_ro.last_block_number()?; + let tip = provider_ro.sealed_header(last_num)?.map(|h| h.hash()); if self.tip != tip { return Err(ConsistentViewError::Inconsistent { tip: GotExpected { got: tip, expected: self.tip }, @@ -65,9 +65,15 @@ where .into()) } + // Check that the best block number is the same as the latest stored header. + // This ensures that the consistent view cannot be used for initializing new providers + // if the node fell back to the staged sync. let best_block_number = provider_ro.best_block_number()?; - if last_entry.map(|(number, _)| number).unwrap_or_default() != best_block_number { - return Err(ConsistentViewError::Syncing(best_block_number).into()) + if last_num != best_block_number { + return Err(ConsistentViewError::Syncing { + best_block: GotExpected { got: best_block_number, expected: last_num }, + } + .into()) } Ok(provider_ro) diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 2aa12600e..85b46550d 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -1219,7 +1219,15 @@ impl BlockNumReader for DatabaseProvider { } fn last_block_number(&self) -> ProviderResult { - Ok(self.tx.cursor_read::()?.last()?.unwrap_or_default().0) + Ok(self + .tx + .cursor_read::()? + .last()? + .map(|(num, _)| num) + .max( + self.static_file_provider.get_highest_static_file_block(StaticFileSegment::Headers), + ) + .unwrap_or_default()) } fn block_number(&self, hash: B256) -> ProviderResult> {