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, NodePrimitives, 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: NodePrimitives>:
44 BlockBodyWriter<Provider, <Primitives::Block as Block>::Body>
45{
46}
47impl<T, Provider, Primitives: NodePrimitives> 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: NodePrimitives>:
77 BlockBodyReader<Provider, Block = Primitives::Block>
78{
79}
80impl<T, Provider, Primitives: NodePrimitives> 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
117 .append(block_number, &StoredBlockOmmers { ommers: body.ommers.clone() })?;
118 }
119
120 if let Some(withdrawals) = body.withdrawals.clone() &&
122 !withdrawals.is_empty()
123 {
124 withdrawals_cursor.append(block_number, &StoredBlockWithdrawals { withdrawals })?;
125 }
126 }
127
128 Ok(())
129 }
130
131 fn remove_block_bodies_above(
132 &self,
133 provider: &Provider,
134 block: BlockNumber,
135 ) -> ProviderResult<()> {
136 provider.tx_ref().unwind_table_by_num::<tables::BlockWithdrawals>(block)?;
137 provider.tx_ref().unwind_table_by_num::<tables::BlockOmmers<H>>(block)?;
138
139 Ok(())
140 }
141}
142
143impl<Provider, T, H> BlockBodyReader<Provider> for EthStorage<T, H>
144where
145 Provider: DBProvider + ChainSpecProvider<ChainSpec: EthereumHardforks>,
146 T: SignedTransaction,
147 H: FullBlockHeader,
148{
149 type Block = alloy_consensus::Block<T, H>;
150
151 fn read_block_bodies(
152 &self,
153 provider: &Provider,
154 inputs: Vec<ReadBodyInput<'_, Self::Block>>,
155 ) -> ProviderResult<Vec<<Self::Block as Block>::Body>> {
156 let chain_spec = provider.chain_spec();
158
159 let mut withdrawals_cursor = provider.tx_ref().cursor_read::<tables::BlockWithdrawals>()?;
160
161 let mut bodies = Vec::with_capacity(inputs.len());
162
163 for (header, transactions) in inputs {
164 let withdrawals = if chain_spec.is_shanghai_active_at_timestamp(header.timestamp()) {
167 withdrawals_cursor
168 .seek_exact(header.number())?
169 .map(|(_, w)| w.withdrawals)
170 .unwrap_or_default()
171 .into()
172 } else {
173 None
174 };
175 let ommers = if chain_spec.is_paris_active_at_block(header.number()) {
176 Vec::new()
177 } else {
178 provider
180 .tx_ref()
181 .cursor_read::<tables::BlockOmmers<H>>()?
182 .seek_exact(header.number())?
183 .map(|(_, stored_ommers)| stored_ommers.ommers)
184 .unwrap_or_default()
185 };
186 bodies.push(alloy_consensus::BlockBody { transactions, ommers, withdrawals });
187 }
188
189 Ok(bodies)
190 }
191}
192
193#[derive(Debug, Clone, Copy)]
199pub struct EmptyBodyStorage<T, H>(PhantomData<(T, H)>);
200
201impl<T, H> Default for EmptyBodyStorage<T, H> {
202 fn default() -> Self {
203 Self(PhantomData)
204 }
205}
206
207impl<Provider, T, H> BlockBodyWriter<Provider, alloy_consensus::BlockBody<T, H>>
208 for EmptyBodyStorage<T, H>
209where
210 T: SignedTransaction,
211 H: FullBlockHeader,
212{
213 fn write_block_bodies(
214 &self,
215 _provider: &Provider,
216 _bodies: Vec<(u64, Option<&alloy_consensus::BlockBody<T, H>>)>,
217 ) -> ProviderResult<()> {
218 Ok(())
220 }
221
222 fn remove_block_bodies_above(
223 &self,
224 _provider: &Provider,
225 _block: BlockNumber,
226 ) -> ProviderResult<()> {
227 Ok(())
229 }
230}
231
232impl<Provider, T, H> BlockBodyReader<Provider> for EmptyBodyStorage<T, H>
233where
234 Provider: ChainSpecProvider<ChainSpec: EthereumHardforks>,
235 T: SignedTransaction,
236 H: FullBlockHeader,
237{
238 type Block = alloy_consensus::Block<T, H>;
239
240 fn read_block_bodies(
241 &self,
242 provider: &Provider,
243 inputs: Vec<ReadBodyInput<'_, Self::Block>>,
244 ) -> ProviderResult<Vec<<Self::Block as Block>::Body>> {
245 let chain_spec = provider.chain_spec();
246
247 Ok(inputs
248 .into_iter()
249 .map(|(header, transactions)| {
250 alloy_consensus::BlockBody {
251 transactions,
252 ommers: vec![], withdrawals: chain_spec
254 .is_shanghai_active_at_timestamp(header.timestamp())
255 .then(Default::default),
256 }
257 })
258 .collect())
259 }
260}