Skip to content

Commit

Permalink
feat: generic TxEnv (#13957)
Browse files Browse the repository at this point in the history
  • Loading branch information
klkvr authored Jan 24, 2025
1 parent 1296bac commit 006eea0
Show file tree
Hide file tree
Showing 18 changed files with 271 additions and 244 deletions.
17 changes: 3 additions & 14 deletions crates/ethereum/evm/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use reth_evm::{
},
state_change::post_block_balance_increments,
system_calls::{OnStateHook, SystemCaller},
ConfigureEvm, Evm, TxEnvOverrides,
ConfigureEvm, Evm,
};
use reth_primitives::{EthPrimitives, Receipt, RecoveredBlock};
use reth_primitives_traits::{BlockBody, SignedTransaction};
Expand Down Expand Up @@ -94,8 +94,6 @@ where
chain_spec: Arc<ChainSpec>,
/// How to create an EVM.
evm_config: EvmConfig,
/// Optional overrides for the transactions environment.
tx_env_overrides: Option<Box<dyn TxEnvOverrides>>,
/// Current state for block execution.
state: State<DB>,
/// Utility to call system smart contracts.
Expand All @@ -109,7 +107,7 @@ where
/// Creates a new [`EthExecutionStrategy`]
pub fn new(state: State<DB>, chain_spec: Arc<ChainSpec>, evm_config: EvmConfig) -> Self {
let system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone());
Self { state, chain_spec, evm_config, system_caller, tx_env_overrides: None }
Self { state, chain_spec, evm_config, system_caller }
}
}

Expand All @@ -123,13 +121,8 @@ where
{
type DB = DB;
type Error = BlockExecutionError;

type Primitives = EthPrimitives;

fn init(&mut self, tx_env_overrides: Box<dyn TxEnvOverrides>) {
self.tx_env_overrides = Some(tx_env_overrides);
}

fn apply_pre_execution_changes(
&mut self,
block: &RecoveredBlock<reth_primitives::Block>,
Expand Down Expand Up @@ -166,11 +159,7 @@ where
.into())
}

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(&mut tx_env);
}
let tx_env = self.evm_config.tx_env(transaction, *sender);

