use crate::ConfigureEvm;
use alloc::{boxed::Box, sync::Arc};
use alloy_consensus::BlockHeader;
use alloy_eips::{
eip7002::WITHDRAWAL_REQUEST_TYPE, eip7251::CONSOLIDATION_REQUEST_TYPE, eip7685::Requests,
};
use alloy_primitives::Bytes;
use core::fmt::Display;
use reth_chainspec::EthereumHardforks;
use reth_execution_errors::BlockExecutionError;
use revm::{Database, DatabaseCommit, Evm};
use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, EvmState, B256};
mod eip2935;
mod eip4788;
mod eip7002;
mod eip7251;
pub trait OnStateHook {
fn on_state(&mut self, state: &EvmState);
}
impl<F> OnStateHook for F
where
F: FnMut(&EvmState),
{
fn on_state(&mut self, state: &EvmState) {
self(state)
}
}
#[derive(Default, Debug, Clone)]
#[non_exhaustive]
pub struct NoopHook;
impl OnStateHook for NoopHook {
fn on_state(&mut self, _state: &EvmState) {}
}
#[allow(missing_debug_implementations)]
pub struct SystemCaller<EvmConfig, Chainspec> {
evm_config: EvmConfig,
chain_spec: Arc<Chainspec>,
hook: Option<Box<dyn OnStateHook>>,
}
impl<EvmConfig, Chainspec> SystemCaller<EvmConfig, Chainspec> {
pub const fn new(evm_config: EvmConfig, chain_spec: Arc<Chainspec>) -> Self {
Self { evm_config, chain_spec, hook: None }
}
pub fn with_state_hook(&mut self, hook: Option<Box<dyn OnStateHook>>) -> &mut Self {
self.hook = hook;
self
}
pub fn finish(self) {}
}
fn initialize_evm<'a, DB>(
db: &'a mut DB,
initialized_cfg: &'a CfgEnvWithHandlerCfg,
initialized_block_env: &'a BlockEnv,
) -> Evm<'a, (), &'a mut DB>
where
DB: Database,
{
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()
}
impl<EvmConfig, Chainspec> SystemCaller<EvmConfig, Chainspec>
where
EvmConfig: ConfigureEvm,
Chainspec: EthereumHardforks,
{
pub fn apply_pre_execution_changes<DB, Ext, Block>(
&mut self,
block: &Block,
evm: &mut Evm<'_, Ext, DB>,
) -> Result<(), BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
Block: reth_primitives_traits::Block<Header = EvmConfig::Header>,
{
self.apply_blockhashes_contract_call(
block.header().timestamp(),
block.header().number(),
block.header().parent_hash(),
evm,
)?;
self.apply_beacon_root_contract_call(
block.header().timestamp(),
block.header().number(),
block.header().parent_beacon_block_root(),
evm,
)?;
Ok(())
}
pub fn apply_post_execution_changes<DB, Ext>(
&mut self,
evm: &mut Evm<'_, Ext, DB>,
) -> Result<Requests, BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
let mut requests = Requests::default();
let withdrawal_requests = self.apply_withdrawal_requests_contract_call(evm)?;
if !withdrawal_requests.is_empty() {
requests.push_request_with_type(WITHDRAWAL_REQUEST_TYPE, withdrawal_requests);
}
let consolidation_requests = self.apply_consolidation_requests_contract_call(evm)?;
if !consolidation_requests.is_empty() {
requests.push_request_with_type(CONSOLIDATION_REQUEST_TYPE, consolidation_requests);
}
Ok(requests)
}
pub fn pre_block_blockhashes_contract_call<DB>(
&mut self,
db: &mut DB,
initialized_cfg: &CfgEnvWithHandlerCfg,
initialized_block_env: &BlockEnv,
parent_block_hash: B256,
) -> Result<(), BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
let mut evm = initialize_evm(db, initialized_cfg, initialized_block_env);
self.apply_blockhashes_contract_call(
initialized_block_env.timestamp.to(),
initialized_block_env.number.to(),
parent_block_hash,
&mut evm,
)?;
Ok(())
}
pub fn apply_blockhashes_contract_call<DB, Ext>(
&mut self,
timestamp: u64,
block_number: u64,
parent_block_hash: B256,
evm: &mut Evm<'_, Ext, DB>,
) -> Result<(), BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
let result_and_state = eip2935::transact_blockhashes_contract_call(
&self.evm_config,
&self.chain_spec,
timestamp,
block_number,
parent_block_hash,
evm,
)?;
if let Some(res) = result_and_state {
if let Some(ref mut hook) = self.hook {
hook.on_state(&res.state);
}
evm.context.evm.db.commit(res.state);
}
Ok(())
}
pub fn pre_block_beacon_root_contract_call<DB>(
&mut self,
db: &mut DB,
initialized_cfg: &CfgEnvWithHandlerCfg,
initialized_block_env: &BlockEnv,
parent_beacon_block_root: Option<B256>,
) -> Result<(), BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
let mut evm = initialize_evm(db, initialized_cfg, initialized_block_env);
self.apply_beacon_root_contract_call(
initialized_block_env.timestamp.to(),
initialized_block_env.number.to(),
parent_beacon_block_root,
&mut evm,
)?;
Ok(())
}
pub fn apply_beacon_root_contract_call<DB, Ext>(
&mut self,
timestamp: u64,
block_number: u64,
parent_block_hash: Option<B256>,
evm: &mut Evm<'_, Ext, DB>,
) -> Result<(), BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
let result_and_state = eip4788::transact_beacon_root_contract_call(
&self.evm_config,
&self.chain_spec,
timestamp,
block_number,
parent_block_hash,
evm,
)?;
if let Some(res) = result_and_state {
if let Some(ref mut hook) = self.hook {
hook.on_state(&res.state);
}
evm.context.evm.db.commit(res.state);
}
Ok(())
}
pub fn post_block_withdrawal_requests_contract_call<DB>(
&mut self,
db: &mut DB,
initialized_cfg: &CfgEnvWithHandlerCfg,
initialized_block_env: &BlockEnv,
) -> Result<Bytes, BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
let mut evm = initialize_evm(db, initialized_cfg, initialized_block_env);
let result = self.apply_withdrawal_requests_contract_call(&mut evm)?;
Ok(result)
}
pub fn apply_withdrawal_requests_contract_call<DB, Ext>(
&mut self,
evm: &mut Evm<'_, Ext, DB>,
) -> Result<Bytes, BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
let result_and_state =
eip7002::transact_withdrawal_requests_contract_call(&self.evm_config.clone(), evm)?;
if let Some(ref mut hook) = self.hook {
hook.on_state(&result_and_state.state);
}
evm.context.evm.db.commit(result_and_state.state);
eip7002::post_commit(result_and_state.result)
}
pub fn post_block_consolidation_requests_contract_call<DB>(
&mut self,
db: &mut DB,
initialized_cfg: &CfgEnvWithHandlerCfg,
initialized_block_env: &BlockEnv,
) -> Result<Bytes, BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
let mut evm = initialize_evm(db, initialized_cfg, initialized_block_env);
let res = self.apply_consolidation_requests_contract_call(&mut evm)?;
Ok(res)
}
pub fn apply_consolidation_requests_contract_call<DB, Ext>(
&mut self,
evm: &mut Evm<'_, Ext, DB>,
) -> Result<Bytes, BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
let result_and_state =
eip7251::transact_consolidation_requests_contract_call(&self.evm_config.clone(), evm)?;
if let Some(ref mut hook) = self.hook {
hook.on_state(&result_and_state.state);
}
evm.context.evm.db.commit(result_and_state.state);
eip7251::post_commit(result_and_state.result)
}
pub fn on_state(&mut self, state: &EvmState) {
if let Some(ref mut hook) = &mut self.hook {
hook.on_state(state);
}
}
}