diff --git a/crates/storage/provider/src/providers/blockchain_provider.rs b/crates/storage/provider/src/providers/blockchain_provider.rs index 92451fab15e7..7e7c7e8f441f 100644 --- a/crates/storage/provider/src/providers/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/blockchain_provider.rs @@ -457,6 +457,13 @@ impl WithdrawalsProvider for BlockchainProvider { ) -> ProviderResult> { self.consistent_provider()?.withdrawals_by_block(id, timestamp) } + + fn withdrawals_by_block_range( + &self, + range: RangeInclusive, + ) -> ProviderResult>> { + self.consistent_provider()?.withdrawals_by_block_range(range) + } } impl OmmersProvider for BlockchainProvider { diff --git a/crates/storage/provider/src/providers/consistent.rs b/crates/storage/provider/src/providers/consistent.rs index 3082faf45b51..c15928e34826 100644 --- a/crates/storage/provider/src/providers/consistent.rs +++ b/crates/storage/provider/src/providers/consistent.rs @@ -1151,6 +1151,20 @@ impl WithdrawalsProvider for ConsistentProvider { }, ) } + + fn withdrawals_by_block_range( + &self, + range: RangeInclusive, + ) -> ProviderResult>> { + self.get_in_memory_or_storage_by_block_range_while( + range, + |db_provider, range, _| db_provider.withdrawals_by_block_range(range), + |block_state, _| { + Some(block_state.block_ref().recovered_block().body().withdrawals().cloned()) + }, + |_| true, + ) + } } impl OmmersProvider for ConsistentProvider { diff --git a/crates/storage/provider/src/providers/database/mod.rs b/crates/storage/provider/src/providers/database/mod.rs index 157922b50362..04275ff2446f 100644 --- a/crates/storage/provider/src/providers/database/mod.rs +++ b/crates/storage/provider/src/providers/database/mod.rs @@ -549,6 +549,24 @@ impl WithdrawalsProvider for ProviderFactory { ) -> ProviderResult> { self.provider()?.withdrawals_by_block(id, timestamp) } + + fn withdrawals_by_block_range( + &self, + range: RangeInclusive, + ) -> ProviderResult>> { + self.static_file_provider.get_range_with_static_file_or_database( + StaticFileSegment::BlockMeta, + *range.start()..*range.end() + 1, + |static_file, range, _| { + static_file.withdrawals_by_block_range(range.start..=range.end.saturating_sub(1)) + }, + |range, _| { + self.provider()? + .withdrawals_by_block_range(range.start..=range.end.saturating_sub(1)) + }, + |_| true, + ) + } } impl OmmersProvider for ProviderFactory { diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 31105c33665b..6522678b27cc 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -1593,6 +1593,30 @@ impl> Withdrawals } Ok(None) } + + fn withdrawals_by_block_range( + &self, + range: RangeInclusive, + ) -> ProviderResult>> { + self.static_file_provider.get_range_with_static_file_or_database( + StaticFileSegment::BlockMeta, + *range.start()..*range.end() + 1, + |static_file, range, _| { + static_file.withdrawals_by_block_range(range.start..=range.end.saturating_sub(1)) + }, + |range, _| { + let mut withdrawals_cursor = self.tx.cursor_read::()?; + let mut withdrawals = vec![]; + + for number in range { + withdrawals + .push(withdrawals_cursor.seek_exact(number)?.map(|(_, w)| w.withdrawals)) + } + Ok(withdrawals) + }, + |_| true, + ) + } } impl OmmersProvider for DatabaseProvider { diff --git a/crates/storage/provider/src/providers/static_file/jar.rs b/crates/storage/provider/src/providers/static_file/jar.rs index 8a7654470d08..6c25ad5be2f2 100644 --- a/crates/storage/provider/src/providers/static_file/jar.rs +++ b/crates/storage/provider/src/providers/static_file/jar.rs @@ -370,6 +370,24 @@ impl WithdrawalsProvider for StaticFileJarProvider<'_, N> { // Only accepts block number queries Err(ProviderError::UnsupportedProvider) } + + fn withdrawals_by_block_range( + &self, + range: RangeInclusive, + ) -> ProviderResult>> { + let mut cursor = self.cursor()?; + let mut withdrawals_by_block = + Vec::with_capacity((range.end() - range.start() + 1) as usize); + + for num in range { + if let Some(withdrawals) = + cursor.get_one::(num.into())?.map(|w| w.withdrawals) + { + withdrawals_by_block.push(withdrawals) + } + } + Ok(withdrawals_by_block) + } } impl> OmmersProvider for StaticFileJarProvider<'_, N> { diff --git a/crates/storage/provider/src/providers/static_file/manager.rs b/crates/storage/provider/src/providers/static_file/manager.rs index 51b68f8f2cc6..cb840d2dd88c 100644 --- a/crates/storage/provider/src/providers/static_file/manager.rs +++ b/crates/storage/provider/src/providers/static_file/manager.rs @@ -18,7 +18,7 @@ use reth_db::{ lockfile::StorageLock, static_file::{ iter_static_files, BlockHashMask, BodyIndicesMask, HeaderMask, HeaderWithHashMask, - ReceiptMask, StaticFileCursor, TDWithHashMask, TransactionMask, + ReceiptMask, StaticFileCursor, TDWithHashMask, TransactionMask, WithdrawalsMask, }, table::{Decompress, Value}, tables, @@ -1696,6 +1696,20 @@ impl WithdrawalsProvider for StaticFileProvider { // Only accepts block number queries Err(ProviderError::UnsupportedProvider) } + + fn withdrawals_by_block_range( + &self, + range: RangeInclusive, + ) -> ProviderResult>> { + self.fetch_range_with_predicate( + StaticFileSegment::BlockMeta, + *range.start()..*range.end() + 1, + |cursor, number| { + cursor.get_one::(number.into()).map(|w| w.map(|w| w.withdrawals)) + }, + |_| true, + ) + } } impl> OmmersProvider for StaticFileProvider { diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index e6efe8012492..980a470978e8 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -765,6 +765,13 @@ impl WithdrawalsProvider for MockEthProvider { ) -> ProviderResult> { Ok(None) } + + fn withdrawals_by_block_range( + &self, + _range: RangeInclusive, + ) -> ProviderResult>> { + Ok(vec![]) + } } impl OmmersProvider for MockEthProvider { diff --git a/crates/storage/storage-api/src/chain.rs b/crates/storage/storage-api/src/chain.rs index cb2b38dc7b41..5b11c64d24d5 100644 --- a/crates/storage/storage-api/src/chain.rs +++ b/crates/storage/storage-api/src/chain.rs @@ -1,12 +1,12 @@ -use crate::{DBProvider, OmmersProvider, StorageLocation}; -use alloy_consensus::Header; +use crate::{DBProvider, OmmersProvider, StorageLocation, WithdrawalsProvider}; +use alloy_consensus::{BlockHeader, Header}; use alloy_primitives::BlockNumber; use reth_chainspec::{ChainSpecProvider, EthereumHardforks}; use reth_db::{ - cursor::{DbCursorRO, DbCursorRW}, + cursor::DbCursorRW, models::{StoredBlockOmmers, StoredBlockWithdrawals}, tables, - transaction::{DbTx, DbTxMut}, + transaction::DbTxMut, DbTxUnwindExt, }; use reth_primitives::TransactionSigned; @@ -141,7 +141,8 @@ impl BlockBodyReader for EthStorage where Provider: DBProvider + ChainSpecProvider - + OmmersProvider
, + + OmmersProvider
+ + WithdrawalsProvider, T: SignedTransaction, { type Block = reth_primitives::Block; @@ -151,25 +152,29 @@ where provider: &Provider, inputs: Vec>, ) -> ProviderResult::Body>> { + if inputs.is_empty() { + return Ok(vec![]) + } + + let block_range = inputs + .first() + .and_then(|(first, _)| inputs.last().map(|(last, _)| first.number()..=last.number())) + .expect("qed"); + // TODO: Ideally storage should hold its own copy of chain spec let chain_spec = provider.chain_spec(); - let mut withdrawals_cursor = provider.tx_ref().cursor_read::()?; - let mut bodies = Vec::with_capacity(inputs.len()); - for (header, transactions) in inputs { - // If we are past shanghai, then all blocks should have a withdrawal list, - // even if empty - let withdrawals = if chain_spec.is_shanghai_active_at_timestamp(header.timestamp) { - withdrawals_cursor - .seek_exact(header.number)? - .map(|(_, w)| w.withdrawals) - .unwrap_or_default() - .into() - } else { - None - }; + for ((header, transactions), withdrawals) in + inputs.into_iter().zip(provider.withdrawals_by_block_range(block_range)?) + { + // // If we are past shanghai, then all blocks should have a withdrawal list, + // // even if empty + // let withdrawals = + // chain_spec.is_shanghai_active_at_timestamp(header.timestamp). + // then_some(withdrawals); + let ommers = if chain_spec.final_paris_total_difficulty(header.number).is_some() { Vec::new() } else { diff --git a/crates/storage/storage-api/src/noop.rs b/crates/storage/storage-api/src/noop.rs index 20d975852f4d..2bcc9ddf0ec9 100644 --- a/crates/storage/storage-api/src/noop.rs +++ b/crates/storage/storage-api/src/noop.rs @@ -538,6 +538,13 @@ impl WithdrawalsProvider for NoopProvider ProviderResult> { Ok(None) } + + fn withdrawals_by_block_range( + &self, + _range: RangeInclusive, + ) -> ProviderResult>> { + Ok(vec![]) + } } impl OmmersProvider for NoopProvider { diff --git a/crates/storage/storage-api/src/withdrawals.rs b/crates/storage/storage-api/src/withdrawals.rs index fdfb27aa7072..83f5926daf1c 100644 --- a/crates/storage/storage-api/src/withdrawals.rs +++ b/crates/storage/storage-api/src/withdrawals.rs @@ -1,5 +1,7 @@ use alloy_eips::{eip4895::Withdrawals, BlockHashOrNumber}; +use alloy_primitives::BlockNumber; use reth_storage_errors::provider::ProviderResult; +use std::ops::RangeInclusive; /// Client trait for fetching [`alloy_eips::eip4895::Withdrawal`] related data. #[auto_impl::auto_impl(&, Arc)] @@ -10,4 +12,13 @@ pub trait WithdrawalsProvider: Send + Sync { id: BlockHashOrNumber, timestamp: u64, ) -> ProviderResult>; + + /// Returns the withdrawals per block within the requested block range. + /// + /// If it's dealing with a pre-shangai block, the withdrawal element will be `None`, otherwise + /// `Some`. + fn withdrawals_by_block_range( + &self, + range: RangeInclusive, + ) -> ProviderResult>>; }