reth_evm/system_calls/
eip4788.rs

1//! [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788) system call implementation.
2use alloc::{boxed::Box, string::ToString};
3
4use crate::ConfigureEvm;
5use alloy_eips::eip4788::BEACON_ROOTS_ADDRESS;
6use alloy_primitives::B256;
7use reth_chainspec::EthereumHardforks;
8use reth_execution_errors::{BlockExecutionError, BlockValidationError};
9use revm::{interpreter::Host, Database, Evm};
10use revm_primitives::ResultAndState;
11
12/// Applies the pre-block call to the [EIP-4788] beacon block root contract, using the given block,
13/// chain spec, EVM.
14///
15/// Note: this does not commit the state changes to the database, it only transact the call.
16///
17/// Returns `None` if Cancun is not active or the block is the genesis block, otherwise returns the
18/// result of the call.
19///
20/// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788
21#[inline]
22pub(crate) fn transact_beacon_root_contract_call<EvmConfig, EXT, DB, Spec>(
23    evm_config: &EvmConfig,
24    chain_spec: &Spec,
25    block_timestamp: u64,
26    block_number: u64,
27    parent_beacon_block_root: Option<B256>,
28    evm: &mut Evm<'_, EXT, DB>,
29) -> Result<Option<ResultAndState>, BlockExecutionError>
30where
31    DB: Database,
32    DB::Error: core::fmt::Display,
33    EvmConfig: ConfigureEvm,
34    Spec: EthereumHardforks,
35{
36    if !chain_spec.is_cancun_active_at_timestamp(block_timestamp) {
37        return Ok(None)
38    }
39
40    let parent_beacon_block_root =
41        parent_beacon_block_root.ok_or(BlockValidationError::MissingParentBeaconBlockRoot)?;
42
43    // if the block number is zero (genesis block) then the parent beacon block root must
44    // be 0x0 and no system transaction may occur as per EIP-4788
45    if block_number == 0 {
46        if !parent_beacon_block_root.is_zero() {
47            return Err(BlockValidationError::CancunGenesisParentBeaconBlockRootNotZero {
48                parent_beacon_block_root,
49            }
50            .into())
51        }
52        return Ok(None)
53    }
54
55    // get previous env
56    let previous_env = Box::new(evm.context.env().clone());
57
58    // modify env for pre block call
59    evm_config.fill_tx_env_system_contract_call(
60        &mut evm.context.evm.env,
61        alloy_eips::eip4788::SYSTEM_ADDRESS,
62        BEACON_ROOTS_ADDRESS,
63        parent_beacon_block_root.0.into(),
64    );
65
66    let mut res = match evm.transact() {
67        Ok(res) => res,
68        Err(e) => {
69            evm.context.evm.env = previous_env;
70            return Err(BlockValidationError::BeaconRootContractCall {
71                parent_beacon_block_root: Box::new(parent_beacon_block_root),
72                message: e.to_string(),
73            }
74            .into())
75        }
76    };
77
78    res.state.remove(&alloy_eips::eip4788::SYSTEM_ADDRESS);
79    res.state.remove(&evm.block().coinbase);
80
81    // re-set the previous env
82    evm.context.evm.env = previous_env;
83
84    Ok(Some(res))
85}