reth_storage_api/
chain.rsuse crate::DBProvider;
use alloy_primitives::BlockNumber;
use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
use reth_db::{
cursor::{DbCursorRO, DbCursorRW},
models::{StoredBlockOmmers, StoredBlockWithdrawals},
tables,
transaction::{DbTx, DbTxMut},
DbTxUnwindExt,
};
use reth_primitives_traits::{Block, BlockBody, FullNodePrimitives};
use reth_storage_errors::provider::ProviderResult;
#[auto_impl::auto_impl(&, Arc)]
pub trait BlockBodyWriter<Provider, Body: BlockBody> {
fn write_block_bodies(
&self,
provider: &Provider,
bodies: Vec<(BlockNumber, Option<Body>)>,
) -> ProviderResult<()>;
fn remove_block_bodies_above(
&self,
provider: &Provider,
block: BlockNumber,
) -> ProviderResult<()>;
}
pub trait ChainStorageWriter<Provider, Primitives: FullNodePrimitives>:
BlockBodyWriter<Provider, <Primitives::Block as Block>::Body>
{
}
impl<T, Provider, Primitives: FullNodePrimitives> ChainStorageWriter<Provider, Primitives> for T where
T: BlockBodyWriter<Provider, <Primitives::Block as Block>::Body>
{
}
pub type ReadBodyInput<'a, B> =
(&'a <B as Block>::Header, Vec<<<B as Block>::Body as BlockBody>::Transaction>);
#[auto_impl::auto_impl(&, Arc)]
pub trait BlockBodyReader<Provider> {
type Block: Block;
fn read_block_bodies(
&self,
provider: &Provider,
inputs: Vec<ReadBodyInput<'_, Self::Block>>,
) -> ProviderResult<Vec<<Self::Block as Block>::Body>>;
}
pub trait ChainStorageReader<Provider, Primitives: FullNodePrimitives>:
BlockBodyReader<Provider, Block = Primitives::Block>
{
}
impl<T, Provider, Primitives: FullNodePrimitives> ChainStorageReader<Provider, Primitives> for T where
T: BlockBodyReader<Provider, Block = Primitives::Block>
{
}
#[derive(Debug, Default, Clone, Copy)]
pub struct EthStorage;
impl<Provider> BlockBodyWriter<Provider, reth_primitives::BlockBody> for EthStorage
where
Provider: DBProvider<Tx: DbTxMut>,
{
fn write_block_bodies(
&self,
provider: &Provider,
bodies: Vec<(u64, Option<reth_primitives::BlockBody>)>,
) -> ProviderResult<()> {
let mut ommers_cursor = provider.tx_ref().cursor_write::<tables::BlockOmmers>()?;
let mut withdrawals_cursor =
provider.tx_ref().cursor_write::<tables::BlockWithdrawals>()?;
for (block_number, body) in bodies {
let Some(body) = body else { continue };
if !body.ommers.is_empty() {
ommers_cursor.append(block_number, StoredBlockOmmers { ommers: body.ommers })?;
}
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,
) -> ProviderResult<()> {
provider.tx_ref().unwind_table_by_num::<tables::BlockWithdrawals>(block)?;
provider.tx_ref().unwind_table_by_num::<tables::BlockOmmers>(block)?;
Ok(())
}
}
impl<Provider> BlockBodyReader<Provider> for EthStorage
where
Provider: DBProvider + ChainSpecProvider<ChainSpec: EthereumHardforks>,
{
type Block = reth_primitives::Block;
fn read_block_bodies(
&self,
provider: &Provider,
inputs: Vec<ReadBodyInput<'_, Self::Block>>,
) -> ProviderResult<Vec<<Self::Block as Block>::Body>> {
let chain_spec = provider.chain_spec();
let mut ommers_cursor = provider.tx_ref().cursor_read::<tables::BlockOmmers>()?;
let mut withdrawals_cursor = provider.tx_ref().cursor_read::<tables::BlockWithdrawals>()?;
let mut bodies = Vec::with_capacity(inputs.len());
for (header, transactions) in inputs {
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)
}
}