diff --git a/Cargo.lock b/Cargo.lock index 1a984e04d..4fa6fc73d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7007,6 +7007,7 @@ dependencies = [ "reth-revm", "revm", "revm-primitives", + "thiserror", "tracing", ] diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 5346eafbd..64d311549 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -2162,7 +2162,7 @@ mod tests { .assert(&tree); // unwind canonical - assert_eq!(tree.unwind(block1.number), Ok(())); + assert!(tree.unwind(block1.number).is_ok()); // Trie state: // b2 b2a (pending block) // / / @@ -2226,7 +2226,7 @@ mod tests { .assert(&tree); // update canonical block to b2, this would make b2a be removed - assert_eq!(tree.connect_buffered_blocks_to_canonical_hashes_and_finalize(12), Ok(())); + assert!(tree.connect_buffered_blocks_to_canonical_hashes_and_finalize(12).is_ok()); assert_eq!( tree.is_block_known(block2.num_hash()).unwrap(), diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index ff3a4e76d..db361f35d 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -523,9 +523,10 @@ mod tests { .expect_err( "Executing cancun block without parent beacon block root field should fail", ); + assert_eq!( - err, - BlockExecutionError::Validation(BlockValidationError::MissingParentBeaconBlockRoot) + err.as_validation().unwrap().clone(), + BlockValidationError::MissingParentBeaconBlockRoot ); // fix header, set a gas limit diff --git a/crates/interfaces/src/blockchain_tree/error.rs b/crates/interfaces/src/blockchain_tree/error.rs index b63698576..b805c6ee8 100644 --- a/crates/interfaces/src/blockchain_tree/error.rs +++ b/crates/interfaces/src/blockchain_tree/error.rs @@ -293,8 +293,7 @@ impl InsertBlockErrorKind { BlockExecutionError::CanonicalCommit { .. } | BlockExecutionError::AppendChainDoesntConnect { .. } | BlockExecutionError::UnavailableForTest => false, - #[cfg(feature = "optimism")] - BlockExecutionError::OptimismBlockExecution(_) => false, + BlockExecutionError::Other(_) => false, } } InsertBlockErrorKind::Tree(err) => { diff --git a/crates/interfaces/src/error.rs b/crates/interfaces/src/error.rs index df307ae09..38498c312 100644 --- a/crates/interfaces/src/error.rs +++ b/crates/interfaces/src/error.rs @@ -16,7 +16,7 @@ pub type RethResult = Result; /// This enum encapsulates various error types that can occur during blockchain interactions. /// /// It allows for structured error handling based on the nature of the encountered issue. -#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)] +#[derive(Debug, thiserror::Error)] pub enum RethError { /// Error encountered during block execution. #[error(transparent)] diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index e8f7f40b1..04b9832f0 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -80,7 +80,7 @@ pub enum BlockValidationError { } /// BlockExecutor Errors -#[derive(Error, Debug, Clone, PartialEq, Eq)] +#[derive(Error, Debug)] pub enum BlockExecutionError { /// Validation error, transparently wrapping `BlockValidationError` #[error(transparent)] @@ -118,35 +118,28 @@ pub enum BlockExecutionError { /// Error when fetching latest block state. #[error(transparent)] LatestBlock(#[from] ProviderError), - /// Optimism Block Executor Errors - #[cfg(feature = "optimism")] #[error(transparent)] - OptimismBlockExecution(#[from] OptimismBlockExecutionError), -} - -/// Optimism Block Executor Errors -#[cfg(feature = "optimism")] -#[derive(Error, Debug, Clone, PartialEq, Eq)] -pub enum OptimismBlockExecutionError { - /// Error when trying to parse L1 block info - #[error("could not get L1 block info from L2 block: {message:?}")] - L1BlockInfoError { - /// The inner error message - message: String, - }, - /// Thrown when force deploy of create2deployer code fails. - #[error("failed to force create2deployer account code")] - ForceCreate2DeployerFail, - /// Thrown when a blob transaction is included in a sequencer's block. - #[error("blob transaction included in sequencer block")] - BlobTransactionRejected, - /// Thrown when a database account could not be loaded. - #[error("failed to load account {0}")] - AccountLoadFailed(reth_primitives::Address), + Other(Box), } impl BlockExecutionError { + /// Create a new `BlockExecutionError::Other` variant. + pub fn other(error: E) -> Self + where + E: std::error::Error + Send + Sync + 'static, + { + Self::Other(Box::new(error)) + } + + /// Returns the inner `BlockValidationError` if the error is a validation error. + pub const fn as_validation(&self) -> Option<&BlockValidationError> { + match self { + Self::Validation(err) => Some(err), + _ => None, + } + } + /// Returns `true` if the error is fatal. /// /// This represents an unrecoverable database related error. diff --git a/crates/optimism/evm/Cargo.toml b/crates/optimism/evm/Cargo.toml index 8e5afc5ef..4e5fd2f19 100644 --- a/crates/optimism/evm/Cargo.toml +++ b/crates/optimism/evm/Cargo.toml @@ -23,6 +23,7 @@ revm.workspace = true revm-primitives.workspace = true # misc +thiserror.workspace = true tracing.workspace = true [dev-dependencies] diff --git a/crates/optimism/evm/src/error.rs b/crates/optimism/evm/src/error.rs new file mode 100644 index 000000000..de923d44c --- /dev/null +++ b/crates/optimism/evm/src/error.rs @@ -0,0 +1,29 @@ +//! Error types for the Optimism EVM module. + +use reth_interfaces::executor::BlockExecutionError; + +/// Optimism Block Executor Errors +#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] +pub enum OptimismBlockExecutionError { + /// Error when trying to parse L1 block info + #[error("could not get L1 block info from L2 block: {message:?}")] + L1BlockInfoError { + /// The inner error message + message: String, + }, + /// Thrown when force deploy of create2deployer code fails. + #[error("failed to force create2deployer account code")] + ForceCreate2DeployerFail, + /// Thrown when a blob transaction is included in a sequencer's block. + #[error("blob transaction included in sequencer block")] + BlobTransactionRejected, + /// Thrown when a database account could not be loaded. + #[error("failed to load account {0}")] + AccountLoadFailed(reth_primitives::Address), +} + +impl From for BlockExecutionError { + fn from(err: OptimismBlockExecutionError) -> Self { + BlockExecutionError::other(err) + } +} diff --git a/crates/optimism/evm/src/execute.rs b/crates/optimism/evm/src/execute.rs index d19d441a8..c6bb5c7cf 100644 --- a/crates/optimism/evm/src/execute.rs +++ b/crates/optimism/evm/src/execute.rs @@ -1,6 +1,9 @@ //! Optimism block executor. -use crate::{l1::ensure_create2_deployer, verify::verify_receipts, OptimismEvmConfig}; +use crate::{ + l1::ensure_create2_deployer, verify::verify_receipts, OptimismBlockExecutionError, + OptimismEvmConfig, +}; use reth_evm::{ execute::{ BatchBlockExecutionOutput, BatchExecutor, BlockExecutionInput, BlockExecutionOutput, @@ -9,7 +12,7 @@ use reth_evm::{ ConfigureEvm, ConfigureEvmEnv, }; use reth_interfaces::{ - executor::{BlockExecutionError, BlockValidationError, OptimismBlockExecutionError}, + executor::{BlockExecutionError, BlockValidationError}, provider::ProviderError, }; use reth_primitives::{ @@ -141,13 +144,8 @@ where // blocks will always have at least a single transaction in them (the L1 info transaction), // so we can safely assume that this will always be triggered upon the transition and that // the above check for empty blocks will never be hit on OP chains. - ensure_create2_deployer(self.chain_spec.clone(), block.timestamp, evm.db_mut()).map_err( - |_| { - BlockExecutionError::OptimismBlockExecution( - OptimismBlockExecutionError::ForceCreate2DeployerFail, - ) - }, - )?; + ensure_create2_deployer(self.chain_spec.clone(), block.timestamp, evm.db_mut()) + .map_err(|_| OptimismBlockExecutionError::ForceCreate2DeployerFail)?; let mut cumulative_gas_used = 0; let mut receipts = Vec::with_capacity(block.body.len()); @@ -167,9 +165,7 @@ where // An optimism block should never contain blob transactions. if matches!(transaction.tx_type(), TxType::Eip4844) { - return Err(BlockExecutionError::OptimismBlockExecution( - OptimismBlockExecutionError::BlobTransactionRejected, - )); + return Err(OptimismBlockExecutionError::BlobTransactionRejected.into()); } // Cache the depositor account prior to the state transition for the deposit nonce. @@ -184,11 +180,7 @@ where .map(|acc| acc.account_info().unwrap_or_default()) }) .transpose() - .map_err(|_| { - BlockExecutionError::OptimismBlockExecution( - OptimismBlockExecutionError::AccountLoadFailed(*sender), - ) - })?; + .map_err(|_| OptimismBlockExecutionError::AccountLoadFailed(*sender))?; let mut buf = Vec::with_capacity(transaction.length_without_header()); transaction.encode_enveloped(&mut buf); diff --git a/crates/optimism/evm/src/l1.rs b/crates/optimism/evm/src/l1.rs index 896cbc36a..7b605448f 100644 --- a/crates/optimism/evm/src/l1.rs +++ b/crates/optimism/evm/src/l1.rs @@ -1,9 +1,7 @@ //! Optimism-specific implementation and utilities for the executor -use reth_interfaces::{ - executor::{self as reth_executor, BlockExecutionError}, - RethError, -}; +use crate::OptimismBlockExecutionError; +use reth_interfaces::{executor::BlockExecutionError, RethError}; use reth_primitives::{address, b256, hex, Address, Block, Bytes, ChainSpec, Hardfork, B256, U256}; use revm::{ primitives::{Bytecode, HashMap, SpecId}, @@ -29,20 +27,19 @@ const L1_BLOCK_ECOTONE_SELECTOR: [u8; 4] = hex!("440a5e20"); /// transaction in the L2 block. /// /// Returns an error if the L1 info transaction is not found, if the block is empty. -pub fn extract_l1_info(block: &Block) -> Result { +pub fn extract_l1_info(block: &Block) -> Result { let l1_info_tx_data = block .body .first() - .ok_or(reth_executor::OptimismBlockExecutionError::L1BlockInfoError { + .ok_or(OptimismBlockExecutionError::L1BlockInfoError { message: "could not find l1 block info tx in the L2 block".to_string(), }) .map(|tx| tx.input())?; if l1_info_tx_data.len() < 4 { - return Err(reth_executor::OptimismBlockExecutionError::L1BlockInfoError { + return Err(OptimismBlockExecutionError::L1BlockInfoError { message: "invalid l1 block info transaction calldata in the L2 block".to_string(), - } - .into()) + }) } // If the first 4 bytes of the calldata are the L1BlockInfoEcotone selector, then we parse the @@ -56,7 +53,7 @@ pub fn extract_l1_info(block: &Block) -> Result Result { +pub fn parse_l1_info_tx_bedrock(data: &[u8]) -> Result { // The setL1BlockValues tx calldata must be exactly 260 bytes long, considering that // we already removed the first 4 bytes (the function selector). Detailed breakdown: // 32 bytes for the block number @@ -68,33 +65,25 @@ pub fn parse_l1_info_tx_bedrock(data: &[u8]) -> Result Result Result { +pub fn parse_l1_info_tx_ecotone(data: &[u8]) -> Result { if data.len() != 160 { - return Err(reth_executor::BlockExecutionError::OptimismBlockExecution( - reth_executor::OptimismBlockExecutionError::L1BlockInfoError { - message: "unexpected l1 block info tx calldata length found".to_string(), - }, - )) + return Err(OptimismBlockExecutionError::L1BlockInfoError { + message: "unexpected l1 block info tx calldata length found".to_string(), + }) } let l1_blob_base_fee_scalar = U256::try_from_be_slice(&data[8..12]).ok_or( - reth_executor::BlockExecutionError::OptimismBlockExecution( - reth_executor::OptimismBlockExecutionError::L1BlockInfoError { - message: "could not convert l1 blob base fee scalar".to_string(), - }, - ), + OptimismBlockExecutionError::L1BlockInfoError { + message: "could not convert l1 blob base fee scalar".to_string(), + }, )?; let l1_base_fee_scalar = U256::try_from_be_slice(&data[12..16]).ok_or( - reth_executor::BlockExecutionError::OptimismBlockExecution( - reth_executor::OptimismBlockExecutionError::L1BlockInfoError { - message: "could not convert l1 base fee scalar".to_string(), - }, - ), + OptimismBlockExecutionError::L1BlockInfoError { + message: "could not convert l1 base fee scalar".to_string(), + }, )?; let l1_base_fee = U256::try_from_be_slice(&data[32..64]).ok_or( - reth_executor::BlockExecutionError::OptimismBlockExecution( - reth_executor::OptimismBlockExecutionError::L1BlockInfoError { - message: "could not convert l1 blob base fee".to_string(), - }, - ), + OptimismBlockExecutionError::L1BlockInfoError { + message: "could not convert l1 blob base fee".to_string(), + }, )?; let l1_blob_base_fee = U256::try_from_be_slice(&data[64..96]).ok_or( - reth_executor::BlockExecutionError::OptimismBlockExecution( - reth_executor::OptimismBlockExecutionError::L1BlockInfoError { - message: "could not convert l1 blob base fee".to_string(), - }, - ), + OptimismBlockExecutionError::L1BlockInfoError { + message: "could not convert l1 blob base fee".to_string(), + }, )?; let mut l1block = L1BlockInfo::default(); @@ -216,11 +195,10 @@ impl RethL1BlockInfo for L1BlockInfo { } else if chain_spec.is_fork_active_at_timestamp(Hardfork::Bedrock, timestamp) { SpecId::BEDROCK } else { - return Err(reth_executor::BlockExecutionError::OptimismBlockExecution( - reth_executor::OptimismBlockExecutionError::L1BlockInfoError { - message: "Optimism hardforks are not active".to_string(), - }, - )) + return Err(OptimismBlockExecutionError::L1BlockInfoError { + message: "Optimism hardforks are not active".to_string(), + } + .into()) }; Ok(self.calculate_tx_l1_cost(input, spec_id)) } @@ -236,11 +214,10 @@ impl RethL1BlockInfo for L1BlockInfo { } else if chain_spec.is_fork_active_at_timestamp(Hardfork::Bedrock, timestamp) { SpecId::BEDROCK } else { - return Err(reth_executor::BlockExecutionError::OptimismBlockExecution( - reth_executor::OptimismBlockExecutionError::L1BlockInfoError { - message: "Optimism hardforks are not active".to_string(), - }, - )) + return Err(OptimismBlockExecutionError::L1BlockInfoError { + message: "Optimism hardforks are not active".to_string(), + } + .into()) }; Ok(self.data_gas(input, spec_id)) } diff --git a/crates/optimism/evm/src/lib.rs b/crates/optimism/evm/src/lib.rs index c51265983..748eeab7b 100644 --- a/crates/optimism/evm/src/lib.rs +++ b/crates/optimism/evm/src/lib.rs @@ -22,7 +22,9 @@ pub use execute::*; pub mod l1; pub use l1::*; +mod error; pub mod verify; +pub use error::OptimismBlockExecutionError; /// Optimism-related EVM configuration. #[derive(Debug, Default, Clone, Copy)] diff --git a/crates/storage/provider/src/chain.rs b/crates/storage/provider/src/chain.rs index a596d93ea..9b9c66d4b 100644 --- a/crates/storage/provider/src/chain.rs +++ b/crates/storage/provider/src/chain.rs @@ -498,7 +498,7 @@ mod tests { let chain2 = Chain { blocks: BTreeMap::from([(3, block3), (4, block4)]), ..Default::default() }; - assert_eq!(chain1.append_chain(chain2.clone()), Ok(())); + assert!(chain1.append_chain(chain2.clone()).is_ok()); // chain1 got changed so this will fail assert!(chain1.append_chain(chain2).is_err());