mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
365 lines
12 KiB
Rust
365 lines
12 KiB
Rust
use crate::{
|
|
providers::state::{historical::HistoricalStateProvider, latest::LatestStateProvider},
|
|
traits::{BlockSource, ReceiptProvider},
|
|
BlockHashProvider, BlockNumProvider, BlockProvider, EvmEnvProvider, HeaderProvider,
|
|
ProviderError, StageCheckpointProvider, StateProviderBox, TransactionsProvider,
|
|
WithdrawalsProvider,
|
|
};
|
|
use reth_db::{database::Database, models::StoredBlockBodyIndices};
|
|
use reth_interfaces::Result;
|
|
use reth_primitives::{
|
|
stage::{StageCheckpoint, StageId},
|
|
Address, Block, BlockHash, BlockHashOrNumber, BlockNumber, BlockWithSenders, ChainInfo,
|
|
ChainSpec, Header, Receipt, SealedBlock, SealedHeader, TransactionMeta, TransactionSigned,
|
|
TransactionSignedNoHash, TxHash, TxNumber, Withdrawal, H256, U256,
|
|
};
|
|
use reth_revm_primitives::primitives::{BlockEnv, CfgEnv};
|
|
use std::{ops::RangeBounds, sync::Arc};
|
|
use tracing::trace;
|
|
|
|
mod provider;
|
|
pub use provider::{DatabaseProvider, DatabaseProviderRO, DatabaseProviderRW};
|
|
|
|
/// A common provider that fetches data from a database.
|
|
///
|
|
/// This provider implements most provider or provider factory traits.
|
|
#[derive(Debug)]
|
|
pub struct ProviderFactory<DB> {
|
|
/// Database
|
|
db: DB,
|
|
/// Chain spec
|
|
chain_spec: Arc<ChainSpec>,
|
|
}
|
|
|
|
impl<DB: Database> ProviderFactory<DB> {
|
|
/// Returns a provider with a created `DbTx` inside, which allows fetching data from the
|
|
/// database using different types of providers. Example: [`HeaderProvider`]
|
|
/// [`BlockHashProvider`]. This may fail if the inner read database transaction fails to open.
|
|
pub fn provider(&self) -> Result<DatabaseProviderRO<'_, DB>> {
|
|
Ok(DatabaseProvider::new(self.db.tx()?, self.chain_spec.clone()))
|
|
}
|
|
|
|
/// Returns a provider with a created `DbTxMut` inside, which allows fetching and updating
|
|
/// data from the database using different types of providers. Example: [`HeaderProvider`]
|
|
/// [`BlockHashProvider`]. This may fail if the inner read/write database transaction fails to
|
|
/// open.
|
|
pub fn provider_rw(&self) -> Result<DatabaseProviderRW<'_, DB>> {
|
|
Ok(DatabaseProviderRW(DatabaseProvider::new_rw(self.db.tx_mut()?, self.chain_spec.clone())))
|
|
}
|
|
}
|
|
|
|
impl<DB> ProviderFactory<DB> {
|
|
/// create new database provider
|
|
pub fn new(db: DB, chain_spec: Arc<ChainSpec>) -> Self {
|
|
Self { db, chain_spec }
|
|
}
|
|
}
|
|
|
|
impl<DB: Clone> Clone for ProviderFactory<DB> {
|
|
fn clone(&self) -> Self {
|
|
Self { db: self.db.clone(), chain_spec: Arc::clone(&self.chain_spec) }
|
|
}
|
|
}
|
|
|
|
impl<DB: Database> ProviderFactory<DB> {
|
|
/// Storage provider for latest block
|
|
pub fn latest(&self) -> Result<StateProviderBox<'_>> {
|
|
trace!(target: "providers::db", "Returning latest state provider");
|
|
Ok(Box::new(LatestStateProvider::new(self.db.tx()?)))
|
|
}
|
|
|
|
/// Storage provider for state at that given block
|
|
pub fn history_by_block_number(
|
|
&self,
|
|
mut block_number: BlockNumber,
|
|
) -> Result<StateProviderBox<'_>> {
|
|
let provider = self.provider()?;
|
|
|
|
if block_number == provider.best_block_number().unwrap_or_default() &&
|
|
block_number == provider.last_block_number().unwrap_or_default()
|
|
{
|
|
return Ok(Box::new(LatestStateProvider::new(provider.into_tx())))
|
|
}
|
|
|
|
// +1 as the changeset that we want is the one that was applied after this block.
|
|
block_number += 1;
|
|
|
|
trace!(target: "providers::db", ?block_number, "Returning historical state provider for block number");
|
|
Ok(Box::new(HistoricalStateProvider::new(provider.into_tx(), block_number)))
|
|
}
|
|
|
|
/// Storage provider for state at that given block hash
|
|
pub fn history_by_block_hash(&self, block_hash: BlockHash) -> Result<StateProviderBox<'_>> {
|
|
let provider = self.provider()?;
|
|
|
|
let mut block_number = provider
|
|
.block_number(block_hash)?
|
|
.ok_or(ProviderError::BlockHashNotFound(block_hash))?;
|
|
|
|
if block_number == provider.best_block_number().unwrap_or_default() &&
|
|
block_number == provider.last_block_number().unwrap_or_default()
|
|
{
|
|
return Ok(Box::new(LatestStateProvider::new(provider.into_tx())))
|
|
}
|
|
|
|
// +1 as the changeset that we want is the one that was applied after this block.
|
|
// as the changeset contains old values.
|
|
block_number += 1;
|
|
|
|
trace!(target: "providers::db", ?block_hash, "Returning historical state provider for block hash");
|
|
Ok(Box::new(HistoricalStateProvider::new(provider.into_tx(), block_number)))
|
|
}
|
|
}
|
|
|
|
impl<DB: Database> HeaderProvider for ProviderFactory<DB> {
|
|
fn header(&self, block_hash: &BlockHash) -> Result<Option<Header>> {
|
|
self.provider()?.header(block_hash)
|
|
}
|
|
|
|
fn header_by_number(&self, num: BlockNumber) -> Result<Option<Header>> {
|
|
self.provider()?.header_by_number(num)
|
|
}
|
|
|
|
fn header_td(&self, hash: &BlockHash) -> Result<Option<U256>> {
|
|
self.provider()?.header_td(hash)
|
|
}
|
|
|
|
fn header_td_by_number(&self, number: BlockNumber) -> Result<Option<U256>> {
|
|
self.provider()?.header_td_by_number(number)
|
|
}
|
|
|
|
fn headers_range(&self, range: impl RangeBounds<BlockNumber>) -> Result<Vec<Header>> {
|
|
self.provider()?.headers_range(range)
|
|
}
|
|
|
|
fn sealed_headers_range(
|
|
&self,
|
|
range: impl RangeBounds<BlockNumber>,
|
|
) -> Result<Vec<SealedHeader>> {
|
|
self.provider()?.sealed_headers_range(range)
|
|
}
|
|
|
|
fn sealed_header(&self, number: BlockNumber) -> Result<Option<SealedHeader>> {
|
|
self.provider()?.sealed_header(number)
|
|
}
|
|
}
|
|
|
|
impl<DB: Database> BlockHashProvider for ProviderFactory<DB> {
|
|
fn block_hash(&self, number: u64) -> Result<Option<H256>> {
|
|
self.provider()?.block_hash(number)
|
|
}
|
|
|
|
fn canonical_hashes_range(&self, start: BlockNumber, end: BlockNumber) -> Result<Vec<H256>> {
|
|
self.provider()?.canonical_hashes_range(start, end)
|
|
}
|
|
}
|
|
|
|
impl<DB: Database> BlockNumProvider for ProviderFactory<DB> {
|
|
fn chain_info(&self) -> Result<ChainInfo> {
|
|
self.provider()?.chain_info()
|
|
}
|
|
|
|
fn best_block_number(&self) -> Result<BlockNumber> {
|
|
self.provider()?.best_block_number()
|
|
}
|
|
|
|
fn last_block_number(&self) -> Result<BlockNumber> {
|
|
self.provider()?.last_block_number()
|
|
}
|
|
|
|
fn block_number(&self, hash: H256) -> Result<Option<BlockNumber>> {
|
|
self.provider()?.block_number(hash)
|
|
}
|
|
}
|
|
|
|
impl<DB: Database> BlockProvider for ProviderFactory<DB> {
|
|
fn find_block_by_hash(&self, hash: H256, source: BlockSource) -> Result<Option<Block>> {
|
|
self.provider()?.find_block_by_hash(hash, source)
|
|
}
|
|
|
|
fn block(&self, id: BlockHashOrNumber) -> Result<Option<Block>> {
|
|
self.provider()?.block(id)
|
|
}
|
|
|
|
fn pending_block(&self) -> Result<Option<SealedBlock>> {
|
|
self.provider()?.pending_block()
|
|
}
|
|
|
|
fn ommers(&self, id: BlockHashOrNumber) -> Result<Option<Vec<Header>>> {
|
|
self.provider()?.ommers(id)
|
|
}
|
|
|
|
fn block_body_indices(&self, number: BlockNumber) -> Result<Option<StoredBlockBodyIndices>> {
|
|
self.provider()?.block_body_indices(number)
|
|
}
|
|
|
|
fn block_with_senders(&self, number: BlockNumber) -> Result<Option<BlockWithSenders>> {
|
|
self.provider()?.block_with_senders(number)
|
|
}
|
|
}
|
|
|
|
impl<DB: Database> TransactionsProvider for ProviderFactory<DB> {
|
|
fn transaction_id(&self, tx_hash: TxHash) -> Result<Option<TxNumber>> {
|
|
self.provider()?.transaction_id(tx_hash)
|
|
}
|
|
|
|
fn transaction_by_id(&self, id: TxNumber) -> Result<Option<TransactionSigned>> {
|
|
self.provider()?.transaction_by_id(id)
|
|
}
|
|
|
|
fn transaction_by_hash(&self, hash: TxHash) -> Result<Option<TransactionSigned>> {
|
|
self.provider()?.transaction_by_hash(hash)
|
|
}
|
|
|
|
fn transaction_by_hash_with_meta(
|
|
&self,
|
|
tx_hash: TxHash,
|
|
) -> Result<Option<(TransactionSigned, TransactionMeta)>> {
|
|
self.provider()?.transaction_by_hash_with_meta(tx_hash)
|
|
}
|
|
|
|
fn transaction_block(&self, id: TxNumber) -> Result<Option<BlockNumber>> {
|
|
self.provider()?.transaction_block(id)
|
|
}
|
|
|
|
fn transactions_by_block(
|
|
&self,
|
|
id: BlockHashOrNumber,
|
|
) -> Result<Option<Vec<TransactionSigned>>> {
|
|
self.provider()?.transactions_by_block(id)
|
|
}
|
|
|
|
fn transactions_by_block_range(
|
|
&self,
|
|
range: impl RangeBounds<BlockNumber>,
|
|
) -> Result<Vec<Vec<TransactionSigned>>> {
|
|
self.provider()?.transactions_by_block_range(range)
|
|
}
|
|
|
|
fn transactions_by_tx_range(
|
|
&self,
|
|
range: impl RangeBounds<TxNumber>,
|
|
) -> Result<Vec<TransactionSignedNoHash>> {
|
|
self.provider()?.transactions_by_tx_range(range)
|
|
}
|
|
|
|
fn senders_by_tx_range(&self, range: impl RangeBounds<TxNumber>) -> Result<Vec<Address>> {
|
|
self.provider()?.senders_by_tx_range(range)
|
|
}
|
|
|
|
fn transaction_sender(&self, id: TxNumber) -> Result<Option<Address>> {
|
|
self.provider()?.transaction_sender(id)
|
|
}
|
|
}
|
|
|
|
impl<DB: Database> ReceiptProvider for ProviderFactory<DB> {
|
|
fn receipt(&self, id: TxNumber) -> Result<Option<Receipt>> {
|
|
self.provider()?.receipt(id)
|
|
}
|
|
|
|
fn receipt_by_hash(&self, hash: TxHash) -> Result<Option<Receipt>> {
|
|
self.provider()?.receipt_by_hash(hash)
|
|
}
|
|
|
|
fn receipts_by_block(&self, block: BlockHashOrNumber) -> Result<Option<Vec<Receipt>>> {
|
|
self.provider()?.receipts_by_block(block)
|
|
}
|
|
}
|
|
|
|
impl<DB: Database> WithdrawalsProvider for ProviderFactory<DB> {
|
|
fn withdrawals_by_block(
|
|
&self,
|
|
id: BlockHashOrNumber,
|
|
timestamp: u64,
|
|
) -> Result<Option<Vec<Withdrawal>>> {
|
|
self.provider()?.withdrawals_by_block(id, timestamp)
|
|
}
|
|
|
|
fn latest_withdrawal(&self) -> Result<Option<Withdrawal>> {
|
|
self.provider()?.latest_withdrawal()
|
|
}
|
|
}
|
|
|
|
impl<DB: Database> StageCheckpointProvider for ProviderFactory<DB> {
|
|
fn get_stage_checkpoint(&self, id: StageId) -> Result<Option<StageCheckpoint>> {
|
|
self.provider()?.get_stage_checkpoint(id)
|
|
}
|
|
}
|
|
|
|
impl<DB: Database> EvmEnvProvider for ProviderFactory<DB> {
|
|
fn fill_env_at(
|
|
&self,
|
|
cfg: &mut CfgEnv,
|
|
block_env: &mut BlockEnv,
|
|
at: BlockHashOrNumber,
|
|
) -> Result<()> {
|
|
self.provider()?.fill_env_at(cfg, block_env, at)
|
|
}
|
|
|
|
fn fill_env_with_header(
|
|
&self,
|
|
cfg: &mut CfgEnv,
|
|
block_env: &mut BlockEnv,
|
|
header: &Header,
|
|
) -> Result<()> {
|
|
self.provider()?.fill_env_with_header(cfg, block_env, header)
|
|
}
|
|
|
|
fn fill_block_env_at(&self, block_env: &mut BlockEnv, at: BlockHashOrNumber) -> Result<()> {
|
|
self.provider()?.fill_block_env_at(block_env, at)
|
|
}
|
|
|
|
fn fill_block_env_with_header(&self, block_env: &mut BlockEnv, header: &Header) -> Result<()> {
|
|
self.provider()?.fill_block_env_with_header(block_env, header)
|
|
}
|
|
|
|
fn fill_cfg_env_at(&self, cfg: &mut CfgEnv, at: BlockHashOrNumber) -> Result<()> {
|
|
self.provider()?.fill_cfg_env_at(cfg, at)
|
|
}
|
|
|
|
fn fill_cfg_env_with_header(&self, cfg: &mut CfgEnv, header: &Header) -> Result<()> {
|
|
self.provider()?.fill_cfg_env_with_header(cfg, header)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::ProviderFactory;
|
|
use crate::{BlockHashProvider, BlockNumProvider};
|
|
use reth_db::mdbx::{test_utils::create_test_db, EnvKind, WriteMap};
|
|
use reth_primitives::{ChainSpecBuilder, H256};
|
|
use std::sync::Arc;
|
|
|
|
#[test]
|
|
fn common_history_provider() {
|
|
let chain_spec = ChainSpecBuilder::mainnet().build();
|
|
let db = create_test_db::<WriteMap>(EnvKind::RW);
|
|
let provider = ProviderFactory::new(db, Arc::new(chain_spec));
|
|
let _ = provider.latest();
|
|
}
|
|
|
|
#[test]
|
|
fn default_chain_info() {
|
|
let chain_spec = ChainSpecBuilder::mainnet().build();
|
|
let db = create_test_db::<WriteMap>(EnvKind::RW);
|
|
let factory = ProviderFactory::new(db, Arc::new(chain_spec));
|
|
let provider = factory.provider().unwrap();
|
|
|
|
let chain_info = provider.chain_info().expect("should be ok");
|
|
assert_eq!(chain_info.best_number, 0);
|
|
assert_eq!(chain_info.best_hash, H256::zero());
|
|
}
|
|
|
|
#[test]
|
|
fn provider_flow() {
|
|
let chain_spec = ChainSpecBuilder::mainnet().build();
|
|
let db = create_test_db::<WriteMap>(EnvKind::RW);
|
|
let factory = ProviderFactory::new(db, Arc::new(chain_spec));
|
|
let provider = factory.provider().unwrap();
|
|
provider.block_hash(0).unwrap();
|
|
let provider_rw = factory.provider_rw().unwrap();
|
|
provider_rw.block_hash(0).unwrap();
|
|
provider.block_hash(0).unwrap();
|
|
}
|
|
}
|