use crate::{
BlockIdReader, BlockNumReader, HeaderProvider, ReceiptProvider, ReceiptProviderIdExt,
RequestsProvider, TransactionVariant, TransactionsProvider, WithdrawalsProvider,
};
use reth_db_api::models::StoredBlockBodyIndices;
use reth_primitives::{
Block, BlockHashOrNumber, BlockId, BlockNumber, BlockNumberOrTag, BlockWithSenders, Header,
Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, B256,
};
use reth_storage_errors::provider::ProviderResult;
use std::ops::RangeInclusive;
/// A helper enum that represents the origin of the requested block.
///
/// This helper type's sole purpose is to give the caller more control over from where blocks can be
/// fetched.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum BlockSource {
/// Check all available sources.
///
/// Note: it's expected that looking up pending blocks is faster than looking up blocks in the
/// database so this prioritizes Pending > Database.
#[default]
Any,
/// The block was fetched from the pending block source, the blockchain tree that buffers
/// blocks that are not yet part of the canonical chain.
Pending,
/// The block must be part of the canonical chain.
Canonical,
}
impl BlockSource {
/// Returns `true` if the block source is `Pending` or `Any`.
pub const fn is_pending(&self) -> bool {
matches!(self, Self::Pending | Self::Any)
}
/// Returns `true` if the block source is `Canonical` or `Any`.
pub const fn is_canonical(&self) -> bool {
matches!(self, Self::Canonical | Self::Any)
}
}
/// Api trait for fetching `Block` related data.
///
/// If not requested otherwise, implementers of this trait should prioritize fetching blocks from
/// the database.
#[auto_impl::auto_impl(&, Arc)]
pub trait BlockReader:
BlockNumReader
+ HeaderProvider
+ TransactionsProvider
+ ReceiptProvider
+ RequestsProvider
+ WithdrawalsProvider
+ Send
+ Sync
{
/// Tries to find in the given block source.
///
/// Note: this only operates on the hash because the number might be ambiguous.
///
/// Returns `None` if block is not found.
fn find_block_by_hash(&self, hash: B256, source: BlockSource) -> ProviderResult>;
/// Returns the block with given id from the database.
///
/// Returns `None` if block is not found.
fn block(&self, id: BlockHashOrNumber) -> ProviderResult >;
/// Returns the pending block if available
///
/// Note: This returns a [SealedBlock] because it's expected that this is sealed by the provider
/// and the caller does not know the hash.
fn pending_block(&self) -> ProviderResult >;
/// Returns the pending block if available
///
/// Note: This returns a [SealedBlockWithSenders] because it's expected that this is sealed by
/// the provider and the caller does not know the hash.
fn pending_block_with_senders(&self) -> ProviderResult >;
/// Returns the pending block and receipts if available.
fn pending_block_and_receipts(&self) -> ProviderResult )>>;
/// Returns the ommers/uncle headers of the given block from the database.
///
/// Returns `None` if block is not found.
fn ommers(&self, id: BlockHashOrNumber) -> ProviderResult >>;
/// Returns the block with matching hash from the database.
///
/// Returns `None` if block is not found.
fn block_by_hash(&self, hash: B256) -> ProviderResult > {
self.block(hash.into())
}
/// Returns the block with matching number from database.
///
/// Returns `None` if block is not found.
fn block_by_number(&self, num: u64) -> ProviderResult > {
self.block(num.into())
}
/// Returns the block body indices with matching number from database.
///
/// Returns `None` if block is not found.
fn block_body_indices(&self, num: u64) -> ProviderResult >;
/// Returns the block with senders with matching number or hash from database.
///
/// Returns the block's transactions in the requested variant.
///
/// Returns `None` if block is not found.
fn block_with_senders(
&self,
id: BlockHashOrNumber,
transaction_kind: TransactionVariant,
) -> ProviderResult >;
/// Returns the sealed block with senders with matching number or hash from database.
///
/// Returns the block's transactions in the requested variant.
///
/// Returns `None` if block is not found.
fn sealed_block_with_senders(
&self,
id: BlockHashOrNumber,
transaction_kind: TransactionVariant,
) -> ProviderResult >;
/// Returns all blocks in the given inclusive range.
///
/// Note: returns only available blocks
fn block_range(&self, range: RangeInclusive) -> ProviderResult>;
/// Returns a range of blocks from the database, along with the senders of each
/// transaction in the blocks.
fn block_with_senders_range(
&self,
range: RangeInclusive,
) -> ProviderResult>;
/// Returns a range of sealed blocks from the database, along with the senders of each
/// transaction in the blocks.
fn sealed_block_with_senders_range(
&self,
range: RangeInclusive,
) -> ProviderResult>;
}
/// Trait extension for `BlockReader`, for types that implement `BlockId` conversion.
///
/// The `BlockReader` trait should be implemented on types that can retrieve a block from either
/// a block number or hash. However, it might be desirable to fetch a block from a `BlockId` type,
/// which can be a number, hash, or tag such as `BlockNumberOrTag::Safe`.
///
/// Resolving tags requires keeping track of block hashes or block numbers associated with the tag,
/// so this trait can only be implemented for types that implement `BlockIdReader`. The
/// `BlockIdReader` methods should be used to resolve `BlockId`s to block numbers or hashes, and
/// retrieving the block should be done using the type's `BlockReader` methods.
#[auto_impl::auto_impl(&, Arc)]
pub trait BlockReaderIdExt: BlockReader + BlockIdReader + ReceiptProviderIdExt {
/// Returns the block with matching tag from the database
///
/// Returns `None` if block is not found.
fn block_by_number_or_tag(&self, id: BlockNumberOrTag) -> ProviderResult> {
self.convert_block_number(id)?.map_or_else(|| Ok(None), |num| self.block(num.into()))
}
/// Returns the pending block header if available
///
/// Note: This returns a [`SealedHeader`] because it's expected that this is sealed by the
/// provider and the caller does not know the hash.
fn pending_header(&self) -> ProviderResult > {
self.sealed_header_by_id(BlockNumberOrTag::Pending.into())
}
/// Returns the latest block header if available
///
/// Note: This returns a [`SealedHeader`] because it's expected that this is sealed by the
/// provider and the caller does not know the hash.
fn latest_header(&self) -> ProviderResult > {
self.sealed_header_by_id(BlockNumberOrTag::Latest.into())
}
/// Returns the safe block header if available
///
/// Note: This returns a [`SealedHeader`] because it's expected that this is sealed by the
/// provider and the caller does not know the hash.
fn safe_header(&self) -> ProviderResult > {
self.sealed_header_by_id(BlockNumberOrTag::Safe.into())
}
/// Returns the finalized block header if available
///
/// Note: This returns a [`SealedHeader`] because it's expected that this is sealed by the
/// provider and the caller does not know the hash.
fn finalized_header(&self) -> ProviderResult > {
self.sealed_header_by_id(BlockNumberOrTag::Finalized.into())
}
/// Returns the block with the matching [`BlockId`] from the database.
///
/// Returns `None` if block is not found.
fn block_by_id(&self, id: BlockId) -> ProviderResult >;
/// Returns the block with senders with matching [`BlockId`].
///
/// Returns the block's transactions in the requested variant.
///
/// Returns `None` if block is not found.
fn block_with_senders_by_id(
&self,
id: BlockId,
transaction_kind: TransactionVariant,
) -> ProviderResult > {
match id {
BlockId::Hash(hash) => {
self.block_with_senders(hash.block_hash.into(), transaction_kind)
}
BlockId::Number(num) => self.convert_block_number(num)?.map_or_else(
|| Ok(None),
|num| self.block_with_senders(num.into(), transaction_kind),
),
}
}
/// Returns the header with matching tag from the database
///
/// Returns `None` if header is not found.
fn header_by_number_or_tag(&self, id: BlockNumberOrTag) -> ProviderResult > {
self.convert_block_number(id)?
.map_or_else(|| Ok(None), |num| self.header_by_hash_or_number(num.into()))
}
/// Returns the header with matching tag from the database
///
/// Returns `None` if header is not found.
fn sealed_header_by_number_or_tag(
&self,
id: BlockNumberOrTag,
) -> ProviderResult > {
self.convert_block_number(id)?
.map_or_else(|| Ok(None), |num| self.header_by_hash_or_number(num.into()))?
.map_or_else(|| Ok(None), |h| Ok(Some(h.seal_slow())))
}
/// Returns the sealed header with the matching `BlockId` from the database.
///
/// Returns `None` if header is not found.
fn sealed_header_by_id(&self, id: BlockId) -> ProviderResult >;
/// Returns the header with the matching `BlockId` from the database.
///
/// Returns `None` if header is not found.
fn header_by_id(&self, id: BlockId) -> ProviderResult >;
/// Returns the ommers with the matching tag from the database.
fn ommers_by_number_or_tag(&self, id: BlockNumberOrTag) -> ProviderResult >> {
self.convert_block_number(id)?.map_or_else(|| Ok(None), |num| self.ommers(num.into()))
}
/// Returns the ommers with the matching `BlockId` from the database.
///
/// Returns `None` if block is not found.
fn ommers_by_id(&self, id: BlockId) -> ProviderResult >>;
}