feat(provider): consistent database view (#6896)

This commit is contained in:
Roman Krasiuk
2024-03-01 13:41:14 +01:00
committed by GitHub
parent 3d4cac87d3
commit 02111c4213
3 changed files with 113 additions and 17 deletions

View File

@@ -153,3 +153,20 @@ pub struct RootMismatch {
/// The target block hash.
pub block_hash: BlockHash,
}
/// Consistent database view error.
#[derive(Error, Debug)]
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 thrown on inconsistent database view.
#[error("inconsistent database state: {tip:?}")]
InconsistentView {
/// The tip diff.
tip: GotExpected<Option<B256>>,
},
/// Underlying provider error.
#[error(transparent)]
Provider(#[from] ProviderError),
}

View File

@@ -0,0 +1,71 @@
use crate::{BlockNumReader, DatabaseProviderFactory, DatabaseProviderRO, ProviderError};
use reth_db::{cursor::DbCursorRO, database::Database, tables, transaction::DbTx};
use reth_interfaces::provider::{ConsistentViewError, ProviderResult};
use reth_primitives::{GotExpected, B256};
use std::marker::PhantomData;
/// A consistent view over state in the database.
///
/// View gets initialized with the latest or provided tip.
/// Upon every attempt to create a database provider, the view will
/// perform a consistency check of current tip against the initial one.
///
/// ## Usage
///
/// The view should only be used outside of staged-sync.
/// Otherwise, any attempt to create a provider will result in [ConsistentViewError::Syncing].
#[derive(Clone, Debug)]
pub struct ConsistentDbView<DB, Provider> {
database: PhantomData<DB>,
provider: Provider,
tip: Option<B256>,
}
impl<DB, Provider> ConsistentDbView<DB, Provider>
where
DB: Database,
Provider: DatabaseProviderFactory<DB>,
{
/// Creates new consistent database view.
pub fn new(provider: Provider) -> Self {
Self { database: PhantomData, provider, tip: None }
}
/// Initializes the view with provided tip.
pub fn with_tip(mut self, tip: B256) -> Self {
self.tip = Some(tip);
self
}
/// Initializes the view with latest tip.
pub fn with_latest_tip(mut self) -> ProviderResult<Self> {
let provider = self.provider.database_provider_ro()?;
let tip = provider.tx_ref().cursor_read::<tables::CanonicalHeaders>()?.last()?;
self.tip = tip.map(|(_, hash)| hash);
Ok(self)
}
/// Creates new read-only provider and performs consistency checks on the current tip.
pub fn provider_ro(&self) -> Result<DatabaseProviderRO<DB>, ConsistentViewError> {
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);
if self.tip != tip {
return Err(ConsistentViewError::InconsistentView {
tip: GotExpected { got: tip, expected: self.tip },
})
}
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))
}
Ok(provider_ro)
}
}

View File

@@ -1,14 +1,20 @@
use crate::{
AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt,
BlockchainTreePendingStateProvider, BundleStateDataProvider, CanonChainTracker,
BlockSource, BlockchainTreePendingStateProvider, BundleStateDataProvider, CanonChainTracker,
CanonStateNotifications, CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader,
DatabaseProviderFactory, EvmEnvProvider, HeaderProvider, ProviderError, PruneCheckpointReader,
ReceiptProvider, ReceiptProviderIdExt, StageCheckpointReader, StateProviderBox,
StateProviderFactory, TransactionVariant, TransactionsProvider, WithdrawalsProvider,
};
use reth_db::{database::Database, models::StoredBlockBodyIndices};
use reth_db::{
database::Database,
models::{AccountBeforeTx, StoredBlockBodyIndices},
};
use reth_interfaces::{
blockchain_tree::{BlockchainTreeEngine, BlockchainTreeViewer},
blockchain_tree::{
error::InsertBlockError, BlockValidationKind, BlockchainTreeEngine, BlockchainTreeViewer,
CanonicalOutcome, InsertPayloadOk,
},
consensus::ForkchoiceState,
provider::ProviderResult,
RethError, RethResult,
@@ -31,27 +37,29 @@ use std::{
};
use tracing::trace;
mod database;
pub use database::*;
mod static_file;
pub use static_file::{
StaticFileJarProvider, StaticFileProvider, StaticFileProviderRW, StaticFileProviderRWRefMut,
StaticFileWriter,
};
mod state;
pub use state::{
historical::{HistoricalStateProvider, HistoricalStateProviderRef},
latest::{LatestStateProvider, LatestStateProviderRef},
};
mod bundle_state_provider;
mod chain_info;
mod database;
mod static_file;
pub use static_file::{
StaticFileJarProvider, StaticFileProvider, StaticFileProviderRW, StaticFileProviderRWRefMut,
StaticFileWriter,
};
mod state;
use crate::{providers::chain_info::ChainInfoTracker, traits::BlockSource};
pub use bundle_state_provider::BundleStateProvider;
pub use database::*;
use reth_db::models::AccountBeforeTx;
use reth_interfaces::blockchain_tree::{
error::InsertBlockError, BlockValidationKind, CanonicalOutcome, InsertPayloadOk,
};
mod chain_info;
use chain_info::ChainInfoTracker;
mod consistent_view;
pub use consistent_view::ConsistentDbView;
/// The main type for interacting with the blockchain.
///