reth_storage_api/
block.rs

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