diff --git a/Cargo.lock b/Cargo.lock index 99cfc38d7..00d261324 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7197,6 +7197,7 @@ dependencies = [ name = "reth-evm" version = "1.0.0" dependencies = [ + "alloy-eips", "auto_impl", "futures-util", "parking_lot 0.12.3", diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index 084bb412b..b4da027f8 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -11,6 +11,7 @@ use reth_evm::{ BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput, BlockExecutorProvider, BlockValidationError, Executor, ProviderError, }, + system_calls::apply_beacon_root_contract_call, ConfigureEvm, }; use reth_execution_types::ExecutionOutcome; @@ -22,8 +23,8 @@ use reth_revm::{ batch::{BlockBatchRecord, BlockExecutorStats}, db::states::bundle_state::BundleRetention, state_change::{ - apply_beacon_root_contract_call, apply_blockhashes_update, - apply_withdrawal_requests_contract_call, post_block_balance_increments, + apply_blockhashes_update, apply_withdrawal_requests_contract_call, + post_block_balance_increments, }, Evm, State, }; @@ -147,7 +148,7 @@ where DB::Error: Into + std::fmt::Display, { // apply pre execution changes - apply_beacon_root_contract_call( + apply_beacon_root_contract_call::( &self.chain_spec, block.timestamp, block.number, diff --git a/crates/ethereum/evm/src/lib.rs b/crates/ethereum/evm/src/lib.rs index 86386ceb5..1c0b6b83b 100644 --- a/crates/ethereum/evm/src/lib.rs +++ b/crates/ethereum/evm/src/lib.rs @@ -16,7 +16,7 @@ use reth_chainspec::{ChainSpec, Head}; use reth_evm::{ConfigureEvm, ConfigureEvmEnv}; use reth_primitives::{transaction::FillTxEnv, Address, Header, TransactionSigned, U256}; use reth_revm::{Database, EvmBuilder}; -use revm_primitives::{AnalysisKind, CfgEnvWithHandlerCfg, TxEnv}; +use revm_primitives::{AnalysisKind, Bytes, CfgEnvWithHandlerCfg, Env, TxEnv, TxKind}; mod config; pub use config::{revm_spec, revm_spec_by_timestamp_after_merge}; @@ -61,6 +61,45 @@ impl ConfigureEvmEnv for EthEvmConfig { fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) { transaction.fill_tx_env(tx_env, sender); } + + fn fill_tx_env_system_contract_call( + 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; + } } impl ConfigureEvm for EthEvmConfig { diff --git a/crates/ethereum/payload/src/lib.rs b/crates/ethereum/payload/src/lib.rs index 84d43b33c..dabaedeeb 100644 --- a/crates/ethereum/payload/src/lib.rs +++ b/crates/ethereum/payload/src/lib.rs @@ -11,11 +11,10 @@ use reth_basic_payload_builder::{ commit_withdrawals, is_better_payload, post_block_withdrawal_requests_contract_call, - pre_block_beacon_root_contract_call, BuildArguments, BuildOutcome, PayloadBuilder, - PayloadConfig, WithdrawalsOutcome, + BuildArguments, BuildOutcome, PayloadBuilder, PayloadConfig, WithdrawalsOutcome, }; use reth_errors::RethError; -use reth_evm::ConfigureEvm; +use reth_evm::{system_calls::pre_block_beacon_root_contract_call, ConfigureEvm}; use reth_evm_ethereum::{eip6110::parse_deposits_from_receipts, EthEvmConfig}; use reth_execution_types::ExecutionOutcome; use reth_payload_builder::{ @@ -114,11 +113,13 @@ where // apply eip-4788 pre block contract call pre_block_beacon_root_contract_call( &mut db, + self.evm_config.clone(), &chain_spec, - block_number, &initialized_cfg, &initialized_block_env, - &attributes, + block_number, + attributes.timestamp, + attributes.parent_beacon_block_root, ) .map_err(|err| { warn!(target: "payload_builder", @@ -126,7 +127,7 @@ where %err, "failed to apply beacon root contract call for empty payload" ); - err + PayloadBuilderError::Internal(err.into()) })?; // apply eip-2935 blockhashes update @@ -288,12 +289,22 @@ where // apply eip-4788 pre block contract call pre_block_beacon_root_contract_call( &mut db, + evm_config.clone(), &chain_spec, - block_number, &initialized_cfg, &initialized_block_env, - &attributes, - )?; + block_number, + attributes.timestamp, + attributes.parent_beacon_block_root, + ) + .map_err(|err| { + warn!(target: "payload_builder", + parent_hash=%parent_block.hash(), + %err, + "failed to apply beacon root contract call for empty payload" + ); + PayloadBuilderError::Internal(err.into()) + })?; // apply eip-2935 blockhashes update apply_blockhashes_update( diff --git a/crates/evm/Cargo.toml b/crates/evm/Cargo.toml index 23f7e1b25..ab3383719 100644 --- a/crates/evm/Cargo.toml +++ b/crates/evm/Cargo.toml @@ -21,7 +21,7 @@ reth-storage-errors.workspace = true reth-execution-types.workspace = true revm.workspace = true - +alloy-eips.workspace = true auto_impl.workspace = true futures-util.workspace = true parking_lot = { workspace = true, optional = true } diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index e5e9dbdf7..325eb7e29 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -19,12 +19,15 @@ use reth_primitives::{ header::block_coinbase, Address, Header, TransactionSigned, TransactionSignedEcRecovered, U256, }; use revm::{inspector_handle_register, Database, Evm, EvmBuilder, GetInspector}; -use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, SpecId, TxEnv}; +use revm_primitives::{ + BlockEnv, Bytes, CfgEnvWithHandlerCfg, Env, EnvWithHandlerCfg, SpecId, TxEnv, +}; pub mod either; pub mod execute; pub mod noop; pub mod provider; +pub mod system_calls; #[cfg(any(test, feature = "test-utils"))] /// test helpers for mocking executor @@ -117,6 +120,14 @@ pub trait ConfigureEvmEnv: Send + Sync + Unpin + Clone + 'static { /// Fill transaction environment from a [`TransactionSigned`] and the given sender address. fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address); + /// Fill transaction environment with a system contract call. + fn fill_tx_env_system_contract_call( + env: &mut Env, + caller: Address, + contract: Address, + data: Bytes, + ); + /// Fill [`CfgEnvWithHandlerCfg`] fields according to the chain spec and given header fn fill_cfg_env( cfg_env: &mut CfgEnvWithHandlerCfg, diff --git a/crates/evm/src/system_calls.rs b/crates/evm/src/system_calls.rs new file mode 100644 index 000000000..2e247ba38 --- /dev/null +++ b/crates/evm/src/system_calls.rs @@ -0,0 +1,125 @@ +//! System contract call functions. + +use alloy_eips::eip4788::BEACON_ROOTS_ADDRESS; +use reth_chainspec::{ChainSpec, EthereumHardforks}; +use reth_execution_errors::{BlockExecutionError, BlockValidationError}; +use revm::{interpreter::Host, Database, DatabaseCommit, Evm}; +use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, B256}; + +use crate::ConfigureEvm; + +/// Apply the [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788) pre block contract call. +/// +/// This constructs a new [Evm] with the given DB, and environment +/// ([`CfgEnvWithHandlerCfg`] and [`BlockEnv`]) to execute the pre block contract call. +/// +/// This uses [`apply_beacon_root_contract_call`] to ultimately apply the beacon root contract state +/// change. +#[allow(clippy::too_many_arguments)] +pub fn pre_block_beacon_root_contract_call( + db: &mut DB, + _emv_config: EvmConfig, + chain_spec: &ChainSpec, + initialized_cfg: &CfgEnvWithHandlerCfg, + initialized_block_env: &BlockEnv, + block_number: u64, + block_timestamp: u64, + parent_beacon_block_root: Option, +) -> Result<(), BlockExecutionError> +where + DB: Database + DatabaseCommit, + DB::Error: std::fmt::Display, + EvmConfig: ConfigureEvm, +{ + // apply pre-block EIP-4788 contract call + let mut evm_pre_block = Evm::builder() + .with_db(db) + .with_env_with_handler_cfg(EnvWithHandlerCfg::new_with_cfg_env( + initialized_cfg.clone(), + initialized_block_env.clone(), + Default::default(), + )) + .build(); + + // initialize a block from the env, because the pre block call needs the block itself + apply_beacon_root_contract_call::( + chain_spec, + block_timestamp, + block_number, + parent_beacon_block_root, + &mut evm_pre_block, + ) +} + +/// Applies the pre-block call to the [EIP-4788] beacon block root contract, using the given block, +/// [`ChainSpec`], EVM. +/// +/// If Cancun is not activated or the block is the genesis block, then this is a no-op, and no +/// state changes are made. +/// +/// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 +#[inline] +pub fn apply_beacon_root_contract_call( + chain_spec: &ChainSpec, + block_timestamp: u64, + block_number: u64, + parent_beacon_block_root: Option, + evm: &mut Evm<'_, EXT, DB>, +) -> Result<(), BlockExecutionError> +where + DB: Database + DatabaseCommit, + DB::Error: core::fmt::Display, + EvmConfig: ConfigureEvm, +{ + if !chain_spec.is_cancun_active_at_timestamp(block_timestamp) { + return Ok(()) + } + + let parent_beacon_block_root = + parent_beacon_block_root.ok_or(BlockValidationError::MissingParentBeaconBlockRoot)?; + + // 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 parent_beacon_block_root != B256::ZERO { + return Err(BlockValidationError::CancunGenesisParentBeaconBlockRootNotZero { + parent_beacon_block_root, + } + .into()) + } + return Ok(()) + } + + // get previous env + let previous_env = Box::new(evm.context.env().clone()); + + // modify env for pre block call + EvmConfig::fill_tx_env_system_contract_call( + &mut evm.context.evm.env, + alloy_eips::eip4788::SYSTEM_ADDRESS, + BEACON_ROOTS_ADDRESS, + parent_beacon_block_root.0.into(), + ); + + let mut state = match evm.transact() { + Ok(res) => res.state, + 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(), + } + .into()) + } + }; + + state.remove(&alloy_eips::eip4788::SYSTEM_ADDRESS); + state.remove(&evm.block().coinbase); + + evm.context.evm.db.commit(state); + + // re-set the previous env + evm.context.evm.env = previous_env; + + Ok(()) +} diff --git a/crates/optimism/evm/src/execute.rs b/crates/optimism/evm/src/execute.rs index de4fc071e..2c797c786 100644 --- a/crates/optimism/evm/src/execute.rs +++ b/crates/optimism/evm/src/execute.rs @@ -7,6 +7,7 @@ use reth_evm::{ BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput, BlockExecutorProvider, BlockValidationError, Executor, ProviderError, }, + system_calls::apply_beacon_root_contract_call, ConfigureEvm, }; use reth_execution_types::ExecutionOutcome; @@ -16,7 +17,7 @@ use reth_prune_types::PruneModes; use reth_revm::{ batch::{BlockBatchRecord, BlockExecutorStats}, db::states::bundle_state::BundleRetention, - state_change::{apply_beacon_root_contract_call, post_block_balance_increments}, + state_change::post_block_balance_increments, Evm, State, }; use revm_primitives::{ @@ -121,7 +122,7 @@ where DB: Database + std::fmt::Display>, { // apply pre execution changes - apply_beacon_root_contract_call( + apply_beacon_root_contract_call::( &self.chain_spec, block.timestamp, block.number, diff --git a/crates/optimism/evm/src/lib.rs b/crates/optimism/evm/src/lib.rs index 2d1b0afb0..61f6838dd 100644 --- a/crates/optimism/evm/src/lib.rs +++ b/crates/optimism/evm/src/lib.rs @@ -27,6 +27,7 @@ pub use l1::*; mod error; pub use error::OptimismBlockExecutionError; +use revm_primitives::{Bytes, Env, OptimismFields, TxKind}; /// Optimism-related EVM configuration. #[derive(Debug, Default, Clone, Copy)] @@ -38,6 +39,49 @@ impl ConfigureEvmEnv for OptimismEvmConfig { transaction.fill_tx_env(tx_env, sender); } + fn fill_tx_env_system_contract_call( + env: &mut Env, + caller: Address, + contract: Address, + data: Bytes, + ) { + env.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, + optimism: OptimismFields { + source_hash: None, + mint: None, + is_system_transaction: Some(false), + // The L1 fee is not charged for the EIP-4788 transaction, submit zero bytes for the + // enveloped tx size. + enveloped_tx: Some(Bytes::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; + } + fn fill_cfg_env( cfg_env: &mut CfgEnvWithHandlerCfg, chain_spec: &ChainSpec, diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index 9bb8d3cdc..4df6c67d2 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -6,7 +6,7 @@ use crate::{ }; use reth_basic_payload_builder::*; use reth_chainspec::{ChainSpec, EthereumHardforks, OptimismHardfork}; -use reth_evm::ConfigureEvm; +use reth_evm::{system_calls::pre_block_beacon_root_contract_call, ConfigureEvm}; use reth_execution_types::ExecutionOutcome; use reth_payload_builder::error::PayloadBuilderError; use reth_primitives::{ @@ -125,11 +125,13 @@ where // apply eip-4788 pre block contract call pre_block_beacon_root_contract_call( &mut db, + self.evm_config.clone(), &chain_spec, - block_number, &initialized_cfg, &initialized_block_env, - &attributes, + block_number, + attributes.payload_attributes.timestamp, + attributes.payload_attributes.parent_beacon_block_root, ) .map_err(|err| { warn!(target: "payload_builder", @@ -137,7 +139,7 @@ where %err, "failed to apply beacon root contract call for empty payload" ); - err + PayloadBuilderError::Internal(err.into()) })?; let WithdrawalsOutcome { withdrawals_root, withdrawals } = commit_withdrawals( @@ -286,12 +288,22 @@ where // apply eip-4788 pre block contract call pre_block_beacon_root_contract_call( &mut db, + evm_config.clone(), &chain_spec, - block_number, &initialized_cfg, &initialized_block_env, - &attributes, - )?; + block_number, + attributes.payload_attributes.timestamp, + attributes.payload_attributes.parent_beacon_block_root, + ) + .map_err(|err| { + warn!(target: "payload_builder", + parent_hash=%parent_block.hash(), + %err, + "failed to apply beacon root contract call for empty payload" + ); + PayloadBuilderError::Internal(err.into()) + })?; // 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), diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index d677ce842..d7c58d3bd 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -25,8 +25,7 @@ use reth_provider::{ BlockReaderIdExt, BlockSource, CanonStateNotification, ProviderError, StateProviderFactory, }; use reth_revm::state_change::{ - apply_beacon_root_contract_call, apply_withdrawal_requests_contract_call, - post_block_withdrawals_balance_increments, + apply_withdrawal_requests_contract_call, post_block_withdrawals_balance_increments, }; use reth_tasks::TaskSpawner; use reth_transaction_pool::TransactionPool; @@ -923,49 +922,6 @@ pub fn commit_withdrawals>( }) } -/// Apply the [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788) pre block contract call. -/// -/// This constructs a new [Evm] with the given DB, and environment -/// ([`CfgEnvWithHandlerCfg`] and [`BlockEnv`]) to execute the pre block contract call. -/// -/// The parent beacon block root used for the call is gathered from the given -/// [`PayloadBuilderAttributes`]. -/// -/// This uses [`apply_beacon_root_contract_call`] to ultimately apply the beacon root contract state -/// change. -pub fn pre_block_beacon_root_contract_call( - db: &mut DB, - chain_spec: &ChainSpec, - block_number: u64, - initialized_cfg: &CfgEnvWithHandlerCfg, - initialized_block_env: &BlockEnv, - attributes: &Attributes, -) -> Result<(), PayloadBuilderError> -where - DB::Error: std::fmt::Display, - Attributes: PayloadBuilderAttributes, -{ - // apply pre-block EIP-4788 contract call - let mut evm_pre_block = Evm::builder() - .with_db(db) - .with_env_with_handler_cfg(EnvWithHandlerCfg::new_with_cfg_env( - initialized_cfg.clone(), - initialized_block_env.clone(), - Default::default(), - )) - .build(); - - // initialize a block from the env, because the pre block call needs the block itself - apply_beacon_root_contract_call( - chain_spec, - attributes.timestamp(), - block_number, - attributes.parent_beacon_block_root(), - &mut evm_pre_block, - ) - .map_err(|err| PayloadBuilderError::Internal(err.into())) -} - /// Apply the [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) post block contract call. /// /// This constructs a new [Evm] with the given DB, and environment diff --git a/crates/primitives/src/revm/env.rs b/crates/primitives/src/revm/env.rs index 5c7ae7b0d..81fee7d8c 100644 --- a/crates/primitives/src/revm/env.rs +++ b/crates/primitives/src/revm/env.rs @@ -1,34 +1,15 @@ use crate::{ revm_primitives::{Env, TxEnv}, - Address, Bytes, TxKind, B256, U256, + Address, Bytes, TxKind, U256, }; -use alloy_eips::{eip4788::BEACON_ROOTS_ADDRESS, eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS}; +use alloy_eips::eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS; #[cfg(feature = "optimism")] use revm_primitives::OptimismFields; #[cfg(not(feature = "std"))] use alloc::vec::Vec; -/// Fill transaction environment with the EIP-4788 system contract message data. -/// -/// This requirements for the beacon root contract call defined by -/// [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788) are: -/// -/// At the start of processing any execution block where `block.timestamp >= FORK_TIMESTAMP` (i.e. -/// before processing any transactions), call [`BEACON_ROOTS_ADDRESS`] as -/// [`SYSTEM_ADDRESS`](alloy_eips::eip4788::SYSTEM_ADDRESS) with the 32-byte input of -/// `header.parent_beacon_block_root`. This will trigger the `set()` routine of the beacon roots -/// contract. -pub fn fill_tx_env_with_beacon_root_contract_call(env: &mut Env, parent_beacon_block_root: B256) { - fill_tx_env_with_system_contract_call( - env, - alloy_eips::eip4788::SYSTEM_ADDRESS, - BEACON_ROOTS_ADDRESS, - parent_beacon_block_root.0.into(), - ); -} - /// Fill transaction environment with the EIP-7002 withdrawal requests contract message data. // /// This requirement for the withdrawal requests contract call defined by diff --git a/crates/revm/src/state_change.rs b/crates/revm/src/state_change.rs index 5fad6ae42..01098182f 100644 --- a/crates/revm/src/state_change.rs +++ b/crates/revm/src/state_change.rs @@ -7,11 +7,8 @@ use reth_chainspec::{ChainSpec, EthereumHardforks}; use reth_consensus_common::calc; use reth_execution_errors::{BlockExecutionError, BlockValidationError}; use reth_primitives::{ - revm::env::{ - fill_tx_env_with_beacon_root_contract_call, - fill_tx_env_with_withdrawal_requests_contract_call, - }, - Address, Block, Request, Withdrawal, Withdrawals, B256, U256, + revm::env::fill_tx_env_with_withdrawal_requests_contract_call, Address, Block, Request, + Withdrawal, Withdrawals, B256, U256, }; use reth_storage_errors::provider::ProviderError; use revm::{ @@ -139,72 +136,6 @@ fn eip2935_block_hash_slot>>( Ok((slot, EvmStorageSlot::new_changed(current_hash, block_hash.into()))) } -/// Applies the pre-block call to the [EIP-4788] beacon block root contract, using the given block, -/// [`ChainSpec`], EVM. -/// -/// If Cancun is not activated or the block is the genesis block, then this is a no-op, and no -/// state changes are made. -/// -/// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 -#[inline] -pub fn apply_beacon_root_contract_call( - chain_spec: &ChainSpec, - block_timestamp: u64, - block_number: u64, - parent_beacon_block_root: Option, - evm: &mut Evm<'_, EXT, DB>, -) -> Result<(), BlockExecutionError> -where - DB::Error: core::fmt::Display, -{ - if !chain_spec.is_cancun_active_at_timestamp(block_timestamp) { - return Ok(()) - } - - let parent_beacon_block_root = - parent_beacon_block_root.ok_or(BlockValidationError::MissingParentBeaconBlockRoot)?; - - // 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 parent_beacon_block_root != B256::ZERO { - return Err(BlockValidationError::CancunGenesisParentBeaconBlockRootNotZero { - parent_beacon_block_root, - } - .into()) - } - return Ok(()) - } - - // get previous env - let previous_env = Box::new(evm.context.env().clone()); - - // modify env for pre block call - fill_tx_env_with_beacon_root_contract_call(&mut evm.context.evm.env, parent_beacon_block_root); - - let mut state = match evm.transact() { - Ok(res) => res.state, - 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(), - } - .into()) - } - }; - - state.remove(&alloy_eips::eip4788::SYSTEM_ADDRESS); - state.remove(&evm.block().coinbase); - - evm.context.evm.db.commit(state); - - // re-set the previous env - evm.context.evm.env = previous_env; - - Ok(()) -} - /// Returns a map of addresses to their balance increments if the Shanghai hardfork is active at the /// given timestamp. /// diff --git a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs index 8f3e38817..b421d7b10 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs @@ -5,7 +5,7 @@ use std::time::{Duration, Instant}; use futures::Future; use reth_chainspec::EthereumHardforks; -use reth_evm::{ConfigureEvm, ConfigureEvmEnv}; +use reth_evm::{system_calls::pre_block_beacon_root_contract_call, ConfigureEvm, ConfigureEvmEnv}; use reth_execution_types::ExecutionOutcome; use reth_primitives::{ constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE, EMPTY_ROOT_HASH}, @@ -25,8 +25,8 @@ use reth_revm::{ database::StateProviderDatabase, state_change::post_block_withdrawals_balance_increments, }; use reth_rpc_eth_types::{ - pending_block::{pre_block_beacon_root_contract_call, pre_block_blockhashes_update}, - EthApiError, EthResult, PendingBlock, PendingBlockEnv, PendingBlockEnvOrigin, + pending_block::pre_block_blockhashes_update, EthApiError, EthResult, PendingBlock, + PendingBlockEnv, PendingBlockEnvOrigin, }; use reth_transaction_pool::{BestTransactionsAttributes, TransactionPool}; use revm::{db::states::bundle_state::BundleRetention, DatabaseCommit, State}; @@ -235,12 +235,15 @@ pub trait LoadPendingBlock { // parent beacon block root pre_block_beacon_root_contract_call( &mut db, + self.evm_config().clone(), chain_spec.as_ref(), - block_number, &cfg, &block_env, + block_number, + block_env.timestamp.to::(), origin.header().parent_beacon_block_root, - )?; + ) + .map_err(|err| EthApiError::Internal(err.into()))?; origin.header().parent_beacon_block_root } else { None diff --git a/crates/rpc/rpc-eth-types/src/pending_block.rs b/crates/rpc/rpc-eth-types/src/pending_block.rs index 7e27a9d6b..64dd2aeb5 100644 --- a/crates/rpc/rpc-eth-types/src/pending_block.rs +++ b/crates/rpc/rpc-eth-types/src/pending_block.rs @@ -8,10 +8,10 @@ use derive_more::Constructor; use reth_chainspec::ChainSpec; use reth_primitives::{BlockId, BlockNumberOrTag, SealedBlockWithSenders, SealedHeader, B256}; use reth_provider::ProviderError; -use reth_revm::state_change::{apply_beacon_root_contract_call, apply_blockhashes_update}; +use reth_revm::state_change::apply_blockhashes_update; use revm_primitives::{ db::{Database, DatabaseCommit}, - BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, + BlockEnv, CfgEnvWithHandlerCfg, }; use super::{EthApiError, EthResult}; @@ -27,45 +27,6 @@ pub struct PendingBlockEnv { pub origin: PendingBlockEnvOrigin, } -/// Apply the [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788) pre block contract call. -/// -/// This constructs a new [Evm](revm::Evm) with the given DB, and environment -/// [`CfgEnvWithHandlerCfg`] and [`BlockEnv`] to execute the pre block contract call. -/// -/// This uses [`apply_beacon_root_contract_call`] to ultimately apply the beacon root contract state -/// change. -pub fn pre_block_beacon_root_contract_call( - db: &mut DB, - chain_spec: &ChainSpec, - block_number: u64, - initialized_cfg: &CfgEnvWithHandlerCfg, - initialized_block_env: &BlockEnv, - parent_beacon_block_root: Option, -) -> EthResult<()> -where - DB::Error: fmt::Display, -{ - // apply pre-block EIP-4788 contract call - let mut evm_pre_block = revm::Evm::builder() - .with_db(db) - .with_env_with_handler_cfg(EnvWithHandlerCfg::new_with_cfg_env( - initialized_cfg.clone(), - initialized_block_env.clone(), - Default::default(), - )) - .build(); - - // initialize a block from the env, because the pre block call needs the block itself - apply_beacon_root_contract_call( - chain_spec, - initialized_block_env.timestamp.to::(), - block_number, - parent_beacon_block_root, - &mut evm_pre_block, - ) - .map_err(|err| EthApiError::Internal(err.into())) -} - /// Apply the [EIP-2935](https://eips.ethereum.org/EIPS/eip-2935) pre block state transitions. /// /// This constructs a new [Evm](revm::Evm) with the given DB, and environment diff --git a/examples/custom-evm/src/main.rs b/examples/custom-evm/src/main.rs index 388438857..a9d8058b9 100644 --- a/examples/custom-evm/src/main.rs +++ b/examples/custom-evm/src/main.rs @@ -93,6 +93,15 @@ impl ConfigureEvmEnv for MyEvmConfig { fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) { EthEvmConfig::default().fill_tx_env(tx_env, transaction, sender) } + + fn fill_tx_env_system_contract_call( + env: &mut Env, + caller: Address, + contract: Address, + data: Bytes, + ) { + EthEvmConfig::fill_tx_env_system_contract_call(env, caller, contract, data) + } } impl ConfigureEvm for MyEvmConfig { diff --git a/examples/stateful-precompile/src/main.rs b/examples/stateful-precompile/src/main.rs index adca439d7..538adfaaf 100644 --- a/examples/stateful-precompile/src/main.rs +++ b/examples/stateful-precompile/src/main.rs @@ -145,6 +145,15 @@ impl ConfigureEvmEnv for MyEvmConfig { ) { EthEvmConfig::fill_cfg_env(cfg_env, chain_spec, header, total_difficulty) } + + fn fill_tx_env_system_contract_call( + env: &mut Env, + caller: Address, + contract: Address, + data: Bytes, + ) { + EthEvmConfig::fill_tx_env_system_contract_call(env, caller, contract, data) + } } impl ConfigureEvm for MyEvmConfig {