1use crate::{DBProvider, OmmersProvider, StorageLocation};
2use alloc::vec::Vec;
3use alloy_consensus::Header;
4use alloy_primitives::BlockNumber;
5use core::marker::PhantomData;
6use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
7use reth_db_api::{
8 cursor::{DbCursorRO, DbCursorRW},
9 models::StoredBlockOmmers,
10 tables,
11 transaction::{DbTx, DbTxMut},
12 DbTxUnwindExt,
13};
14use reth_db_models::StoredBlockWithdrawals;
15use reth_ethereum_primitives::TransactionSigned;
16use reth_primitives_traits::{
17 Block, BlockBody, FullBlockHeader, FullNodePrimitives, SignedTransaction,
18};
19use reth_storage_errors::provider::ProviderResult;
20
21#[auto_impl::auto_impl(&, Arc)]
26pub trait BlockBodyWriter<Provider, Body: BlockBody> {
27 fn write_block_bodies(
29 &self,
30 provider: &Provider,
31 bodies: Vec<(BlockNumber, Option<Body>)>,
32 write_to: StorageLocation,
33 ) -> ProviderResult<()>;
34
35 fn remove_block_bodies_above(
37 &self,
38 provider: &Provider,
39 block: BlockNumber,
40 remove_from: StorageLocation,
41 ) -> ProviderResult<()>;
42}
43
44pub trait ChainStorageWriter<Provider, Primitives: FullNodePrimitives>:
46 BlockBodyWriter<Provider, <Primitives::Block as Block>::Body>
47{
48}
49impl<T, Provider, Primitives: FullNodePrimitives> ChainStorageWriter<Provider, Primitives> for T where
50 T: BlockBodyWriter<Provider, <Primitives::Block as Block>::Body>
51{
52}
53
54pub type ReadBodyInput<'a, B> =
57 (&'a <B as Block>::Header, Vec<<<B as Block>::Body as BlockBody>::Transaction>);
58
59#[auto_impl::auto_impl(&, Arc)]
65pub trait BlockBodyReader<Provider> {
66 type Block: Block;
68
69 fn read_block_bodies(
71 &self,
72 provider: &Provider,
73 inputs: Vec<ReadBodyInput<'_, Self::Block>>,
74 ) -> ProviderResult<Vec<<Self::Block as Block>::Body>>;
75}
76
77pub trait ChainStorageReader<Provider, Primitives: FullNodePrimitives>:
79 BlockBodyReader<Provider, Block = Primitives::Block>
80{
81}
82impl<T, Provider, Primitives: FullNodePrimitives> ChainStorageReader<Provider, Primitives> for T where
83 T: BlockBodyReader<Provider, Block = Primitives::Block>
84{
85}
86
87#[derive(Debug, Clone, Copy)]
89pub struct EthStorage<T = TransactionSigned, H = Header>(PhantomData<(T, H)>);
90
91impl<T, H> Default for EthStorage<T, H> {
92 fn default() -> Self {
93 Self(Default::default())
94 }
95}
96
97impl<Provider, T, H> BlockBodyWriter<Provider, alloy_consensus::BlockBody<T, H>>
98 for EthStorage<T, H>
99where
100 Provider: DBProvider<Tx: DbTxMut>,
101 T: SignedTransaction,
102 H: FullBlockHeader,
103{
104 fn write_block_bodies(
105 &self,
106 provider: &Provider,
107 bodies: Vec<(u64, Option<alloy_consensus::BlockBody<T, H>>)>,
108 _write_to: StorageLocation,
109 ) -> ProviderResult<()> {
110 let mut ommers_cursor = provider.tx_ref().cursor_write::<tables::BlockOmmers<H>>()?;
111 let mut withdrawals_cursor =
112 provider.tx_ref().cursor_write::<tables::BlockWithdrawals>()?;
113
114 for (block_number, body) in bodies {
115 let Some(body) = body else { continue };
116
117 if !body.ommers.is_empty() {
119 ommers_cursor.append(block_number, &StoredBlockOmmers { ommers: body.ommers })?;
120 }
121
122 if let Some(withdrawals) = body.withdrawals {
124 if !withdrawals.is_empty() {
125 withdrawals_cursor
126 .append(block_number, &StoredBlockWithdrawals { withdrawals })?;
127 }
128 }
129 }
130
131 Ok(())
132 }
133
134 fn remove_block_bodies_above(
135 &self,
136 provider: &Provider,
137 block: BlockNumber,
138 _remove_from: StorageLocation,
139 ) -> ProviderResult<()> {
140 provider.tx_ref().unwind_table_by_num::<tables::BlockWithdrawals>(block)?;
141 provider.tx_ref().unwind_table_by_num::<tables::BlockOmmers>(block)?;
142
143 Ok(())
144 }
145}
146
147impl<Provider, T, H> BlockBodyReader<Provider> for EthStorage<T, H>
148where
149 Provider:
150 DBProvider + ChainSpecProvider<ChainSpec: EthereumHardforks> + OmmersProvider<Header = H>,
151 T: SignedTransaction,
152 H: FullBlockHeader,
153{
154 type Block = alloy_consensus::Block<T, H>;
155
156 fn read_block_bodies(
157 &self,
158 provider: &Provider,
159 inputs: Vec<ReadBodyInput<'_, Self::Block>>,
160 ) -> ProviderResult<Vec<<Self::Block as Block>::Body>> {
161 let chain_spec = provider.chain_spec();
163
164 let mut withdrawals_cursor = provider.tx_ref().cursor_read::<tables::BlockWithdrawals>()?;
165
166 let mut bodies = Vec::with_capacity(inputs.len());
167
168 for (header, transactions) in inputs {
169 let withdrawals = if chain_spec.is_shanghai_active_at_timestamp(header.timestamp()) {
172 withdrawals_cursor
173 .seek_exact(header.number())?
174 .map(|(_, w)| w.withdrawals)
175 .unwrap_or_default()
176 .into()
177 } else {
178 None
179 };
180 let ommers = if chain_spec.is_paris_active_at_block(header.number()) {
181 Vec::new()
182 } else {
183 provider.ommers(header.number().into())?.unwrap_or_default()
184 };
185
186 bodies.push(alloy_consensus::BlockBody { transactions, ommers, withdrawals });
187 }
188
189 Ok(bodies)
190 }
191}