feat: Implement BlockReader::block_with_senders_range (#7402)

This commit is contained in:
Abner Zheng
2024-04-12 23:57:00 +08:00
committed by GitHub
parent aa1fbfcba6
commit 7918759b2f
7 changed files with 160 additions and 55 deletions

View File

@ -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> {

View File

@ -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)
})
}
}

View File

@ -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>

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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.