reth_storage_api/
block.rs

1use crate::{
2    BlockBodyIndicesProvider, BlockNumReader, HeaderProvider, OmmersProvider, ReceiptProvider,
3    ReceiptProviderIdExt, TransactionVariant, TransactionsProvider, WithdrawalsProvider,
4};
5use alloc::{sync::Arc, vec::Vec};
6use alloy_eips::{BlockHashOrNumber, BlockId, BlockNumberOrTag};
7use alloy_primitives::{BlockNumber, B256};
8use core::ops::RangeInclusive;
9use reth_primitives_traits::{RecoveredBlock, SealedBlock, SealedHeader};
10use reth_storage_errors::provider::ProviderResult;
11
12/// A helper enum that represents the origin of the requested block.
13///
14/// This helper type's sole purpose is to give the caller more control over from where blocks can be
15/// fetched.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
17pub enum BlockSource {
18    /// Check all available sources.
19    ///
20    /// Note: it's expected that looking up pending blocks is faster than looking up blocks in the
21    /// database so this prioritizes Pending > Database.
22    #[default]
23    Any,
24    /// The block was fetched from the pending block source, the blockchain tree that buffers
25    /// blocks that are not yet part of the canonical chain.
26    Pending,
27    /// The block must be part of the canonical chain.
28    Canonical,
29}
30
31impl BlockSource {
32    /// Returns `true` if the block source is `Pending` or `Any`.
33    pub const fn is_pending(&self) -> bool {
34        matches!(self, Self::Pending | Self::Any)
35    }
36
37    /// Returns `true` if the block source is `Canonical` or `Any`.
38    pub const fn is_canonical(&self) -> bool {
39        matches!(self, Self::Canonical | Self::Any)
40    }
41}
42
43/// A helper type alias to access [`BlockReader::Block`].
44pub type ProviderBlock<P> = <P as BlockReader>::Block;
45
46/// Api trait for fetching `Block` related data.
47///
48/// If not requested otherwise, implementers of this trait should prioritize fetching blocks from
49/// the database.
50pub trait BlockReader:
51    BlockNumReader
52    + HeaderProvider
53    + BlockBodyIndicesProvider
54    + TransactionsProvider
55    + ReceiptProvider
56    + WithdrawalsProvider
57    + OmmersProvider
58    + Send
59    + Sync
60{
61    /// The block type this provider reads.
62    type Block: reth_primitives_traits::Block<
63        Body: reth_primitives_traits::BlockBody<Transaction = Self::Transaction>,
64        Header = Self::Header,
65    >;
66
67    /// Tries to find in the given block source.
68    ///
69    /// Note: this only operates on the hash because the number might be ambiguous.
70    ///
71    /// Returns `None` if block is not found.
72    fn find_block_by_hash(
73        &self,
74        hash: B256,
75        source: BlockSource,
76    ) -> ProviderResult<Option<Self::Block>>;
77
78    /// Returns the block with given id from the database.
79    ///
80    /// Returns `None` if block is not found.
81    fn block(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>>;
82
83    /// Returns the pending block if available
84    ///
85    /// Note: This returns a [`SealedBlock`] because it's expected that this is sealed by the
86    /// provider and the caller does not know the hash.
87    fn pending_block(&self) -> ProviderResult<Option<SealedBlock<Self::Block>>>;
88
89    /// Returns the pending block if available
90    ///
91    /// Note: This returns a [`RecoveredBlock`] because it's expected that this is sealed by
92    /// the provider and the caller does not know the hash.
93    fn pending_block_with_senders(&self) -> ProviderResult<Option<RecoveredBlock<Self::Block>>>;
94
95    /// Returns the pending block and receipts if available.
96    #[allow(clippy::type_complexity)]
97    fn pending_block_and_receipts(
98        &self,
99    ) -> ProviderResult<Option<(SealedBlock<Self::Block>, Vec<Self::Receipt>)>>;
100
101    /// Returns the block with matching hash from the database.
102    ///
103    /// Returns `None` if block is not found.
104    fn block_by_hash(&self, hash: B256) -> ProviderResult<Option<Self::Block>> {
105        self.block(hash.into())
106    }
107
108    /// Returns the block with matching number from database.
109    ///
110    /// Returns `None` if block is not found.
111    fn block_by_number(&self, num: u64) -> ProviderResult<Option<Self::Block>> {
112        self.block(num.into())
113    }
114
115    /// Returns the block with senders with matching number or hash from database.
116    ///
117    /// Returns the block's transactions in the requested variant.
118    ///
119    /// Returns `None` if block is not found.
120    fn recovered_block(
121        &self,
122        id: BlockHashOrNumber,
123        transaction_kind: TransactionVariant,
124    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>>;
125
126    /// Returns the sealed block with senders with matching number or hash from database.
127    ///
128    /// Returns the block's transactions in the requested variant.
129    ///
130    /// Returns `None` if block is not found.
131    fn sealed_block_with_senders(
132        &self,
133        id: BlockHashOrNumber,
134        transaction_kind: TransactionVariant,
135    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>>;
136
137    /// Returns all blocks in the given inclusive range.
138    ///
139    /// Note: returns only available blocks
140    fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Self::Block>>;
141
142    /// Returns a range of blocks from the database, along with the senders of each
143    /// transaction in the blocks.
144    fn block_with_senders_range(
145        &self,
146        range: RangeInclusive<BlockNumber>,
147    ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>>;
148
149    /// Returns a range of sealed blocks from the database, along with the senders of each
150    /// transaction in the blocks.
151    fn recovered_block_range(
152        &self,
153        range: RangeInclusive<BlockNumber>,
154    ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>>;
155}
156
157impl<T: BlockReader> BlockReader for Arc<T> {
158    type Block = T::Block;
159
160    fn find_block_by_hash(
161        &self,
162        hash: B256,
163        source: BlockSource,
164    ) -> ProviderResult<Option<Self::Block>> {
165        T::find_block_by_hash(self, hash, source)
166    }
167    fn block(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
168        T::block(self, id)
169    }
170    fn pending_block(&self) -> ProviderResult<Option<SealedBlock<Self::Block>>> {
171        T::pending_block(self)
172    }
173    fn pending_block_with_senders(&self) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
174        T::pending_block_with_senders(self)
175    }
176    fn pending_block_and_receipts(
177        &self,
178    ) -> ProviderResult<Option<(SealedBlock<Self::Block>, Vec<Self::Receipt>)>> {
179        T::pending_block_and_receipts(self)
180    }
181    fn block_by_hash(&self, hash: B256) -> ProviderResult<Option<Self::Block>> {
182        T::block_by_hash(self, hash)
183    }
184    fn block_by_number(&self, num: u64) -> ProviderResult<Option<Self::Block>> {
185        T::block_by_number(self, num)
186    }
187    fn recovered_block(
188        &self,
189        id: BlockHashOrNumber,
190        transaction_kind: TransactionVariant,
191    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
192        T::recovered_block(self, id, transaction_kind)
193    }
194    fn sealed_block_with_senders(
195        &self,
196        id: BlockHashOrNumber,
197        transaction_kind: TransactionVariant,
198    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
199        T::sealed_block_with_senders(self, id, transaction_kind)
200    }
201    fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Self::Block>> {
202        T::block_range(self, range)
203    }
204    fn block_with_senders_range(
205        &self,
206        range: RangeInclusive<BlockNumber>,
207    ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
208        T::block_with_senders_range(self, range)
209    }
210    fn recovered_block_range(
211        &self,
212        range: RangeInclusive<BlockNumber>,
213    ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
214        T::recovered_block_range(self, range)
215    }
216}
217
218impl<T: BlockReader> BlockReader for &T {
219    type Block = T::Block;
220
221    fn find_block_by_hash(
222        &self,
223        hash: B256,
224        source: BlockSource,
225    ) -> ProviderResult<Option<Self::Block>> {
226        T::find_block_by_hash(self, hash, source)
227    }
228    fn block(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
229        T::block(self, id)
230    }
231    fn pending_block(&self) -> ProviderResult<Option<SealedBlock<Self::Block>>> {
232        T::pending_block(self)
233    }
234    fn pending_block_with_senders(&self) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
235        T::pending_block_with_senders(self)
236    }
237    fn pending_block_and_receipts(
238        &self,
239    ) -> ProviderResult<Option<(SealedBlock<Self::Block>, Vec<Self::Receipt>)>> {
240        T::pending_block_and_receipts(self)
241    }
242    fn block_by_hash(&self, hash: B256) -> ProviderResult<Option<Self::Block>> {
243        T::block_by_hash(self, hash)
244    }
245    fn block_by_number(&self, num: u64) -> ProviderResult<Option<Self::Block>> {
246        T::block_by_number(self, num)
247    }
248    fn recovered_block(
249        &self,
250        id: BlockHashOrNumber,
251        transaction_kind: TransactionVariant,
252    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
253        T::recovered_block(self, id, transaction_kind)
254    }
255    fn sealed_block_with_senders(
256        &self,
257        id: BlockHashOrNumber,
258        transaction_kind: TransactionVariant,
259    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
260        T::sealed_block_with_senders(self, id, transaction_kind)
261    }
262    fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Self::Block>> {
263        T::block_range(self, range)
264    }
265    fn block_with_senders_range(
266        &self,
267        range: RangeInclusive<BlockNumber>,
268    ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
269        T::block_with_senders_range(self, range)
270    }
271    fn recovered_block_range(
272        &self,
273        range: RangeInclusive<BlockNumber>,
274    ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
275        T::recovered_block_range(self, range)
276    }
277}
278
279/// Trait extension for `BlockReader`, for types that implement `BlockId` conversion.
280///
281/// The `BlockReader` trait should be implemented on types that can retrieve a block from either
282/// a block number or hash. However, it might be desirable to fetch a block from a `BlockId` type,
283/// which can be a number, hash, or tag such as `BlockNumberOrTag::Safe`.
284///
285/// Resolving tags requires keeping track of block hashes or block numbers associated with the tag,
286/// so this trait can only be implemented for types that implement `BlockIdReader`. The
287/// `BlockIdReader` methods should be used to resolve `BlockId`s to block numbers or hashes, and
288/// retrieving the block should be done using the type's `BlockReader` methods.
289pub trait BlockReaderIdExt: BlockReader + ReceiptProviderIdExt {
290    /// Returns the block with matching tag from the database
291    ///
292    /// Returns `None` if block is not found.
293    fn block_by_number_or_tag(&self, id: BlockNumberOrTag) -> ProviderResult<Option<Self::Block>> {
294        self.convert_block_number(id)?.map_or_else(|| Ok(None), |num| self.block(num.into()))
295    }
296
297    /// Returns the pending block header if available
298    ///
299    /// Note: This returns a [`SealedHeader`] because it's expected that this is sealed by the
300    /// provider and the caller does not know the hash.
301    fn pending_header(&self) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
302        self.sealed_header_by_id(BlockNumberOrTag::Pending.into())
303    }
304
305    /// Returns the latest block header if available
306    ///
307    /// Note: This returns a [`SealedHeader`] because it's expected that this is sealed by the
308    /// provider and the caller does not know the hash.
309    fn latest_header(&self) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
310        self.sealed_header_by_id(BlockNumberOrTag::Latest.into())
311    }
312
313    /// Returns the safe block header if available
314    ///
315    /// Note: This returns a [`SealedHeader`] because it's expected that this is sealed by the
316    /// provider and the caller does not know the hash.
317    fn safe_header(&self) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
318        self.sealed_header_by_id(BlockNumberOrTag::Safe.into())
319    }
320
321    /// Returns the finalized block header if available
322    ///
323    /// Note: This returns a [`SealedHeader`] because it's expected that this is sealed by the
324    /// provider and the caller does not know the hash.
325    fn finalized_header(&self) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
326        self.sealed_header_by_id(BlockNumberOrTag::Finalized.into())
327    }
328
329    /// Returns the block with the matching [`BlockId`] from the database.
330    ///
331    /// Returns `None` if block is not found.
332    fn block_by_id(&self, id: BlockId) -> ProviderResult<Option<Self::Block>>;
333
334    /// Returns the block with senders with matching [`BlockId`].
335    ///
336    /// Returns the block's transactions in the requested variant.
337    ///
338    /// Returns `None` if block is not found.
339    fn block_with_senders_by_id(
340        &self,
341        id: BlockId,
342        transaction_kind: TransactionVariant,
343    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
344        match id {
345            BlockId::Hash(hash) => self.recovered_block(hash.block_hash.into(), transaction_kind),
346            BlockId::Number(num) => self
347                .convert_block_number(num)?
348                .map_or_else(|| Ok(None), |num| self.recovered_block(num.into(), transaction_kind)),
349        }
350    }
351
352    /// Returns the header with matching tag from the database
353    ///
354    /// Returns `None` if header is not found.
355    fn header_by_number_or_tag(
356        &self,
357        id: BlockNumberOrTag,
358    ) -> ProviderResult<Option<Self::Header>> {
359        self.convert_block_number(id)?
360            .map_or_else(|| Ok(None), |num| self.header_by_hash_or_number(num.into()))
361    }
362
363    /// Returns the header with matching tag from the database
364    ///
365    /// Returns `None` if header is not found.
366    fn sealed_header_by_number_or_tag(
367        &self,
368        id: BlockNumberOrTag,
369    ) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
370        self.convert_block_number(id)?
371            .map_or_else(|| Ok(None), |num| self.header_by_hash_or_number(num.into()))?
372            .map_or_else(|| Ok(None), |h| Ok(Some(SealedHeader::seal_slow(h))))
373    }
374
375    /// Returns the sealed header with the matching `BlockId` from the database.
376    ///
377    /// Returns `None` if header is not found.
378    fn sealed_header_by_id(
379        &self,
380        id: BlockId,
381    ) -> ProviderResult<Option<SealedHeader<Self::Header>>>;
382
383    /// Returns the header with the matching `BlockId` from the database.
384    ///
385    /// Returns `None` if header is not found.
386    fn header_by_id(&self, id: BlockId) -> ProviderResult<Option<Self::Header>>;
387
388    /// Returns the ommers with the matching tag from the database.
389    fn ommers_by_number_or_tag(
390        &self,
391        id: BlockNumberOrTag,
392    ) -> ProviderResult<Option<Vec<Self::Header>>> {
393        self.convert_block_number(id)?.map_or_else(|| Ok(None), |num| self.ommers(num.into()))
394    }
395
396    /// Returns the ommers with the matching `BlockId` from the database.
397    ///
398    /// Returns `None` if block is not found.
399    fn ommers_by_id(&self, id: BlockId) -> ProviderResult<Option<Vec<Self::Header>>>;
400}
401
402/// Functionality to read the last known chain blocks from the database.
403pub trait ChainStateBlockReader: Send + Sync {
404    /// Returns the last finalized block number.
405    ///
406    /// If no finalized block has been written yet, this returns `None`.
407    fn last_finalized_block_number(&self) -> ProviderResult<Option<BlockNumber>>;
408    /// Returns the last safe block number.
409    ///
410    /// If no safe block has been written yet, this returns `None`.
411    fn last_safe_block_number(&self) -> ProviderResult<Option<BlockNumber>>;
412}
413
414/// Functionality to write the last known chain blocks to the database.
415pub trait ChainStateBlockWriter: Send + Sync {
416    /// Saves the given finalized block number in the DB.
417    fn save_finalized_block_number(&self, block_number: BlockNumber) -> ProviderResult<()>;
418
419    /// Saves the given safe block number in the DB.
420    fn save_safe_block_number(&self, block_number: BlockNumber) -> ProviderResult<()>;
421}