reth_storage_api/
block.rs

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