From c46f23f8de3948e6315481a052157c64b21d9913 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 18 Jan 2025 19:42:39 +0400 Subject: [PATCH] feat: add `Evm` trait (#13823) --- Cargo.lock | 1 + .../engine/invalid-block-hooks/src/witness.rs | 14 +- crates/engine/util/src/reorg.rs | 6 +- crates/ethereum/evm/Cargo.toml | 6 +- crates/ethereum/evm/src/execute.rs | 8 +- crates/ethereum/evm/src/lib.rs | 178 +++++++++++------- crates/ethereum/node/src/evm.rs | 2 +- crates/ethereum/payload/src/lib.rs | 8 +- crates/evm/src/lib.rs | 83 ++++++-- crates/evm/src/system_calls/eip2935.rs | 34 +--- crates/evm/src/system_calls/eip4788.rs | 35 +--- crates/evm/src/system_calls/eip7002.rs | 34 +--- crates/evm/src/system_calls/eip7251.rs | 34 +--- crates/evm/src/system_calls/mod.rs | 80 +++----- crates/optimism/evm/src/execute.rs | 8 +- crates/optimism/evm/src/lib.rs | 122 ++++++++---- crates/optimism/payload/src/builder.rs | 12 +- crates/rpc/rpc-eth-api/src/helpers/call.rs | 54 +++--- .../rpc-eth-api/src/helpers/pending_block.rs | 7 +- crates/rpc/rpc-eth-api/src/helpers/trace.rs | 31 ++- crates/rpc/rpc/src/eth/bundle.rs | 10 +- crates/rpc/rpc/src/eth/sim_bundle.rs | 10 +- .../custom-beacon-withdrawals/src/main.rs | 80 ++------ examples/custom-evm/src/main.rs | 27 ++- examples/stateful-precompile/src/main.rs | 29 ++- 25 files changed, 453 insertions(+), 460 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02e235c99..12a33b4b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7514,6 +7514,7 @@ dependencies = [ "alloy-genesis", "alloy-primitives", "alloy-sol-types", + "derive_more", "reth-chainspec", "reth-consensus", "reth-ethereum-consensus", diff --git a/crates/engine/invalid-block-hooks/src/witness.rs b/crates/engine/invalid-block-hooks/src/witness.rs index b7e3b4f19..2ce50cde3 100644 --- a/crates/engine/invalid-block-hooks/src/witness.rs +++ b/crates/engine/invalid-block-hooks/src/witness.rs @@ -6,14 +6,13 @@ use pretty_assertions::Comparison; use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_engine_primitives::InvalidBlockHook; use reth_evm::{ - state_change::post_block_balance_increments, system_calls::SystemCaller, ConfigureEvm, + state_change::post_block_balance_increments, system_calls::SystemCaller, ConfigureEvm, Evm, }; use reth_primitives::{NodePrimitives, RecoveredBlock, SealedHeader}; use reth_primitives_traits::{BlockBody, SignedTransaction}; use reth_provider::{BlockExecutionOutput, ChainSpecProvider, StateProviderFactory}; use reth_revm::{ - database::StateProviderDatabase, db::states::bundle_state::BundleRetention, DatabaseCommit, - StateBuilder, + database::StateProviderDatabase, db::states::bundle_state::BundleRetention, StateBuilder, }; use reth_rpc_api::DebugApiClient; use reth_tracing::tracing::warn; @@ -88,13 +87,8 @@ where // Re-execute all of the transactions in the block to load all touched accounts into // the cache DB. for tx in block.body().transactions() { - self.evm_config.fill_tx_env( - evm.tx_mut(), - tx, - tx.recover_signer().ok_or_eyre("failed to recover sender")?, - ); - let result = evm.transact()?; - evm.db_mut().commit(result.state); + let signer = tx.recover_signer().ok_or_eyre("failed to recover sender")?; + evm.transact_commit(self.evm_config.tx_env(tx, signer))?; } drop(evm); diff --git a/crates/engine/util/src/reorg.rs b/crates/engine/util/src/reorg.rs index 4f8ed0ce3..89dcd1bf6 100644 --- a/crates/engine/util/src/reorg.rs +++ b/crates/engine/util/src/reorg.rs @@ -15,7 +15,7 @@ use reth_errors::{BlockExecutionError, BlockValidationError, RethError, RethResu use reth_ethereum_forks::EthereumHardforks; use reth_evm::{ state_change::post_block_withdrawals_balance_increments, system_calls::SystemCaller, - ConfigureEvm, + ConfigureEvm, Evm, }; use reth_payload_validator::ExecutionPayloadValidator; use reth_primitives::{ @@ -325,8 +325,8 @@ where let tx_recovered = tx.clone().try_into_ecrecovered().map_err(|_| { BlockExecutionError::Validation(BlockValidationError::SenderRecoveryError) })?; - evm_config.fill_tx_env(evm.tx_mut(), &tx_recovered, tx_recovered.signer()); - let exec_result = match evm.transact() { + let tx_env = evm_config.tx_env(&tx_recovered, tx_recovered.signer()); + let exec_result = match evm.transact(tx_env) { Ok(result) => result, error @ Err(EVMError::Transaction(_) | EVMError::Header(_)) => { trace!(target: "engine::stream::reorg", hash = %tx.tx_hash(), ?error, "Error executing transaction from next block"); diff --git a/crates/ethereum/evm/Cargo.toml b/crates/ethereum/evm/Cargo.toml index 7e6ed8052..19a026517 100644 --- a/crates/ethereum/evm/Cargo.toml +++ b/crates/ethereum/evm/Cargo.toml @@ -30,6 +30,9 @@ alloy-eips.workspace = true alloy-sol-types.workspace = true alloy-consensus.workspace = true +# Misc +derive_more.workspace = true + [dev-dependencies] reth-testing-utils.workspace = true reth-evm = { workspace = true, features = ["test-utils"] } @@ -55,5 +58,6 @@ std = [ "reth-ethereum-forks/std", "serde_json/std", "reth-primitives-traits/std", - "reth-chainspec/std" + "reth-chainspec/std", + "derive_more/std" ] diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index 8bc327227..eef78ebf8 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -19,7 +19,7 @@ use reth_evm::{ }, state_change::post_block_balance_increments, system_calls::{OnStateHook, SystemCaller}, - ConfigureEvm, TxEnvOverrides, + ConfigureEvm, Evm, TxEnvOverrides, }; use reth_primitives::{EthPrimitives, Receipt, RecoveredBlock}; use reth_primitives_traits::{BlockBody, SignedTransaction}; @@ -166,14 +166,14 @@ where .into()) } - self.evm_config.fill_tx_env(evm.tx_mut(), transaction, *sender); + let mut tx_env = self.evm_config.tx_env(transaction, *sender); if let Some(tx_env_overrides) = &mut self.tx_env_overrides { - tx_env_overrides.apply(evm.tx_mut()); + tx_env_overrides.apply(&mut tx_env); } // Execute transaction. - let result_and_state = evm.transact().map_err(move |err| { + let result_and_state = evm.transact(tx_env).map_err(move |err| { let new_err = err.map_db_err(|e| e.into()); // Ensure hash is calculated for error log, if not already done BlockValidationError::EVM { diff --git a/crates/ethereum/evm/src/lib.rs b/crates/ethereum/evm/src/lib.rs index 50907223a..e1594f565 100644 --- a/crates/ethereum/evm/src/lib.rs +++ b/crates/ethereum/evm/src/lib.rs @@ -17,18 +17,18 @@ extern crate alloc; -use core::convert::Infallible; - use alloc::{sync::Arc, vec::Vec}; use alloy_consensus::{BlockHeader, Header}; -use alloy_primitives::{Address, Bytes, TxKind, U256}; +use alloy_primitives::{Address, U256}; +use core::{convert::Infallible, fmt::Debug}; use reth_chainspec::ChainSpec; -use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes}; +use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, Evm, NextBlockEnvAttributes}; use reth_primitives::TransactionSigned; use reth_primitives_traits::transaction::execute::FillTxEnv; -use reth_revm::{inspector_handle_register, EvmBuilder}; +use reth_revm::{inspector_handle_register, Database, EvmBuilder}; use revm_primitives::{ - AnalysisKind, BlobExcessGasAndPrice, BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, Env, SpecId, TxEnv, + AnalysisKind, BlobExcessGasAndPrice, BlockEnv, Bytes, CfgEnv, CfgEnvWithHandlerCfg, EVMError, + Env, ResultAndState, SpecId, TxEnv, TxKind, }; mod config; @@ -44,6 +44,90 @@ pub mod dao_fork; /// [EIP-6110](https://eips.ethereum.org/EIPS/eip-6110) handling. pub mod eip6110; +/// Ethereum EVM implementation. +#[derive(derive_more::Debug, derive_more::Deref, derive_more::DerefMut, derive_more::From)] +#[debug(bound(DB::Error: Debug))] +pub struct EthEvm<'a, EXT, DB: Database>(reth_revm::Evm<'a, EXT, DB>); + +impl Evm for EthEvm<'_, EXT, DB> { + type DB = DB; + type Tx = TxEnv; + type Error = EVMError; + + fn block(&self) -> &BlockEnv { + self.0.block() + } + + fn into_env(self) -> EvmEnv { + let Env { cfg, block, tx: _ } = *self.0.context.evm.inner.env; + EvmEnv { + cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { + cfg_env: cfg, + handler_cfg: self.0.handler.cfg, + }, + block_env: block, + } + } + + fn transact(&mut self, tx: Self::Tx) -> Result { + *self.tx_mut() = tx; + self.0.transact() + } + + fn transact_system_call( + &mut self, + caller: Address, + contract: Address, + data: Bytes, + ) -> Result { + #[allow(clippy::needless_update)] // side-effect of optimism fields + let tx_env = TxEnv { + caller, + transact_to: TxKind::Call(contract), + // Explicitly set nonce to None so revm does not do any nonce checks + nonce: None, + gas_limit: 30_000_000, + value: U256::ZERO, + data, + // Setting the gas price to zero enforces that no value is transferred as part of the + // call, and that the call will not count against the block's gas limit + gas_price: U256::ZERO, + // The chain ID check is not relevant here and is disabled if set to None + chain_id: None, + // Setting the gas priority fee to None ensures the effective gas price is derived from + // the `gas_price` field, which we need to be zero + gas_priority_fee: None, + access_list: Vec::new(), + // blob fields can be None for this tx + blob_hashes: Vec::new(), + max_fee_per_blob_gas: None, + // TODO remove this once this crate is no longer built with optimism + ..Default::default() + }; + + *self.tx_mut() = tx_env; + + let prev_block_env = self.block().clone(); + + // ensure the block gas limit is >= the tx + self.block_mut().gas_limit = U256::from(self.tx().gas_limit); + + // disable the base fee check for this call by setting the base fee to zero + self.block_mut().basefee = U256::ZERO; + + let res = self.0.transact(); + + // re-set the block env + *self.block_mut() = prev_block_env; + + res + } + + fn db_mut(&mut self) -> &mut Self::DB { + &mut self.context.evm.db + } +} + /// Ethereum-related EVM configuration. #[derive(Debug, Clone)] pub struct EthEvmConfig { @@ -71,46 +155,6 @@ impl ConfigureEvmEnv for EthEvmConfig { transaction.fill_tx_env(tx_env, sender); } - fn fill_tx_env_system_contract_call( - &self, - env: &mut Env, - caller: Address, - contract: Address, - data: Bytes, - ) { - #[allow(clippy::needless_update)] // side-effect of optimism fields - let tx = TxEnv { - caller, - transact_to: TxKind::Call(contract), - // Explicitly set nonce to None so revm does not do any nonce checks - nonce: None, - gas_limit: 30_000_000, - value: U256::ZERO, - data, - // Setting the gas price to zero enforces that no value is transferred as part of the - // call, and that the call will not count against the block's gas limit - gas_price: U256::ZERO, - // The chain ID check is not relevant here and is disabled if set to None - chain_id: None, - // Setting the gas priority fee to None ensures the effective gas price is derived from - // the `gas_price` field, which we need to be zero - gas_priority_fee: None, - access_list: Vec::new(), - // blob fields can be None for this tx - blob_hashes: Vec::new(), - max_fee_per_blob_gas: None, - // TODO remove this once this crate is no longer built with optimism - ..Default::default() - }; - env.tx = tx; - - // ensure the block gas limit is >= the tx - env.block.gas_limit = U256::from(env.tx.gas_limit); - - // disable the base fee check for this call by setting the base fee to zero - env.block.basefee = U256::ZERO; - } - fn fill_cfg_env(&self, cfg_env: &mut CfgEnvWithHandlerCfg, header: &Header) { let spec_id = config::revm_spec(self.chain_spec(), header); @@ -184,18 +228,22 @@ impl ConfigureEvmEnv for EthEvmConfig { } impl ConfigureEvm for EthEvmConfig { - fn evm_with_env( + type Evm<'a, DB: Database + 'a, I: 'a> = EthEvm<'a, I, DB>; + + fn evm_with_env( &self, db: DB, evm_env: EvmEnv, tx: TxEnv, - ) -> reth_revm::Evm<'_, (), DB> { - EvmBuilder::default() - .with_db(db) - .with_cfg_env_with_handler_cfg(evm_env.cfg_env_with_handler_cfg) - .with_block_env(evm_env.block_env) - .with_tx_env(tx) - .build() + ) -> Self::Evm<'_, DB, ()> { + EthEvm( + EvmBuilder::default() + .with_db(db) + .with_cfg_env_with_handler_cfg(evm_env.cfg_env_with_handler_cfg) + .with_block_env(evm_env.block_env) + .with_tx_env(tx) + .build(), + ) } fn evm_with_env_and_inspector( @@ -204,19 +252,21 @@ impl ConfigureEvm for EthEvmConfig { evm_env: EvmEnv, tx: TxEnv, inspector: I, - ) -> reth_revm::Evm<'_, I, DB> + ) -> Self::Evm<'_, DB, I> where - DB: reth_revm::Database, + DB: Database, I: reth_revm::GetInspector, { - EvmBuilder::default() - .with_db(db) - .with_external_context(inspector) - .with_cfg_env_with_handler_cfg(evm_env.cfg_env_with_handler_cfg) - .with_block_env(evm_env.block_env) - .with_tx_env(tx) - .append_handler_register(inspector_handle_register) - .build() + EthEvm( + EvmBuilder::default() + .with_db(db) + .with_external_context(inspector) + .with_cfg_env_with_handler_cfg(evm_env.cfg_env_with_handler_cfg) + .with_block_env(evm_env.block_env) + .with_tx_env(tx) + .append_handler_register(inspector_handle_register) + .build(), + ) } } diff --git a/crates/ethereum/node/src/evm.rs b/crates/ethereum/node/src/evm.rs index bcdcaac6b..6af6f9271 100644 --- a/crates/ethereum/node/src/evm.rs +++ b/crates/ethereum/node/src/evm.rs @@ -5,4 +5,4 @@ pub use reth_evm::execute::BasicBlockExecutorProvider; #[doc(inline)] pub use reth_evm_ethereum::execute::{EthExecutionStrategyFactory, EthExecutorProvider}; #[doc(inline)] -pub use reth_evm_ethereum::EthEvmConfig; +pub use reth_evm_ethereum::{EthEvm, EthEvmConfig}; diff --git a/crates/ethereum/payload/src/lib.rs b/crates/ethereum/payload/src/lib.rs index 7d172784e..1c79e9cc5 100644 --- a/crates/ethereum/payload/src/lib.rs +++ b/crates/ethereum/payload/src/lib.rs @@ -22,7 +22,9 @@ use reth_basic_payload_builder::{ use reth_chain_state::ExecutedBlock; use reth_chainspec::{ChainSpec, ChainSpecProvider}; use reth_errors::RethError; -use reth_evm::{env::EvmEnv, system_calls::SystemCaller, ConfigureEvm, NextBlockEnvAttributes}; +use reth_evm::{ + env::EvmEnv, system_calls::SystemCaller, ConfigureEvm, Evm, NextBlockEnvAttributes, +}; use reth_evm_ethereum::{eip6110::parse_deposits_from_receipts, EthEvmConfig}; use reth_execution_types::ExecutionOutcome; use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes}; @@ -270,9 +272,9 @@ where } // Configure the environment for the tx. - *evm.tx_mut() = evm_config.tx_env(tx.tx(), tx.signer()); + let tx_env = evm_config.tx_env(tx.tx(), tx.signer()); - let ResultAndState { result, state } = match evm.transact() { + let ResultAndState { result, state } = match evm.transact(tx_env) { Ok(res) => res, Err(err) => { match err { diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index 1271bb14b..ff16d63fa 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -20,8 +20,8 @@ extern crate alloc; use alloy_consensus::BlockHeader as _; use alloy_primitives::{Address, Bytes, B256, U256}; use reth_primitives_traits::{BlockHeader, SignedTransaction}; -use revm::{Database, Evm, GetInspector}; -use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, Env, SpecId, TxEnv}; +use revm::{Database, DatabaseCommit, GetInspector}; +use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, EVMError, ResultAndState, SpecId, TxEnv}; pub mod either; /// EVM environment configuration. @@ -38,13 +38,64 @@ pub mod system_calls; /// test helpers for mocking executor pub mod test_utils; +/// An abstraction over EVM. +/// +/// At this point, assumed to be implemented on wrappers around [`revm::Evm`]. +pub trait Evm { + /// Database type held by the EVM. + type DB; + /// Transaction environment + type Tx; + /// Error type. + type Error; + + /// Reference to [`BlockEnv`]. + fn block(&self) -> &BlockEnv; + + /// Consumes the type and returns the underlying [`EvmEnv`]. + fn into_env(self) -> EvmEnv; + + /// Executes the given transaction. + fn transact(&mut self, tx: Self::Tx) -> Result; + + /// Executes a system call. + fn transact_system_call( + &mut self, + caller: Address, + contract: Address, + data: Bytes, + ) -> Result; + + /// Returns a mutable reference to the underlying database. + fn db_mut(&mut self) -> &mut Self::DB; + + /// Executes a transaction and commits the state changes to the underlying database. + fn transact_commit(&mut self, tx_env: Self::Tx) -> Result + where + Self::DB: DatabaseCommit, + { + let result = self.transact(tx_env)?; + self.db_mut().commit(result.state.clone()); + + Ok(result) + } +} + /// Trait for configuring the EVM for executing full blocks. pub trait ConfigureEvm: ConfigureEvmEnv { + /// The EVM implementation. + type Evm<'a, DB: Database + 'a, I: 'a>: Evm>; + /// Returns a new EVM with the given database configured with the given environment settings, /// including the spec id and transaction environment. /// /// This will preserve any handler modifications - fn evm_with_env(&self, db: DB, evm_env: EvmEnv, tx: TxEnv) -> Evm<'_, (), DB>; + fn evm_with_env( + &self, + db: DB, + evm_env: EvmEnv, + tx: TxEnv, + ) -> Self::Evm<'_, DB, ()>; /// Returns a new EVM with the given database configured with `cfg` and `block_env` /// configuration derived from the given header. Relies on @@ -53,7 +104,7 @@ pub trait ConfigureEvm: ConfigureEvmEnv { /// # Caution /// /// This does not initialize the tx environment. - fn evm_for_block(&self, db: DB, header: &Self::Header) -> Evm<'_, (), DB> { + fn evm_for_block(&self, db: DB, header: &Self::Header) -> Self::Evm<'_, DB, ()> { let evm_env = self.cfg_and_block_env(header); self.evm_with_env(db, evm_env, Default::default()) } @@ -70,7 +121,7 @@ pub trait ConfigureEvm: ConfigureEvmEnv { evm_env: EvmEnv, tx: TxEnv, inspector: I, - ) -> Evm<'_, I, DB> + ) -> Self::Evm<'_, DB, I> where DB: Database, I: GetInspector; @@ -81,11 +132,18 @@ where T: ConfigureEvm, &'b T: ConfigureEvmEnv
, { - fn evm_for_block(&self, db: DB, header: &Self::Header) -> Evm<'_, (), DB> { + type Evm<'a, DB: Database + 'a, I: 'a> = T::Evm<'a, DB, I>; + + fn evm_for_block(&self, db: DB, header: &Self::Header) -> Self::Evm<'_, DB, ()> { (*self).evm_for_block(db, header) } - fn evm_with_env(&self, db: DB, evm_env: EvmEnv, tx: TxEnv) -> Evm<'_, (), DB> { + fn evm_with_env( + &self, + db: DB, + evm_env: EvmEnv, + tx: TxEnv, + ) -> Self::Evm<'_, DB, ()> { (*self).evm_with_env(db, evm_env, tx) } @@ -95,7 +153,7 @@ where evm_env: EvmEnv, tx_env: TxEnv, inspector: I, - ) -> Evm<'_, I, DB> + ) -> Self::Evm<'_, DB, I> where DB: Database, I: GetInspector, @@ -129,15 +187,6 @@ pub trait ConfigureEvmEnv: Send + Sync + Unpin + Clone + 'static { /// Fill transaction environment from a transaction and the given sender address. fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &Self::Transaction, sender: Address); - /// Fill transaction environment with a system contract call. - fn fill_tx_env_system_contract_call( - &self, - env: &mut Env, - caller: Address, - contract: Address, - data: Bytes, - ); - /// Returns a [`CfgEnvWithHandlerCfg`] for the given header. fn cfg_env(&self, header: &Self::Header) -> CfgEnvWithHandlerCfg { let mut cfg = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); diff --git a/crates/evm/src/system_calls/eip2935.rs b/crates/evm/src/system_calls/eip2935.rs index 05637d4e1..6b85866d3 100644 --- a/crates/evm/src/system_calls/eip2935.rs +++ b/crates/evm/src/system_calls/eip2935.rs @@ -1,13 +1,14 @@ //! [EIP-2935](https://eips.ethereum.org/EIPS/eip-2935) system call implementation. -use alloc::{boxed::Box, string::ToString}; +use core::fmt::Display; + +use alloc::string::ToString; use alloy_eips::eip2935::HISTORY_STORAGE_ADDRESS; -use crate::ConfigureEvm; +use crate::Evm; use alloy_primitives::B256; use reth_chainspec::EthereumHardforks; use reth_execution_errors::{BlockExecutionError, BlockValidationError}; -use revm::{interpreter::Host, Database, Evm}; use revm_primitives::ResultAndState; /// Applies the pre-block call to the [EIP-2935] blockhashes contract, using the given block, @@ -23,19 +24,13 @@ use revm_primitives::ResultAndState; /// /// [EIP-2935]: https://eips.ethereum.org/EIPS/eip-2935 #[inline] -pub(crate) fn transact_blockhashes_contract_call( - evm_config: &EvmConfig, +pub(crate) fn transact_blockhashes_contract_call( chain_spec: impl EthereumHardforks, block_timestamp: u64, block_number: u64, parent_block_hash: B256, - evm: &mut Evm<'_, EXT, DB>, -) -> Result, BlockExecutionError> -where - DB: Database, - DB::Error: core::fmt::Display, - EvmConfig: ConfigureEvm, -{ + evm: &mut impl Evm, +) -> Result, BlockExecutionError> { if !chain_spec.is_prague_active_at_timestamp(block_timestamp) { return Ok(None) } @@ -46,21 +41,13 @@ where return Ok(None) } - // get previous env - let previous_env = Box::new(evm.context.env().clone()); - - // modify env for pre block call - evm_config.fill_tx_env_system_contract_call( - &mut evm.context.evm.env, + let mut res = match evm.transact_system_call( alloy_eips::eip4788::SYSTEM_ADDRESS, HISTORY_STORAGE_ADDRESS, parent_block_hash.0.into(), - ); - - let mut res = match evm.transact() { + ) { Ok(res) => res, Err(e) => { - evm.context.evm.env = previous_env; return Err(BlockValidationError::BlockHashContractCall { message: e.to_string() }.into()) } }; @@ -73,8 +60,5 @@ where res.state.remove(&alloy_eips::eip4788::SYSTEM_ADDRESS); res.state.remove(&evm.block().coinbase); - // re-set the previous env - evm.context.evm.env = previous_env; - Ok(Some(res)) } diff --git a/crates/evm/src/system_calls/eip4788.rs b/crates/evm/src/system_calls/eip4788.rs index 4c78ff059..549c1cdf6 100644 --- a/crates/evm/src/system_calls/eip4788.rs +++ b/crates/evm/src/system_calls/eip4788.rs @@ -1,12 +1,11 @@ //! [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788) system call implementation. +use crate::Evm; use alloc::{boxed::Box, string::ToString}; - -use crate::ConfigureEvm; use alloy_eips::eip4788::BEACON_ROOTS_ADDRESS; use alloy_primitives::B256; +use core::fmt::Display; use reth_chainspec::EthereumHardforks; use reth_execution_errors::{BlockExecutionError, BlockValidationError}; -use revm::{interpreter::Host, Database, Evm}; use revm_primitives::ResultAndState; /// Applies the pre-block call to the [EIP-4788] beacon block root contract, using the given block, @@ -19,20 +18,13 @@ use revm_primitives::ResultAndState; /// /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 #[inline] -pub(crate) fn transact_beacon_root_contract_call( - evm_config: &EvmConfig, - chain_spec: &Spec, +pub(crate) fn transact_beacon_root_contract_call( + chain_spec: impl EthereumHardforks, block_timestamp: u64, block_number: u64, parent_beacon_block_root: Option, - evm: &mut Evm<'_, EXT, DB>, -) -> Result, BlockExecutionError> -where - DB: Database, - DB::Error: core::fmt::Display, - EvmConfig: ConfigureEvm, - Spec: EthereumHardforks, -{ + evm: &mut impl Evm, +) -> Result, BlockExecutionError> { if !chain_spec.is_cancun_active_at_timestamp(block_timestamp) { return Ok(None) } @@ -52,21 +44,13 @@ where return Ok(None) } - // get previous env - let previous_env = Box::new(evm.context.env().clone()); - - // modify env for pre block call - evm_config.fill_tx_env_system_contract_call( - &mut evm.context.evm.env, + let mut res = match evm.transact_system_call( alloy_eips::eip4788::SYSTEM_ADDRESS, BEACON_ROOTS_ADDRESS, parent_beacon_block_root.0.into(), - ); - - let mut res = match evm.transact() { + ) { Ok(res) => res, Err(e) => { - evm.context.evm.env = previous_env; return Err(BlockValidationError::BeaconRootContractCall { parent_beacon_block_root: Box::new(parent_beacon_block_root), message: e.to_string(), @@ -83,8 +67,5 @@ where res.state.remove(&alloy_eips::eip4788::SYSTEM_ADDRESS); res.state.remove(&evm.block().coinbase); - // re-set the previous env - evm.context.evm.env = previous_env; - Ok(Some(res)) } diff --git a/crates/evm/src/system_calls/eip7002.rs b/crates/evm/src/system_calls/eip7002.rs index d949dd5a5..51db7c084 100644 --- a/crates/evm/src/system_calls/eip7002.rs +++ b/crates/evm/src/system_calls/eip7002.rs @@ -1,10 +1,10 @@ //! [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) system call implementation. -use crate::ConfigureEvm; -use alloc::{boxed::Box, format}; +use crate::Evm; +use alloc::format; use alloy_eips::eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS; use alloy_primitives::Bytes; +use core::fmt::Display; use reth_execution_errors::{BlockExecutionError, BlockValidationError}; -use revm::{interpreter::Host, Database, Evm}; use revm_primitives::{ExecutionResult, ResultAndState}; /// Applies the post-block call to the EIP-7002 withdrawal requests contract. @@ -13,19 +13,10 @@ use revm_primitives::{ExecutionResult, ResultAndState}; /// /// Note: this does not commit the state changes to the database, it only transact the call. #[inline] -pub(crate) fn transact_withdrawal_requests_contract_call( - evm_config: &EvmConfig, - evm: &mut Evm<'_, EXT, DB>, -) -> Result -where - DB: Database, - DB::Error: core::fmt::Display, - EvmConfig: ConfigureEvm, -{ - // get previous env - let previous_env = Box::new(evm.context.env().clone()); - - // Fill transaction environment with the EIP-7002 withdrawal requests contract message data. +pub(crate) fn transact_withdrawal_requests_contract_call( + evm: &mut impl Evm, +) -> Result { + // Execute EIP-7002 withdrawal requests contract message data. // // This requirement for the withdrawal requests contract call defined by // [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) is: @@ -33,17 +24,13 @@ where // At the end of processing any execution block where `block.timestamp >= FORK_TIMESTAMP` (i.e. // after processing all transactions and after performing the block body withdrawal requests // validations), call the contract as `SYSTEM_ADDRESS`. - evm_config.fill_tx_env_system_contract_call( - &mut evm.context.evm.env, + let mut res = match evm.transact_system_call( alloy_eips::eip7002::SYSTEM_ADDRESS, WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, Bytes::new(), - ); - - let mut res = match evm.transact() { + ) { Ok(res) => res, Err(e) => { - evm.context.evm.env = previous_env; return Err(BlockValidationError::WithdrawalRequestsContractCall { message: format!("execution failed: {e}"), } @@ -59,9 +46,6 @@ where res.state.remove(&alloy_eips::eip7002::SYSTEM_ADDRESS); res.state.remove(&evm.block().coinbase); - // re-set the previous env - evm.context.evm.env = previous_env; - Ok(res) } diff --git a/crates/evm/src/system_calls/eip7251.rs b/crates/evm/src/system_calls/eip7251.rs index 86a1bee53..3f9431b81 100644 --- a/crates/evm/src/system_calls/eip7251.rs +++ b/crates/evm/src/system_calls/eip7251.rs @@ -1,10 +1,10 @@ //! [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) system call implementation. -use crate::ConfigureEvm; -use alloc::{boxed::Box, format}; +use crate::Evm; +use alloc::format; use alloy_eips::eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS; use alloy_primitives::Bytes; +use core::fmt::Display; use reth_execution_errors::{BlockExecutionError, BlockValidationError}; -use revm::{interpreter::Host, Database, Evm}; use revm_primitives::{ExecutionResult, ResultAndState}; /// Applies the post-block call to the EIP-7251 consolidation requests contract. @@ -14,19 +14,10 @@ use revm_primitives::{ExecutionResult, ResultAndState}; /// /// Note: this does not commit the state changes to the database, it only transact the call. #[inline] -pub(crate) fn transact_consolidation_requests_contract_call( - evm_config: &EvmConfig, - evm: &mut Evm<'_, EXT, DB>, -) -> Result -where - DB: Database, - DB::Error: core::fmt::Display, - EvmConfig: ConfigureEvm, -{ - // get previous env - let previous_env = Box::new(evm.context.env().clone()); - - // Fill transaction environment with the EIP-7251 consolidation requests contract message data. +pub(crate) fn transact_consolidation_requests_contract_call( + evm: &mut impl Evm, +) -> Result { + // Execute EIP-7251 consolidation requests contract message data. // // This requirement for the consolidation requests contract call defined by // [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) is: @@ -35,17 +26,13 @@ where // after processing all transactions and after performing the block body requests validations) // clienst software MUST [..] call the contract as `SYSTEM_ADDRESS` and empty input data to // trigger the system subroutine execute. - evm_config.fill_tx_env_system_contract_call( - &mut evm.context.evm.env, + let mut res = match evm.transact_system_call( alloy_eips::eip7002::SYSTEM_ADDRESS, CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, Bytes::new(), - ); - - let mut res = match evm.transact() { + ) { Ok(res) => res, Err(e) => { - evm.context.evm.env = previous_env; return Err(BlockValidationError::ConsolidationRequestsContractCall { message: format!("execution failed: {e}"), } @@ -61,9 +48,6 @@ where res.state.remove(&alloy_eips::eip7002::SYSTEM_ADDRESS); res.state.remove(&evm.block().coinbase); - // re-set the previous env - evm.context.evm.env = previous_env; - Ok(res) } diff --git a/crates/evm/src/system_calls/mod.rs b/crates/evm/src/system_calls/mod.rs index 63527b29b..f620a3caf 100644 --- a/crates/evm/src/system_calls/mod.rs +++ b/crates/evm/src/system_calls/mod.rs @@ -1,6 +1,6 @@ //! System contract call functions. -use crate::{ConfigureEvm, EvmEnv}; +use crate::{ConfigureEvm, Evm, EvmEnv}; use alloc::{boxed::Box, sync::Arc}; use alloy_consensus::BlockHeader; use alloy_eips::{ @@ -10,7 +10,7 @@ use alloy_primitives::Bytes; use core::fmt::Display; use reth_chainspec::EthereumHardforks; use reth_execution_errors::BlockExecutionError; -use revm::{Database, DatabaseCommit, Evm}; +use revm::{Database, DatabaseCommit}; use revm_primitives::{EvmState, B256}; mod eip2935; @@ -76,15 +76,11 @@ where Chainspec: EthereumHardforks, { /// Apply pre execution changes. - pub fn apply_pre_execution_changes( + pub fn apply_pre_execution_changes( &mut self, header: &EvmConfig::Header, - evm: &mut Evm<'_, Ext, DB>, - ) -> Result<(), BlockExecutionError> - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { + evm: &mut impl Evm, + ) -> Result<(), BlockExecutionError> { self.apply_blockhashes_contract_call( header.timestamp(), header.number(), @@ -102,14 +98,10 @@ where } /// Apply post execution changes. - pub fn apply_post_execution_changes( + pub fn apply_post_execution_changes( &mut self, - evm: &mut Evm<'_, Ext, DB>, - ) -> Result - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { + evm: &mut impl Evm, + ) -> Result { let mut requests = Requests::default(); // Collect all EIP-7685 requests @@ -152,19 +144,14 @@ where } /// Applies the pre-block call to the EIP-2935 blockhashes contract. - pub fn apply_blockhashes_contract_call( + pub fn apply_blockhashes_contract_call( &mut self, timestamp: u64, block_number: u64, parent_block_hash: B256, - evm: &mut Evm<'_, Ext, DB>, - ) -> Result<(), BlockExecutionError> - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { + evm: &mut impl Evm, + ) -> Result<(), BlockExecutionError> { let result_and_state = eip2935::transact_blockhashes_contract_call( - &self.evm_config, &self.chain_spec, timestamp, block_number, @@ -176,7 +163,7 @@ where if let Some(ref mut hook) = self.hook { hook.on_state(&res.state); } - evm.context.evm.db.commit(res.state); + evm.db_mut().commit(res.state); } Ok(()) @@ -207,19 +194,14 @@ where } /// Applies the pre-block call to the EIP-4788 beacon root contract. - pub fn apply_beacon_root_contract_call( + pub fn apply_beacon_root_contract_call( &mut self, timestamp: u64, block_number: u64, parent_block_hash: Option, - evm: &mut Evm<'_, Ext, DB>, - ) -> Result<(), BlockExecutionError> - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { + evm: &mut impl Evm, + ) -> Result<(), BlockExecutionError> { let result_and_state = eip4788::transact_beacon_root_contract_call( - &self.evm_config, &self.chain_spec, timestamp, block_number, @@ -231,7 +213,7 @@ where if let Some(ref mut hook) = self.hook { hook.on_state(&res.state); } - evm.context.evm.db.commit(res.state); + evm.db_mut().commit(res.state); } Ok(()) @@ -256,21 +238,16 @@ where } /// Applies the post-block call to the EIP-7002 withdrawal request contract. - pub fn apply_withdrawal_requests_contract_call( + pub fn apply_withdrawal_requests_contract_call( &mut self, - evm: &mut Evm<'_, Ext, DB>, - ) -> Result - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { - let result_and_state = - eip7002::transact_withdrawal_requests_contract_call(&self.evm_config.clone(), evm)?; + evm: &mut impl Evm, + ) -> Result { + let result_and_state = eip7002::transact_withdrawal_requests_contract_call(evm)?; if let Some(ref mut hook) = self.hook { hook.on_state(&result_and_state.state); } - evm.context.evm.db.commit(result_and_state.state); + evm.db_mut().commit(result_and_state.state); eip7002::post_commit(result_and_state.result) } @@ -294,21 +271,16 @@ where } /// Applies the post-block call to the EIP-7251 consolidation requests contract. - pub fn apply_consolidation_requests_contract_call( + pub fn apply_consolidation_requests_contract_call( &mut self, - evm: &mut Evm<'_, Ext, DB>, - ) -> Result - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { - let result_and_state = - eip7251::transact_consolidation_requests_contract_call(&self.evm_config.clone(), evm)?; + evm: &mut impl Evm, + ) -> Result { + let result_and_state = eip7251::transact_consolidation_requests_contract_call(evm)?; if let Some(ref mut hook) = self.hook { hook.on_state(&result_and_state.state); } - evm.context.evm.db.commit(result_and_state.state); + evm.db_mut().commit(result_and_state.state); eip7251::post_commit(result_and_state.result) } diff --git a/crates/optimism/evm/src/execute.rs b/crates/optimism/evm/src/execute.rs index bfac67fbb..c34329bf6 100644 --- a/crates/optimism/evm/src/execute.rs +++ b/crates/optimism/evm/src/execute.rs @@ -19,7 +19,7 @@ use reth_evm::{ }, state_change::post_block_balance_increments, system_calls::{OnStateHook, SystemCaller}, - ConfigureEvm, TxEnvOverrides, + ConfigureEvm, Evm, TxEnvOverrides, }; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_consensus::validate_block_post_execution; @@ -226,14 +226,14 @@ where .transpose() .map_err(|_| OpBlockExecutionError::AccountLoadFailed(*sender))?; - self.evm_config.fill_tx_env(evm.tx_mut(), transaction, *sender); + let mut tx_env = self.evm_config.tx_env(transaction, *sender); if let Some(tx_env_overrides) = &mut self.tx_env_overrides { - tx_env_overrides.apply(evm.tx_mut()); + tx_env_overrides.apply(&mut tx_env); } // Execute transaction. - let result_and_state = evm.transact().map_err(move |err| { + let result_and_state = evm.transact(tx_env).map_err(move |err| { let new_err = err.map_db_err(|e| e.into()); // Ensure hash is calculated for error log, if not already done BlockValidationError::EVM { diff --git a/crates/optimism/evm/src/lib.rs b/crates/optimism/evm/src/lib.rs index ad172c764..3ecdd39bc 100644 --- a/crates/optimism/evm/src/lib.rs +++ b/crates/optimism/evm/src/lib.rs @@ -12,19 +12,20 @@ extern crate alloc; -use alloc::{sync::Arc, vec::Vec}; +use alloc::sync::Arc; use alloy_consensus::Header; use alloy_eips::eip7840::BlobParams; use alloy_primitives::{Address, U256}; +use core::fmt::Debug; use op_alloy_consensus::EIP1559ParamError; -use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes}; +use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, Evm, NextBlockEnvAttributes}; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_primitives::OpTransactionSigned; use reth_primitives_traits::FillTxEnv; use reth_revm::{ inspector_handle_register, primitives::{AnalysisKind, CfgEnvWithHandlerCfg, TxEnv}, - Database, Evm, EvmBuilder, GetInspector, + Database, EvmBuilder, GetInspector, }; mod config; @@ -39,44 +40,48 @@ pub use receipts::*; mod error; pub use error::OpBlockExecutionError; use revm_primitives::{ - BlobExcessGasAndPrice, BlockEnv, Bytes, CfgEnv, Env, HandlerCfg, OptimismFields, SpecId, TxKind, + BlobExcessGasAndPrice, BlockEnv, Bytes, CfgEnv, EVMError, Env, HandlerCfg, OptimismFields, + ResultAndState, SpecId, TxKind, }; -/// Optimism-related EVM configuration. -#[derive(Debug, Clone)] -pub struct OpEvmConfig { - chain_spec: Arc, -} +/// OP EVM implementation. +#[derive(derive_more::Debug, derive_more::Deref, derive_more::DerefMut, derive_more::From)] +#[debug(bound(DB::Error: Debug))] +pub struct OpEvm<'a, EXT, DB: Database>(reth_revm::Evm<'a, EXT, DB>); -impl OpEvmConfig { - /// Creates a new [`OpEvmConfig`] with the given chain spec. - pub const fn new(chain_spec: Arc) -> Self { - Self { chain_spec } +impl Evm for OpEvm<'_, EXT, DB> { + type DB = DB; + type Tx = TxEnv; + type Error = EVMError; + + fn block(&self) -> &BlockEnv { + self.0.block() } - /// Returns the chain spec associated with this configuration. - pub const fn chain_spec(&self) -> &Arc { - &self.chain_spec - } -} - -impl ConfigureEvmEnv for OpEvmConfig { - type Header = Header; - type Transaction = OpTransactionSigned; - type Error = EIP1559ParamError; - - fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &OpTransactionSigned, sender: Address) { - transaction.fill_tx_env(tx_env, sender); + fn into_env(self) -> EvmEnv { + let Env { cfg, block, tx: _ } = *self.0.context.evm.inner.env; + EvmEnv { + cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { + cfg_env: cfg, + handler_cfg: self.0.handler.cfg, + }, + block_env: block, + } } - fn fill_tx_env_system_contract_call( - &self, - env: &mut Env, + fn transact(&mut self, tx: Self::Tx) -> Result { + *self.tx_mut() = tx; + self.0.transact() + } + + fn transact_system_call( + &mut self, caller: Address, contract: Address, data: Bytes, - ) { - env.tx = TxEnv { + ) -> Result { + #[allow(clippy::needless_update)] // side-effect of optimism fields + let tx_env = TxEnv { caller, transact_to: TxKind::Call(contract), // Explicitly set nonce to None so revm does not do any nonce checks @@ -107,11 +112,54 @@ impl ConfigureEvmEnv for OpEvmConfig { }, }; + *self.tx_mut() = tx_env; + + let prev_block_env = self.block().clone(); + // ensure the block gas limit is >= the tx - env.block.gas_limit = U256::from(env.tx.gas_limit); + self.block_mut().gas_limit = U256::from(self.tx().gas_limit); // disable the base fee check for this call by setting the base fee to zero - env.block.basefee = U256::ZERO; + self.block_mut().basefee = U256::ZERO; + + let res = self.0.transact(); + + // re-set the block env + *self.block_mut() = prev_block_env; + + res + } + + fn db_mut(&mut self) -> &mut Self::DB { + &mut self.context.evm.db + } +} + +/// Optimism-related EVM configuration. +#[derive(Debug, Clone)] +pub struct OpEvmConfig { + chain_spec: Arc, +} + +impl OpEvmConfig { + /// Creates a new [`OpEvmConfig`] with the given chain spec. + pub const fn new(chain_spec: Arc) -> Self { + Self { chain_spec } + } + + /// Returns the chain spec associated with this configuration. + pub const fn chain_spec(&self) -> &Arc { + &self.chain_spec + } +} + +impl ConfigureEvmEnv for OpEvmConfig { + type Header = Header; + type Transaction = OpTransactionSigned; + type Error = EIP1559ParamError; + + fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &OpTransactionSigned, sender: Address) { + transaction.fill_tx_env(tx_env, sender); } fn fill_cfg_env(&self, cfg_env: &mut CfgEnvWithHandlerCfg, header: &Self::Header) { @@ -168,12 +216,14 @@ impl ConfigureEvmEnv for OpEvmConfig { } impl ConfigureEvm for OpEvmConfig { + type Evm<'a, DB: Database + 'a, I: 'a> = OpEvm<'a, I, DB>; + fn evm_with_env( &self, db: DB, mut evm_env: EvmEnv, tx: TxEnv, - ) -> Evm<'_, (), DB> { + ) -> Self::Evm<'_, DB, ()> { evm_env.cfg_env_with_handler_cfg.handler_cfg.is_optimism = true; EvmBuilder::default() @@ -182,6 +232,7 @@ impl ConfigureEvm for OpEvmConfig { .with_block_env(evm_env.block_env) .with_tx_env(tx) .build() + .into() } fn evm_with_env_and_inspector( @@ -190,7 +241,7 @@ impl ConfigureEvm for OpEvmConfig { mut evm_env: EvmEnv, tx: TxEnv, inspector: I, - ) -> Evm<'_, I, DB> + ) -> Self::Evm<'_, DB, I> where DB: Database, I: GetInspector, @@ -205,6 +256,7 @@ impl ConfigureEvm for OpEvmConfig { .with_tx_env(tx) .append_handler_register(inspector_handle_register) .build() + .into() } } diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index 3f8c72ece..85df416d7 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -15,7 +15,9 @@ use op_alloy_rpc_types_engine::OpPayloadAttributes; use reth_basic_payload_builder::*; use reth_chain_state::ExecutedBlock; use reth_chainspec::{ChainSpecProvider, EthereumHardforks}; -use reth_evm::{env::EvmEnv, system_calls::SystemCaller, ConfigureEvm, NextBlockEnvAttributes}; +use reth_evm::{ + env::EvmEnv, system_calls::SystemCaller, ConfigureEvm, Evm, NextBlockEnvAttributes, +}; use reth_execution_types::ExecutionOutcome; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_consensus::calculate_receipt_root_no_memo_optimism; @@ -777,9 +779,9 @@ where )) })?; - *evm.tx_mut() = self.evm_config.tx_env(sequencer_tx.tx(), sequencer_tx.signer()); + let tx_env = self.evm_config.tx_env(sequencer_tx.tx(), sequencer_tx.signer()); - let ResultAndState { result, state } = match evm.transact() { + let ResultAndState { result, state } = match evm.transact(tx_env) { Ok(res) => res, Err(err) => { match err { @@ -874,9 +876,9 @@ where } // Configure the environment for the tx. - *evm.tx_mut() = self.evm_config.tx_env(tx.tx(), tx.signer()); + let tx_env = self.evm_config.tx_env(tx.tx(), tx.signer()); - let ResultAndState { result, state } = match evm.transact() { + let ResultAndState { result, state } = match evm.transact(tx_env) { Ok(res) => res, Err(err) => { match err { diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index 538c98080..7d6f7d3b2 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -17,16 +17,14 @@ use alloy_rpc_types_eth::{ }; use futures::Future; use reth_chainspec::EthChainSpec; -use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv}; +use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, Evm}; use reth_node_api::BlockBody; use reth_primitives_traits::SignedTransaction; use reth_provider::{BlockIdReader, ChainSpecProvider, ProviderHeader}; use reth_revm::{ database::StateProviderDatabase, db::CacheDB, - primitives::{ - BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ExecutionResult, ResultAndState, TxEnv, - }, + primitives::{BlockEnv, ExecutionResult, ResultAndState, TxEnv}, DatabaseRef, }; use reth_rpc_eth_types::{ @@ -41,7 +39,6 @@ use reth_rpc_eth_types::{ }; use revm::{Database, DatabaseCommit, GetInspector}; use revm_inspectors::{access_list::AccessListInspector, transfer::TransferInspector}; -use revm_primitives::Env; use tracing::trace; /// Result type for `eth_simulateV1` RPC method. @@ -493,7 +490,7 @@ pub trait Call: f(StateProviderTraitObjWrapper(&state)) } - /// Executes the [`EnvWithHandlerCfg`] against the given [Database] without committing state + /// Executes the [`TxEnv`] against the given [Database] without committing state /// changes. fn transact( &self, @@ -505,21 +502,14 @@ pub trait Call: DB: Database, EthApiError: From, { - let mut evm = self.evm_config().evm_with_env(db, evm_env, tx_env); - let res = evm.transact().map_err(Self::Error::from_evm_err)?; - let (_, env) = evm.into_db_and_env_with_handler_cfg(); + let mut evm = self.evm_config().evm_with_env(db, evm_env, Default::default()); + let res = evm.transact(tx_env.clone()).map_err(Self::Error::from_evm_err)?; + let evm_env = evm.into_env(); - let EnvWithHandlerCfg { env, handler_cfg } = env; - let Env { cfg, block, tx } = *env; - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { cfg_env: cfg, handler_cfg }, - block_env: block, - }; - - Ok((res, (evm_env, tx))) + Ok((res, (evm_env, tx_env))) } - /// Executes the [`EnvWithHandlerCfg`] against the given [Database] without committing state + /// Executes the [`EvmEnv`] against the given [Database] without committing state /// changes. fn transact_with_inspector( &self, @@ -532,18 +522,16 @@ pub trait Call: DB: Database, EthApiError: From, { - let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env, tx_env, inspector); - let res = evm.transact().map_err(Self::Error::from_evm_err)?; - let (_, env) = evm.into_db_and_env_with_handler_cfg(); + let mut evm = self.evm_config().evm_with_env_and_inspector( + db, + evm_env, + Default::default(), + inspector, + ); + let res = evm.transact(tx_env.clone()).map_err(Self::Error::from_evm_err)?; + let evm_env = evm.into_env(); - let EnvWithHandlerCfg { env, handler_cfg } = env; - let Env { cfg, block, tx } = *env; - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { cfg_env: cfg, handler_cfg }, - block_env: block, - }; - - Ok((res, (evm_env, tx))) + Ok((res, (evm_env, tx_env))) } /// Executes the call request at the given [`BlockId`]. @@ -581,7 +569,7 @@ pub trait Call: /// Prepares the state and env for the given [`TransactionRequest`] at the given [`BlockId`] and /// executes the closure on a new task returning the result of the closure. /// - /// This returns the configured [`EnvWithHandlerCfg`] for the given [`TransactionRequest`] at + /// This returns the configured [`EvmEnv`] for the given [`TransactionRequest`] at /// the given [`BlockId`] and with configured call settings: `prepare_call_env`. /// /// This is primarily used by `eth_call`. @@ -704,8 +692,8 @@ pub trait Call: break } - self.evm_config().fill_tx_env(evm.tx_mut(), tx, *sender); - evm.transact_commit().map_err(Self::Error::from_evm_err)?; + let tx_env = self.evm_config().tx_env(tx, *sender); + evm.transact_commit(tx_env).map_err(Self::Error::from_evm_err)?; index += 1; } Ok(index) @@ -790,7 +778,7 @@ pub trait Call: Ok(env) } - /// Prepares the [`EnvWithHandlerCfg`] for execution of calls. + /// Prepares the [`EvmEnv`] for execution of calls. /// /// Does not commit any changes to the underlying database. /// diff --git a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs index 968ef379f..0d1e00f64 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs @@ -13,7 +13,7 @@ use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_errors::RethError; use reth_evm::{ env::EvmEnv, state_change::post_block_withdrawals_balance_increments, - system_calls::SystemCaller, ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes, + system_calls::SystemCaller, ConfigureEvm, ConfigureEvmEnv, Evm, NextBlockEnvAttributes, }; use reth_primitives::{InvalidTransactionError, RecoveredBlock}; use reth_primitives_traits::Receipt; @@ -325,9 +325,10 @@ pub trait LoadPendingBlock: } let tx_env = self.evm_config().tx_env(tx.tx(), tx.signer()); - let mut evm = self.evm_config().evm_with_env(&mut db, evm_env.clone(), tx_env); + let mut evm = + self.evm_config().evm_with_env(&mut db, evm_env.clone(), Default::default()); - let ResultAndState { result, state } = match evm.transact() { + let ResultAndState { result, state } = match evm.transact(tx_env) { Ok(res) => res, Err(err) => { match err { diff --git a/crates/rpc/rpc-eth-api/src/helpers/trace.rs b/crates/rpc/rpc-eth-api/src/helpers/trace.rs index 5e99ba134..1069f7b68 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/trace.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/trace.rs @@ -7,7 +7,7 @@ use alloy_primitives::B256; use alloy_rpc_types_eth::{BlockId, TransactionInfo}; use futures::Future; use reth_chainspec::ChainSpecProvider; -use reth_evm::{env::EvmEnv, system_calls::SystemCaller, ConfigureEvm, ConfigureEvmEnv}; +use reth_evm::{env::EvmEnv, system_calls::SystemCaller, ConfigureEvm, ConfigureEvmEnv, Evm}; use reth_primitives::RecoveredBlock; use reth_primitives_traits::{BlockBody, SignedTransaction}; use reth_provider::{BlockReader, ProviderBlock, ProviderHeader, ProviderTx}; @@ -18,9 +18,7 @@ use reth_rpc_eth_types::{ }; use revm::{db::CacheDB, Database, DatabaseCommit, GetInspector, Inspector}; use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig}; -use revm_primitives::{ - CfgEnvWithHandlerCfg, Env, EnvWithHandlerCfg, EvmState, ExecutionResult, ResultAndState, TxEnv, -}; +use revm_primitives::{EvmState, ExecutionResult, ResultAndState, TxEnv}; use std::{fmt::Display, sync::Arc}; /// Executes CPU heavy tasks. @@ -33,7 +31,7 @@ pub trait Trace: >, > { - /// Executes the [`EnvWithHandlerCfg`] against the given [Database] without committing state + /// Executes the [`EvmEnv`] against the given [Database] without committing state /// changes. fn inspect( &self, @@ -47,23 +45,22 @@ pub trait Trace: EthApiError: From, I: GetInspector, { - let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env, tx_env, inspector); - let res = evm.transact().map_err(Self::Error::from_evm_err)?; - let (_, env) = evm.into_db_and_env_with_handler_cfg(); - let EnvWithHandlerCfg { env, handler_cfg } = env; - let Env { cfg, block, tx } = *env; - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { cfg_env: cfg, handler_cfg }, - block_env: block, - }; - Ok((res, (evm_env, tx))) + let mut evm = self.evm_config().evm_with_env_and_inspector( + db, + evm_env, + Default::default(), + inspector, + ); + let res = evm.transact(tx_env.clone()).map_err(Self::Error::from_evm_err)?; + let evm_env = evm.into_env(); + Ok((res, (evm_env, tx_env))) } /// Executes the transaction on top of the given [`BlockId`] with a tracer configured by the /// config. /// /// The callback is then called with the [`TracingInspector`] and the [`ResultAndState`] after - /// the configured [`EnvWithHandlerCfg`] was inspected. + /// the configured [`EvmEnv`] was inspected. /// /// Caution: this is blocking fn trace_at( @@ -92,7 +89,7 @@ pub trait Trace: /// config. /// /// The callback is then called with the [`TracingInspector`] and the [`ResultAndState`] after - /// the configured [`EnvWithHandlerCfg`] was inspected. + /// the configured [`EvmEnv`] was inspected. fn spawn_trace_at_with_state( &self, evm_env: EvmEnv, diff --git a/crates/rpc/rpc/src/eth/bundle.rs b/crates/rpc/rpc/src/eth/bundle.rs index be431f603..9a859d068 100644 --- a/crates/rpc/rpc/src/eth/bundle.rs +++ b/crates/rpc/rpc/src/eth/bundle.rs @@ -6,7 +6,7 @@ use alloy_primitives::{Keccak256, U256}; use alloy_rpc_types_mev::{EthCallBundle, EthCallBundleResponse, EthCallBundleTransactionResult}; use jsonrpsee::core::RpcResult; use reth_chainspec::EthChainSpec; -use reth_evm::{ConfigureEvm, ConfigureEvmEnv}; +use reth_evm::{ConfigureEvm, ConfigureEvmEnv, Evm}; use reth_primitives_traits::SignedTransaction; use reth_provider::{ChainSpecProvider, HeaderProvider}; use reth_revm::database::StateProviderDatabase; @@ -197,9 +197,9 @@ where hasher.update(*tx.tx_hash()); let gas_price = tx.effective_gas_price(basefee); - eth_api.evm_config().fill_tx_env(evm.tx_mut(), &tx, signer); - let ResultAndState { result, state } = - evm.transact().map_err(Eth::Error::from_evm_err)?; + let ResultAndState { result, state } = evm + .transact(eth_api.evm_config().tx_env(&tx, signer)) + .map_err(Eth::Error::from_evm_err)?; let gas_used = result.gas_used(); total_gas_used += gas_used; @@ -245,7 +245,7 @@ where if transactions.peek().is_some() { // need to apply the state changes of this call before executing // the next call - evm.context.evm.db.commit(state) + evm.db_mut().commit(state) } } diff --git a/crates/rpc/rpc/src/eth/sim_bundle.rs b/crates/rpc/rpc/src/eth/sim_bundle.rs index 1c24a5f4a..b137bf38c 100644 --- a/crates/rpc/rpc/src/eth/sim_bundle.rs +++ b/crates/rpc/rpc/src/eth/sim_bundle.rs @@ -10,7 +10,7 @@ use alloy_rpc_types_mev::{ }; use jsonrpsee::core::RpcResult; use reth_chainspec::EthChainSpec; -use reth_evm::{ConfigureEvm, ConfigureEvmEnv}; +use reth_evm::{ConfigureEvm, ConfigureEvmEnv, Evm}; use reth_provider::{ChainSpecProvider, HeaderProvider, ProviderTx}; use reth_revm::database::StateProviderDatabase; use reth_rpc_api::MevSimApiServer; @@ -324,10 +324,10 @@ where ) .into()); } - eth_api.evm_config().fill_tx_env(evm.tx_mut(), &item.tx, item.signer); - let ResultAndState { result, state } = - evm.transact().map_err(EthApiError::from_eth_err)?; + let ResultAndState { result, state } = evm + .transact(eth_api.evm_config().tx_env(&item.tx, item.signer)) + .map_err(EthApiError::from_eth_err)?; if !result.is_success() && !item.can_revert { return Err(EthApiError::InvalidParams( @@ -366,7 +366,7 @@ where } // Apply state changes - evm.context.evm.db.commit(state); + evm.db_mut().commit(state); } // After processing all transactions, process refunds diff --git a/examples/custom-beacon-withdrawals/src/main.rs b/examples/custom-beacon-withdrawals/src/main.rs index 7bb8a77d2..8e52447ac 100644 --- a/examples/custom-beacon-withdrawals/src/main.rs +++ b/examples/custom-beacon-withdrawals/src/main.rs @@ -7,23 +7,23 @@ use alloy_consensus::BlockHeader; use alloy_eips::{eip4895::Withdrawal, eip7685::Requests}; use alloy_sol_macro::sol; use alloy_sol_types::SolCall; -#[cfg(feature = "optimism")] -use reth::revm::primitives::OptimismFields; use reth::{ api::{ConfigureEvm, NodeTypesWithEngine}, builder::{components::ExecutorBuilder, BuilderContext, FullNodeTypes}, cli::Cli, providers::ProviderError, revm::{ - interpreter::Host, - primitives::{address, Address, Bytes, Env, TransactTo, TxEnv, U256}, - Database, DatabaseCommit, Evm, State, + primitives::{address, Address}, + Database, DatabaseCommit, State, }, }; use reth_chainspec::{ChainSpec, EthereumHardforks}; -use reth_evm::execute::{ - BlockExecutionError, BlockExecutionStrategy, BlockExecutionStrategyFactory, ExecuteOutput, - InternalBlockExecutionError, +use reth_evm::{ + execute::{ + BlockExecutionError, BlockExecutionStrategy, BlockExecutionStrategyFactory, ExecuteOutput, + InternalBlockExecutionError, + }, + Evm, }; use reth_evm_ethereum::EthEvmConfig; use reth_node_ethereum::{node::EthereumAddOns, BasicBlockExecutorProvider, EthereumNode}; @@ -177,19 +177,11 @@ sol!( /// Applies the post-block call to the withdrawal / deposit contract, using the given block, /// [`ChainSpec`], EVM. -pub fn apply_withdrawals_contract_call( +pub fn apply_withdrawals_contract_call( withdrawals: &[Withdrawal], - evm: &mut Evm<'_, EXT, DB>, -) -> Result<(), BlockExecutionError> -where - DB::Error: std::fmt::Display, -{ - // get previous env - let previous_env = Box::new(evm.context.env().clone()); - - // modify env for pre block call - fill_tx_env_with_system_contract_call( - &mut evm.context.evm.env, + evm: &mut impl Evm, +) -> Result<(), BlockExecutionError> { + let mut state = match evm.transact_system_call( SYSTEM_ADDRESS, WITHDRAWALS_ADDRESS, withdrawalsCall { @@ -198,12 +190,9 @@ where } .abi_encode() .into(), - ); - - let mut state = match evm.transact() { + ) { Ok(res) => res.state, Err(e) => { - evm.context.evm.env = previous_env; return Err(BlockExecutionError::Internal(InternalBlockExecutionError::Other( format!("withdrawal contract system call revert: {}", e).into(), ))) @@ -213,47 +202,8 @@ where // Clean-up post system tx context state.remove(&SYSTEM_ADDRESS); state.remove(&evm.block().coinbase); - evm.context.evm.db.commit(state); - // re-set the previous env - evm.context.evm.env = previous_env; + + evm.db_mut().commit(state); Ok(()) } - -fn fill_tx_env_with_system_contract_call( - env: &mut Env, - caller: Address, - contract: Address, - data: Bytes, -) { - env.tx = TxEnv { - caller, - transact_to: TransactTo::Call(contract), - // Explicitly set nonce to None so revm does not do any nonce checks - nonce: None, - gas_limit: 30_000_000, - value: U256::ZERO, - data, - // Setting the gas price to zero enforces that no value is transferred as part of the call, - // and that the call will not count against the block's gas limit - gas_price: U256::ZERO, - // The chain ID check is not relevant here and is disabled if set to None - chain_id: None, - // Setting the gas priority fee to None ensures the effective gas price is derived from the - // `gas_price` field, which we need to be zero - gas_priority_fee: None, - access_list: Vec::new(), - // blob fields can be None for this tx - blob_hashes: Vec::new(), - max_fee_per_blob_gas: None, - authorization_list: None, - #[cfg(feature = "optimism")] - optimism: OptimismFields::default(), - }; - - // ensure the block gas limit is >= the tx - env.block.gas_limit = U256::from(env.tx.gas_limit); - - // disable the base fee check for this call by setting the base fee to zero - env.block.basefee = U256::ZERO; -} diff --git a/examples/custom-evm/src/main.rs b/examples/custom-evm/src/main.rs index ac534d43c..794cdb3de 100644 --- a/examples/custom-evm/src/main.rs +++ b/examples/custom-evm/src/main.rs @@ -16,7 +16,7 @@ use reth::{ inspector_handle_register, precompile::{Precompile, PrecompileOutput, PrecompileSpecId}, primitives::{CfgEnvWithHandlerCfg, Env, PrecompileResult, TxEnv}, - ContextPrecompiles, Database, Evm, EvmBuilder, GetInspector, + ContextPrecompiles, Database, EvmBuilder, GetInspector, }, rpc::types::engine::PayloadAttributes, tasks::TaskManager, @@ -24,7 +24,7 @@ use reth::{ }; use reth_chainspec::{Chain, ChainSpec}; use reth_evm::env::EvmEnv; -use reth_evm_ethereum::EthEvmConfig; +use reth_evm_ethereum::{EthEvm, EthEvmConfig}; use reth_node_api::{ ConfigureEvm, ConfigureEvmEnv, FullNodeTypes, NextBlockEnvAttributes, NodeTypes, NodeTypesWithEngine, PayloadTypes, @@ -93,16 +93,6 @@ impl ConfigureEvmEnv for MyEvmConfig { self.inner.fill_tx_env(tx_env, transaction, sender); } - fn fill_tx_env_system_contract_call( - &self, - env: &mut Env, - caller: Address, - contract: Address, - data: Bytes, - ) { - self.inner.fill_tx_env_system_contract_call(env, caller, contract, data); - } - fn fill_cfg_env(&self, cfg_env: &mut CfgEnvWithHandlerCfg, header: &Self::Header) { self.inner.fill_cfg_env(cfg_env, header); } @@ -117,7 +107,14 @@ impl ConfigureEvmEnv for MyEvmConfig { } impl ConfigureEvm for MyEvmConfig { - fn evm_with_env(&self, db: DB, evm_env: EvmEnv, tx: TxEnv) -> Evm<'_, (), DB> { + type Evm<'a, DB: Database + 'a, I: 'a> = EthEvm<'a, I, DB>; + + fn evm_with_env( + &self, + db: DB, + evm_env: EvmEnv, + tx: TxEnv, + ) -> Self::Evm<'_, DB, ()> { EvmBuilder::default() .with_db(db) .with_cfg_env_with_handler_cfg(evm_env.cfg_env_with_handler_cfg) @@ -126,6 +123,7 @@ impl ConfigureEvm for MyEvmConfig { // add additional precompiles .append_handler_register(MyEvmConfig::set_precompiles) .build() + .into() } fn evm_with_env_and_inspector( @@ -134,7 +132,7 @@ impl ConfigureEvm for MyEvmConfig { evm_env: EvmEnv, tx: TxEnv, inspector: I, - ) -> Evm<'_, I, DB> + ) -> Self::Evm<'_, DB, I> where DB: Database, I: GetInspector, @@ -149,6 +147,7 @@ impl ConfigureEvm for MyEvmConfig { .append_handler_register(MyEvmConfig::set_precompiles) .append_handler_register(inspector_handle_register) .build() + .into() } } diff --git a/examples/stateful-precompile/src/main.rs b/examples/stateful-precompile/src/main.rs index 2cdebfe56..71f468722 100644 --- a/examples/stateful-precompile/src/main.rs +++ b/examples/stateful-precompile/src/main.rs @@ -16,7 +16,7 @@ use reth::{ primitives::{ CfgEnvWithHandlerCfg, Env, PrecompileResult, SpecId, StatefulPrecompileMut, TxEnv, }, - ContextPrecompile, ContextPrecompiles, Database, Evm, EvmBuilder, GetInspector, + ContextPrecompile, ContextPrecompiles, Database, EvmBuilder, GetInspector, }, tasks::TaskManager, }; @@ -25,8 +25,8 @@ use reth_evm::env::EvmEnv; use reth_node_api::{ConfigureEvm, ConfigureEvmEnv, FullNodeTypes, NodeTypes}; use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig}; use reth_node_ethereum::{ - node::EthereumAddOns, BasicBlockExecutorProvider, EthEvmConfig, EthExecutionStrategyFactory, - EthereumNode, + evm::EthEvm, node::EthereumAddOns, BasicBlockExecutorProvider, EthEvmConfig, + EthExecutionStrategyFactory, EthereumNode, }; use reth_primitives::{EthPrimitives, TransactionSigned}; use reth_tracing::{RethTracer, Tracer}; @@ -155,16 +155,6 @@ impl ConfigureEvmEnv for MyEvmConfig { self.inner.fill_tx_env(tx_env, transaction, sender) } - fn fill_tx_env_system_contract_call( - &self, - env: &mut Env, - caller: Address, - contract: Address, - data: Bytes, - ) { - self.inner.fill_tx_env_system_contract_call(env, caller, contract, data) - } - fn fill_cfg_env(&self, cfg_env: &mut CfgEnvWithHandlerCfg, header: &Self::Header) { self.inner.fill_cfg_env(cfg_env, header) } @@ -179,7 +169,14 @@ impl ConfigureEvmEnv for MyEvmConfig { } impl ConfigureEvm for MyEvmConfig { - fn evm_with_env(&self, db: DB, evm_env: EvmEnv, tx: TxEnv) -> Evm<'_, (), DB> { + type Evm<'a, DB: Database + 'a, I: 'a> = EthEvm<'a, I, DB>; + + fn evm_with_env( + &self, + db: DB, + evm_env: EvmEnv, + tx: TxEnv, + ) -> Self::Evm<'_, DB, ()> { let new_cache = self.precompile_cache.clone(); EvmBuilder::default() .with_db(db) @@ -191,6 +188,7 @@ impl ConfigureEvm for MyEvmConfig { MyEvmConfig::set_precompiles(handler, new_cache.clone()) })) .build() + .into() } fn evm_with_env_and_inspector( @@ -199,7 +197,7 @@ impl ConfigureEvm for MyEvmConfig { evm_env: EvmEnv, tx: TxEnv, inspector: I, - ) -> Evm<'_, I, DB> + ) -> Self::Evm<'_, DB, I> where DB: Database, I: GetInspector, @@ -217,6 +215,7 @@ impl ConfigureEvm for MyEvmConfig { })) .append_handler_register(inspector_handle_register) .build() + .into() } }