refactor: simplify SystemCaller API (#14578)

This commit is contained in:
Arsenii Kulikov
2025-02-19 14:08:49 +04:00
committed by GitHub
parent 849c04cb34
commit e92cf35ac9
11 changed files with 81 additions and 217 deletions

View File

@ -78,8 +78,7 @@ where
// Setup EVM
let mut evm = self.evm_config.evm_for_block(&mut db, block.header());
let mut system_caller =
SystemCaller::new(self.evm_config.clone(), self.provider.chain_spec());
let mut system_caller = SystemCaller::new(self.provider.chain_spec());
// Apply pre-block system contract calls.
system_caller.apply_pre_execution_changes(block.header(), &mut evm)?;

View File

@ -293,14 +293,10 @@ where
let mut evm = evm_config.evm_for_block(&mut state, &reorg_target.header);
// apply eip-4788 pre block contract call
let mut system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone());
let mut system_caller = SystemCaller::new(chain_spec.clone());
system_caller.apply_beacon_root_contract_call(
reorg_target.timestamp,
reorg_target.number,
reorg_target.parent_beacon_block_root,
&mut evm,
)?;
system_caller
.apply_beacon_root_contract_call(reorg_target.parent_beacon_block_root, &mut evm)?;
let mut cumulative_gas_used = 0;
let mut sum_blob_gas_used = 0;

View File

@ -90,7 +90,7 @@ where
/// Current state for block execution.
state: State<DB>,
/// Utility to call system smart contracts.
system_caller: SystemCaller<EvmConfig, ChainSpec>,
system_caller: SystemCaller<Arc<ChainSpec>>,
}
impl<DB, EvmConfig> EthExecutionStrategy<DB, EvmConfig>
@ -99,7 +99,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());
let system_caller = SystemCaller::new(chain_spec.clone());
Self { state, chain_spec, evm_config, system_caller }
}
}

View File

