chore: move pre_block_beacon_root_contract_call to evm crates (#9244)

This commit is contained in:
joshieDo
2024-07-03 16:54:28 +02:00
committed by GitHub
parent 08fc298e55
commit 7a647f4f1e
17 changed files with 302 additions and 207 deletions

1
Cargo.lock generated
View File

@ -7197,6 +7197,7 @@ dependencies = [
name = "reth-evm" name = "reth-evm"
version = "1.0.0" version = "1.0.0"
dependencies = [ dependencies = [
"alloy-eips",
"auto_impl", "auto_impl",
"futures-util", "futures-util",
"parking_lot 0.12.3", "parking_lot 0.12.3",

View File

@ -11,6 +11,7 @@ use reth_evm::{
BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput, BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput,
BlockExecutorProvider, BlockValidationError, Executor, ProviderError, BlockExecutorProvider, BlockValidationError, Executor, ProviderError,
}, },
system_calls::apply_beacon_root_contract_call,
ConfigureEvm, ConfigureEvm,
}; };
use reth_execution_types::ExecutionOutcome; use reth_execution_types::ExecutionOutcome;
@ -22,8 +23,8 @@ use reth_revm::{
batch::{BlockBatchRecord, BlockExecutorStats}, batch::{BlockBatchRecord, BlockExecutorStats},
db::states::bundle_state::BundleRetention, db::states::bundle_state::BundleRetention,
state_change::{ state_change::{
apply_beacon_root_contract_call, apply_blockhashes_update, apply_blockhashes_update, apply_withdrawal_requests_contract_call,
apply_withdrawal_requests_contract_call, post_block_balance_increments, post_block_balance_increments,
}, },
Evm, State, Evm, State,
}; };
@ -147,7 +148,7 @@ where
DB::Error: Into<ProviderError> + std::fmt::Display, DB::Error: Into<ProviderError> + std::fmt::Display,
{ {
// apply pre execution changes // apply pre execution changes
apply_beacon_root_contract_call( apply_beacon_root_contract_call::<EvmConfig, _, _>(
&self.chain_spec, &self.chain_spec,
block.timestamp, block.timestamp,
block.number, block.number,

View File

@ -16,7 +16,7 @@ use reth_chainspec::{ChainSpec, Head};
use reth_evm::{ConfigureEvm, ConfigureEvmEnv}; use reth_evm::{ConfigureEvm, ConfigureEvmEnv};
use reth_primitives::{transaction::FillTxEnv, Address, Header, TransactionSigned, U256}; use reth_primitives::{transaction::FillTxEnv, Address, Header, TransactionSigned, U256};
use reth_revm::{Database, EvmBuilder}; use reth_revm::{Database, EvmBuilder};
use revm_primitives::{AnalysisKind, CfgEnvWithHandlerCfg, TxEnv}; use revm_primitives::{AnalysisKind, Bytes, CfgEnvWithHandlerCfg, Env, TxEnv, TxKind};
mod config; mod config;
pub use config::{revm_spec, revm_spec_by_timestamp_after_merge}; 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) { fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) {
transaction.fill_tx_env(tx_env, sender); 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 { impl ConfigureEvm for EthEvmConfig {

View File

@ -11,11 +11,10 @@
use reth_basic_payload_builder::{ use reth_basic_payload_builder::{
commit_withdrawals, is_better_payload, post_block_withdrawal_requests_contract_call, commit_withdrawals, is_better_payload, post_block_withdrawal_requests_contract_call,
pre_block_beacon_root_contract_call, BuildArguments, BuildOutcome, PayloadBuilder, BuildArguments, BuildOutcome, PayloadBuilder, PayloadConfig, WithdrawalsOutcome,
PayloadConfig, WithdrawalsOutcome,
}; };
use reth_errors::RethError; 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_evm_ethereum::{eip6110::parse_deposits_from_receipts, EthEvmConfig};
use reth_execution_types::ExecutionOutcome; use reth_execution_types::ExecutionOutcome;
use reth_payload_builder::{ use reth_payload_builder::{
@ -114,11 +113,13 @@ where
// apply eip-4788 pre block contract call // apply eip-4788 pre block contract call
pre_block_beacon_root_contract_call( pre_block_beacon_root_contract_call(
&mut db, &mut db,
self.evm_config.clone(),
&chain_spec, &chain_spec,
block_number,
&initialized_cfg, &initialized_cfg,
&initialized_block_env, &initialized_block_env,
&attributes, block_number,
attributes.timestamp,
attributes.parent_beacon_block_root,
) )
.map_err(|err| { .map_err(|err| {
warn!(target: "payload_builder", warn!(target: "payload_builder",
@ -126,7 +127,7 @@ where
%err, %err,
"failed to apply beacon root contract call for empty payload" "failed to apply beacon root contract call for empty payload"
); );
err PayloadBuilderError::Internal(err.into())
})?; })?;
// apply eip-2935 blockhashes update // apply eip-2935 blockhashes update
@ -288,12 +289,22 @@ where
// apply eip-4788 pre block contract call // apply eip-4788 pre block contract call
pre_block_beacon_root_contract_call( pre_block_beacon_root_contract_call(
&mut db, &mut db,
evm_config.clone(),
&chain_spec, &chain_spec,
block_number,
&initialized_cfg, &initialized_cfg,
&initialized_block_env, &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 eip-2935 blockhashes update
apply_blockhashes_update( apply_blockhashes_update(

View File

@ -21,7 +21,7 @@ reth-storage-errors.workspace = true
reth-execution-types.workspace = true reth-execution-types.workspace = true
revm.workspace = true revm.workspace = true
alloy-eips.workspace = true
auto_impl.workspace = true auto_impl.workspace = true
futures-util.workspace = true futures-util.workspace = true
parking_lot = { workspace = true, optional = true } parking_lot = { workspace = true, optional = true }

View File

@ -19,12 +19,15 @@ use reth_primitives::{
header::block_coinbase, Address, Header, TransactionSigned, TransactionSignedEcRecovered, U256, header::block_coinbase, Address, Header, TransactionSigned, TransactionSignedEcRecovered, U256,
}; };
use revm::{inspector_handle_register, Database, Evm, EvmBuilder, GetInspector}; 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 either;
pub mod execute; pub mod execute;
pub mod noop; pub mod noop;
pub mod provider; pub mod provider;
pub mod system_calls;
#[cfg(any(test, feature = "test-utils"))] #[cfg(any(test, feature = "test-utils"))]
/// test helpers for mocking executor /// 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. /// Fill transaction environment from a [`TransactionSigned`] and the given sender address.
fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, 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 /// Fill [`CfgEnvWithHandlerCfg`] fields according to the chain spec and given header
fn fill_cfg_env( fn fill_cfg_env(
cfg_env: &mut CfgEnvWithHandlerCfg, cfg_env: &mut CfgEnvWithHandlerCfg,

View File

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

View File

@ -7,6 +7,7 @@ use reth_evm::{
BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput, BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput,
BlockExecutorProvider, BlockValidationError, Executor, ProviderError, BlockExecutorProvider, BlockValidationError, Executor, ProviderError,
}, },
system_calls::apply_beacon_root_contract_call,
ConfigureEvm, ConfigureEvm,
}; };
use reth_execution_types::ExecutionOutcome; use reth_execution_types::ExecutionOutcome;
@ -16,7 +17,7 @@ use reth_prune_types::PruneModes;
use reth_revm::{ use reth_revm::{
batch::{BlockBatchRecord, BlockExecutorStats}, batch::{BlockBatchRecord, BlockExecutorStats},
db::states::bundle_state::BundleRetention, db::states::bundle_state::BundleRetention,
state_change::{apply_beacon_root_contract_call, post_block_balance_increments}, state_change::post_block_balance_increments,
Evm, State, Evm, State,
}; };
use revm_primitives::{ use revm_primitives::{
@ -121,7 +122,7 @@ where
DB: Database<Error: Into<ProviderError> + std::fmt::Display>, DB: Database<Error: Into<ProviderError> + std::fmt::Display>,
{ {
// apply pre execution changes // apply pre execution changes
apply_beacon_root_contract_call( apply_beacon_root_contract_call::<EvmConfig, _, _>(
&self.chain_spec, &self.chain_spec,
block.timestamp, block.timestamp,
block.number, block.number,

View File

@ -27,6 +27,7 @@ pub use l1::*;
mod error; mod error;
pub use error::OptimismBlockExecutionError; pub use error::OptimismBlockExecutionError;
use revm_primitives::{Bytes, Env, OptimismFields, TxKind};
/// Optimism-related EVM configuration. /// Optimism-related EVM configuration.
#[derive(Debug, Default, Clone, Copy)] #[derive(Debug, Default, Clone, Copy)]
@ -38,6 +39,49 @@ impl ConfigureEvmEnv for OptimismEvmConfig {
transaction.fill_tx_env(tx_env, sender); 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( fn fill_cfg_env(
cfg_env: &mut CfgEnvWithHandlerCfg, cfg_env: &mut CfgEnvWithHandlerCfg,
chain_spec: &ChainSpec, chain_spec: &ChainSpec,

View File

@ -6,7 +6,7 @@ use crate::{
}; };
use reth_basic_payload_builder::*; use reth_basic_payload_builder::*;
use reth_chainspec::{ChainSpec, EthereumHardforks, OptimismHardfork}; 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_execution_types::ExecutionOutcome;
use reth_payload_builder::error::PayloadBuilderError; use reth_payload_builder::error::PayloadBuilderError;
use reth_primitives::{ use reth_primitives::{
@ -125,11 +125,13 @@ where
// apply eip-4788 pre block contract call // apply eip-4788 pre block contract call
pre_block_beacon_root_contract_call( pre_block_beacon_root_contract_call(
&mut db, &mut db,
self.evm_config.clone(),
&chain_spec, &chain_spec,
block_number,
&initialized_cfg, &initialized_cfg,
&initialized_block_env, &initialized_block_env,
&attributes, block_number,
attributes.payload_attributes.timestamp,
attributes.payload_attributes.parent_beacon_block_root,
) )
.map_err(|err| { .map_err(|err| {
warn!(target: "payload_builder", warn!(target: "payload_builder",
@ -137,7 +139,7 @@ where
%err, %err,
"failed to apply beacon root contract call for empty payload" "failed to apply beacon root contract call for empty payload"
); );
err PayloadBuilderError::Internal(err.into())
})?; })?;
let WithdrawalsOutcome { withdrawals_root, withdrawals } = commit_withdrawals( let WithdrawalsOutcome { withdrawals_root, withdrawals } = commit_withdrawals(
@ -286,12 +288,22 @@ where
// apply eip-4788 pre block contract call // apply eip-4788 pre block contract call
pre_block_beacon_root_contract_call( pre_block_beacon_root_contract_call(
&mut db, &mut db,
evm_config.clone(),
&chain_spec, &chain_spec,
block_number,
&initialized_cfg, &initialized_cfg,
&initialized_block_env, &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 // 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), // blocks will always have at least a single transaction in them (the L1 info transaction),

View File

@ -25,8 +25,7 @@ use reth_provider::{
BlockReaderIdExt, BlockSource, CanonStateNotification, ProviderError, StateProviderFactory, BlockReaderIdExt, BlockSource, CanonStateNotification, ProviderError, StateProviderFactory,
}; };
use reth_revm::state_change::{ use reth_revm::state_change::{
apply_beacon_root_contract_call, apply_withdrawal_requests_contract_call, apply_withdrawal_requests_contract_call, post_block_withdrawals_balance_increments,
post_block_withdrawals_balance_increments,
}; };
use reth_tasks::TaskSpawner; use reth_tasks::TaskSpawner;
use reth_transaction_pool::TransactionPool; use reth_transaction_pool::TransactionPool;
@ -923,49 +922,6 @@ pub fn commit_withdrawals<DB: Database<Error = ProviderError>>(
}) })
} }
/// 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: Database + DatabaseCommit, Attributes>(
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. /// 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 /// This constructs a new [Evm] with the given DB, and environment

View File

@ -1,34 +1,15 @@
use crate::{ use crate::{
revm_primitives::{Env, TxEnv}, 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")] #[cfg(feature = "optimism")]
use revm_primitives::OptimismFields; use revm_primitives::OptimismFields;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use alloc::vec::Vec; 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. /// Fill transaction environment with the EIP-7002 withdrawal requests contract message data.
// //
/// This requirement for the withdrawal requests contract call defined by /// This requirement for the withdrawal requests contract call defined by

View File

@ -7,11 +7,8 @@ use reth_chainspec::{ChainSpec, EthereumHardforks};
use reth_consensus_common::calc; use reth_consensus_common::calc;
use reth_execution_errors::{BlockExecutionError, BlockValidationError}; use reth_execution_errors::{BlockExecutionError, BlockValidationError};
use reth_primitives::{ use reth_primitives::{
revm::env::{ revm::env::fill_tx_env_with_withdrawal_requests_contract_call, Address, Block, Request,
fill_tx_env_with_beacon_root_contract_call, Withdrawal, Withdrawals, B256, U256,
fill_tx_env_with_withdrawal_requests_contract_call,
},
Address, Block, Request, Withdrawal, Withdrawals, B256, U256,
}; };
use reth_storage_errors::provider::ProviderError; use reth_storage_errors::provider::ProviderError;
use revm::{ use revm::{
@ -139,72 +136,6 @@ fn eip2935_block_hash_slot<DB: Database<Error: Into<ProviderError>>>(
Ok((slot, EvmStorageSlot::new_changed(current_hash, block_hash.into()))) 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<EXT, DB: Database + DatabaseCommit>(
chain_spec: &ChainSpec,
block_timestamp: u64,
block_number: u64,
parent_beacon_block_root: Option<B256>,
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 /// Returns a map of addresses to their balance increments if the Shanghai hardfork is active at the
/// given timestamp. /// given timestamp.
/// ///

View File

@ -5,7 +5,7 @@ use std::time::{Duration, Instant};
use futures::Future; use futures::Future;
use reth_chainspec::EthereumHardforks; 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_execution_types::ExecutionOutcome;
use reth_primitives::{ use reth_primitives::{
constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE, EMPTY_ROOT_HASH}, 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, database::StateProviderDatabase, state_change::post_block_withdrawals_balance_increments,
}; };
use reth_rpc_eth_types::{ use reth_rpc_eth_types::{
pending_block::{pre_block_beacon_root_contract_call, pre_block_blockhashes_update}, pending_block::pre_block_blockhashes_update, EthApiError, EthResult, PendingBlock,
EthApiError, EthResult, PendingBlock, PendingBlockEnv, PendingBlockEnvOrigin, PendingBlockEnv, PendingBlockEnvOrigin,
}; };
use reth_transaction_pool::{BestTransactionsAttributes, TransactionPool}; use reth_transaction_pool::{BestTransactionsAttributes, TransactionPool};
use revm::{db::states::bundle_state::BundleRetention, DatabaseCommit, State}; use revm::{db::states::bundle_state::BundleRetention, DatabaseCommit, State};
@ -235,12 +235,15 @@ pub trait LoadPendingBlock {
// parent beacon block root // parent beacon block root
pre_block_beacon_root_contract_call( pre_block_beacon_root_contract_call(
&mut db, &mut db,
self.evm_config().clone(),
chain_spec.as_ref(), chain_spec.as_ref(),
block_number,
&cfg, &cfg,
&block_env, &block_env,
block_number,
block_env.timestamp.to::<u64>(),
origin.header().parent_beacon_block_root, origin.header().parent_beacon_block_root,
)?; )
.map_err(|err| EthApiError::Internal(err.into()))?;
origin.header().parent_beacon_block_root origin.header().parent_beacon_block_root
} else { } else {
None None

View File

@ -8,10 +8,10 @@ use derive_more::Constructor;
use reth_chainspec::ChainSpec; use reth_chainspec::ChainSpec;
use reth_primitives::{BlockId, BlockNumberOrTag, SealedBlockWithSenders, SealedHeader, B256}; use reth_primitives::{BlockId, BlockNumberOrTag, SealedBlockWithSenders, SealedHeader, B256};
use reth_provider::ProviderError; 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::{ use revm_primitives::{
db::{Database, DatabaseCommit}, db::{Database, DatabaseCommit},
BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, BlockEnv, CfgEnvWithHandlerCfg,
}; };
use super::{EthApiError, EthResult}; use super::{EthApiError, EthResult};
@ -27,45 +27,6 @@ pub struct PendingBlockEnv {
pub origin: PendingBlockEnvOrigin, 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: Database + DatabaseCommit>(
db: &mut DB,
chain_spec: &ChainSpec,
block_number: u64,
initialized_cfg: &CfgEnvWithHandlerCfg,
initialized_block_env: &BlockEnv,
parent_beacon_block_root: Option<B256>,
) -> 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::<u64>(),
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. /// 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 /// This constructs a new [Evm](revm::Evm) with the given DB, and environment

View File

@ -93,6 +93,15 @@ impl ConfigureEvmEnv for MyEvmConfig {
fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) { fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) {
EthEvmConfig::default().fill_tx_env(tx_env, transaction, sender) 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 { impl ConfigureEvm for MyEvmConfig {

View File

@ -145,6 +145,15 @@ impl ConfigureEvmEnv for MyEvmConfig {
) { ) {
EthEvmConfig::fill_cfg_env(cfg_env, chain_spec, header, total_difficulty) 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 { impl ConfigureEvm for MyEvmConfig {