feat: add Evm trait (#13823)

This commit is contained in:
Arsenii Kulikov
2025-01-18 19:42:39 +04:00
committed by GitHub
parent cef0c927c9
commit c46f23f8de
25 changed files with 453 additions and 460 deletions

1
Cargo.lock generated
View File

@ -7514,6 +7514,7 @@ dependencies = [
"alloy-genesis",
"alloy-primitives",
"alloy-sol-types",
"derive_more",
"reth-chainspec",
"reth-consensus",
"reth-ethereum-consensus",

View File

@ -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);

View File

@ -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");

View File

@ -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"
]

View File

@ -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 {

View File

@ -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<EXT, DB: Database> Evm for EthEvm<'_, EXT, DB> {
type DB = DB;
type Tx = TxEnv;
type Error = EVMError<DB::Error>;
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<ResultAndState, Self::Error> {
*self.tx_mut() = tx;
self.0.transact()
}
fn transact_system_call(
&mut self,
caller: Address,
contract: Address,
data: Bytes,
) -> Result<ResultAndState, Self::Error> {
#[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<DB: reth_revm::Database>(
type Evm<'a, DB: Database + 'a, I: 'a> = EthEvm<'a, I, DB>;
fn evm_with_env<DB: Database>(
&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<DB, I>(
@ -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<DB>,
{
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(),
)
}
}

View File

@ -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};

View File

@ -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 {

View File

@ -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<ResultAndState, Self::Error>;
/// Executes a system call.
fn transact_system_call(
&mut self,
caller: Address,
contract: Address,
data: Bytes,
) -> Result<ResultAndState, Self::Error>;
/// 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<ResultAndState, Self::Error>
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<Tx = TxEnv, DB = DB, Error = EVMError<DB::Error>>;
/// 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<DB: Database>(&self, db: DB, evm_env: EvmEnv, tx: TxEnv) -> Evm<'_, (), DB>;
fn evm_with_env<DB: Database>(
&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<DB: Database>(&self, db: DB, header: &Self::Header) -> Evm<'_, (), DB> {
fn evm_for_block<DB: Database>(&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<DB>;
@ -81,11 +132,18 @@ where
T: ConfigureEvm,
&'b T: ConfigureEvmEnv<Header = T::Header>,
{
fn evm_for_block<DB: Database>(&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<DB: Database>(&self, db: DB, header: &Self::Header) -> Self::Evm<'_, DB, ()> {
(*self).evm_for_block(db, header)
}
fn evm_with_env<DB: Database>(&self, db: DB, evm_env: EvmEnv, tx: TxEnv) -> Evm<'_, (), DB> {
fn evm_with_env<DB: Database>(
&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<DB>,
@ -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());

View File

@ -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<EvmConfig, EXT, DB>(
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<Option<ResultAndState>, BlockExecutionError>
where
DB: Database,
DB::Error: core::fmt::Display,
EvmConfig: ConfigureEvm,
{
evm: &mut impl Evm<Error: Display>,
) -> Result<Option<ResultAndState>, 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))
}

View File

@ -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<EvmConfig, EXT, DB, Spec>(
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<B256>,
evm: &mut Evm<'_, EXT, DB>,
) -> Result<Option<ResultAndState>, BlockExecutionError>
where
DB: Database,
DB::Error: core::fmt::Display,
EvmConfig: ConfigureEvm,
Spec: EthereumHardforks,
{
evm: &mut impl Evm<Error: Display>,
) -> Result<Option<ResultAndState>, 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))
}

View File

@ -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<EvmConfig, EXT, DB>(
evm_config: &EvmConfig,
evm: &mut Evm<'_, EXT, DB>,
) -> Result<ResultAndState, BlockExecutionError>
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<Error: Display>,
) -> Result<ResultAndState, BlockExecutionError> {
// 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)
}

View File

@ -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<EvmConfig, EXT, DB>(
evm_config: &EvmConfig,
evm: &mut Evm<'_, EXT, DB>,
) -> Result<ResultAndState, BlockExecutionError>
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<Error: Display>,
) -> Result<ResultAndState, BlockExecutionError> {
// 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)
}

View File

