alloy_evm/block/
error.rs

1use crate::{EvmError, InvalidTxError};
2use alloc::{
3    boxed::Box,
4    string::{String, ToString},
5};
6use alloy_primitives::B256;
7
8/// Block validation error.
9#[derive(Debug, thiserror::Error)]
10pub enum BlockValidationError {
11    /// EVM error with transaction hash and message
12    #[error("EVM reported invalid transaction ({hash}): {error}")]
13    InvalidTx {
14        /// The hash of the transaction
15        hash: B256,
16        /// The EVM error.
17        error: Box<dyn InvalidTxError>,
18    },
19    /// Error when incrementing balance in post execution
20    #[error("incrementing balance in post execution failed")]
21    IncrementBalanceFailed,
22    /// Error when transaction gas limit exceeds available block gas
23    #[error(
24        "transaction gas limit {transaction_gas_limit} is more than blocks available gas {block_available_gas}"
25    )]
26    TransactionGasLimitMoreThanAvailableBlockGas {
27        /// The transaction's gas limit
28        transaction_gas_limit: u64,
29        /// The available block gas
30        block_available_gas: u64,
31    },
32    /// Error for EIP-4788 when parent beacon block root is missing
33    #[error("EIP-4788 parent beacon block root missing for active Cancun block")]
34    MissingParentBeaconBlockRoot,
35    /// Error for Cancun genesis block when parent beacon block root is not zero
36    #[error(
37        "the parent beacon block root is not zero for Cancun genesis block: {parent_beacon_block_root}"
38    )]
39    CancunGenesisParentBeaconBlockRootNotZero {
40        /// The beacon block root
41        parent_beacon_block_root: B256,
42    },
43    /// EVM error during [EIP-4788] beacon root contract call.
44    ///
45    /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788
46    #[error("failed to apply beacon root contract call at {parent_beacon_block_root}: {message}")]
47    BeaconRootContractCall {
48        /// The beacon block root
49        parent_beacon_block_root: Box<B256>,
50        /// The error message.
51        message: String,
52    },
53    /// EVM error during [EIP-2935] blockhash contract call.
54    ///
55    /// [EIP-2935]: https://eips.ethereum.org/EIPS/eip-2935
56    #[error("failed to apply blockhash contract call: {message}")]
57    BlockHashContractCall {
58        /// The error message.
59        message: String,
60    },
61    /// EVM error during withdrawal requests contract call [EIP-7002]
62    ///
63    /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002
64    #[error("failed to apply withdrawal requests contract call: {message}")]
65    WithdrawalRequestsContractCall {
66        /// The error message.
67        message: String,
68    },
69    /// EVM error during consolidation requests contract call [EIP-7251]
70    ///
71    /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251
72    #[error("failed to apply consolidation requests contract call: {message}")]
73    ConsolidationRequestsContractCall {
74        /// The error message.
75        message: String,
76    },
77    /// Error when decoding deposit requests from receipts [EIP-6110]
78    ///
79    /// [EIP-6110]: https://eips.ethereum.org/EIPS/eip-6110
80    #[error("failed to decode deposit requests from receipts: {_0}")]
81    DepositRequestDecode(String),
82    /// Arbitrary Block validation errors.
83    #[error(transparent)]
84    Other(Box<dyn core::error::Error + Send + Sync + 'static>),
85}
86
87impl BlockValidationError {
88    /// Create a new [`BlockValidationError::Other`] variant.
89    pub fn other<E>(error: E) -> Self
90    where
91        E: core::error::Error + Send + Sync + 'static,
92    {
93        Self::Other(Box::new(error))
94    }
95
96    /// Create a new [`BlockValidationError::Other`] variant from a given message.
97    pub fn msg(msg: impl core::fmt::Display) -> Self {
98        Self::Other(msg.to_string().into())
99    }
100}
101
102/// `BlockExecutor` Errors
103#[derive(Debug, thiserror::Error)]
104pub enum BlockExecutionError {
105    /// Validation error, transparently wrapping [`BlockValidationError`]
106    #[error(transparent)]
107    Validation(#[from] BlockValidationError),
108    /// Internal, i.e. non consensus or validation related Block Executor Errors
109    #[error(transparent)]
110    Internal(#[from] InternalBlockExecutionError),
111}
112
113impl BlockExecutionError {
114    /// Create a new [`BlockExecutionError::Internal`] variant, containing a
115    /// [`InternalBlockExecutionError::Other`] error.
116    pub fn other<E>(error: E) -> Self
117    where
118        E: core::error::Error + Send + Sync + 'static,
119    {
120        Self::Internal(InternalBlockExecutionError::other(error))
121    }
122
123    /// Create a new [`BlockExecutionError::Internal`] variant, containing a
124    /// [`InternalBlockExecutionError::Other`] error with the given message.
125    pub fn msg(msg: impl core::fmt::Display) -> Self {
126        Self::Internal(InternalBlockExecutionError::msg(msg))
127    }
128
129    /// Returns the inner `BlockValidationError` if the error is a validation error.
130    pub const fn as_validation(&self) -> Option<&BlockValidationError> {
131        match self {
132            Self::Validation(err) => Some(err),
133            _ => None,
134        }
135    }
136
137    /// Handles an EVM error occurred when executing a transaction.
138    ///
139    /// If an error matches [`EvmError::InvalidTransaction`], it will be wrapped into
140    /// [`BlockValidationError::InvalidTx`], otherwise into [`InternalBlockExecutionError::EVM`].
141    pub fn evm<E: EvmError>(error: E, hash: B256) -> Self {
142        match error.try_into_invalid_tx_err() {
143            Ok(err) => {
144                Self::Validation(BlockValidationError::InvalidTx { hash, error: Box::new(err) })
145            }
146            Err(err) => {
147                Self::Internal(InternalBlockExecutionError::EVM { hash, error: Box::new(err) })
148            }
149        }
150    }
151}
152
153/// Internal (i.e., not validation or consensus related) `BlockExecutor` Errors
154#[derive(Debug, thiserror::Error)]
155pub enum InternalBlockExecutionError {
156    /// EVM error occurred when executing transaction. This is different from
157    /// [`BlockValidationError::InvalidTx`] because it will only contain EVM errors which are not
158    /// transaction validation errors and are assumed to be fatal.
159    #[error("internal EVM error occurred when executing transaction {hash}: {error}")]
160    EVM {
161        /// The hash of the transaction
162        hash: B256,
163        /// The EVM error.
164        error: Box<dyn core::error::Error + Send + Sync>,
165    },
166    /// Arbitrary Block Executor Errors
167    #[error(transparent)]
168    Other(Box<dyn core::error::Error + Send + Sync + 'static>),
169}
170
171impl InternalBlockExecutionError {
172    /// Create a new [`InternalBlockExecutionError::Other`] variant.
173    pub fn other<E>(error: E) -> Self
174    where
175        E: core::error::Error + Send + Sync + 'static,
176    {
177        Self::Other(Box::new(error))
178    }
179
180    /// Create a new [`InternalBlockExecutionError::Other`] from a given message.
181    pub fn msg(msg: impl core::fmt::Display) -> Self {
182        Self::Other(msg.to_string().into())
183    }
184
185    /// Returns the arbitrary error if it is [`InternalBlockExecutionError::Other`]
186    pub fn as_other(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
187        match self {
188            Self::Other(err) => Some(&**err),
189            _ => None,
190        }
191    }
192
193    /// Attempts to downcast the [`InternalBlockExecutionError::Other`] variant to a concrete type
194    pub fn downcast<T: core::error::Error + 'static>(self) -> Result<Box<T>, Self> {
195        match self {
196            Self::Other(err) => err.downcast().map_err(Self::Other),
197            err => Err(err),
198        }
199    }
200
201    /// Returns a reference to the [`InternalBlockExecutionError::Other`] value if this type is a
202    /// [`InternalBlockExecutionError::Other`] of that type. Returns None otherwise.
203    pub fn downcast_other<T: core::error::Error + 'static>(&self) -> Option<&T> {
204        let other = self.as_other()?;
205        other.downcast_ref()
206    }
207
208    /// Returns true if the this type is a [`InternalBlockExecutionError::Other`] of that error
209    /// type. Returns false otherwise.
210    pub fn is_other<T: core::error::Error + 'static>(&self) -> bool {
211        self.as_other().map(|err| err.is::<T>()).unwrap_or(false)
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218
219    #[derive(thiserror::Error, Debug)]
220    #[error("err")]
221    struct E;
222
223    #[test]
224    fn other_downcast() {
225        let err = InternalBlockExecutionError::other(E);
226        assert!(err.is_other::<E>());
227
228        assert!(err.downcast_other::<E>().is_some());
229        assert!(err.downcast::<E>().is_ok());
230    }
231}