Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(provider): consistent database view #6896

Merged
merged 1 commit into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions crates/interfaces/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
71 changes: 71 additions & 0 deletions crates/storage/provider/src/providers/consistent_view.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}
40 changes: 24 additions & 16 deletions crates/storage/provider/src/providers/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -31,28 +37,30 @@ use std::{
};
use tracing::trace;

pub use state::{
historical::{HistoricalStateProvider, HistoricalStateProviderRef},
latest::{LatestStateProvider, LatestStateProviderRef},
};

mod bundle_state_provider;
mod chain_info;
mod database;
pub use 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,
pub use state::{
historical::{HistoricalStateProvider, HistoricalStateProviderRef},
latest::{LatestStateProvider, LatestStateProviderRef},
};

mod bundle_state_provider;
pub use bundle_state_provider::BundleStateProvider;

mod chain_info;
use chain_info::ChainInfoTracker;

mod consistent_view;
pub use consistent_view::ConsistentDbView;

/// The main type for interacting with the blockchain.
///
/// This type serves as the main entry point for interacting with the blockchain and provides data
Expand Down
Loading