Skip to main content

reth_storage_api/
block.rs

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