1use crate::DBProvider;
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 ) -> ProviderResult<()>;
33
34 fn remove_block_bodies_above(
36 &self,
37 provider: &Provider,
38 block: BlockNumber,
39 ) -> ProviderResult<()>;
40}
41
42pub trait ChainStorageWriter<Provider, Primitives: FullNodePrimitives>:
44 BlockBodyWriter<Provider, <Primitives::Block as Block>::Body>
45{
46}
47impl<T, Provider, Primitives: FullNodePrimitives> ChainStorageWriter<Provider, Primitives> for T where
48 T: BlockBodyWriter<Provider, <Primitives::Block as Block>::Body>
49{
50}
51
52pub type ReadBodyInput<'a, B> =
55 (&'a <B as Block>::Header, Vec<<<B as Block>::Body as BlockBody>::Transaction>);
56
57#[auto_impl::auto_impl(&, Arc)]
63pub trait BlockBodyReader<Provider> {
64 type Block: Block;
66
67 fn read_block_bodies(
69 &self,
70 provider: &Provider,
71 inputs: Vec<ReadBodyInput<'_, Self::Block>>,
72 ) -> ProviderResult<Vec<<Self::Block as Block>::Body>>;
73}
74
75pub trait ChainStorageReader<Provider, Primitives: FullNodePrimitives>:
77 BlockBodyReader<Provider, Block = Primitives::Block>
78{
79}
80impl<T, Provider, Primitives: FullNodePrimitives> ChainStorageReader<Provider, Primitives> for T where
81 T: BlockBodyReader<Provider, Block = Primitives::Block>
82{
83}
84
85#[derive(Debug, Clone, Copy)]
87pub struct EthStorage<T = TransactionSigned, H = Header>(PhantomData<(T, H)>);
88
89impl<T, H> Default for EthStorage<T, H> {
90 fn default() -> Self {
91 Self(Default::default())
92 }
93}
94
95impl<Provider, T, H> BlockBodyWriter<Provider, alloy_consensus::BlockBody<T, H>>
96 for EthStorage<T, H>
97where
98 Provider: DBProvider<Tx: DbTxMut>,
99 T: SignedTransaction,
100 H: FullBlockHeader,
101{
102 fn write_block_bodies(
103 &self,
104 provider: &Provider,
105 bodies: Vec<(u64, Option<alloy_consensus::BlockBody<T, H>>)>,
106 ) -> ProviderResult<()> {
107 let mut ommers_cursor = provider.tx_ref().cursor_write::<tables::BlockOmmers<H>>()?;
108 let mut withdrawals_cursor =
109 provider.tx_ref().cursor_write::<tables::BlockWithdrawals>()?;
110
111 for (block_number, body) in bodies {
112 let Some(body) = body else { continue };
113
114 if !body.ommers.is_empty() {
116 ommers_cursor.append(block_number, &StoredBlockOmmers { ommers: body.ommers })?;
117 }
118
119 if let Some(withdrawals) = body.withdrawals &&
121 !withdrawals.is_empty()
122 {
123 withdrawals_cursor.append(block_number, &StoredBlockWithdrawals { withdrawals })?;
124 }
125 }
126
127 Ok(())
128 }
129
130 fn remove_block_bodies_above(
131 &self,
132 provider: &Provider,
133 block: BlockNumber,
134 ) -> ProviderResult<()> {
135 provider.tx_ref().unwind_table_by_num::<tables::BlockWithdrawals>(block)?;
136 provider.tx_ref().unwind_table_by_num::<tables::BlockOmmers<H>>(block)?;
137
138 Ok(())
139 }
140}
141
142impl<Provider, T, H> BlockBodyReader<Provider> for EthStorage<T, H>
143where
144 Provider: DBProvider + ChainSpecProvider<ChainSpec: EthereumHardforks>,
145 T: SignedTransaction,
146 H: FullBlockHeader,
147{
148 type Block = alloy_consensus::Block<T, H>;
149
150 fn read_block_bodies(
151 &self,
152 provider: &Provider,
153 inputs: Vec<ReadBodyInput<'_, Self::Block>>,
154 ) -> ProviderResult<Vec<<Self::Block as Block>::Body>> {
155 let chain_spec = provider.chain_spec();
157
158 let mut withdrawals_cursor = provider.tx_ref().cursor_read::<tables::BlockWithdrawals>()?;
159
160 let mut bodies = Vec::with_capacity(inputs.len());
161
162 for (header, transactions) in inputs {
163 let withdrawals = if chain_spec.is_shanghai_active_at_timestamp(header.timestamp()) {
166 withdrawals_cursor
167 .seek_exact(header.number())?
168 .map(|(_, w)| w.withdrawals)
169 .unwrap_or_default()
170 .into()
171 } else {
172 None
173 };
174 let ommers = if chain_spec.is_paris_active_at_block(header.number()) {
175 Vec::new()
176 } else {
177 provider
179 .tx_ref()
180 .cursor_read::<tables::BlockOmmers<H>>()?
181 .seek_exact(header.number())?
182 .map(|(_, stored_ommers)| stored_ommers.ommers)
183 .unwrap_or_default()
184 };
185 bodies.push(alloy_consensus::BlockBody { transactions, ommers, withdrawals });
186 }
187
188 Ok(bodies)
189 }
190}
191
192#[derive(Debug, Clone, Copy)]
198pub struct EmptyBodyStorage<T, H>(PhantomData<(T, H)>);
199
200impl<T, H> Default for EmptyBodyStorage<T, H> {
201 fn default() -> Self {
202 Self(PhantomData)
203 }
204}
205
206impl<Provider, T, H> BlockBodyWriter<Provider, alloy_consensus::BlockBody<T, H>>
207 for EmptyBodyStorage<T, H>
208where
209 T: SignedTransaction,
210 H: FullBlockHeader,
211{
212 fn write_block_bodies(
213 &self,
214 _provider: &Provider,
215 _bodies: Vec<(u64, Option<alloy_consensus::BlockBody<T, H>>)>,
216 ) -> ProviderResult<()> {
217 Ok(())
219 }
220
221 fn remove_block_bodies_above(
222 &self,
223 _provider: &Provider,
224 _block: BlockNumber,
225 ) -> ProviderResult<()> {
226 Ok(())
228 }
229}
230
231impl<Provider, T, H> BlockBodyReader<Provider> for EmptyBodyStorage<T, H>
232where
233 Provider: ChainSpecProvider<ChainSpec: EthereumHardforks>,
234 T: SignedTransaction,
235 H: FullBlockHeader,
236{
237 type Block = alloy_consensus::Block<T, H>;
238
239 fn read_block_bodies(
240 &self,
241 provider: &Provider,
242 inputs: Vec<ReadBodyInput<'_, Self::Block>>,
243 ) -> ProviderResult<Vec<<Self::Block as Block>::Body>> {
244 let chain_spec = provider.chain_spec();
245
246 Ok(inputs
247 .into_iter()
248 .map(|(header, transactions)| {
249 alloy_consensus::BlockBody {
250 transactions,
251 ommers: vec![], withdrawals: chain_spec
253 .is_shanghai_active_at_timestamp(header.timestamp())
254 .then(Default::default),
255 }
256 })
257 .collect())
258 }
259}