Skip to main content

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