reth_blockchain_tree_api/
error.rs

1//! Error handling for the blockchain tree
2
3use alloy_consensus::BlockHeader;
4use alloy_primitives::{BlockHash, BlockNumber};
5use reth_consensus::ConsensusError;
6use reth_execution_errors::{
7    BlockExecutionError, BlockValidationError, InternalBlockExecutionError,
8};
9use reth_primitives::{SealedBlock, SealedBlockFor};
10use reth_primitives_traits::{Block, BlockBody};
11pub use reth_storage_errors::provider::ProviderError;
12
13/// Various error cases that can occur when a block violates tree assumptions.
14#[derive(Debug, Clone, Copy, thiserror::Error, Eq, PartialEq)]
15pub enum BlockchainTreeError {
16    /// Thrown if the block number is lower than the last finalized block number.
17    #[error("block number is lower than the last finalized block number #{last_finalized}")]
18    PendingBlockIsFinalized {
19        /// The block number of the last finalized block.
20        last_finalized: BlockNumber,
21    },
22    /// Thrown if no side chain could be found for the block.
23    #[error("chainId can't be found in BlockchainTree with internal index {chain_id}")]
24    BlockSideChainIdConsistency {
25        /// The internal identifier for the side chain.
26        chain_id: u64,
27    },
28    /// Thrown if a canonical chain header cannot be found.
29    #[error("canonical chain header {block_hash} can't be found")]
30    CanonicalChain {
31        /// The block hash of the missing canonical chain header.
32        block_hash: BlockHash,
33    },
34    /// Thrown if a block number cannot be found in the blockchain tree chain.
35    #[error("block number #{block_number} not found in blockchain tree chain")]
36    BlockNumberNotFoundInChain {
37        /// The block number that could not be found.
38        block_number: BlockNumber,
39    },
40    /// Thrown if a block hash cannot be found in the blockchain tree chain.
41    #[error("block hash {block_hash} not found in blockchain tree chain")]
42    BlockHashNotFoundInChain {
43        /// The block hash that could not be found.
44        block_hash: BlockHash,
45    },
46    /// Thrown if the block failed to buffer
47    #[error("block with hash {block_hash} failed to buffer")]
48    BlockBufferingFailed {
49        /// The block hash of the block that failed to buffer.
50        block_hash: BlockHash,
51    },
52    /// Thrown when trying to access genesis parent.
53    #[error("genesis block has no parent")]
54    GenesisBlockHasNoParent,
55}
56
57/// Canonical Errors
58#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
59pub enum CanonicalError {
60    /// Error originating from validation operations.
61    #[error(transparent)]
62    Validation(#[from] BlockValidationError),
63    /// Error originating from blockchain tree operations.
64    #[error(transparent)]
65    BlockchainTree(#[from] BlockchainTreeError),
66    /// Error originating from a provider operation.
67    #[error(transparent)]
68    Provider(#[from] ProviderError),
69    /// Error indicating a transaction reverted during execution.
70    #[error("transaction error on revert: {0}")]
71    CanonicalRevert(String),
72    /// Error indicating a transaction failed to commit during execution.
73    #[error("transaction error on commit: {0}")]
74    CanonicalCommit(String),
75    /// Error indicating that a previous optimistic sync target was re-orged
76    #[error("transaction error on revert: {0}")]
77    OptimisticTargetRevert(BlockNumber),
78}
79
80impl CanonicalError {
81    /// Returns `true` if the error is fatal.
82    pub const fn is_fatal(&self) -> bool {
83        matches!(self, Self::CanonicalCommit(_) | Self::CanonicalRevert(_))
84    }
85
86    /// Returns `true` if the underlying error matches
87    /// [`BlockchainTreeError::BlockHashNotFoundInChain`].
88    pub const fn is_block_hash_not_found(&self) -> bool {
89        matches!(self, Self::BlockchainTree(BlockchainTreeError::BlockHashNotFoundInChain { .. }))
90    }
91
92    /// Returns `Some(BlockNumber)` if the underlying error matches
93    /// [`CanonicalError::OptimisticTargetRevert`].
94    pub const fn optimistic_revert_block_number(&self) -> Option<BlockNumber> {
95        match self {
96            Self::OptimisticTargetRevert(block_number) => Some(*block_number),
97            _ => None,
98        }
99    }
100}
101
102/// Error thrown when inserting a block failed because the block is considered invalid.
103#[derive(thiserror::Error)]
104#[error(transparent)]
105pub struct InsertBlockError {
106    inner: Box<InsertBlockErrorData>,
107}
108
109// === impl InsertBlockError ===
110
111impl InsertBlockError {
112    /// Create a new `InsertInvalidBlockError`
113    pub fn new(block: SealedBlock, kind: InsertBlockErrorKind) -> Self {
114        Self { inner: InsertBlockErrorData::boxed(block, kind) }
115    }
116
117    /// Create a new `InsertInvalidBlockError` from a tree error
118    pub fn tree_error(error: BlockchainTreeError, block: SealedBlock) -> Self {
119        Self::new(block, InsertBlockErrorKind::Tree(error))
120    }
121
122    /// Create a new `InsertInvalidBlockError` from a consensus error
123    pub fn consensus_error(error: ConsensusError, block: SealedBlock) -> Self {
124        Self::new(block, InsertBlockErrorKind::Consensus(error))
125    }
126
127    /// Create a new `InsertInvalidBlockError` from a consensus error
128    pub fn sender_recovery_error(block: SealedBlock) -> Self {
129        Self::new(block, InsertBlockErrorKind::SenderRecovery)
130    }
131
132    /// Create a new `InsertInvalidBlockError` from an execution error
133    pub fn execution_error(error: BlockExecutionError, block: SealedBlock) -> Self {
134        Self::new(block, InsertBlockErrorKind::Execution(error))
135    }
136
137    /// Consumes the error and returns the block that resulted in the error
138    #[inline]
139    pub fn into_block(self) -> SealedBlock {
140        self.inner.block
141    }
142
143    /// Returns the error kind
144    #[inline]
145    pub const fn kind(&self) -> &InsertBlockErrorKind {
146        &self.inner.kind
147    }
148
149    /// Returns the block that resulted in the error
150    #[inline]
151    pub const fn block(&self) -> &SealedBlock {
152        &self.inner.block
153    }
154
155    /// Consumes the type and returns the block and error kind.
156    #[inline]
157    pub fn split(self) -> (SealedBlock, InsertBlockErrorKind) {
158        let inner = *self.inner;
159        (inner.block, inner.kind)
160    }
161}
162
163impl std::fmt::Debug for InsertBlockError {
164    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165        std::fmt::Debug::fmt(&self.inner, f)
166    }
167}
168
169#[derive(thiserror::Error, Debug)]
170#[error("Failed to insert block (hash={}, number={}, parent_hash={}): {kind}", 
171        .block.hash(),
172        .block.number,
173        .block.parent_hash)]
174struct InsertBlockErrorData {
175    block: SealedBlock,
176    #[source]
177    kind: InsertBlockErrorKind,
178}
179
180impl InsertBlockErrorData {
181    const fn new(block: SealedBlock, kind: InsertBlockErrorKind) -> Self {
182        Self { block, kind }
183    }
184
185    fn boxed(block: SealedBlock, kind: InsertBlockErrorKind) -> Box<Self> {
186        Box::new(Self::new(block, kind))
187    }
188}
189
190struct InsertBlockErrorDataTwo<B: Block> {
191    block: SealedBlockFor<B>,
192    kind: InsertBlockErrorKindTwo,
193}
194
195impl<B: Block> std::fmt::Display for InsertBlockErrorDataTwo<B> {
196    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197        write!(
198            f,
199            "Failed to insert block (hash={}, number={}, parent_hash={}): {}",
200            self.block.hash(),
201            self.block.number(),
202            self.block.parent_hash(),
203            self.kind
204        )
205    }
206}
207
208impl<B: Block> std::fmt::Debug for InsertBlockErrorDataTwo<B> {
209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210        f.debug_struct("InsertBlockError")
211            .field("error", &self.kind)
212            .field("hash", &self.block.hash())
213            .field("number", &self.block.number())
214            .field("parent_hash", &self.block.parent_hash())
215            .field("num_txs", &self.block.body.transactions().len())
216            .finish_non_exhaustive()
217    }
218}
219
220impl<B: Block> core::error::Error for InsertBlockErrorDataTwo<B> {
221    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
222        Some(&self.kind)
223    }
224}
225
226impl<B: Block> InsertBlockErrorDataTwo<B> {
227    const fn new(block: SealedBlockFor<B>, kind: InsertBlockErrorKindTwo) -> Self {
228        Self { block, kind }
229    }
230
231    fn boxed(block: SealedBlockFor<B>, kind: InsertBlockErrorKindTwo) -> Box<Self> {
232        Box::new(Self::new(block, kind))
233    }
234}
235
236/// Error thrown when inserting a block failed because the block is considered invalid.
237#[derive(thiserror::Error)]
238#[error(transparent)]
239pub struct InsertBlockErrorTwo<B: Block> {
240    inner: Box<InsertBlockErrorDataTwo<B>>,
241}
242
243// === impl InsertBlockErrorTwo ===
244
245impl<B: Block> InsertBlockErrorTwo<B> {
246    /// Create a new `InsertInvalidBlockErrorTwo`
247    pub fn new(block: SealedBlockFor<B>, kind: InsertBlockErrorKindTwo) -> Self {
248        Self { inner: InsertBlockErrorDataTwo::boxed(block, kind) }
249    }
250
251    /// Create a new `InsertInvalidBlockError` from a consensus error
252    pub fn consensus_error(error: ConsensusError, block: SealedBlockFor<B>) -> Self {
253        Self::new(block, InsertBlockErrorKindTwo::Consensus(error))
254    }
255
256    /// Create a new `InsertInvalidBlockError` from a consensus error
257    pub fn sender_recovery_error(block: SealedBlockFor<B>) -> Self {
258        Self::new(block, InsertBlockErrorKindTwo::SenderRecovery)
259    }
260
261    /// Create a new `InsertInvalidBlockError` from an execution error
262    pub fn execution_error(error: BlockExecutionError, block: SealedBlockFor<B>) -> Self {
263        Self::new(block, InsertBlockErrorKindTwo::Execution(error))
264    }
265
266    /// Consumes the error and returns the block that resulted in the error
267    #[inline]
268    pub fn into_block(self) -> SealedBlockFor<B> {
269        self.inner.block
270    }
271
272    /// Returns the error kind
273    #[inline]
274    pub const fn kind(&self) -> &InsertBlockErrorKindTwo {
275        &self.inner.kind
276    }
277
278    /// Returns the block that resulted in the error
279    #[inline]
280    pub const fn block(&self) -> &SealedBlockFor<B> {
281        &self.inner.block
282    }
283
284    /// Consumes the type and returns the block and error kind.
285    #[inline]
286    pub fn split(self) -> (SealedBlockFor<B>, InsertBlockErrorKindTwo) {
287        let inner = *self.inner;
288        (inner.block, inner.kind)
289    }
290}
291
292impl<B: Block> std::fmt::Debug for InsertBlockErrorTwo<B> {
293    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294        std::fmt::Debug::fmt(&self.inner, f)
295    }
296}
297
298/// All error variants possible when inserting a block
299#[derive(Debug, thiserror::Error)]
300pub enum InsertBlockErrorKindTwo {
301    /// Failed to recover senders for the block
302    #[error("failed to recover senders for block")]
303    SenderRecovery,
304    /// Block violated consensus rules.
305    #[error(transparent)]
306    Consensus(#[from] ConsensusError),
307    /// Block execution failed.
308    #[error(transparent)]
309    Execution(#[from] BlockExecutionError),
310    /// Provider error.
311    #[error(transparent)]
312    Provider(#[from] ProviderError),
313    /// Other errors.
314    #[error(transparent)]
315    Other(#[from] Box<dyn core::error::Error + Send + Sync + 'static>),
316}
317
318impl InsertBlockErrorKindTwo {
319    /// Returns an [`InsertBlockValidationError`] if the error is caused by an invalid block.
320    ///
321    /// Returns an [`InsertBlockFatalError`] if the error is caused by an error that is not
322    /// validation related or is otherwise fatal.
323    ///
324    /// This is intended to be used to determine if we should respond `INVALID` as a response when
325    /// processing a new block.
326    pub fn ensure_validation_error(
327        self,
328    ) -> Result<InsertBlockValidationError, InsertBlockFatalError> {
329        match self {
330            Self::SenderRecovery => Ok(InsertBlockValidationError::SenderRecovery),
331            Self::Consensus(err) => Ok(InsertBlockValidationError::Consensus(err)),
332            // other execution errors that are considered internal errors
333            Self::Execution(err) => {
334                match err {
335                    BlockExecutionError::Validation(err) => {
336                        Ok(InsertBlockValidationError::Validation(err))
337                    }
338                    BlockExecutionError::Consensus(err) => {
339                        Ok(InsertBlockValidationError::Consensus(err))
340                    }
341                    // these are internal errors, not caused by an invalid block
342                    BlockExecutionError::Internal(error) => {
343                        Err(InsertBlockFatalError::BlockExecutionError(error))
344                    }
345                }
346            }
347            Self::Provider(err) => Err(InsertBlockFatalError::Provider(err)),
348            Self::Other(err) => Err(InternalBlockExecutionError::Other(err).into()),
349        }
350    }
351}
352
353/// Error variants that are not caused by invalid blocks
354#[derive(Debug, thiserror::Error)]
355pub enum InsertBlockFatalError {
356    /// A provider error
357    #[error(transparent)]
358    Provider(#[from] ProviderError),
359    /// An internal / fatal block execution error
360    #[error(transparent)]
361    BlockExecutionError(#[from] InternalBlockExecutionError),
362}
363
364/// Error variants that are caused by invalid blocks
365#[derive(Debug, thiserror::Error)]
366pub enum InsertBlockValidationError {
367    /// Failed to recover senders for the block
368    #[error("failed to recover senders for block")]
369    SenderRecovery,
370    /// Block violated consensus rules.
371    #[error(transparent)]
372    Consensus(#[from] ConsensusError),
373    /// Validation error, transparently wrapping [`BlockValidationError`]
374    #[error(transparent)]
375    Validation(#[from] BlockValidationError),
376}
377
378impl InsertBlockValidationError {
379    /// Returns true if this is a block pre merge error.
380    pub const fn is_block_pre_merge(&self) -> bool {
381        matches!(self, Self::Validation(BlockValidationError::BlockPreMerge { .. }))
382    }
383}
384
385/// All error variants possible when inserting a block
386#[derive(Debug, thiserror::Error)]
387pub enum InsertBlockErrorKind {
388    /// Failed to recover senders for the block
389    #[error("failed to recover senders for block")]
390    SenderRecovery,
391    /// Block violated consensus rules.
392    #[error(transparent)]
393    Consensus(#[from] ConsensusError),
394    /// Block execution failed.
395    #[error(transparent)]
396    Execution(#[from] BlockExecutionError),
397    /// Block violated tree invariants.
398    #[error(transparent)]
399    Tree(#[from] BlockchainTreeError),
400    /// Provider error.
401    #[error(transparent)]
402    Provider(#[from] ProviderError),
403    /// An internal error occurred, like interacting with the database.
404    #[error(transparent)]
405    Internal(#[from] Box<dyn core::error::Error + Send + Sync>),
406    /// Canonical error.
407    #[error(transparent)]
408    Canonical(#[from] CanonicalError),
409}
410
411impl InsertBlockErrorKind {
412    /// Returns true if the error is a tree error
413    pub const fn is_tree_error(&self) -> bool {
414        matches!(self, Self::Tree(_))
415    }
416
417    /// Returns true if the error is a consensus error
418    pub const fn is_consensus_error(&self) -> bool {
419        matches!(self, Self::Consensus(_))
420    }
421
422    /// Returns true if this error is a state root error
423    pub const fn is_state_root_error(&self) -> bool {
424        // we need to get the state root errors inside of the different variant branches
425        match self {
426            Self::Execution(err) => {
427                matches!(
428                    err,
429                    BlockExecutionError::Validation(BlockValidationError::StateRoot { .. })
430                )
431            }
432            Self::Canonical(err) => {
433                matches!(
434                    err,
435                    CanonicalError::Validation(BlockValidationError::StateRoot { .. }) |
436                        CanonicalError::Provider(
437                            ProviderError::StateRootMismatch(_) |
438                                ProviderError::UnwindStateRootMismatch(_)
439                        )
440                )
441            }
442            Self::Provider(err) => {
443                matches!(
444                    err,
445                    ProviderError::StateRootMismatch(_) | ProviderError::UnwindStateRootMismatch(_)
446                )
447            }
448            _ => false,
449        }
450    }
451
452    /// Returns true if the error is caused by an invalid block
453    ///
454    /// This is intended to be used to determine if the block should be marked as invalid.
455    #[allow(clippy::match_same_arms)]
456    pub const fn is_invalid_block(&self) -> bool {
457        match self {
458            Self::SenderRecovery | Self::Consensus(_) => true,
459            // other execution errors that are considered internal errors
460            Self::Execution(err) => {
461                match err {
462                    BlockExecutionError::Validation(_) | BlockExecutionError::Consensus(_) => {
463                        // this is caused by an invalid block
464                        true
465                    }
466                    // these are internal errors, not caused by an invalid block
467                    BlockExecutionError::Internal(_) => false,
468                }
469            }
470            Self::Tree(err) => {
471                match err {
472                    BlockchainTreeError::PendingBlockIsFinalized { .. } => {
473                        // the block's number is lower than the finalized block's number
474                        true
475                    }
476                    BlockchainTreeError::BlockSideChainIdConsistency { .. } |
477                    BlockchainTreeError::CanonicalChain { .. } |
478                    BlockchainTreeError::BlockNumberNotFoundInChain { .. } |
479                    BlockchainTreeError::BlockHashNotFoundInChain { .. } |
480                    BlockchainTreeError::BlockBufferingFailed { .. } |
481                    BlockchainTreeError::GenesisBlockHasNoParent => false,
482                }
483            }
484            Self::Provider(_) | Self::Internal(_) => {
485                // any other error, such as database errors, are considered internal errors
486                false
487            }
488            Self::Canonical(err) => match err {
489                CanonicalError::BlockchainTree(_) |
490                CanonicalError::CanonicalCommit(_) |
491                CanonicalError::CanonicalRevert(_) |
492                CanonicalError::OptimisticTargetRevert(_) |
493                CanonicalError::Provider(_) => false,
494                CanonicalError::Validation(_) => true,
495            },
496        }
497    }
498
499    /// Returns true if this is a block pre merge error.
500    pub const fn is_block_pre_merge(&self) -> bool {
501        matches!(
502            self,
503            Self::Execution(BlockExecutionError::Validation(
504                BlockValidationError::BlockPreMerge { .. }
505            ))
506        )
507    }
508
509    /// Returns true if the error is an execution error
510    pub const fn is_execution_error(&self) -> bool {
511        matches!(self, Self::Execution(_))
512    }
513
514    /// Returns true if the error is an internal error
515    pub const fn is_internal(&self) -> bool {
516        matches!(self, Self::Internal(_))
517    }
518
519    /// Returns the error if it is a tree error
520    pub const fn as_tree_error(&self) -> Option<BlockchainTreeError> {
521        match self {
522            Self::Tree(err) => Some(*err),
523            _ => None,
524        }
525    }
526
527    /// Returns the error if it is a consensus error
528    pub const fn as_consensus_error(&self) -> Option<&ConsensusError> {
529        match self {
530            Self::Consensus(err) => Some(err),
531            _ => None,
532        }
533    }
534
535    /// Returns the error if it is an execution error
536    pub const fn as_execution_error(&self) -> Option<&BlockExecutionError> {
537        match self {
538            Self::Execution(err) => Some(err),
539            _ => None,
540        }
541    }
542}