// Execute transaction.
let result_and_state = evm.transact(tx_env).map_err(move |err| {
Expand Down
1 change: 1 addition & 0 deletions crates/ethereum/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ impl ConfigureEvmEnv for EthEvmConfig {
type Header = Header;
type Transaction = TransactionSigned;
type Error = Infallible;
type TxEnv = TxEnv;

fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) {
transaction.fill_tx_env(tx_env, sender);
Expand Down
8 changes: 0 additions & 8 deletions crates/evm/src/either.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::{
execute::{BatchExecutor, BlockExecutorProvider, Executor},
system_calls::OnStateHook,
};
use alloc::boxed::Box;
use alloy_primitives::BlockNumber;
use reth_prune_types::PruneModes;
use reth_storage_errors::provider::ProviderError;
Expand Down Expand Up @@ -60,13 +59,6 @@ where
type Output = A::Output;
type Error = A::Error;

fn init(&mut self, tx_env_overrides: Box<dyn crate::TxEnvOverrides>) {
match self {
Self::Left(a) => a.init(tx_env_overrides),
Self::Right(b) => b.init(tx_env_overrides),
}
}

fn execute(self, input: Self::Input<'_>) -> Result<Self::Output, Self::Error> {
match self {
Self::Left(a) => a.execute(input),
Expand Down
37 changes: 2 additions & 35 deletions crates/evm/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub use reth_execution_errors::{
pub use reth_execution_types::{BlockExecutionOutput, ExecutionOutcome};
pub use reth_storage_errors::provider::ProviderError;

use crate::{system_calls::OnStateHook, TxEnvOverrides};
use crate::system_calls::OnStateHook;
use alloc::{boxed::Box, vec::Vec};
use alloy_eips::eip7685::Requests;
use alloy_primitives::{
Expand Down Expand Up @@ -38,9 +38,6 @@ pub trait Executor<DB> {
/// The error type returned by the executor.
type Error;

/// Initialize the executor with the given transaction environment overrides.
fn init(&mut self, _tx_env_overrides: Box<dyn TxEnvOverrides>) {}

/// Consumes the type and executes the block.
///
/// # Note
Expand Down Expand Up @@ -199,9 +196,6 @@ pub trait BlockExecutionStrategy {
/// The error type returned by this strategy's methods.
type Error: From<ProviderError> + core::error::Error;

/// Initialize the strategy with the given transaction environment overrides.
fn init(&mut self, _tx_env_overrides: Box<dyn TxEnvOverrides>) {}

/// Applies any necessary changes before executing the block's transactions.
fn apply_pre_execution_changes(
&mut self,
Expand Down Expand Up @@ -341,10 +335,6 @@ where
type Output = BlockExecutionOutput<<S::Primitives as NodePrimitives>::Receipt>;
type Error = S::Error;

fn init(&mut self, env_overrides: Box<dyn TxEnvOverrides>) {
self.strategy.init(env_overrides);
}

fn execute(mut self, block: Self::Input<'_>) -> Result<Self::Output, Self::Error> {
self.strategy.apply_pre_execution_changes(block)?;
let ExecuteOutput { receipts, gas_used } = self.strategy.execute_transactions(block)?;
Expand Down Expand Up @@ -518,7 +508,7 @@ mod tests {
use reth_chainspec::{ChainSpec, MAINNET};
use reth_primitives::EthPrimitives;
use revm::db::{CacheDB, EmptyDBTyped};
use revm_primitives::{address, bytes, AccountInfo, TxEnv, KECCAK_EMPTY};
use revm_primitives::{address, bytes, AccountInfo, KECCAK_EMPTY};
use std::sync::Arc;

#[derive(Clone, Default)]
Expand Down Expand Up @@ -734,29 +724,6 @@ mod tests {
assert_eq!(block_execution_output.state, expected_finish_result);
}

#[test]
fn test_tx_env_overrider() {
let strategy_factory = TestExecutorStrategyFactory {
execute_transactions_result: ExecuteOutput {
receipts: vec![Receipt::default()],
gas_used: 10,
},
apply_post_execution_changes_result: Requests::new(vec![bytes!("deadbeef")]),
finish_result: BundleState::default(),
};
let provider = BasicBlockExecutorProvider::new(strategy_factory);
let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();

// if we want to apply tx env overrides the executor must be mut.
let mut executor = provider.executor(db);
// execute consumes the executor, so we can only call it once.
executor.init(Box::new(|tx_env: &mut TxEnv| {
tx_env.nonce.take();
}));
let result = executor.execute(&Default::default());
assert!(result.is_ok());
}

fn setup_state_with_account(
addr: Address,
balance: u128,
Expand Down
109 changes: 93 additions & 16 deletions crates/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@

extern crate alloc;

use core::fmt::Debug;

use alloy_consensus::BlockHeader as _;
use alloy_eips::eip2930::AccessList;
use alloy_primitives::{Address, Bytes, B256, U256};
use reth_primitives_traits::{BlockHeader, SignedTransaction};
use revm::{Database, DatabaseCommit, GetInspector};
use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, EVMError, ResultAndState, SpecId, TxEnv};
use revm_primitives::{
BlockEnv, CfgEnvWithHandlerCfg, EVMError, ResultAndState, SpecId, TxEnv, TxKind,
};

pub mod either;
/// EVM environment configuration.
Expand Down Expand Up @@ -87,7 +92,11 @@ pub trait Evm {
/// 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>>;
type Evm<'a, DB: Database + 'a, I: 'a>: Evm<
Tx = Self::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.
Expand Down Expand Up @@ -127,7 +136,7 @@ pub trait ConfigureEvm: ConfigureEvmEnv {
impl<'b, T> ConfigureEvm for &'b T
where
T: ConfigureEvm,
&'b T: ConfigureEvmEnv<Header = T::Header>,
&'b T: ConfigureEvmEnv<Header = T::Header, TxEnv = T::TxEnv>,
{
type Evm<'a, DB: Database + 'a, I: 'a> = T::Evm<'a, DB, I>;

Expand Down Expand Up @@ -165,18 +174,26 @@ pub trait ConfigureEvmEnv: Send + Sync + Unpin + Clone + 'static {
/// The transaction type.
type Transaction: SignedTransaction;

/// Transaction environment used by EVM.
type TxEnv: TransactionEnv;

/// The error type that is returned by [`Self::next_cfg_and_block_env`].
type Error: core::error::Error + Send + Sync;

/// Returns a [`TxEnv`] from a transaction and [`Address`].
fn tx_env(&self, transaction: &Self::Transaction, signer: Address) -> TxEnv {
let mut tx_env = TxEnv::default();
fn tx_env(&self, transaction: &Self::Transaction, signer: Address) -> Self::TxEnv {
let mut tx_env = Default::default();
self.fill_tx_env(&mut tx_env, transaction, signer);
tx_env
}

/// 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);
fn fill_tx_env(
&self,
tx_env: &mut Self::TxEnv,
transaction: &Self::Transaction,
sender: Address,
);

/// Returns a [`CfgEnvWithHandlerCfg`] for the given header.
fn cfg_env(&self, header: &Self::Header) -> CfgEnvWithHandlerCfg {
Expand Down Expand Up @@ -262,17 +279,77 @@ pub struct NextBlockEnvAttributes {
pub gas_limit: u64,
}

/// Function hook that allows to modify a transaction environment.
pub trait TxEnvOverrides {
/// Apply the overrides by modifying the given `TxEnv`.
fn apply(&mut self, env: &mut TxEnv);
/// Abstraction over transaction environment.
pub trait TransactionEnv:
Into<revm_primitives::TxEnv> + Debug + Default + Clone + Send + Sync + 'static
{
/// Returns configured gas limit.
fn gas_limit(&self) -> u64;

/// Set the gas limit.
fn set_gas_limit(&mut self, gas_limit: u64);

/// Set the gas limit.
fn with_gas_limit(mut self, gas_limit: u64) -> Self {
self.set_gas_limit(gas_limit);
self
}

/// Returns configured gas price.
fn gas_price(&self) -> U256;

/// Returns configured value.
fn value(&self) -> U256;

/// Caller of the transaction.
fn caller(&self) -> Address;

/// Set access list.
fn set_access_list(&mut self, access_list: AccessList);

/// Set access list.
fn with_access_list(mut self, access_list: AccessList) -> Self {
self.set_access_list(access_list);
self
}

/// Returns calldata for the transaction.
fn input(&self) -> &Bytes;

/// Returns [`TxKind`] of the transaction.
fn kind(&self) -> TxKind;
}

impl<F> TxEnvOverrides for F
where
F: FnMut(&mut TxEnv),
{
fn apply(&mut self, env: &mut TxEnv) {
self(env)
impl TransactionEnv for TxEnv {
fn gas_limit(&self) -> u64 {
self.gas_limit
}

fn set_gas_limit(&mut self, gas_limit: u64) {
self.gas_limit = gas_limit;
}

fn gas_price(&self) -> U256 {
self.gas_price.to()
}

fn value(&self) -> U256 {
self.value
}

fn caller(&self) -> Address {
self.caller
}

fn set_access_list(&mut self, access_list: AccessList) {
self.access_list = access_list.to_vec();
}

fn input(&self) -> &Bytes {
&self.data
}

fn kind(&self) -> TxKind {
self.transact_to
}
}
23 changes: 3 additions & 20 deletions crates/optimism/evm/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use reth_evm::{
},
state_change::post_block_balance_increments,
system_calls::{OnStateHook, SystemCaller},
ConfigureEvmFor, Evm, TxEnvOverrides,
ConfigureEvmFor, Evm,
};
use reth_optimism_chainspec::OpChainSpec;
use reth_optimism_consensus::validate_block_post_execution;
Expand Down Expand Up @@ -104,8 +104,6 @@ where
chain_spec: Arc<OpChainSpec>,
/// How to create an EVM.
evm_config: EvmConfig,
/// Optional overrides for the transactions environment.
tx_env_overrides: Option<Box<dyn TxEnvOverrides>>,
/// Current state for block execution.
state: State<DB>,
/// Utility to call system smart contracts.
Expand All @@ -127,14 +125,7 @@ where
receipt_builder: Arc<dyn OpReceiptBuilder<N::SignedTx, Receipt = N::Receipt>>,
) -> Self {
let system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone());
Self {
state,
chain_spec,
evm_config,
system_caller,
tx_env_overrides: None,
receipt_builder,
}
Self { state, chain_spec, evm_config, system_caller, receipt_builder }
}
}

Expand All @@ -152,10 +143,6 @@ where
type Primitives = N;
type Error = BlockExecutionError;

fn init(&mut self, tx_env_overrides: Box<dyn TxEnvOverrides>) {
self.tx_env_overrides = Some(tx_env_overrides);
}

fn apply_pre_execution_changes(
&mut self,
block: &RecoveredBlock<N::Block>,
Expand Down Expand Up @@ -223,11 +210,7 @@ where
.transpose()
.map_err(|_| OpBlockExecutionError::AccountLoadFailed(*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(&mut tx_env);
}
let tx_env = self.evm_config.tx_env(transaction, *sender);

// Execute transaction.
let result_and_state = evm.transact(tx_env).map_err(move |err| {
Expand Down
1 change: 1 addition & 0 deletions crates/optimism/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ impl ConfigureEvmEnv for OpEvmConfig {
type Header = Header;
type Transaction = OpTransactionSigned;
type Error = EIP1559ParamError;
type TxEnv = TxEnv;

fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &OpTransactionSigned, sender: Address) {
transaction.fill_tx_env(tx_env, sender);
Expand Down
Loading

0 comments on commit 006eea0

Please sign in to comment.