fix(provider): consider static files in last block num/hash lookups (#7327)

This commit is contained in:
Roman Krasiuk
2024-03-26 15:06:23 +01:00
committed by GitHub
parent 5968e9f3da
commit 1939939bc1
3 changed files with 36 additions and 19 deletions

View File

@ -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<BlockNumber>,
},
/// Error thrown on inconsistent database view.
#[error("inconsistent database state: {tip:?}")]
Inconsistent {

View File

@ -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<Self> {
let tip = provider
.database_provider_ro()?
.tx_ref()
.cursor_read::<tables::CanonicalHeaders>()?
.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<DatabaseProviderRO<DB>> {
// Create a new provider.
let provider_ro = self.provider.database_provider_ro()?;
let last_entry = provider_ro
.tx_ref()
.cursor_read::<tables::CanonicalHeaders>()
.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)

View File

@ -1219,7 +1219,15 @@ impl<TX: DbTx> BlockNumReader for DatabaseProvider<TX> {
}
fn last_block_number(&self) -> ProviderResult<BlockNumber> {
Ok(self.tx.cursor_read::<tables::CanonicalHeaders>()?.last()?.unwrap_or_default().0)
Ok(self
.tx
.cursor_read::<tables::CanonicalHeaders>()?
.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<Option<BlockNumber>> {