mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: Implement BlockReader::block_with_senders_range (#7402)
This commit is contained in:
@ -331,6 +331,13 @@ impl<DB: Database> BlockReader for ProviderFactory<DB> {
|
||||
fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Block>> {
|
||||
self.provider()?.block_range(range)
|
||||
}
|
||||
|
||||
fn block_with_senders_range(
|
||||
&self,
|
||||
range: RangeInclusive<BlockNumber>,
|
||||
) -> ProviderResult<Vec<BlockWithSenders>> {
|
||||
self.provider()?.block_with_senders_range(range)
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB: Database> TransactionsProvider for ProviderFactory<DB> {
|
||||
|
||||
@ -1286,6 +1286,67 @@ impl<TX: DbTx> BlockNumReader for DatabaseProvider<TX> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tx: DbTx> DatabaseProvider<Tx> {
|
||||
fn process_block_range<F, R>(
|
||||
&self,
|
||||
range: RangeInclusive<BlockNumber>,
|
||||
mut assemble_block: F,
|
||||
) -> ProviderResult<Vec<R>>
|
||||
where
|
||||
F: FnMut(Range<TxNumber>, Header, Vec<Header>, Option<Withdrawals>) -> ProviderResult<R>,
|
||||
{
|
||||
if range.is_empty() {
|
||||
return Ok(Vec::new())
|
||||
}
|
||||
|
||||
let len = range.end().saturating_sub(*range.start()) as usize;
|
||||
let mut blocks = Vec::with_capacity(len);
|
||||
|
||||
let headers = self.headers_range(range)?;
|
||||
let mut ommers_cursor = self.tx.cursor_read::<tables::BlockOmmers>()?;
|
||||
let mut withdrawals_cursor = self.tx.cursor_read::<tables::BlockWithdrawals>()?;
|
||||
let mut block_body_cursor = self.tx.cursor_read::<tables::BlockBodyIndices>()?;
|
||||
|
||||
for header in headers {
|
||||
// If the body indices are not found, this means that the transactions either do
|
||||
// not exist in the database yet, or they do exit but are
|
||||
// not indexed. If they exist but are not indexed, we don't
|
||||
// have enough information to return the block anyways, so
|
||||
// we skip the block.
|
||||
if let Some((_, block_body_indices)) = block_body_cursor.seek_exact(header.number)? {
|
||||
let tx_range = block_body_indices.tx_num_range();
|
||||
|
||||
// If we are past shanghai, then all blocks should have a withdrawal list,
|
||||
// even if empty
|
||||
let withdrawals =
|
||||
if self.chain_spec.is_shanghai_active_at_timestamp(header.timestamp) {
|
||||
Some(
|
||||
withdrawals_cursor
|
||||
.seek_exact(header.number)?
|
||||
.map(|(_, w)| w.withdrawals)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let ommers =
|
||||
if self.chain_spec.final_paris_total_difficulty(header.number).is_some() {
|
||||
Vec::new()
|
||||
} else {
|
||||
ommers_cursor
|
||||
.seek_exact(header.number)?
|
||||
.map(|(_, o)| o.ommers)
|
||||
.unwrap_or_default()
|
||||
};
|
||||
if let Ok(b) = assemble_block(tx_range, header, ommers, withdrawals) {
|
||||
blocks.push(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(blocks)
|
||||
}
|
||||
}
|
||||
|
||||
impl<TX: DbTx> BlockReader for DatabaseProvider<TX> {
|
||||
fn find_block_by_hash(&self, hash: B256, source: BlockSource) -> ProviderResult<Option<Block>> {
|
||||
if source.is_database() {
|
||||
@ -1409,27 +1470,8 @@ impl<TX: DbTx> BlockReader for DatabaseProvider<TX> {
|
||||
}
|
||||
|
||||
fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Block>> {
|
||||
if range.is_empty() {
|
||||
return Ok(Vec::new())
|
||||
}
|
||||
|
||||
let len = range.end().saturating_sub(*range.start()) as usize;
|
||||
let mut blocks = Vec::with_capacity(len);
|
||||
|
||||
let headers = self.headers_range(range)?;
|
||||
let mut ommers_cursor = self.tx.cursor_read::<tables::BlockOmmers>()?;
|
||||
let mut withdrawals_cursor = self.tx.cursor_read::<tables::BlockWithdrawals>()?;
|
||||
let mut block_body_cursor = self.tx.cursor_read::<tables::BlockBodyIndices>()?;
|
||||
let mut tx_cursor = self.tx.cursor_read::<tables::Transactions>()?;
|
||||
|
||||
for header in headers {
|
||||
// If the body indices are not found, this means that the transactions either do
|
||||
// not exist in the database yet, or they do exit but are
|
||||
// not indexed. If they exist but are not indexed, we don't
|
||||
// have enough information to return the block anyways, so
|
||||
// we skip the block.
|
||||
if let Some((_, block_body_indices)) = block_body_cursor.seek_exact(header.number)? {
|
||||
let tx_range = block_body_indices.tx_num_range();
|
||||
self.process_block_range(range, |tx_range, header, ommers, withdrawals| {
|
||||
let body = if tx_range.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
@ -1438,34 +1480,53 @@ impl<TX: DbTx> BlockReader for DatabaseProvider<TX> {
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
};
|
||||
Ok(Block { header, body, ommers, withdrawals })
|
||||
})
|
||||
}
|
||||
|
||||
// If we are past shanghai, then all blocks should have a withdrawal list,
|
||||
// even if empty
|
||||
let withdrawals =
|
||||
if self.chain_spec.is_shanghai_active_at_timestamp(header.timestamp) {
|
||||
Some(
|
||||
withdrawals_cursor
|
||||
.seek_exact(header.number)?
|
||||
.map(|(_, w)| w.withdrawals)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let ommers =
|
||||
if self.chain_spec.final_paris_total_difficulty(header.number).is_some() {
|
||||
Vec::new()
|
||||
} else {
|
||||
ommers_cursor
|
||||
.seek_exact(header.number)?
|
||||
.map(|(_, o)| o.ommers)
|
||||
.unwrap_or_default()
|
||||
};
|
||||
fn block_with_senders_range(
|
||||
&self,
|
||||
range: RangeInclusive<BlockNumber>,
|
||||
) -> ProviderResult<Vec<BlockWithSenders>> {
|
||||
let mut tx_cursor = self.tx.cursor_read::<tables::Transactions>()?;
|
||||
let mut senders_cursor = self.tx.cursor_read::<tables::TransactionSenders>()?;
|
||||
|
||||
blocks.push(Block { header, body, ommers, withdrawals });
|
||||
self.process_block_range(range, |tx_range, header, ommers, withdrawals| {
|
||||
let (body, senders) = if tx_range.is_empty() {
|
||||
(Vec::new(), Vec::new())
|
||||
} else {
|
||||
let body = self
|
||||
.transactions_by_tx_range_with_cursor(tx_range.clone(), &mut tx_cursor)?
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<TransactionSigned>>();
|
||||
// fetch senders from the senders table
|
||||
let known_senders =
|
||||
senders_cursor
|
||||
.walk_range(tx_range.clone())?
|
||||
.collect::<Result<HashMap<_, _>, _>>()?;
|
||||
|
||||
let mut senders = Vec::with_capacity(body.len());
|
||||
for (tx_num, tx) in tx_range.zip(body.iter()) {
|
||||
match known_senders.get(&tx_num) {
|
||||
None => {
|
||||
// recover the sender from the transaction if not found
|
||||
let sender = tx
|
||||
.recover_signer_unchecked()
|
||||
.ok_or_else(|| ProviderError::SenderRecoveryError)?;
|
||||
senders.push(sender);
|
||||
}
|
||||
Some(sender) => senders.push(*sender),
|
||||
}
|
||||
}
|
||||
Ok(blocks)
|
||||
|
||||
(body, senders)
|
||||
};
|
||||
|
||||
Block { header, body, ommers, withdrawals }
|
||||
.try_with_senders_unchecked(senders)
|
||||
.map_err(|_| ProviderError::SenderRecoveryError)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -311,6 +311,13 @@ where
|
||||
fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Block>> {
|
||||
self.database.block_range(range)
|
||||
}
|
||||
|
||||
fn block_with_senders_range(
|
||||
&self,
|
||||
range: RangeInclusive<BlockNumber>,
|
||||
) -> ProviderResult<Vec<BlockWithSenders>> {
|
||||
self.database.block_with_senders_range(range)
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB, Tree> TransactionsProvider for BlockchainProvider<DB, Tree>
|
||||
|
||||
@ -1103,6 +1103,13 @@ impl BlockReader for StaticFileProvider {
|
||||
// Required data not present in static_files
|
||||
Err(ProviderError::UnsupportedProvider)
|
||||
}
|
||||
|
||||
fn block_with_senders_range(
|
||||
&self,
|
||||
_range: RangeInclusive<BlockNumber>,
|
||||
) -> ProviderResult<Vec<BlockWithSenders>> {
|
||||
Err(ProviderError::UnsupportedProvider)
|
||||
}
|
||||
}
|
||||
|
||||
impl WithdrawalsProvider for StaticFileProvider {
|
||||
|
||||
@ -483,6 +483,13 @@ impl BlockReader for MockEthProvider {
|
||||
|
||||
Ok(blocks)
|
||||
}
|
||||
|
||||
fn block_with_senders_range(
|
||||
&self,
|
||||
_range: RangeInclusive<BlockNumber>,
|
||||
) -> ProviderResult<Vec<BlockWithSenders>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockReaderIdExt for MockEthProvider {
|
||||
|
||||
@ -12,8 +12,8 @@ use reth_interfaces::provider::ProviderResult;
|
||||
use reth_primitives::{
|
||||
stage::{StageCheckpoint, StageId},
|
||||
trie::AccountProof,
|
||||
Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumber, Bytecode,
|
||||
ChainInfo, ChainSpec, Header, PruneCheckpoint, PruneSegment, Receipt, SealedBlock,
|
||||
Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumber, BlockWithSenders,
|
||||
Bytecode, ChainInfo, ChainSpec, Header, PruneCheckpoint, PruneSegment, Receipt, SealedBlock,
|
||||
SealedBlockWithSenders, SealedHeader, StorageKey, StorageValue, TransactionMeta,
|
||||
TransactionSigned, TransactionSignedNoHash, TxHash, TxNumber, Withdrawal, Withdrawals, B256,
|
||||
MAINNET, U256,
|
||||
@ -116,6 +116,13 @@ impl BlockReader for NoopProvider {
|
||||
fn block_range(&self, _range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Block>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
fn block_with_senders_range(
|
||||
&self,
|
||||
_range: RangeInclusive<BlockNumber>,
|
||||
) -> ProviderResult<Vec<BlockWithSenders>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockReaderIdExt for NoopProvider {
|
||||
|
||||
@ -133,6 +133,15 @@ pub trait BlockReader:
|
||||
///
|
||||
/// Note: returns only available blocks
|
||||
fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Block>>;
|
||||
|
||||
/// retrieves a range of blocks from the database, along with the senders of each
|
||||
/// transaction in the blocks.
|
||||
///
|
||||
/// The `transaction_kind` parameter determines whether to return its hash
|
||||
fn block_with_senders_range(
|
||||
&self,
|
||||
range: RangeInclusive<BlockNumber>,
|
||||
) -> ProviderResult<Vec<BlockWithSenders>>;
|
||||
}
|
||||
|
||||
/// Trait extension for `BlockReader`, for types that implement `BlockId` conversion.
|
||||
|
||||
Reference in New Issue
Block a user