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

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(),
)
}
}