@ -202,11 +202,12 @@ where
let block_number = evm_env.block_env.number;
let beneficiary = evm_env.block_env.beneficiary;
let mut system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone());
let mut evm = evm_config.evm_with_env(&mut db, evm_env);
let mut system_caller = SystemCaller::new(chain_spec.clone());
// apply eip-4788 pre block contract call
system_caller
.pre_block_beacon_root_contract_call(&mut db, &evm_env, attributes.parent_beacon_block_root)
.apply_beacon_root_contract_call(attributes.parent_beacon_block_root, &mut evm)
.map_err(|err| {
warn!(target: "payload_builder",
parent_hash=%parent_header.hash(),
@ -217,18 +218,15 @@ where
})?;
// apply eip-2935 blockhashes update
system_caller.pre_block_blockhashes_contract_call(
&mut db,
&evm_env,
system_caller.apply_blockhashes_contract_call(
parent_header.hash(),
&mut evm,
)
.map_err(|err| {
warn!(target: "payload_builder", parent_hash=%parent_header.hash(), %err, "failed to update parent header blockhashes for payload");
PayloadBuilderError::Internal(err.into())
})?;
let mut evm = evm_config.evm_with_env(&mut db, evm_env);
let mut receipts = Vec::new();
let mut block_blob_count = 0;
let blob_params = chain_spec.blob_params_at_timestamp(attributes.timestamp);

View File

@ -23,18 +23,16 @@ use revm::context_interface::result::ResultAndState;
#[inline]
pub(crate) fn transact_blockhashes_contract_call<Halt>(
chain_spec: impl EthereumHardforks,
block_timestamp: u64,
block_number: u64,
parent_block_hash: B256,
evm: &mut impl Evm<HaltReason = Halt>,
) -> Result<Option<ResultAndState<Halt>>, BlockExecutionError> {
if !chain_spec.is_prague_active_at_timestamp(block_timestamp) {
if !chain_spec.is_prague_active_at_timestamp(evm.block().timestamp) {
return Ok(None)
}
// if the block number is zero (genesis block) then no system transaction may occur as per
// EIP-2935
if block_number == 0 {
if evm.block().number == 0 {
return Ok(None)
}

View File

@ -20,12 +20,10 @@ use revm::context_interface::result::ResultAndState;
#[inline]
pub(crate) fn transact_beacon_root_contract_call<Halt>(
chain_spec: impl EthereumHardforks,
block_timestamp: u64,
block_number: u64,
parent_beacon_block_root: Option<B256>,
evm: &mut impl Evm<Error: Display, HaltReason = Halt>,
) -> Result<Option<ResultAndState<Halt>>, BlockExecutionError> {
if !chain_spec.is_cancun_active_at_timestamp(block_timestamp) {
if !chain_spec.is_cancun_active_at_timestamp(evm.block().timestamp) {
return Ok(None)
}
@ -34,7 +32,7 @@ pub(crate) fn transact_beacon_root_contract_call<Halt>(
// if the block number is zero (genesis block) then the parent beacon block root must
// be 0x0 and no system transaction may occur as per EIP-4788
if block_number == 0 {
if evm.block().number == 0 {
if !parent_beacon_block_root.is_zero() {
return Err(BlockValidationError::CancunGenesisParentBeaconBlockRootNotZero {
parent_beacon_block_root,

View File

@ -1,7 +1,7 @@
//! System contract call functions.
use crate::{ConfigureEvm, Database, Evm, EvmEnv};
use alloc::{boxed::Box, sync::Arc};
use crate::Evm;
use alloc::boxed::Box;
use alloy_consensus::BlockHeader;
use alloy_eips::{
eip7002::WITHDRAWAL_REQUEST_TYPE, eip7251::CONSOLIDATION_REQUEST_TYPE, eip7685::Requests,
@ -78,18 +78,17 @@ impl OnStateHook for NoopHook {
///
/// This can be used to chain system transaction calls.
#[allow(missing_debug_implementations)]
pub struct SystemCaller<EvmConfig, Chainspec> {
evm_config: EvmConfig,
chain_spec: Arc<Chainspec>,
pub struct SystemCaller<ChainSpec> {
chain_spec: ChainSpec,
/// Optional hook to be called after each state change.
hook: Option<Box<dyn OnStateHook>>,
}
impl<EvmConfig, Chainspec> SystemCaller<EvmConfig, Chainspec> {
impl<ChainSpec> SystemCaller<ChainSpec> {
/// Create a new system caller with the given EVM config, database, and chain spec, and creates
/// the EVM with the given initialized config and block environment.
pub const fn new(evm_config: EvmConfig, chain_spec: Arc<Chainspec>) -> Self {
Self { evm_config, chain_spec, hook: None }
pub const fn new(chain_spec: ChainSpec) -> Self {
Self { chain_spec, hook: None }
}
/// Installs a custom hook to be called after each state change.
@ -102,29 +101,18 @@ impl<EvmConfig, Chainspec> SystemCaller<EvmConfig, Chainspec> {
pub fn finish(self) {}
}
impl<EvmConfig, Chainspec> SystemCaller<EvmConfig, Chainspec>
impl<Chainspec> SystemCaller<Chainspec>
where
EvmConfig: ConfigureEvm,
Chainspec: EthereumHardforks,
{
/// Apply pre execution changes.
pub fn apply_pre_execution_changes(
&mut self,
header: &EvmConfig::Header,
header: impl BlockHeader,
evm: &mut impl Evm<DB: DatabaseCommit, Error: Display>,
) -> Result<(), BlockExecutionError> {
self.apply_blockhashes_contract_call(
header.timestamp(),
header.number(),
header.parent_hash(),
evm,
)?;
self.apply_beacon_root_contract_call(
header.timestamp(),
header.number(),
header.parent_beacon_block_root(),
evm,
)?;
self.apply_blockhashes_contract_call(header.parent_hash(), evm)?;
self.apply_beacon_root_contract_call(header.parent_beacon_block_root(), evm)?;
Ok(())
}
@ -151,45 +139,14 @@ where
Ok(requests)
}
/// Applies the pre-block call to the EIP-2935 blockhashes contract.
pub fn pre_block_blockhashes_contract_call<DB>(
&mut self,
db: &mut DB,
evm_env: &EvmEnv<EvmConfig::Spec>,
parent_block_hash: B256,
) -> Result<(), BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
let evm_config = self.evm_config.clone();
let mut evm = evm_config.evm_with_env(db, evm_env.clone());
self.apply_blockhashes_contract_call(
evm_env.block_env.timestamp,
evm_env.block_env.number,
parent_block_hash,
&mut evm,
)?;
Ok(())
}
/// Applies the pre-block call to the EIP-2935 blockhashes contract.
pub fn apply_blockhashes_contract_call(
&mut self,
timestamp: u64,
block_number: u64,
parent_block_hash: B256,
evm: &mut impl Evm<DB: DatabaseCommit, Error: Display>,
evm: &mut impl Evm<DB: DatabaseCommit>,
) -> Result<(), BlockExecutionError> {
let result_and_state = eip2935::transact_blockhashes_contract_call(
&self.chain_spec,
timestamp,
block_number,
parent_block_hash,
evm,
)?;
let result_and_state =
eip2935::transact_blockhashes_contract_call(&self.chain_spec, parent_block_hash, evm)?;
if let Some(res) = result_and_state {
if let Some(ref mut hook) = self.hook {
@ -204,43 +161,15 @@ where
Ok(())
}
/// Applies the pre-block call to the EIP-4788 beacon root contract.
pub fn pre_block_beacon_root_contract_call<DB>(
&mut self,
db: &mut DB,
evm_env: &EvmEnv<EvmConfig::Spec>,
parent_beacon_block_root: Option<B256>,
) -> Result<(), BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
let evm_config = self.evm_config.clone();
let mut evm = evm_config.evm_with_env(db, evm_env.clone());
self.apply_beacon_root_contract_call(
evm_env.block_env.timestamp,
evm_env.block_env.number,
parent_beacon_block_root,
&mut evm,
)?;
Ok(())
}
/// Applies the pre-block call to the EIP-4788 beacon root contract.
pub fn apply_beacon_root_contract_call(
&mut self,
timestamp: u64,
block_number: u64,
parent_block_hash: Option<B256>,
evm: &mut impl Evm<DB: DatabaseCommit, Error: Display>,
parent_beacon_block_root: Option<B256>,
evm: &mut impl Evm<DB: DatabaseCommit>,
) -> Result<(), BlockExecutionError> {
let result_and_state = eip4788::transact_beacon_root_contract_call(
&self.chain_spec,
timestamp,
block_number,
parent_block_hash,
parent_beacon_block_root,
evm,
)?;
@ -257,24 +186,6 @@ where
Ok(())
}
/// Applies the post-block call to the EIP-7002 withdrawal request contract.
pub fn post_block_withdrawal_requests_contract_call<DB>(
&mut self,
db: &mut DB,
evm_env: &EvmEnv<EvmConfig::Spec>,
) -> Result<Bytes, BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
let evm_config = self.evm_config.clone();
let mut evm = evm_config.evm_with_env(db, evm_env.clone());
let result = self.apply_withdrawal_requests_contract_call(&mut evm)?;
Ok(result)
}
/// Applies the post-block call to the EIP-7002 withdrawal request contract.
pub fn apply_withdrawal_requests_contract_call(
&mut self,
@ -295,23 +206,6 @@ where
eip7002::post_commit(result_and_state.result)
}
/// Applies the post-block call to the EIP-7251 consolidation requests contract.
pub fn post_block_consolidation_requests_contract_call<DB>(
&mut self,
db: &mut DB,
evm_env: &EvmEnv<EvmConfig::Spec>,
) -> Result<Bytes, BlockExecutionError>
where
DB: Database + DatabaseCommit,
{
let evm_config = self.evm_config.clone();
let mut evm = evm_config.evm_with_env(db, evm_env.clone());
let res = self.apply_consolidation_requests_contract_call(&mut evm)?;
Ok(res)
}
/// Applies the post-block call to the EIP-7251 consolidation requests contract.
pub fn apply_consolidation_requests_contract_call(
&mut self,

View File

@ -109,7 +109,7 @@ where
/// Current state for block execution.
state: State<DB>,
/// Utility to call system smart contracts.
system_caller: SystemCaller<EvmConfig, ChainSpec>,
system_caller: SystemCaller<Arc<ChainSpec>>,
/// Receipt builder.
receipt_builder:
Arc<dyn OpReceiptBuilder<N::SignedTx, HaltReasonFor<EvmConfig>, Receipt = N::Receipt>>,
@ -130,7 +130,7 @@ where
dyn OpReceiptBuilder<N::SignedTx, HaltReasonFor<EvmConfig>, Receipt = N::Receipt>,
>,
) -> Self {
let system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone());
let system_caller = SystemCaller::new(chain_spec.clone());
Self { state, chain_spec, evm_config, system_caller, receipt_builder }
}
}
@ -158,12 +158,8 @@ where
let mut evm = self.evm_config.evm_for_block(&mut self.state, block.header());
self.system_caller.apply_beacon_root_contract_call(
block.header().timestamp(),
block.header().number(),
block.header().parent_beacon_block_root(),
&mut evm,
)?;
self.system_caller
.apply_beacon_root_contract_call(block.header().parent_beacon_block_root(), &mut evm)?;
// Ensure that the create2deployer is force-deployed at the canyon transition. Optimism
// blocks will always have at least a single transaction in them (the L1 info transaction),

View File

@ -55,7 +55,7 @@ use revm::{
result::{ExecutionResult, ResultAndState},
Block,
},
DatabaseCommit,
Database as _, DatabaseCommit,
};
use std::{fmt::Display, sync::Arc};
use tracing::{debug, trace, warn};
@ -368,19 +368,21 @@ impl<Txs> OpBuilder<'_, Txs> {
let Self { best } = self;
debug!(target: "payload_builder", id=%ctx.payload_id(), parent_header = ?ctx.parent().hash(), parent_number = ctx.parent().number, "building new payload");
let mut evm = ctx.evm_config.evm_with_env(&mut *state, ctx.evm_env.clone());
// 1. apply eip-4788 pre block contract call
ctx.apply_pre_beacon_root_contract_call(state)?;
ctx.apply_pre_beacon_root_contract_call(&mut evm)?;
// 2. ensure create2deployer is force deployed
ctx.ensure_create2_deployer(state)?;
ctx.ensure_create2_deployer(evm.db_mut())?;
// 3. execute sequencer transactions
let mut info = ctx.execute_sequencer_transactions(state)?;
let mut info = ctx.execute_sequencer_transactions(&mut evm)?;
// 4. if mem pool transactions are requested we execute them
if !ctx.attributes().no_tx_pool {
let best_txs = best(ctx.best_transaction_attributes());
if ctx.execute_best_transactions(&mut info, state, best_txs)?.is_some() {
if ctx.execute_best_transactions(&mut info, &mut evm, best_txs)?.is_some() {
return Ok(BuildOutcomeKind::Cancelled)
}
@ -391,6 +393,8 @@ impl<Txs> OpBuilder<'_, Txs> {
}
}
drop(evm);
// merge all transitions into bundle state, this would apply the withdrawal balance changes
// and 4788 contract call
state.merge_transitions(BundleRetention::Reverts);
@ -822,19 +826,14 @@ where
N: OpPayloadPrimitives,
{
/// apply eip-4788 pre block contract call
pub fn apply_pre_beacon_root_contract_call<DB>(
pub fn apply_pre_beacon_root_contract_call(
&self,
db: &mut DB,
) -> Result<(), PayloadBuilderError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
SystemCaller::new(self.evm_config.clone(), self.chain_spec.clone())
.pre_block_beacon_root_contract_call(
db,
&self.evm_env,
evm: &mut impl Evm<DB: DatabaseCommit>,
) -> Result<(), PayloadBuilderError> {
SystemCaller::new(&self.chain_spec)
.apply_beacon_root_contract_call(
self.attributes().payload_attributes.parent_beacon_block_root,
evm,
)
.map_err(|err| {
warn!(target: "payload_builder",
@ -886,15 +885,15 @@ where
}
/// Executes all sequencer transactions that are included in the payload attributes.
pub fn execute_sequencer_transactions<DB>(
pub fn execute_sequencer_transactions(
&self,
db: &mut State<DB>,
) -> Result<ExecutionInfo<N>, PayloadBuilderError>
where
DB: Database<Error = ProviderError>,
{
evm: &mut impl Evm<
DB: Database<Error = ProviderError> + DatabaseCommit,
Tx = EvmConfig::TxEnv,
HaltReason = HaltReasonFor<EvmConfig>,
>,
) -> Result<ExecutionInfo<N>, PayloadBuilderError> {
let mut info = ExecutionInfo::with_capacity(self.attributes().transactions.len());
let mut evm = self.evm_config.evm_with_env(&mut *db, self.evm_env.clone());
for sequencer_tx in &self.attributes().transactions {
// A sequencer's block should never contain blob transactions.
@ -920,8 +919,8 @@ where
let depositor_nonce = (self.is_regolith_active() && sequencer_tx.is_deposit())
.then(|| {
evm.db_mut()
.load_cache_account(sequencer_tx.signer())
.map(|acc| acc.account_info().unwrap_or_default().nonce)
.basic(sequencer_tx.signer())
.map(|acc| acc.unwrap_or_default().nonce)
})
.transpose()
.map_err(|_| {
@ -932,7 +931,7 @@ where
let tx_env = self.evm_config.tx_env(sequencer_tx.tx(), sequencer_tx.signer());
let ResultAndState { result, state } = match evm.transact(tx_env) {
let ResultAndState { result, state: _ } = match evm.transact_commit(tx_env) {
Ok(res) => res,
Err(err) => {
if err.is_invalid_tx_err() {
@ -944,9 +943,6 @@ where
}
};
// commit changes
evm.db_mut().commit(state);
let gas_used = result.gas_used();
// add gas used by the transaction to cumulative gas used, before creating the receipt
@ -971,24 +967,23 @@ where
/// Executes the given best transactions and updates the execution info.
///
/// Returns `Ok(Some(())` if the job was cancelled.
pub fn execute_best_transactions<DB>(
pub fn execute_best_transactions(
&self,
info: &mut ExecutionInfo<N>,
db: &mut State<DB>,
evm: &mut impl Evm<
DB: DatabaseCommit,
Tx = EvmConfig::TxEnv,
HaltReason = HaltReasonFor<EvmConfig>,
>,
mut best_txs: impl PayloadTransactions<
Transaction: PoolTransaction<Consensus = EvmConfig::Transaction>,
>,
) -> Result<Option<()>, PayloadBuilderError>
where
DB: Database<Error = ProviderError>,
{
) -> Result<Option<()>, PayloadBuilderError> {
let block_gas_limit = self.block_gas_limit();
let block_da_limit = self.da_config.max_da_block_size();
let tx_da_limit = self.da_config.max_da_tx_size();
let base_fee = self.base_fee();
let mut evm = self.evm_config.evm_with_env(&mut *db, self.evm_env.clone());
while let Some(tx) = best_txs.next(()) {
let tx = tx.into_consensus();
if info.is_tx_over_limits(tx.tx(), block_gas_limit, tx_da_limit, block_da_limit) {
@ -1013,7 +1008,7 @@ where
// Configure the environment for the tx.
let tx_env = self.evm_config.tx_env(tx.tx(), tx.signer());
let ResultAndState { result, state } = match evm.transact(tx_env) {
let ResultAndState { result, state: _ } = match evm.transact_commit(tx_env) {
Ok(res) => res,
Err(err) => {
if let Some(err) = err.as_invalid_tx_err() {
@ -1034,9 +1029,6 @@ where
}
};
// commit changes
evm.db_mut().commit(state);
let gas_used = result.gas_used();
// add gas used by the transaction to cumulative gas used, before creating the

View File

@ -36,7 +36,6 @@ use revm::{
result::{ExecutionResult, ResultAndState},
Block,
},
DatabaseCommit,
};
use std::time::{Duration, Instant};
use tokio::sync::Mutex;
@ -277,10 +276,11 @@ pub trait LoadPendingBlock:
let chain_spec = self.provider().chain_spec();
let mut system_caller = SystemCaller::new(self.evm_config().clone(), chain_spec.clone());
let mut system_caller = SystemCaller::new(chain_spec.clone());
let mut evm = self.evm_config().evm_with_env(&mut db, evm_env.clone());
system_caller
.pre_block_blockhashes_contract_call(&mut db, &evm_env, parent_hash)
.apply_blockhashes_contract_call(parent_hash, &mut evm)
.map_err(|err| EthApiError::Internal(err.into()))?;
let mut results = Vec::new();
@ -337,9 +337,8 @@ 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());
let ResultAndState { result, state } = match evm.transact(tx_env) {
let ResultAndState { result, state: _ } = match evm.transact_commit(tx_env) {
Ok(res) => res,
Err(err) => {
if let Some(err) = err.as_invalid_tx_err() {
@ -361,10 +360,6 @@ pub trait LoadPendingBlock:
return Err(Self::Error::from_evm_err(err));
}
};
// drop evm to release db reference.
drop(evm);
// commit changes
db.commit(state);
// add to the total blob gas used if the transaction successfully executed
if let Some(tx_blob_gas) = tx.blob_gas_used() {
@ -395,6 +390,9 @@ pub trait LoadPendingBlock:
&[],
);
// release db
drop(evm);
// increment account balances for withdrawals
db.increment_balances(balance_increments).map_err(Self::Error::from_eth_err)?;

View File

@ -468,18 +468,13 @@ pub trait Trace:
db: &mut DB,
evm_env: &EvmEnv<<Self::Evm as ConfigureEvmEnv>::Spec>,
) -> Result<(), Self::Error> {
let mut system_caller =
SystemCaller::new(self.evm_config().clone(), self.provider().chain_spec());
// apply relevant system calls
system_caller
.pre_block_beacon_root_contract_call(db, evm_env, block.parent_beacon_block_root())
.map_err(|_| EthApiError::EvmCustom("failed to apply 4788 system call".to_string()))?;
let mut system_caller = SystemCaller::new(self.provider().chain_spec());
// apply relevant system calls
let mut evm = self.evm_config().evm_with_env(db, evm_env.clone());
system_caller
.pre_block_blockhashes_contract_call(db, evm_env, block.parent_hash())
.map_err(|_| {
EthApiError::EvmCustom("failed to apply blockhashes system call".to_string())
})?;
.apply_pre_execution_changes(block.header(), &mut evm)
.map_err(|_| EthApiError::EvmCustom("failed to apply 4788 system call".to_string()))?;
Ok(())
}