use crate::{DBProvider, OmmersProvider, StorageLocation}; use alloc::vec::Vec; use alloy_consensus::Header; use alloy_primitives::BlockNumber; use core::marker::PhantomData; use reth_chainspec::{ChainSpecProvider, EthereumHardforks}; use reth_db_api::{ cursor::{DbCursorRO, DbCursorRW}, models::StoredBlockOmmers, tables, transaction::{DbTx, DbTxMut}, DbTxUnwindExt, }; use reth_db_models::StoredBlockWithdrawals; use reth_ethereum_primitives::TransactionSigned; use reth_primitives_traits::{ Block, BlockBody, FullBlockHeader, FullNodePrimitives, SignedTransaction, }; use reth_storage_errors::provider::ProviderResult; /// Trait that implements how block bodies are written to the storage. /// /// Note: Within the current abstraction, this should only write to tables unrelated to /// transactions. Writing of transactions is handled separately. #[auto_impl::auto_impl(&, Arc)] pub trait BlockBodyWriter { /// Writes a set of block bodies to the storage. fn write_block_bodies( &self, provider: &Provider, bodies: Vec<(BlockNumber, Option)>, write_to: StorageLocation, ) -> ProviderResult<()>; /// Removes all block bodies above the given block number from the database. fn remove_block_bodies_above( &self, provider: &Provider, block: BlockNumber, remove_from: StorageLocation, ) -> ProviderResult<()>; } /// Trait that implements how chain-specific types are written to the storage. pub trait ChainStorageWriter: BlockBodyWriter::Body> { } impl ChainStorageWriter for T where T: BlockBodyWriter::Body> { } /// Input for reading a block body. Contains a header of block being read and a list of pre-fetched /// transactions. pub type ReadBodyInput<'a, B> = (&'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, Clone, Copy)] pub struct EthStorage(PhantomData<(T, H)>); impl Default for EthStorage { fn default() -> Self { Self(Default::default()) } } impl BlockBodyWriter> for EthStorage where Provider: DBProvider, T: SignedTransaction, H: FullBlockHeader, { fn write_block_bodies( &self, provider: &Provider, bodies: Vec<(u64, Option>)>, _write_to: StorageLocation, ) -> ProviderResult<()> { let mut ommers_cursor = provider.tx_ref().cursor_write::>()?; let mut withdrawals_cursor = provider.tx_ref().cursor_write::()?; for (block_number, body) in bodies { let Some(body) = body else { continue }; // Write ommers if any if !body.ommers.is_empty() { ommers_cursor.append(block_number, &StoredBlockOmmers { ommers: body.ommers })?; } // Write withdrawals if any if let Some(withdrawals) = body.withdrawals { if !withdrawals.is_empty() { withdrawals_cursor .append(block_number, &StoredBlockWithdrawals { withdrawals })?; } } } Ok(()) } fn remove_block_bodies_above( &self, provider: &Provider, block: BlockNumber, _remove_from: StorageLocation, ) -> ProviderResult<()> { provider.tx_ref().unwind_table_by_num::(block)?; provider.tx_ref().unwind_table_by_num::(block)?; Ok(()) } } impl BlockBodyReader for EthStorage where Provider: DBProvider + ChainSpecProvider + OmmersProvider
, T: SignedTransaction, H: FullBlockHeader, { type Block = alloy_consensus::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 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 { provider.ommers(header.number().into())?.unwrap_or_default() }; bodies.push(alloy_consensus::BlockBody { transactions, ommers, withdrawals }); } Ok(bodies) } }