Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions crates/ethereum/evm/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
};
Expand Down Expand Up @@ -147,7 +148,7 @@ where
DB::Error: Into<ProviderError> + std::fmt::Display,
{
// apply pre execution changes
apply_beacon_root_contract_call(
apply_beacon_root_contract_call::<EvmConfig, _, _>(
&self.chain_spec,
block.timestamp,
block.number,
Expand Down
41 changes: 40 additions & 1 deletion crates/ethereum/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand Down
29 changes: 20 additions & 9 deletions crates/ethereum/payload/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -114,19 +113,21 @@ 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",
parent_hash=%parent_block.hash(),
%err,
"failed to apply beacon root contract call for empty payload"
);
err
PayloadBuilderError::Internal(err.into())
})?;

// apply eip-2935 blockhashes update
Expand Down Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion crates/evm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
13 changes: 12 additions & 1 deletion crates/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
125 changes: 125 additions & 0 deletions crates/evm/src/system_calls.rs
Original file line number Diff line number Diff line change
@@ -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<EvmConfig, DB>(
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<B256>,
) -> 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::<EvmConfig, _, _>(
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<EvmConfig, EXT, DB>(
chain_spec: &ChainSpec,
block_timestamp: u64,
block_number: u64,
parent_beacon_block_root: Option<B256>,
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(())
}
5 changes: 3 additions & 2 deletions crates/optimism/evm/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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::{
Expand Down Expand Up @@ -121,7 +122,7 @@ where
DB: Database<Error: Into<ProviderError> + std::fmt::Display>,
{
// apply pre execution changes
apply_beacon_root_contract_call(
apply_beacon_root_contract_call::<EvmConfig, _, _>(
&self.chain_spec,
block.timestamp,
block.number,
Expand Down
Loading