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, 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
149impl<T: BlockReader> BlockReader for Arc<T> {
150    type Block = T::Block;
151
152    fn find_block_by_hash(
153        &self,
154        hash: B256,
155        source: BlockSource,
156    ) -> ProviderResult<Option<Self::Block>> {
157        T::find_block_by_hash(self, hash, source)
158    }
159    fn block(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
160        T::block(self, id)
161    }
162    fn pending_block(&self) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
163        T::pending_block(self)
164    }
165    fn pending_block_and_receipts(
166        &self,
167    ) -> ProviderResult<Option<(RecoveredBlock<Self::Block>, Vec<Self::Receipt>)>> {
168        T::pending_block_and_receipts(self)
169    }
170    fn block_by_hash(&self, hash: B256) -> ProviderResult<Option<Self::Block>> {
171        T::block_by_hash(self, hash)
172    }
173    fn block_by_number(&self, num: u64) -> ProviderResult<Option<Self::Block>> {
174        T::block_by_number(self, num)
175    }
176    fn recovered_block(
177        &self,
178        id: BlockHashOrNumber,
179        transaction_kind: TransactionVariant,
180    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
181        T::recovered_block(self, id, transaction_kind)
182    }
183    fn sealed_block_with_senders(
184        &self,
185        id: BlockHashOrNumber,
186        transaction_kind: TransactionVariant,
187    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
188        T::sealed_block_with_senders(self, id, transaction_kind)
189    }
190    fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Self::Block>> {
191        T::block_range(self, range)
192    }
193    fn block_with_senders_range(
194        &self,
195        range: RangeInclusive<BlockNumber>,
196    ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
197        T::block_with_senders_range(self, range)
198    }
199    fn recovered_block_range(
200        &self,
201        range: RangeInclusive<BlockNumber>,
202    ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
203        T::recovered_block_range(self, range)
204    }
205}
206
207impl<T: BlockReader> BlockReader for &T {
208    type Block = T::Block;
209
210    fn find_block_by_hash(
211        &self,
212        hash: B256,
213        source: BlockSource,
214    ) -> ProviderResult<Option<Self::Block>> {
215        T::find_block_by_hash(self, hash, source)
216    }
217    fn block(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
218        T::block(self, id)
219    }
220    fn pending_block(&self) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
221        T::pending_block(self)
222    }
223    fn pending_block_and_receipts(
224        &self,
225    ) -> ProviderResult<Option<(RecoveredBlock<Self::Block>, Vec<Self::Receipt>)>> {
226        T::pending_block_and_receipts(self)
227    }
228    fn block_by_hash(&self, hash: B256) -> ProviderResult<Option<Self::Block>> {
229        T::block_by_hash(self, hash)
230    }
231    fn block_by_number(&self, num: u64) -> ProviderResult<Option<Self::Block>> {
232        T::block_by_number(self, num)
233    }
234    fn recovered_block(
235        &self,
236        id: BlockHashOrNumber,
237        transaction_kind: TransactionVariant,
238    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
239        T::recovered_block(self, id, transaction_kind)
240    }
241    fn sealed_block_with_senders(
242        &self,
243        id: BlockHashOrNumber,
244        transaction_kind: TransactionVariant,
245    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
246        T::sealed_block_with_senders(self, id, transaction_kind)
247    }
248    fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Self::Block>> {
249        T::block_range(self, range)
250    }
251    fn block_with_senders_range(
252        &self,
253        range: RangeInclusive<BlockNumber>,
254    ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
255        T::block_with_senders_range(self, range)
256    }
257    fn recovered_block_range(
258        &self,
259        range: RangeInclusive<BlockNumber>,
260    ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
261        T::recovered_block_range(self, range)
262    }
263}
264
265/// Trait extension for `BlockReader`, for types that implement `BlockId` conversion.
266///
267/// The `BlockReader` trait should be implemented on types that can retrieve a block from either
268/// a block number or hash. However, it might be desirable to fetch a block from a `BlockId` type,
269/// which can be a number, hash, or tag such as `BlockNumberOrTag::Safe`.
270///
271/// Resolving tags requires keeping track of block hashes or block numbers associated with the tag,
272/// so this trait can only be implemented for types that implement `BlockIdReader`. The
273/// `BlockIdReader` methods should be used to resolve `BlockId`s to block numbers or hashes, and
274/// retrieving the block should be done using the type's `BlockReader` methods.
275pub trait BlockReaderIdExt: BlockReader + ReceiptProviderIdExt {
276    /// Returns the block with matching tag from the database
277    ///
278    /// Returns `None` if block is not found.
279    fn block_by_number_or_tag(&self, id: BlockNumberOrTag) -> ProviderResult<Option<Self::Block>> {
280        self.convert_block_number(id)?.map_or_else(|| Ok(None), |num| self.block(num.into()))
281    }
282
283    /// Returns the pending block header if available
284    ///
285    /// Note: This returns a [`SealedHeader`] because it's expected that this is sealed by the
286    /// provider and the caller does not know the hash.
287    fn pending_header(&self) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
288        self.sealed_header_by_id(BlockNumberOrTag::Pending.into())
289    }
290
291    /// Returns the latest 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 latest_header(&self) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
296        self.sealed_header_by_id(BlockNumberOrTag::Latest.into())
297    }
298
299    /// Returns the safe 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 safe_header(&self) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
304        self.sealed_header_by_id(BlockNumberOrTag::Safe.into())
305    }
306
307    /// Returns the finalized 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 finalized_header(&self) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
312        self.sealed_header_by_id(BlockNumberOrTag::Finalized.into())
313    }
314
315    /// Returns the block with the matching [`BlockId`] from the database.
316    ///
317    /// Returns `None` if block is not found.
318    fn block_by_id(&self, id: BlockId) -> ProviderResult<Option<Self::Block>>;
319
320    /// Returns the block with senders with matching [`BlockId`].
321    ///
322    /// Returns the block's transactions in the requested variant.
323    ///
324    /// Returns `None` if block is not found.
325    fn block_with_senders_by_id(
326        &self,
327        id: BlockId,
328        transaction_kind: TransactionVariant,
329    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
330        match id {
331            BlockId::Hash(hash) => self.recovered_block(hash.block_hash.into(), transaction_kind),
332            BlockId::Number(num) => self
333                .convert_block_number(num)?
334                .map_or_else(|| Ok(None), |num| self.recovered_block(num.into(), transaction_kind)),
335        }
336    }
337
338    /// Returns the header with matching tag from the database
339    ///
340    /// Returns `None` if header is not found.
341    fn header_by_number_or_tag(
342        &self,
343        id: BlockNumberOrTag,
344    ) -> ProviderResult<Option<Self::Header>> {
345        self.convert_block_number(id)?
346            .map_or_else(|| Ok(None), |num| self.header_by_hash_or_number(num.into()))
347    }
348
349    /// Returns the header with matching tag from the database
350    ///
351    /// Returns `None` if header is not found.
352    fn sealed_header_by_number_or_tag(
353        &self,
354        id: BlockNumberOrTag,
355    ) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
356        self.convert_block_number(id)?
357            .map_or_else(|| Ok(None), |num| self.header_by_hash_or_number(num.into()))?
358            .map_or_else(|| Ok(None), |h| Ok(Some(SealedHeader::seal_slow(h))))
359    }
360
361    /// Returns the sealed header with the matching `BlockId` from the database.
362    ///
363    /// Returns `None` if header is not found.
364    fn sealed_header_by_id(
365        &self,
366        id: BlockId,
367    ) -> ProviderResult<Option<SealedHeader<Self::Header>>>;
368
369    /// Returns the header with the matching `BlockId` from the database.
370    ///
371    /// Returns `None` if header is not found.
372    fn header_by_id(&self, id: BlockId) -> ProviderResult<Option<Self::Header>>;
373}
374
375/// Functionality to read the last known chain blocks from the database.
376pub trait ChainStateBlockReader: Send + Sync {
377    /// Returns the last finalized block number.
378    ///
379    /// If no finalized block has been written yet, this returns `None`.
380    fn last_finalized_block_number(&self) -> ProviderResult<Option<BlockNumber>>;
381    /// Returns the last safe block number.
382    ///
383    /// If no safe block has been written yet, this returns `None`.
384    fn last_safe_block_number(&self) -> ProviderResult<Option<BlockNumber>>;
385}
386
387/// Functionality to write the last known chain blocks to the database.
388pub trait ChainStateBlockWriter: Send + Sync {
389    /// Saves the given finalized block number in the DB.
390    fn save_finalized_block_number(&self, block_number: BlockNumber) -> ProviderResult<()>;
391
392    /// Saves the given safe block number in the DB.
393    fn save_safe_block_number(&self, block_number: BlockNumber) -> ProviderResult<()>;
394}