diff --git a/crates/chain-state/Cargo.toml b/crates/chain-state/Cargo.toml index 2f4bed00f9..54f7ac43de 100644 --- a/crates/chain-state/Cargo.toml +++ b/crates/chain-state/Cargo.toml @@ -57,13 +57,13 @@ revm.workspace = true [features] test-utils = [ - "alloy-signer", - "alloy-signer-local", - "rand", - "revm", - "reth-chainspec/test-utils", - "reth-primitives/test-utils", - "reth-trie/test-utils", - "revm?/test-utils", - "reth-primitives-traits/test-utils" + "alloy-signer", + "alloy-signer-local", + "rand", + "revm", + "reth-chainspec/test-utils", + "reth-primitives/test-utils", + "reth-primitives-traits/test-utils", + "reth-trie/test-utils", + "revm?/test-utils", ] diff --git a/crates/chain-state/src/in_memory.rs b/crates/chain-state/src/in_memory.rs index f9a10fc554..933439a7c1 100644 --- a/crates/chain-state/src/in_memory.rs +++ b/crates/chain-state/src/in_memory.rs @@ -15,6 +15,7 @@ use reth_primitives::{ BlockWithSenders, NodePrimitives, Receipts, SealedBlock, SealedBlockWithSenders, SealedHeader, TransactionMeta, TransactionSigned, }; +use reth_primitives_traits::BlockBody as _; use reth_storage_api::StateProviderBox; use reth_trie::{updates::TrieUpdates, HashedPostState}; use std::{collections::BTreeMap, sync::Arc, time::Instant}; @@ -547,8 +548,13 @@ where /// Returns a `TransactionSigned` for the given `TxHash` if found. pub fn transaction_by_hash(&self, hash: TxHash) -> Option { for block_state in self.canonical_chain() { - if let Some(tx) = - block_state.block_ref().block().body.transactions().find(|tx| tx.hash() == hash) + if let Some(tx) = block_state + .block_ref() + .block() + .body + .transactions() + .iter() + .find(|tx| tx.hash() == hash) { return Some(tx.clone()) } @@ -568,6 +574,7 @@ where .block() .body .transactions() + .iter() .enumerate() .find(|(_, tx)| tx.hash() == tx_hash) { @@ -748,6 +755,7 @@ impl BlockState { .block() .body .transactions() + .iter() .find(|tx| tx.hash() == hash) .cloned() }) @@ -764,6 +772,7 @@ impl BlockState { .block() .body .transactions() + .iter() .enumerate() .find(|(_, tx)| tx.hash() == tx_hash) .map(|(index, tx)| { diff --git a/crates/optimism/node/src/node.rs b/crates/optimism/node/src/node.rs index 82b2ce2ebc..cc0a61833e 100644 --- a/crates/optimism/node/src/node.rs +++ b/crates/optimism/node/src/node.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use alloy_consensus::Header; use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig}; -use reth_chainspec::{EthChainSpec, Hardforks}; +use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks}; use reth_db::transaction::{DbTx, DbTxMut}; use reth_evm::{execute::BasicBlockExecutorProvider, ConfigureEvm}; use reth_network::{NetworkConfig, NetworkHandle, NetworkManager, PeersInfo}; @@ -32,8 +32,8 @@ use reth_optimism_rpc::{ use reth_payload_builder::{PayloadBuilderHandle, PayloadBuilderService}; use reth_primitives::BlockBody; use reth_provider::{ - providers::ChainStorage, BlockBodyWriter, CanonStateSubscriptions, DBProvider, EthStorage, - ProviderResult, + providers::ChainStorage, BlockBodyReader, BlockBodyWriter, CanonStateSubscriptions, + ChainSpecProvider, DBProvider, EthStorage, ProviderResult, ReadBodyInput, }; use reth_rpc_server_types::RethRpcModule; use reth_tracing::tracing::{debug, info}; @@ -72,7 +72,31 @@ impl> BlockBodyWriter for } } +impl> + BlockBodyReader for OpStorage +{ + type Block = reth_primitives::Block; + + fn read_block_bodies( + &self, + provider: &Provider, + inputs: Vec>, + ) -> ProviderResult> { + self.0.read_block_bodies(provider, inputs) + } +} + impl ChainStorage for OpStorage { + fn reader( + &self, + ) -> impl reth_provider::ChainStorageReader, OpPrimitives> + where + TX: DbTx + 'static, + Types: reth_provider::providers::NodeTypesForProvider, + { + self + } + fn writer( &self, ) -> impl reth_provider::ChainStorageWriter, OpPrimitives> @@ -83,6 +107,7 @@ impl ChainStorage for OpStorage { self } } + /// Type configuration for a regular Optimism node. #[derive(Debug, Default, Clone)] #[non_exhaustive] diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index b381b7dd80..d7babfc628 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -661,12 +661,6 @@ impl BlockBody { pub fn blob_versioned_hashes(&self) -> Vec<&B256> { self.blob_versioned_hashes_iter().collect() } - - /// Returns an iterator over all transactions. - #[inline] - pub fn transactions(&self) -> impl Iterator + '_ { - self.transactions.iter() - } } impl InMemorySize for BlockBody { diff --git a/crates/prune/prune/src/segments/mod.rs b/crates/prune/prune/src/segments/mod.rs index b3b40aab5b..e828512fa8 100644 --- a/crates/prune/prune/src/segments/mod.rs +++ b/crates/prune/prune/src/segments/mod.rs @@ -148,6 +148,7 @@ impl PruneInput { mod tests { use super::*; use alloy_primitives::B256; + use reth_primitives_traits::BlockBody; use reth_provider::{ providers::BlockchainProvider2, test_utils::{create_test_provider_factory, MockEthProvider}, @@ -245,7 +246,7 @@ mod tests { // Calculate the total number of transactions let num_txs = - blocks.iter().map(|block| block.body.transactions().count() as u64).sum::(); + blocks.iter().map(|block| block.body.transactions().len() as u64).sum::(); assert_eq!(range, 0..=num_txs - 1); } @@ -292,7 +293,7 @@ mod tests { // Calculate the total number of transactions let num_txs = - blocks.iter().map(|block| block.body.transactions().count() as u64).sum::(); + blocks.iter().map(|block| block.body.transactions().len() as u64).sum::(); assert_eq!(range, 0..=num_txs - 1,); } @@ -327,7 +328,7 @@ mod tests { // Get the last tx number // Calculate the total number of transactions let num_txs = - blocks.iter().map(|block| block.body.transactions().count() as u64).sum::(); + blocks.iter().map(|block| block.body.transactions().len() as u64).sum::(); let max_range = num_txs - 1; // Create a prune input with a previous checkpoint that is the last tx number diff --git a/crates/storage/errors/src/provider.rs b/crates/storage/errors/src/provider.rs index 9e6720b844..e69c0343f5 100644 --- a/crates/storage/errors/src/provider.rs +++ b/crates/storage/errors/src/provider.rs @@ -133,6 +133,8 @@ pub enum ProviderError { StorageLockError(StorageLockError), /// Storage writer error. UnifiedStorageWriterError(UnifiedStorageWriterError), + /// Received invalid output from configured storage implementation. + InvalidStorageOutput, } impl From for ProviderError { diff --git a/crates/storage/provider/src/providers/blockchain_provider.rs b/crates/storage/provider/src/providers/blockchain_provider.rs index 6801abee40..d90b227c11 100644 --- a/crates/storage/provider/src/providers/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/blockchain_provider.rs @@ -30,6 +30,7 @@ use reth_primitives::{ Account, Block, BlockWithSenders, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, StorageEntry, TransactionMeta, TransactionSigned, TransactionSignedNoHash, }; +use reth_primitives_traits::BlockBody as _; use reth_prune_types::{PruneCheckpoint, PruneSegment}; use reth_stages_types::{StageCheckpoint, StageId}; use reth_storage_api::{DBProvider, StorageChangeSetReader}; @@ -796,6 +797,7 @@ mod tests { use reth_primitives::{ BlockExt, Receipt, SealedBlock, StaticFileSegment, TransactionSignedNoHash, }; + use reth_primitives_traits::BlockBody as _; use reth_storage_api::{ BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, ChangeSetReader, DatabaseProviderFactory, HeaderProvider, ReceiptProvider, diff --git a/crates/storage/provider/src/providers/consistent.rs b/crates/storage/provider/src/providers/consistent.rs index fc9d739b0f..740392ad99 100644 --- a/crates/storage/provider/src/providers/consistent.rs +++ b/crates/storage/provider/src/providers/consistent.rs @@ -977,7 +977,7 @@ impl TransactionsProvider for ConsistentProvider { fn transaction_by_hash(&self, hash: TxHash) -> ProviderResult> { if let Some(tx) = self.head_block.as_ref().and_then(|b| b.transaction_on_chain(hash)) { - return Ok(Some(tx.into())) + return Ok(Some(tx)) } self.storage_provider.transaction_by_hash(hash) @@ -990,7 +990,7 @@ impl TransactionsProvider for ConsistentProvider { if let Some((tx, meta)) = self.head_block.as_ref().and_then(|b| b.transaction_meta_on_chain(tx_hash)) { - return Ok(Some((tx.into(), meta))) + return Ok(Some((tx, meta))) } self.storage_provider.transaction_by_hash_with_meta(tx_hash) @@ -1011,18 +1011,7 @@ impl TransactionsProvider for ConsistentProvider { self.get_in_memory_or_storage_by_block( id, |provider| provider.transactions_by_block(id), - |block_state| { - Ok(Some( - block_state - .block_ref() - .block() - .body - .transactions - .iter() - .map(|tx| tx.clone().into()) - .collect(), - )) - }, + |block_state| Ok(Some(block_state.block_ref().block().body.transactions.clone())), ) } @@ -1033,18 +1022,7 @@ impl TransactionsProvider for ConsistentProvider { self.get_in_memory_or_storage_by_block_range_while( range, |db_provider, range, _| db_provider.transactions_by_block_range(range), - |block_state, _| { - Some( - block_state - .block_ref() - .block() - .body - .transactions - .iter() - .map(|tx| tx.clone().into()) - .collect(), - ) - }, + |block_state, _| Some(block_state.block_ref().block().body.transactions.clone()), |_| true, ) } diff --git a/crates/storage/provider/src/providers/database/chain.rs b/crates/storage/provider/src/providers/database/chain.rs index 8f9a6395a9..57bc2e0b5c 100644 --- a/crates/storage/provider/src/providers/database/chain.rs +++ b/crates/storage/provider/src/providers/database/chain.rs @@ -1,25 +1,41 @@ -use crate::{providers::NodeTypes, DatabaseProvider}; +use crate::{providers::NodeTypesForProvider, DatabaseProvider}; use reth_db::transaction::{DbTx, DbTxMut}; use reth_node_types::FullNodePrimitives; use reth_primitives::EthPrimitives; -use reth_storage_api::{ChainStorageWriter, EthStorage}; +use reth_storage_api::{ChainStorageReader, ChainStorageWriter, EthStorage}; /// Trait that provides access to implementations of [`ChainStorage`] pub trait ChainStorage: Send + Sync { + /// Provides access to the chain reader. + fn reader(&self) -> impl ChainStorageReader, Primitives> + where + TX: DbTx + 'static, + Types: NodeTypesForProvider; + /// Provides access to the chain writer. fn writer(&self) -> impl ChainStorageWriter, Primitives> where TX: DbTxMut + DbTx + 'static, - Types: NodeTypes; + Types: NodeTypesForProvider; } impl ChainStorage for EthStorage { + fn reader( + &self, + ) -> impl ChainStorageReader, EthPrimitives> + where + TX: DbTx + 'static, + Types: NodeTypesForProvider, + { + self + } + fn writer( &self, ) -> impl ChainStorageWriter, EthPrimitives> where TX: DbTxMut + DbTx + 'static, - Types: NodeTypes, + Types: NodeTypesForProvider, { self } diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index d45b4e5312..47d9308283 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -50,12 +50,14 @@ use reth_node_types::{BlockTy, NodeTypes, TxTy}; use reth_primitives::{ Account, Block, BlockBody, BlockExt, BlockWithSenders, Bytecode, GotExpected, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, StaticFileSegment, StorageEntry, - TransactionMeta, TransactionSigned, TransactionSignedNoHash, + TransactionMeta, TransactionSignedNoHash, }; use reth_primitives_traits::{BlockBody as _, SignedTransaction}; use reth_prune_types::{PruneCheckpoint, PruneModes, PruneSegment}; use reth_stages_types::{StageCheckpoint, StageId}; -use reth_storage_api::{StateProvider, StorageChangeSetReader, TryIntoHistoricalStateProvider}; +use reth_storage_api::{ + BlockBodyReader, StateProvider, StorageChangeSetReader, TryIntoHistoricalStateProvider, +}; use reth_storage_errors::provider::{ProviderResult, RootMismatch}; use reth_trie::{ prefix_set::{PrefixSet, PrefixSetMut, TriePrefixSets}, @@ -517,21 +519,11 @@ impl DatabaseProvider { N::ChainSpec: EthereumHardforks, H: AsRef
, HF: FnOnce(BlockNumber) -> ProviderResult>, - BF: FnOnce( - H, - Vec, - Vec
, - Vec
, - Option, - ) -> ProviderResult>, + BF: FnOnce(H, BlockBody, Vec
) -> ProviderResult>, { let Some(block_number) = self.convert_hash_or_number(id)? else { return Ok(None) }; let Some(header) = header_by_number(block_number)? else { return Ok(None) }; - let ommers = self.ommers(block_number.into())?.unwrap_or_default(); - let withdrawals = - self.withdrawals_by_block(block_number.into(), header.as_ref().timestamp)?; - // Get the block body // // If the body indices are not found, this means that the transactions either do not exist @@ -548,9 +540,14 @@ impl DatabaseProvider { (self.transactions_by_tx_range(tx_range.clone())?, self.senders_by_tx_range(tx_range)?) }; - let body = transactions.into_iter().map(Into::into).collect(); + let body = self + .storage + .reader() + .read_block_bodies(self, vec![(header.as_ref(), transactions)])? + .pop() + .ok_or(ProviderError::InvalidStorageOutput)?; - construct_block(header, body, senders, ommers, withdrawals) + construct_block(header, body, senders) } /// Returns a range of blocks from the database. @@ -572,7 +569,7 @@ impl DatabaseProvider { N::ChainSpec: EthereumHardforks, H: AsRef
, HF: FnOnce(RangeInclusive) -> ProviderResult>, - F: FnMut(H, Range, Vec
, Option) -> ProviderResult, + F: FnMut(H, BlockBody, Range) -> ProviderResult, { if range.is_empty() { return Ok(Vec::new()) @@ -582,50 +579,41 @@ impl DatabaseProvider { let mut blocks = Vec::with_capacity(len); let headers = headers_range(range)?; - let mut ommers_cursor = self.tx.cursor_read::()?; - let mut withdrawals_cursor = self.tx.cursor_read::()?; + let mut tx_cursor = self.tx.cursor_read::>>()?; let mut block_body_cursor = self.tx.cursor_read::()?; + let mut present_headers = Vec::new(); for header in headers { - let header_ref = header.as_ref(); // 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_ref.number)? + block_body_cursor.seek_exact(header.as_ref().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_ref.timestamp) { - withdrawals_cursor - .seek_exact(header_ref.number)? - .map(|(_, w)| w.withdrawals) - .unwrap_or_default() - .into() - } else { - None - }; - let ommers = - if self.chain_spec.final_paris_total_difficulty(header_ref.number).is_some() { - Vec::new() - } else { - ommers_cursor - .seek_exact(header_ref.number)? - .map(|(_, o)| o.ommers) - .unwrap_or_default() - }; - - if let Ok(b) = assemble_block(header, tx_range, ommers, withdrawals) { - blocks.push(b); - } + present_headers.push((header, tx_range)); } } + let mut inputs = Vec::new(); + for (header, tx_range) in &present_headers { + let transactions = if tx_range.is_empty() { + Vec::new() + } else { + self.transactions_by_tx_range_with_cursor(tx_range.clone(), &mut tx_cursor)? + }; + + inputs.push((header.as_ref(), transactions)); + } + + let bodies = self.storage.reader().read_block_bodies(self, inputs)?; + + for ((header, tx_range), body) in present_headers.into_iter().zip(bodies) { + blocks.push(assemble_block(header, body, tx_range)?); + } + Ok(blocks) } @@ -649,34 +637,22 @@ impl DatabaseProvider { N::ChainSpec: EthereumHardforks, H: AsRef
, HF: Fn(RangeInclusive) -> ProviderResult>, - BF: Fn( - H, - Vec, - Vec
, - Option, - Vec
, - ) -> ProviderResult, + BF: Fn(H, BlockBody, Vec
) -> ProviderResult, { - let mut tx_cursor = self.tx.cursor_read::>>()?; let mut senders_cursor = self.tx.cursor_read::()?; - self.block_range(range, headers_range, |header, tx_range, ommers, withdrawals| { - let (body, senders) = if tx_range.is_empty() { - (Vec::new(), Vec::new()) + self.block_range(range, headers_range, |header, body, tx_range| { + let senders = if tx_range.is_empty() { + Vec::new() } else { - let body = self - .transactions_by_tx_range_with_cursor(tx_range.clone(), &mut tx_cursor)? - .into_iter() - .map(Into::into) - .collect::>(); // fetch senders from the senders table let known_senders = senders_cursor .walk_range(tx_range.clone())? .collect::, _>>()?; - let mut senders = Vec::with_capacity(body.len()); - for (tx_num, tx) in tx_range.zip(body.iter()) { + let mut senders = Vec::with_capacity(body.transactions.len()); + for (tx_num, tx) in tx_range.zip(body.transactions()) { match known_senders.get(&tx_num) { None => { // recover the sender from the transaction if not found @@ -689,10 +665,10 @@ impl DatabaseProvider { } } - (body, senders) + senders }; - assemble_block(header, body, ommers, withdrawals, senders) + assemble_block(header, body, senders) }) } @@ -1230,21 +1206,22 @@ impl BlockReader for DatabaseProvid fn block(&self, id: BlockHashOrNumber) -> ProviderResult> { if let Some(number) = self.convert_hash_or_number(id)? { if let Some(header) = self.header_by_number(number)? { - let withdrawals = self.withdrawals_by_block(number.into(), header.timestamp)?; - let ommers = self.ommers(number.into())?.unwrap_or_default(); // 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 return `None`. - let transactions = match self.transactions_by_block(number.into())? { - Some(transactions) => transactions.into_iter().map(Into::into).collect(), - None => return Ok(None), + let Some(transactions) = self.transactions_by_block(number.into())? else { + return Ok(None) }; - return Ok(Some(Block { - header, - body: BlockBody { transactions, ommers, withdrawals }, - })) + let body = self + .storage + .reader() + .read_block_bodies(self, vec![(&header, transactions)])? + .pop() + .ok_or(ProviderError::InvalidStorageOutput)?; + + return Ok(Some(Block { header, body })) } } @@ -1303,8 +1280,8 @@ impl BlockReader for DatabaseProvid id, transaction_kind, |block_number| self.header_by_number(block_number), - |header, transactions, senders, ommers, withdrawals| { - Block { header, body: BlockBody { transactions, ommers, withdrawals } } + |header, body, senders| { + Block { header, body } // Note: we're using unchecked here because we know the block contains valid txs // wrt to its height and can ignore the s value check so pre // EIP-2 txs are allowed @@ -1324,8 +1301,8 @@ impl BlockReader for DatabaseProvid id, transaction_kind, |block_number| self.sealed_header(block_number), - |header, transactions, senders, ommers, withdrawals| { - SealedBlock { header, body: BlockBody { transactions, ommers, withdrawals } } + |header, body, senders| { + SealedBlock { header, body } // Note: we're using unchecked here because we know the block contains valid txs // wrt to its height and can ignore the s value check so pre // EIP-2 txs are allowed @@ -1337,21 +1314,10 @@ impl BlockReader for DatabaseProvid } fn block_range(&self, range: RangeInclusive) -> ProviderResult> { - let mut tx_cursor = self.tx.cursor_read::>>()?; self.block_range( range, |range| self.headers_range(range), - |header, tx_range, ommers, withdrawals| { - let transactions = if tx_range.is_empty() { - Vec::new() - } else { - self.transactions_by_tx_range_with_cursor(tx_range, &mut tx_cursor)? - .into_iter() - .map(Into::into) - .collect() - }; - Ok(Block { header, body: BlockBody { transactions, ommers, withdrawals } }) - }, + |header, body, _| Ok(Block { header, body }), ) } @@ -1362,8 +1328,8 @@ impl BlockReader for DatabaseProvid self.block_with_senders_range( range, |range| self.headers_range(range), - |header, transactions, ommers, withdrawals, senders| { - Block { header, body: BlockBody { transactions, ommers, withdrawals } } + |header, body, senders| { + Block { header, body } .try_with_senders_unchecked(senders) .map_err(|_| ProviderError::SenderRecoveryError) }, @@ -1377,12 +1343,9 @@ impl BlockReader for DatabaseProvid self.block_with_senders_range( range, |range| self.sealed_headers_range(range), - |header, transactions, ommers, withdrawals, senders| { - SealedBlockWithSenders::new( - SealedBlock { header, body: BlockBody { transactions, ommers, withdrawals } }, - senders, - ) - .ok_or(ProviderError::SenderRecoveryError) + |header, body, senders| { + SealedBlockWithSenders::new(SealedBlock { header, body }, senders) + .ok_or(ProviderError::SenderRecoveryError) }, ) } diff --git a/crates/storage/provider/src/providers/mod.rs b/crates/storage/provider/src/providers/mod.rs index 6d8e3ed5e1..30cac220d8 100644 --- a/crates/storage/provider/src/providers/mod.rs +++ b/crates/storage/provider/src/providers/mod.rs @@ -20,7 +20,6 @@ use reth_blockchain_tree_api::{ }; use reth_chain_state::{ChainInfoTracker, ForkChoiceNotifications, ForkChoiceSubscriptions}; use reth_chainspec::{ChainInfo, EthereumHardforks}; -use reth_db::table::Value; use reth_db_api::models::{AccountBeforeTx, StoredBlockBodyIndices}; use reth_evm::ConfigureEvmEnv; use reth_node_types::{FullNodePrimitives, NodeTypes, NodeTypesWithDB, TxTy}; @@ -77,8 +76,9 @@ where ChainSpec: EthereumHardforks, Storage: ChainStorage, Primitives: FullNodePrimitives< - SignedTx: Value + From + Into, + SignedTx = TransactionSigned, BlockHeader = alloy_consensus::Header, + BlockBody = reth_primitives::BlockBody, >, >, { @@ -89,8 +89,9 @@ impl NodeTypesForProvider for T where ChainSpec: EthereumHardforks, Storage: ChainStorage, Primitives: FullNodePrimitives< - SignedTx: Value + From + Into, + SignedTx = TransactionSigned, BlockHeader = alloy_consensus::Header, + BlockBody = reth_primitives::BlockBody, >, > { diff --git a/crates/storage/storage-api/src/chain.rs b/crates/storage/storage-api/src/chain.rs index d5228bdddf..baee2f870a 100644 --- a/crates/storage/storage-api/src/chain.rs +++ b/crates/storage/storage-api/src/chain.rs @@ -1,10 +1,11 @@ use crate::DBProvider; use alloy_primitives::BlockNumber; +use reth_chainspec::{ChainSpecProvider, EthereumHardforks}; use reth_db::{ - cursor::DbCursorRW, + cursor::{DbCursorRO, DbCursorRW}, models::{StoredBlockOmmers, StoredBlockWithdrawals}, tables, - transaction::DbTxMut, + transaction::{DbTx, DbTxMut}, DbTxUnwindExt, }; use reth_primitives_traits::{Block, BlockBody, FullNodePrimitives}; @@ -41,6 +42,38 @@ impl ChainStorageWriter = + (&'a ::Header, Vec<<::Body as BlockBody>::Transaction>); + +/// Trait that implements how block bodies are read from the storage. +/// +/// Note: Within the current abstraction, transactions persistence is handled separately, thus this +/// trait is provided with transactions read beforehand and is expected to construct the block body +/// from those transactions and additional data read from elsewhere. +#[auto_impl::auto_impl(&, Arc)] +pub trait BlockBodyReader { + /// The block type. + type Block: Block; + + /// Receives a list of block headers along with block transactions and returns the block bodies. + fn read_block_bodies( + &self, + provider: &Provider, + inputs: Vec>, + ) -> ProviderResult::Body>>; +} + +/// Trait that implements how chain-specific types are read from storage. +pub trait ChainStorageReader: + BlockBodyReader +{ +} +impl ChainStorageReader for T where + T: BlockBodyReader +{ +} /// Ethereum storage implementation. #[derive(Debug, Default, Clone, Copy)] pub struct EthStorage; @@ -89,3 +122,47 @@ where Ok(()) } } + +impl BlockBodyReader for EthStorage +where + Provider: DBProvider + ChainSpecProvider, +{ + type Block = reth_primitives::Block; + + fn read_block_bodies( + &self, + provider: &Provider, + inputs: Vec>, + ) -> ProviderResult::Body>> { + // TODO: Ideally storage should hold its own copy of chain spec + let chain_spec = provider.chain_spec(); + + let mut ommers_cursor = provider.tx_ref().cursor_read::()?; + 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 + }; + let ommers = if 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() + }; + + bodies.push(reth_primitives::BlockBody { transactions, ommers, withdrawals }); + } + + Ok(bodies) + } +} diff --git a/crates/transaction-pool/src/blobstore/tracker.rs b/crates/transaction-pool/src/blobstore/tracker.rs index 0f48c89a49..b3670496b5 100644 --- a/crates/transaction-pool/src/blobstore/tracker.rs +++ b/crates/transaction-pool/src/blobstore/tracker.rs @@ -2,6 +2,7 @@ use alloy_primitives::{BlockNumber, B256}; use reth_execution_types::ChainBlocks; +use reth_primitives_traits::BlockBody as _; use std::collections::BTreeMap; /// The type that is used to track canonical blob transactions. @@ -42,6 +43,7 @@ impl BlobStoreCanonTracker { let iter = block .body .transactions() + .iter() .filter(|tx| tx.transaction.is_eip4844()) .map(|tx| tx.hash()); (*num, iter)