@ -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<DB, Ext>(
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<DB: DatabaseCommit, Error: Display>,
) -> 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<DB, Ext>(
pub fn apply_post_execution_changes(
&mut self,
evm: &mut Evm<'_, Ext, DB>,
) -> Result<Requests, BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
evm: &mut impl Evm<DB: DatabaseCommit, Error: Display>,
) -> Result<Requests, BlockExecutionError> {
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<DB, Ext>(
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<DB: DatabaseCommit, Error: Display>,
) -> 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<DB, Ext>(
pub fn apply_beacon_root_contract_call(
&mut self,
timestamp: u64,
block_number: u64,
parent_block_hash: Option<B256>,
evm: &mut Evm<'_, Ext, DB>,
) -> Result<(), BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
evm: &mut impl Evm<DB: DatabaseCommit, Error: Display>,
) -> 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<DB, Ext>(
pub fn apply_withdrawal_requests_contract_call(
&mut self,
evm: &mut Evm<'_, Ext, DB>,
) -> Result<Bytes, BlockExecutionError>
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<DB: DatabaseCommit, Error: Display>,
) -> Result<Bytes, BlockExecutionError> {
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<DB, Ext>(
pub fn apply_consolidation_requests_contract_call(
&mut self,
evm: &mut Evm<'_, Ext, DB>,
) -> Result<Bytes, BlockExecutionError>
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<DB: DatabaseCommit, Error: Display>,
) -> Result<Bytes, BlockExecutionError> {
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)
}

View File

@ -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 {

View File

@ -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<OpChainSpec>,
}
/// 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<OpChainSpec>) -> Self {
Self { chain_spec }
impl<EXT, DB: Database> Evm for OpEvm<'_, EXT, DB> {
type DB = DB;
type Tx = TxEnv;
type Error = EVMError<DB::Error>;
fn block(&self) -> &BlockEnv {
self.0.block()
}
/// Returns the chain spec associated with this configuration.
pub const fn chain_spec(&self) -> &Arc<OpChainSpec> {
&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<ResultAndState, Self::Error> {
*self.tx_mut() = tx;
self.0.transact()
}
fn transact_system_call(
&mut self,
caller: Address,
contract: Address,
data: Bytes,
) {
env.tx = TxEnv {
) -> Result<ResultAndState, Self::Error> {
#[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<OpChainSpec>,
}
impl OpEvmConfig {
/// Creates a new [`OpEvmConfig`] with the given chain spec.
pub const fn new(chain_spec: Arc<OpChainSpec>) -> Self {
Self { chain_spec }
}
/// Returns the chain spec associated with this configuration.
pub const fn chain_spec(&self) -> &Arc<OpChainSpec> {
&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<DB: Database>(
&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<DB, I>(
@ -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<DB>,
@ -205,6 +256,7 @@ impl ConfigureEvm for OpEvmConfig {
.with_tx_env(tx)
.append_handler_register(inspector_handle_register)
.build()
.into()
}
}

View File

@ -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 {

View File

@ -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<DB>(
&self,
@ -505,21 +502,14 @@ pub trait Call:
DB: Database,
EthApiError: From<DB::Error>,
{
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<DB>(
&self,
@ -532,18 +522,16 @@ pub trait Call:
DB: Database,
EthApiError: From<DB::Error>,
{
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.
///

View File

@ -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 {

View File

@ -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<DB, I>(
&self,
@ -47,23 +45,22 @@ pub trait Trace:
EthApiError: From<DB::Error>,
I: GetInspector<DB>,
{
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<F, R>(
@ -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<F, R>(
&self,
evm_env: EvmEnv,

View File

@ -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)
}
}

View File

@ -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

View File

@ -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<EXT, DB: Database + DatabaseCommit>(
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<Error: Display, DB: DatabaseCommit>,
) -> 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;
}

View File

@ -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<DB: Database>(&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<DB: Database>(
&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<DB, I>(
@ -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<DB>,
@ -149,6 +147,7 @@ impl ConfigureEvm for MyEvmConfig {
.append_handler_register(MyEvmConfig::set_precompiles)
.append_handler_register(inspector_handle_register)
.build()
.into()
}
}

View File

@ -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<DB: Database>(&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<DB: Database>(
&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<DB, I>(
@ -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<DB>,
@ -217,6 +215,7 @@ impl ConfigureEvm for MyEvmConfig {
}))
.append_handler_register(inspector_handle_register)
.build()
.into()
}
}