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