|
|
|
|
@ -1,158 +1,161 @@
|
|
|
|
|
//! Ethereum block executor.
|
|
|
|
|
//! Ethereum block execution strategy.
|
|
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
|
dao_fork::{DAO_HARDFORK_BENEFICIARY, DAO_HARDKFORK_ACCOUNTS},
|
|
|
|
|
EthEvmConfig,
|
|
|
|
|
};
|
|
|
|
|
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
|
|
|
|
use alloc::{boxed::Box, sync::Arc, vec, vec::Vec};
|
|
|
|
|
use alloy_consensus::Transaction as _;
|
|
|
|
|
use alloy_eips::eip7685::Requests;
|
|
|
|
|
use alloy_primitives::{BlockNumber, U256};
|
|
|
|
|
use core::fmt::Display;
|
|
|
|
|
use reth_chainspec::{ChainSpec, EthereumHardforks, MAINNET};
|
|
|
|
|
use reth_chainspec::{ChainSpec, EthereumHardfork, EthereumHardforks, MAINNET};
|
|
|
|
|
use reth_consensus::ConsensusError;
|
|
|
|
|
use reth_ethereum_consensus::validate_block_post_execution;
|
|
|
|
|
use reth_evm::{
|
|
|
|
|
execute::{
|
|
|
|
|
BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput,
|
|
|
|
|
BlockExecutorProvider, BlockValidationError, Executor, ProviderError,
|
|
|
|
|
BasicBlockExecutorProvider, BlockExecutionError, BlockExecutionStrategy,
|
|
|
|
|
BlockExecutionStrategyFactory, BlockValidationError, ProviderError,
|
|
|
|
|
},
|
|
|
|
|
state_change::post_block_balance_increments,
|
|
|
|
|
system_calls::{NoopHook, OnStateHook, SystemCaller},
|
|
|
|
|
system_calls::{OnStateHook, SystemCaller},
|
|
|
|
|
ConfigureEvm,
|
|
|
|
|
};
|
|
|
|
|
use reth_execution_types::ExecutionOutcome;
|
|
|
|
|
use reth_primitives::{BlockWithSenders, EthereumHardfork, Header, Receipt};
|
|
|
|
|
use reth_prune_types::PruneModes;
|
|
|
|
|
use reth_revm::{
|
|
|
|
|
batch::BlockBatchRecord,
|
|
|
|
|
db::{states::bundle_state::BundleRetention, State},
|
|
|
|
|
Evm,
|
|
|
|
|
};
|
|
|
|
|
use reth_primitives::{BlockWithSenders, Receipt};
|
|
|
|
|
use reth_revm::db::{states::bundle_state::BundleRetention, BundleState, State};
|
|
|
|
|
use revm_primitives::{
|
|
|
|
|
db::{Database, DatabaseCommit},
|
|
|
|
|
BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ResultAndState,
|
|
|
|
|
BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ResultAndState, U256,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Provides executors to execute regular ethereum blocks
|
|
|
|
|
/// Factory for [`EthExecutionStrategy`].
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct EthExecutorProvider<EvmConfig = EthEvmConfig> {
|
|
|
|
|
chain_spec: Arc<ChainSpec>,
|
|
|
|
|
evm_config: EvmConfig,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl EthExecutorProvider {
|
|
|
|
|
/// Creates a new default ethereum executor provider.
|
|
|
|
|
pub fn ethereum(chain_spec: Arc<ChainSpec>) -> Self {
|
|
|
|
|
Self::new(chain_spec.clone(), EthEvmConfig::new(chain_spec))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns a new provider for the mainnet.
|
|
|
|
|
pub fn mainnet() -> Self {
|
|
|
|
|
Self::ethereum(MAINNET.clone())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<EvmConfig> EthExecutorProvider<EvmConfig> {
|
|
|
|
|
/// Creates a new executor provider.
|
|
|
|
|
pub const fn new(chain_spec: Arc<ChainSpec>, evm_config: EvmConfig) -> Self {
|
|
|
|
|
Self { chain_spec, evm_config }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<EvmConfig> EthExecutorProvider<EvmConfig>
|
|
|
|
|
where
|
|
|
|
|
EvmConfig: ConfigureEvm<Header = Header>,
|
|
|
|
|
{
|
|
|
|
|
fn eth_executor<DB>(&self, db: DB) -> EthBlockExecutor<EvmConfig, DB>
|
|
|
|
|
where
|
|
|
|
|
DB: Database<Error: Into<ProviderError>>,
|
|
|
|
|
{
|
|
|
|
|
EthBlockExecutor::new(
|
|
|
|
|
self.chain_spec.clone(),
|
|
|
|
|
self.evm_config.clone(),
|
|
|
|
|
State::builder().with_database(db).with_bundle_update().without_state_clear().build(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<EvmConfig> BlockExecutorProvider for EthExecutorProvider<EvmConfig>
|
|
|
|
|
where
|
|
|
|
|
EvmConfig: ConfigureEvm<Header = Header>,
|
|
|
|
|
{
|
|
|
|
|
type Executor<DB: Database<Error: Into<ProviderError> + Display>> =
|
|
|
|
|
EthBlockExecutor<EvmConfig, DB>;
|
|
|
|
|
|
|
|
|
|
type BatchExecutor<DB: Database<Error: Into<ProviderError> + Display>> =
|
|
|
|
|
EthBatchExecutor<EvmConfig, DB>;
|
|
|
|
|
|
|
|
|
|
fn executor<DB>(&self, db: DB) -> Self::Executor<DB>
|
|
|
|
|
where
|
|
|
|
|
DB: Database<Error: Into<ProviderError> + Display>,
|
|
|
|
|
{
|
|
|
|
|
self.eth_executor(db)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn batch_executor<DB>(&self, db: DB) -> Self::BatchExecutor<DB>
|
|
|
|
|
where
|
|
|
|
|
DB: Database<Error: Into<ProviderError> + Display>,
|
|
|
|
|
{
|
|
|
|
|
let executor = self.eth_executor(db);
|
|
|
|
|
EthBatchExecutor { executor, batch_record: BlockBatchRecord::default() }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Helper type for the output of executing a block.
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
struct EthExecuteOutput {
|
|
|
|
|
receipts: Vec<Receipt>,
|
|
|
|
|
requests: Requests,
|
|
|
|
|
gas_used: u64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Helper container type for EVM with chain spec.
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
struct EthEvmExecutor<EvmConfig> {
|
|
|
|
|
pub struct EthExecutionStrategyFactory<EvmConfig = EthEvmConfig> {
|
|
|
|
|
/// The chainspec
|
|
|
|
|
chain_spec: Arc<ChainSpec>,
|
|
|
|
|
/// How to create an EVM.
|
|
|
|
|
evm_config: EvmConfig,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<EvmConfig> EthEvmExecutor<EvmConfig>
|
|
|
|
|
impl EthExecutionStrategyFactory {
|
|
|
|
|
/// Creates a new default ethereum executor strategy factory.
|
|
|
|
|
pub fn ethereum(chain_spec: Arc<ChainSpec>) -> Self {
|
|
|
|
|
Self::new(chain_spec.clone(), EthEvmConfig::new(chain_spec))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns a new factory for the mainnet.
|
|
|
|
|
pub fn mainnet() -> Self {
|
|
|
|
|
Self::ethereum(MAINNET.clone())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<EvmConfig> EthExecutionStrategyFactory<EvmConfig> {
|
|
|
|
|
/// Creates a new executor strategy factory.
|
|
|
|
|
pub const fn new(chain_spec: Arc<ChainSpec>, evm_config: EvmConfig) -> Self {
|
|
|
|
|
Self { chain_spec, evm_config }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<EvmConfig> BlockExecutionStrategyFactory for EthExecutionStrategyFactory<EvmConfig>
|
|
|
|
|
where
|
|
|
|
|
EvmConfig: ConfigureEvm<Header = Header>,
|
|
|
|
|
EvmConfig:
|
|
|
|
|
Clone + Unpin + Sync + Send + 'static + ConfigureEvm<Header = alloy_consensus::Header>,
|
|
|
|
|
{
|
|
|
|
|
/// Executes the transactions in the block and returns the receipts of the transactions in the
|
|
|
|
|
/// block, the total gas used and the list of EIP-7685 [requests](Requests).
|
|
|
|
|
///
|
|
|
|
|
/// This applies the pre-execution and post-execution changes that require an [EVM](Evm), and
|
|
|
|
|
/// executes the transactions.
|
|
|
|
|
///
|
|
|
|
|
/// The optional `state_hook` will be executed with the state changes if present.
|
|
|
|
|
///
|
|
|
|
|
/// # Note
|
|
|
|
|
///
|
|
|
|
|
/// It does __not__ apply post-execution changes that do not require an [EVM](Evm), for that see
|
|
|
|
|
/// [`EthBlockExecutor::post_execution`].
|
|
|
|
|
fn execute_state_transitions<Ext, DB, F>(
|
|
|
|
|
&self,
|
|
|
|
|
block: &BlockWithSenders,
|
|
|
|
|
mut evm: Evm<'_, Ext, &mut State<DB>>,
|
|
|
|
|
state_hook: Option<F>,
|
|
|
|
|
) -> Result<EthExecuteOutput, BlockExecutionError>
|
|
|
|
|
type Strategy<DB: Database<Error: Into<ProviderError> + Display>> =
|
|
|
|
|
EthExecutionStrategy<DB, EvmConfig>;
|
|
|
|
|
|
|
|
|
|
fn create_strategy<DB>(&self, db: DB) -> Self::Strategy<DB>
|
|
|
|
|
where
|
|
|
|
|
DB: Database,
|
|
|
|
|
DB::Error: Into<ProviderError> + Display,
|
|
|
|
|
F: OnStateHook + 'static,
|
|
|
|
|
DB: Database<Error: Into<ProviderError> + Display>,
|
|
|
|
|
{
|
|
|
|
|
let mut system_caller = SystemCaller::new(self.evm_config.clone(), &self.chain_spec);
|
|
|
|
|
if let Some(hook) = state_hook {
|
|
|
|
|
system_caller.with_state_hook(Some(Box::new(hook) as Box<dyn OnStateHook>));
|
|
|
|
|
}
|
|
|
|
|
let state =
|
|
|
|
|
State::builder().with_database(db).with_bundle_update().without_state_clear().build();
|
|
|
|
|
EthExecutionStrategy::new(state, self.chain_spec.clone(), self.evm_config.clone())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
system_caller.apply_pre_execution_changes(block, &mut evm)?;
|
|
|
|
|
/// Block execution strategy for Ethereum.
|
|
|
|
|
#[allow(missing_debug_implementations)]
|
|
|
|
|
pub struct EthExecutionStrategy<DB, EvmConfig>
|
|
|
|
|
where
|
|
|
|
|
EvmConfig: Clone,
|
|
|
|
|
{
|
|
|
|
|
/// The chainspec
|
|
|
|
|
chain_spec: Arc<ChainSpec>,
|
|
|
|
|
/// How to create an EVM.
|
|
|
|
|
evm_config: EvmConfig,
|
|
|
|
|
/// Current state for block execution.
|
|
|
|
|
state: State<DB>,
|
|
|
|
|
/// Utility to call system smart contracts.
|
|
|
|
|
system_caller: SystemCaller<EvmConfig, ChainSpec>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<DB, EvmConfig> EthExecutionStrategy<DB, EvmConfig>
|
|
|
|
|
where
|
|
|
|
|
EvmConfig: Clone,
|
|
|
|
|
{
|
|
|
|
|
/// Creates a new [`EthExecutionStrategy`]
|
|
|
|
|
pub fn new(state: State<DB>, chain_spec: Arc<ChainSpec>, evm_config: EvmConfig) -> Self {
|
|
|
|
|
let system_caller = SystemCaller::new(evm_config.clone(), (*chain_spec).clone());
|
|
|
|
|
Self { state, chain_spec, evm_config, system_caller }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<DB, EvmConfig> EthExecutionStrategy<DB, EvmConfig>
|
|
|
|
|
where
|
|
|
|
|
DB: Database<Error: Into<ProviderError> + Display>,
|
|
|
|
|
EvmConfig: ConfigureEvm<Header = alloy_consensus::Header>,
|
|
|
|
|
{
|
|
|
|
|
/// Configures a new evm configuration and block environment for the given block.
|
|
|
|
|
///
|
|
|
|
|
/// # Caution
|
|
|
|
|
///
|
|
|
|
|
/// This does not initialize the tx environment.
|
|
|
|
|
fn evm_env_for_block(
|
|
|
|
|
&self,
|
|
|
|
|
header: &alloy_consensus::Header,
|
|
|
|
|
total_difficulty: U256,
|
|
|
|
|
) -> EnvWithHandlerCfg {
|
|
|
|
|
let mut cfg = CfgEnvWithHandlerCfg::new(Default::default(), Default::default());
|
|
|
|
|
let mut block_env = BlockEnv::default();
|
|
|
|
|
self.evm_config.fill_cfg_and_block_env(&mut cfg, &mut block_env, header, total_difficulty);
|
|
|
|
|
|
|
|
|
|
EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<DB, EvmConfig> BlockExecutionStrategy<DB> for EthExecutionStrategy<DB, EvmConfig>
|
|
|
|
|
where
|
|
|
|
|
DB: Database<Error: Into<ProviderError> + Display>,
|
|
|
|
|
EvmConfig: ConfigureEvm<Header = alloy_consensus::Header>,
|
|
|
|
|
{
|
|
|
|
|
type Error = BlockExecutionError;
|
|
|
|
|
|
|
|
|
|
fn apply_pre_execution_changes(
|
|
|
|
|
&mut self,
|
|
|
|
|
block: &BlockWithSenders,
|
|
|
|
|
total_difficulty: U256,
|
|
|
|
|
) -> Result<(), Self::Error> {
|
|
|
|
|
// Set state clear flag if the block is after the Spurious Dragon hardfork.
|
|
|
|
|
let state_clear_flag =
|
|
|
|
|
(*self.chain_spec).is_spurious_dragon_active_at_block(block.header.number);
|
|
|
|
|
self.state.set_state_clear_flag(state_clear_flag);
|
|
|
|
|
|
|
|
|
|
let env = self.evm_env_for_block(&block.header, total_difficulty);
|
|
|
|
|
let mut evm = self.evm_config.evm_with_env(&mut self.state, env);
|
|
|
|
|
|
|
|
|
|
self.system_caller.apply_pre_execution_changes(block, &mut evm)?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn execute_transactions(
|
|
|
|
|
&mut self,
|
|
|
|
|
block: &BlockWithSenders,
|
|
|
|
|
total_difficulty: U256,
|
|
|
|
|
) -> Result<(Vec<Receipt>, u64), Self::Error> {
|
|
|
|
|
let env = self.evm_env_for_block(&block.header, total_difficulty);
|
|
|
|
|
let mut evm = self.evm_config.evm_with_env(&mut self.state, env);
|
|
|
|
|
|
|
|
|
|
// execute transactions
|
|
|
|
|
let mut cumulative_gas_used = 0;
|
|
|
|
|
let mut receipts = Vec::with_capacity(block.body.transactions.len());
|
|
|
|
|
for (sender, transaction) in block.transactions_with_sender() {
|
|
|
|
|
@ -178,7 +181,7 @@ where
|
|
|
|
|
error: Box::new(new_err),
|
|
|
|
|
}
|
|
|
|
|
})?;
|
|
|
|
|
system_caller.on_state(&result_and_state);
|
|
|
|
|
self.system_caller.on_state(&result_and_state);
|
|
|
|
|
let ResultAndState { result, state } = result_and_state;
|
|
|
|
|
evm.db_mut().commit(state);
|
|
|
|
|
|
|
|
|
|
@ -200,137 +203,36 @@ where
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
Ok((receipts, cumulative_gas_used))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn apply_post_execution_changes(
|
|
|
|
|
&mut self,
|
|
|
|
|
block: &BlockWithSenders,
|
|
|
|
|
total_difficulty: U256,
|
|
|
|
|
receipts: &[Receipt],
|
|
|
|
|
) -> Result<Requests, Self::Error> {
|
|
|
|
|
let env = self.evm_env_for_block(&block.header, total_difficulty);
|
|
|
|
|
let mut evm = self.evm_config.evm_with_env(&mut self.state, env);
|
|
|
|
|
|
|
|
|
|
let requests = if self.chain_spec.is_prague_active_at_timestamp(block.timestamp) {
|
|
|
|
|
// Collect all EIP-6110 deposits
|
|
|
|
|
let deposit_requests =
|
|
|
|
|
crate::eip6110::parse_deposits_from_receipts(&self.chain_spec, &receipts)?;
|
|
|
|
|
crate::eip6110::parse_deposits_from_receipts(&self.chain_spec, receipts)?;
|
|
|
|
|
|
|
|
|
|
let mut requests = Requests::new(vec![deposit_requests]);
|
|
|
|
|
requests.extend(system_caller.apply_post_execution_changes(&mut evm)?);
|
|
|
|
|
requests.extend(self.system_caller.apply_post_execution_changes(&mut evm)?);
|
|
|
|
|
requests
|
|
|
|
|
} else {
|
|
|
|
|
Requests::default()
|
|
|
|
|
};
|
|
|
|
|
drop(evm);
|
|
|
|
|
|
|
|
|
|
Ok(EthExecuteOutput { receipts, requests, gas_used: cumulative_gas_used })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A basic Ethereum block executor.
|
|
|
|
|
///
|
|
|
|
|
/// Expected usage:
|
|
|
|
|
/// - Create a new instance of the executor.
|
|
|
|
|
/// - Execute the block.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct EthBlockExecutor<EvmConfig, DB> {
|
|
|
|
|
/// Chain specific evm config that's used to execute a block.
|
|
|
|
|
executor: EthEvmExecutor<EvmConfig>,
|
|
|
|
|
/// The state to use for execution
|
|
|
|
|
state: State<DB>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<EvmConfig, DB> EthBlockExecutor<EvmConfig, DB> {
|
|
|
|
|
/// Creates a new Ethereum block executor.
|
|
|
|
|
pub const fn new(chain_spec: Arc<ChainSpec>, evm_config: EvmConfig, state: State<DB>) -> Self {
|
|
|
|
|
Self { executor: EthEvmExecutor { chain_spec, evm_config }, state }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
fn chain_spec(&self) -> &ChainSpec {
|
|
|
|
|
&self.executor.chain_spec
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns mutable reference to the state that wraps the underlying database.
|
|
|
|
|
#[allow(unused)]
|
|
|
|
|
fn state_mut(&mut self) -> &mut State<DB> {
|
|
|
|
|
&mut self.state
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<EvmConfig, DB> EthBlockExecutor<EvmConfig, DB>
|
|
|
|
|
where
|
|
|
|
|
EvmConfig: ConfigureEvm<Header = Header>,
|
|
|
|
|
DB: Database<Error: Into<ProviderError> + Display>,
|
|
|
|
|
{
|
|
|
|
|
/// Configures a new evm configuration and block environment for the given block.
|
|
|
|
|
///
|
|
|
|
|
/// # Caution
|
|
|
|
|
///
|
|
|
|
|
/// This does not initialize the tx environment.
|
|
|
|
|
fn evm_env_for_block(&self, header: &Header, total_difficulty: U256) -> EnvWithHandlerCfg {
|
|
|
|
|
let mut cfg = CfgEnvWithHandlerCfg::new(Default::default(), Default::default());
|
|
|
|
|
let mut block_env = BlockEnv::default();
|
|
|
|
|
self.executor.evm_config.fill_cfg_and_block_env(
|
|
|
|
|
&mut cfg,
|
|
|
|
|
&mut block_env,
|
|
|
|
|
header,
|
|
|
|
|
total_difficulty,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Convenience method to invoke `execute_without_verification_with_state_hook` setting the
|
|
|
|
|
/// state hook as `None`.
|
|
|
|
|
fn execute_without_verification(
|
|
|
|
|
&mut self,
|
|
|
|
|
block: &BlockWithSenders,
|
|
|
|
|
total_difficulty: U256,
|
|
|
|
|
) -> Result<EthExecuteOutput, BlockExecutionError> {
|
|
|
|
|
self.execute_without_verification_with_state_hook(block, total_difficulty, None::<NoopHook>)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Execute a single block and apply the state changes to the internal state.
|
|
|
|
|
///
|
|
|
|
|
/// Returns the receipts of the transactions in the block, the total gas used and the list of
|
|
|
|
|
/// EIP-7685 [requests](Requests).
|
|
|
|
|
///
|
|
|
|
|
/// Returns an error if execution fails.
|
|
|
|
|
fn execute_without_verification_with_state_hook<F>(
|
|
|
|
|
&mut self,
|
|
|
|
|
block: &BlockWithSenders,
|
|
|
|
|
total_difficulty: U256,
|
|
|
|
|
state_hook: Option<F>,
|
|
|
|
|
) -> Result<EthExecuteOutput, BlockExecutionError>
|
|
|
|
|
where
|
|
|
|
|
F: OnStateHook + 'static,
|
|
|
|
|
{
|
|
|
|
|
// 1. prepare state on new block
|
|
|
|
|
self.on_new_block(&block.header);
|
|
|
|
|
|
|
|
|
|
// 2. configure the evm and execute
|
|
|
|
|
let env = self.evm_env_for_block(&block.header, total_difficulty);
|
|
|
|
|
let output = {
|
|
|
|
|
let evm = self.executor.evm_config.evm_with_env(&mut self.state, env);
|
|
|
|
|
self.executor.execute_state_transitions(block, evm, state_hook)
|
|
|
|
|
}?;
|
|
|
|
|
|
|
|
|
|
// 3. apply post execution changes
|
|
|
|
|
self.post_execution(block, total_difficulty)?;
|
|
|
|
|
|
|
|
|
|
Ok(output)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Apply settings before a new block is executed.
|
|
|
|
|
pub(crate) fn on_new_block(&mut self, header: &Header) {
|
|
|
|
|
// Set state clear flag if the block is after the Spurious Dragon hardfork.
|
|
|
|
|
let state_clear_flag = self.chain_spec().is_spurious_dragon_active_at_block(header.number);
|
|
|
|
|
self.state.set_state_clear_flag(state_clear_flag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Apply post execution state changes that do not require an [EVM](Evm), such as: block
|
|
|
|
|
/// rewards, withdrawals, and irregular DAO hardfork state change
|
|
|
|
|
pub fn post_execution(
|
|
|
|
|
&mut self,
|
|
|
|
|
block: &BlockWithSenders,
|
|
|
|
|
total_difficulty: U256,
|
|
|
|
|
) -> Result<(), BlockExecutionError> {
|
|
|
|
|
let mut balance_increments =
|
|
|
|
|
post_block_balance_increments(self.chain_spec(), block, total_difficulty);
|
|
|
|
|
post_block_balance_increments(&self.chain_spec, block, total_difficulty);
|
|
|
|
|
|
|
|
|
|
// Irregular state change at Ethereum DAO hardfork
|
|
|
|
|
if self.chain_spec().fork(EthereumHardfork::Dao).transitions_at_block(block.number) {
|
|
|
|
|
if self.chain_spec.fork(EthereumHardfork::Dao).transitions_at_block(block.number) {
|
|
|
|
|
// drain balances from hardcoded addresses.
|
|
|
|
|
let drained_balance: u128 = self
|
|
|
|
|
.state
|
|
|
|
|
@ -347,155 +249,59 @@ where
|
|
|
|
|
.increment_balances(balance_increments)
|
|
|
|
|
.map_err(|_| BlockValidationError::IncrementBalanceFailed)?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<EvmConfig, DB> Executor<DB> for EthBlockExecutor<EvmConfig, DB>
|
|
|
|
|
where
|
|
|
|
|
EvmConfig: ConfigureEvm<Header = Header>,
|
|
|
|
|
DB: Database<Error: Into<ProviderError> + Display>,
|
|
|
|
|
{
|
|
|
|
|
type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>;
|
|
|
|
|
type Output = BlockExecutionOutput<Receipt>;
|
|
|
|
|
type Error = BlockExecutionError;
|
|
|
|
|
|
|
|
|
|
/// Executes the block and commits the changes to the internal state.
|
|
|
|
|
///
|
|
|
|
|
/// Returns the receipts of the transactions in the block.
|
|
|
|
|
///
|
|
|
|
|
/// Returns an error if the block could not be executed or failed verification.
|
|
|
|
|
fn execute(mut self, input: Self::Input<'_>) -> Result<Self::Output, Self::Error> {
|
|
|
|
|
let BlockExecutionInput { block, total_difficulty } = input;
|
|
|
|
|
let EthExecuteOutput { receipts, requests, gas_used } =
|
|
|
|
|
self.execute_without_verification(block, total_difficulty)?;
|
|
|
|
|
|
|
|
|
|
// NOTE: we need to merge keep the reverts for the bundle retention
|
|
|
|
|
self.state.merge_transitions(BundleRetention::Reverts);
|
|
|
|
|
|
|
|
|
|
Ok(BlockExecutionOutput { state: self.state.take_bundle(), receipts, requests, gas_used })
|
|
|
|
|
Ok(requests)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn execute_with_state_closure<F>(
|
|
|
|
|
mut self,
|
|
|
|
|
input: Self::Input<'_>,
|
|
|
|
|
mut witness: F,
|
|
|
|
|
) -> Result<Self::Output, Self::Error>
|
|
|
|
|
where
|
|
|
|
|
F: FnMut(&State<DB>),
|
|
|
|
|
{
|
|
|
|
|
let BlockExecutionInput { block, total_difficulty } = input;
|
|
|
|
|
let EthExecuteOutput { receipts, requests, gas_used } =
|
|
|
|
|
self.execute_without_verification(block, total_difficulty)?;
|
|
|
|
|
|
|
|
|
|
// NOTE: we need to merge keep the reverts for the bundle retention
|
|
|
|
|
self.state.merge_transitions(BundleRetention::Reverts);
|
|
|
|
|
witness(&self.state);
|
|
|
|
|
Ok(BlockExecutionOutput { state: self.state.take_bundle(), receipts, requests, gas_used })
|
|
|
|
|
fn state_ref(&self) -> &State<DB> {
|
|
|
|
|
&self.state
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn execute_with_state_hook<F>(
|
|
|
|
|
mut self,
|
|
|
|
|
input: Self::Input<'_>,
|
|
|
|
|
state_hook: F,
|
|
|
|
|
) -> Result<Self::Output, Self::Error>
|
|
|
|
|
where
|
|
|
|
|
F: OnStateHook + 'static,
|
|
|
|
|
{
|
|
|
|
|
let BlockExecutionInput { block, total_difficulty } = input;
|
|
|
|
|
let EthExecuteOutput { receipts, requests, gas_used } = self
|
|
|
|
|
.execute_without_verification_with_state_hook(
|
|
|
|
|
block,
|
|
|
|
|
total_difficulty,
|
|
|
|
|
Some(state_hook),
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
// NOTE: we need to merge keep the reverts for the bundle retention
|
|
|
|
|
self.state.merge_transitions(BundleRetention::Reverts);
|
|
|
|
|
Ok(BlockExecutionOutput { state: self.state.take_bundle(), receipts, requests, gas_used })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/// An executor for a batch of blocks.
|
|
|
|
|
///
|
|
|
|
|
/// State changes are tracked until the executor is finalized.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct EthBatchExecutor<EvmConfig, DB> {
|
|
|
|
|
/// The executor used to execute single blocks
|
|
|
|
|
///
|
|
|
|
|
/// All state changes are committed to the [State].
|
|
|
|
|
executor: EthBlockExecutor<EvmConfig, DB>,
|
|
|
|
|
/// Keeps track of the batch and records receipts based on the configured prune mode
|
|
|
|
|
batch_record: BlockBatchRecord,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<EvmConfig, DB> EthBatchExecutor<EvmConfig, DB> {
|
|
|
|
|
/// Returns mutable reference to the state that wraps the underlying database.
|
|
|
|
|
#[allow(unused)]
|
|
|
|
|
fn state_mut(&mut self) -> &mut State<DB> {
|
|
|
|
|
self.executor.state_mut()
|
|
|
|
|
&mut self.state
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn with_state_hook(&mut self, hook: Option<Box<dyn OnStateHook>>) {
|
|
|
|
|
self.system_caller.with_state_hook(hook);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn finish(&mut self) -> BundleState {
|
|
|
|
|
self.state.merge_transitions(BundleRetention::Reverts);
|
|
|
|
|
self.state.take_bundle()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn validate_block_post_execution(
|
|
|
|
|
&self,
|
|
|
|
|
block: &BlockWithSenders,
|
|
|
|
|
receipts: &[Receipt],
|
|
|
|
|
requests: &Requests,
|
|
|
|
|
) -> Result<(), ConsensusError> {
|
|
|
|
|
validate_block_post_execution(block, &self.chain_spec.clone(), receipts, requests)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<EvmConfig, DB> BatchExecutor<DB> for EthBatchExecutor<EvmConfig, DB>
|
|
|
|
|
where
|
|
|
|
|
EvmConfig: ConfigureEvm<Header = Header>,
|
|
|
|
|
DB: Database<Error: Into<ProviderError> + Display>,
|
|
|
|
|
{
|
|
|
|
|
type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>;
|
|
|
|
|
type Output = ExecutionOutcome;
|
|
|
|
|
type Error = BlockExecutionError;
|
|
|
|
|
/// Helper type with backwards compatible methods to obtain Ethereum executor
|
|
|
|
|
/// providers.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct EthExecutorProvider;
|
|
|
|
|
|
|
|
|
|
fn execute_and_verify_one(&mut self, input: Self::Input<'_>) -> Result<(), Self::Error> {
|
|
|
|
|
let BlockExecutionInput { block, total_difficulty } = input;
|
|
|
|
|
|
|
|
|
|
if self.batch_record.first_block().is_none() {
|
|
|
|
|
self.batch_record.set_first_block(block.number);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let EthExecuteOutput { receipts, requests, gas_used: _ } =
|
|
|
|
|
self.executor.execute_without_verification(block, total_difficulty)?;
|
|
|
|
|
|
|
|
|
|
validate_block_post_execution(block, self.executor.chain_spec(), &receipts, &requests)?;
|
|
|
|
|
|
|
|
|
|
// prepare the state according to the prune mode
|
|
|
|
|
let retention = self.batch_record.bundle_retention(block.number);
|
|
|
|
|
self.executor.state.merge_transitions(retention);
|
|
|
|
|
|
|
|
|
|
// store receipts in the set
|
|
|
|
|
self.batch_record.save_receipts(receipts)?;
|
|
|
|
|
|
|
|
|
|
// store requests in the set
|
|
|
|
|
self.batch_record.save_requests(requests);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
impl EthExecutorProvider {
|
|
|
|
|
/// Creates a new default ethereum executor provider.
|
|
|
|
|
pub fn ethereum(
|
|
|
|
|
chain_spec: Arc<ChainSpec>,
|
|
|
|
|
) -> BasicBlockExecutorProvider<EthExecutionStrategyFactory> {
|
|
|
|
|
BasicBlockExecutorProvider::new(EthExecutionStrategyFactory::ethereum(chain_spec))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn finalize(mut self) -> Self::Output {
|
|
|
|
|
ExecutionOutcome::new(
|
|
|
|
|
self.executor.state.take_bundle(),
|
|
|
|
|
self.batch_record.take_receipts(),
|
|
|
|
|
self.batch_record.first_block().unwrap_or_default(),
|
|
|
|
|
self.batch_record.take_requests(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_tip(&mut self, tip: BlockNumber) {
|
|
|
|
|
self.batch_record.set_tip(tip);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_prune_modes(&mut self, prune_modes: PruneModes) {
|
|
|
|
|
self.batch_record.set_prune_modes(prune_modes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn size_hint(&self) -> Option<usize> {
|
|
|
|
|
Some(self.executor.state.bundle_state.size_hint())
|
|
|
|
|
/// Returns a new provider for the mainnet.
|
|
|
|
|
pub fn mainnet() -> BasicBlockExecutorProvider<EthExecutionStrategyFactory> {
|
|
|
|
|
BasicBlockExecutorProvider::new(EthExecutionStrategyFactory::mainnet())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
use alloy_consensus::TxLegacy;
|
|
|
|
|
use alloy_consensus::{Header, TxLegacy};
|
|
|
|
|
use alloy_eips::{
|
|
|
|
|
eip2935::{HISTORY_STORAGE_ADDRESS, HISTORY_STORAGE_CODE},
|
|
|
|
|
eip4788::{BEACON_ROOTS_ADDRESS, BEACON_ROOTS_CODE, SYSTEM_ADDRESS},
|
|
|
|
|
@ -504,6 +310,10 @@ mod tests {
|
|
|
|
|
};
|
|
|
|
|
use alloy_primitives::{b256, fixed_bytes, keccak256, Bytes, TxKind, B256};
|
|
|
|
|
use reth_chainspec::{ChainSpecBuilder, ForkCondition};
|
|
|
|
|
use reth_evm::execute::{
|
|
|
|
|
BasicBlockExecutorProvider, BatchExecutor, BlockExecutorProvider, Executor,
|
|
|
|
|
};
|
|
|
|
|
use reth_execution_types::BlockExecutionOutput;
|
|
|
|
|
use reth_primitives::{
|
|
|
|
|
constants::ETH_TO_WEI, public_key_to_address, Account, Block, BlockBody, Transaction,
|
|
|
|
|
};
|
|
|
|
|
@ -553,8 +363,13 @@ mod tests {
|
|
|
|
|
db
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn executor_provider(chain_spec: Arc<ChainSpec>) -> EthExecutorProvider<EthEvmConfig> {
|
|
|
|
|
EthExecutorProvider { evm_config: EthEvmConfig::new(chain_spec.clone()), chain_spec }
|
|
|
|
|
fn executor_provider(
|
|
|
|
|
chain_spec: Arc<ChainSpec>,
|
|
|
|
|
) -> BasicBlockExecutorProvider<EthExecutionStrategyFactory> {
|
|
|
|
|
let strategy_factory =
|
|
|
|
|
EthExecutionStrategyFactory::new(chain_spec.clone(), EthEvmConfig::new(chain_spec));
|
|
|
|
|
|
|
|
|
|
BasicBlockExecutorProvider::new(strategy_factory)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
@ -573,10 +388,11 @@ mod tests {
|
|
|
|
|
|
|
|
|
|
let provider = executor_provider(chain_spec);
|
|
|
|
|
|
|
|
|
|
let mut executor = provider.batch_executor(StateProviderDatabase::new(&db));
|
|
|
|
|
|
|
|
|
|
// attempt to execute a block without parent beacon block root, expect err
|
|
|
|
|
let err = provider
|
|
|
|
|
.executor(StateProviderDatabase::new(&db))
|
|
|
|
|
.execute(
|
|
|
|
|
let err = executor
|
|
|
|
|
.execute_and_verify_one(
|
|
|
|
|
(
|
|
|
|
|
&BlockWithSenders {
|
|
|
|
|
block: Block {
|
|
|
|
|
@ -605,19 +421,24 @@ mod tests {
|
|
|
|
|
// fix header, set a gas limit
|
|
|
|
|
header.parent_beacon_block_root = Some(B256::with_last_byte(0x69));
|
|
|
|
|
|
|
|
|
|
let mut executor = provider.executor(StateProviderDatabase::new(&db));
|
|
|
|
|
|
|
|
|
|
// Now execute a block with the fixed header, ensure that it does not fail
|
|
|
|
|
executor
|
|
|
|
|
.execute_without_verification(
|
|
|
|
|
&BlockWithSenders {
|
|
|
|
|
block: Block {
|
|
|
|
|
header: header.clone(),
|
|
|
|
|
body: BlockBody { transactions: vec![], ommers: vec![], withdrawals: None },
|
|
|
|
|
.execute_and_verify_one(
|
|
|
|
|
(
|
|
|
|
|
&BlockWithSenders {
|
|
|
|
|
block: Block {
|
|
|
|
|
header: header.clone(),
|
|
|
|
|
body: BlockBody {
|
|
|
|
|
transactions: vec![],
|
|
|
|
|
ommers: vec![],
|
|
|
|
|
withdrawals: None,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
senders: vec![],
|
|
|
|
|
},
|
|
|
|
|
senders: vec![],
|
|
|
|
|
},
|
|
|
|
|
U256::ZERO,
|
|
|
|
|
U256::ZERO,
|
|
|
|
|
)
|
|
|
|
|
.into(),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
@ -631,16 +452,17 @@ mod tests {
|
|
|
|
|
let parent_beacon_block_root_index =
|
|
|
|
|
timestamp_index % history_buffer_length + history_buffer_length;
|
|
|
|
|
|
|
|
|
|
// get timestamp storage and compare
|
|
|
|
|
let timestamp_storage =
|
|
|
|
|
executor.state.storage(BEACON_ROOTS_ADDRESS, U256::from(timestamp_index)).unwrap();
|
|
|
|
|
let timestamp_storage = executor.with_state_mut(|state| {
|
|
|
|
|
state.storage(BEACON_ROOTS_ADDRESS, U256::from(timestamp_index)).unwrap()
|
|
|
|
|
});
|
|
|
|
|
assert_eq!(timestamp_storage, U256::from(header.timestamp));
|
|
|
|
|
|
|
|
|
|
// get parent beacon block root storage and compare
|
|
|
|
|
let parent_beacon_block_root_storage = executor
|
|
|
|
|
.state
|
|
|
|
|
.storage(BEACON_ROOTS_ADDRESS, U256::from(parent_beacon_block_root_index))
|
|
|
|
|
.expect("storage value should exist");
|
|
|
|
|
let parent_beacon_block_root_storage = executor.with_state_mut(|state| {
|
|
|
|
|
state
|
|
|
|
|
.storage(BEACON_ROOTS_ADDRESS, U256::from(parent_beacon_block_root_index))
|
|
|
|
|
.expect("storage value should exist")
|
|
|
|
|
});
|
|
|
|
|
assert_eq!(parent_beacon_block_root_storage, U256::from(0x69));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -747,7 +569,8 @@ mod tests {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// ensure that the nonce of the system address account has not changed
|
|
|
|
|
let nonce = executor.state_mut().basic(SYSTEM_ADDRESS).unwrap().unwrap().nonce;
|
|
|
|
|
let nonce =
|
|
|
|
|
executor.with_state_mut(|state| state.basic(SYSTEM_ADDRESS).unwrap().unwrap().nonce);
|
|
|
|
|
assert_eq!(nonce, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -805,11 +628,12 @@ mod tests {
|
|
|
|
|
|
|
|
|
|
// there is no system contract call so there should be NO STORAGE CHANGES
|
|
|
|
|
// this means we'll check the transition state
|
|
|
|
|
let transition_state = executor
|
|
|
|
|
.state_mut()
|
|
|
|
|
.transition_state
|
|
|
|
|
.take()
|
|
|
|
|
.expect("the evm should be initialized with bundle updates");
|
|
|
|
|
let transition_state = executor.with_state_mut(|state| {
|
|
|
|
|
state
|
|
|
|
|
.transition_state
|
|
|
|
|
.take()
|
|
|
|
|
.expect("the evm should be initialized with bundle updates")
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// assert that it is the default (empty) transition state
|
|
|
|
|
assert_eq!(transition_state, TransitionState::default());
|
|
|
|
|
@ -867,17 +691,15 @@ mod tests {
|
|
|
|
|
timestamp_index % history_buffer_length + history_buffer_length;
|
|
|
|
|
|
|
|
|
|
// get timestamp storage and compare
|
|
|
|
|
let timestamp_storage = executor
|
|
|
|
|
.state_mut()
|
|
|
|
|
.storage(BEACON_ROOTS_ADDRESS, U256::from(timestamp_index))
|
|
|
|
|
.unwrap();
|
|
|
|
|
let timestamp_storage = executor.with_state_mut(|state| {
|
|
|
|
|
state.storage(BEACON_ROOTS_ADDRESS, U256::from(timestamp_index)).unwrap()
|
|
|
|
|
});
|
|
|
|
|
assert_eq!(timestamp_storage, U256::from(header.timestamp));
|
|
|
|
|
|
|
|
|
|
// get parent beacon block root storage and compare
|
|
|
|
|
let parent_beacon_block_root_storage = executor
|
|
|
|
|
.state_mut()
|
|
|
|
|
.storage(BEACON_ROOTS_ADDRESS, U256::from(parent_beacon_block_root_index))
|
|
|
|
|
.unwrap();
|
|
|
|
|
let parent_beacon_block_root_storage = executor.with_state_mut(|state| {
|
|
|
|
|
state.storage(BEACON_ROOTS_ADDRESS, U256::from(parent_beacon_block_root_index)).unwrap()
|
|
|
|
|
});
|
|
|
|
|
assert_eq!(parent_beacon_block_root_storage, U256::from(0x69));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -903,7 +725,6 @@ mod tests {
|
|
|
|
|
|
|
|
|
|
db
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn eip_2935_pre_fork() {
|
|
|
|
|
let db = create_state_provider_with_block_hashes(1);
|
|
|
|
|
@ -942,12 +763,11 @@ mod tests {
|
|
|
|
|
//
|
|
|
|
|
// we load the account first, because revm expects it to be
|
|
|
|
|
// loaded
|
|
|
|
|
executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap();
|
|
|
|
|
assert!(executor
|
|
|
|
|
.state_mut()
|
|
|
|
|
executor.with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap());
|
|
|
|
|
assert!(executor.with_state_mut(|state| state
|
|
|
|
|
.storage(HISTORY_STORAGE_ADDRESS, U256::ZERO)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.is_zero());
|
|
|
|
|
.is_zero()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
@ -986,12 +806,11 @@ mod tests {
|
|
|
|
|
//
|
|
|
|
|
// we load the account first, because revm expects it to be
|
|
|
|
|
// loaded
|
|
|
|
|
executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap();
|
|
|
|
|
assert!(executor
|
|
|
|
|
.state_mut()
|
|
|
|
|
executor.with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap());
|
|
|
|
|
assert!(executor.with_state_mut(|state| state
|
|
|
|
|
.storage(HISTORY_STORAGE_ADDRESS, U256::ZERO)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.is_zero());
|
|
|
|
|
.is_zero()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
@ -1033,21 +852,20 @@ mod tests {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// the hash for the ancestor of the fork activation block should be present
|
|
|
|
|
assert!(executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some());
|
|
|
|
|
assert!(executor
|
|
|
|
|
.with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some()));
|
|
|
|
|
assert_ne!(
|
|
|
|
|
executor
|
|
|
|
|
.state_mut()
|
|
|
|
|
executor.with_state_mut(|state| state
|
|
|
|
|
.storage(HISTORY_STORAGE_ADDRESS, U256::from(fork_activation_block - 1))
|
|
|
|
|
.unwrap(),
|
|
|
|
|
.unwrap()),
|
|
|
|
|
U256::ZERO
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// the hash of the block itself should not be in storage
|
|
|
|
|
assert!(executor
|
|
|
|
|
.state_mut()
|
|
|
|
|
assert!(executor.with_state_mut(|state| state
|
|
|
|
|
.storage(HISTORY_STORAGE_ADDRESS, U256::from(fork_activation_block))
|
|
|
|
|
.unwrap()
|
|
|
|
|
.is_zero());
|
|
|
|
|
.is_zero()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
@ -1090,15 +908,15 @@ mod tests {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// the hash for the ancestor of the fork activation block should be present
|
|
|
|
|
assert!(executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some());
|
|
|
|
|
assert!(executor
|
|
|
|
|
.with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some()));
|
|
|
|
|
assert_ne!(
|
|
|
|
|
executor
|
|
|
|
|
.state_mut()
|
|
|
|
|
executor.with_state_mut(|state| state
|
|
|
|
|
.storage(
|
|
|
|
|
HISTORY_STORAGE_ADDRESS,
|
|
|
|
|
U256::from(fork_activation_block % BLOCKHASH_SERVE_WINDOW as u64 - 1)
|
|
|
|
|
)
|
|
|
|
|
.unwrap(),
|
|
|
|
|
.unwrap()),
|
|
|
|
|
U256::ZERO
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
@ -1141,12 +959,11 @@ mod tests {
|
|
|
|
|
//
|
|
|
|
|
// we load the account first, because revm expects it to be
|
|
|
|
|
// loaded
|
|
|
|
|
executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap();
|
|
|
|
|
assert!(executor
|
|
|
|
|
.state_mut()
|
|
|
|
|
executor.with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap());
|
|
|
|
|
assert!(executor.with_state_mut(|state| state
|
|
|
|
|
.storage(HISTORY_STORAGE_ADDRESS, U256::ZERO)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.is_zero());
|
|
|
|
|
.is_zero()));
|
|
|
|
|
|
|
|
|
|
// attempt to execute block 1, this should not fail
|
|
|
|
|
let header = Header {
|
|
|
|
|
@ -1174,16 +991,18 @@ mod tests {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// the block hash of genesis should now be in storage, but not block 1
|
|
|
|
|
assert!(executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some());
|
|
|
|
|
assert!(executor
|
|
|
|
|
.with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some()));
|
|
|
|
|
assert_ne!(
|
|
|
|
|
executor.state_mut().storage(HISTORY_STORAGE_ADDRESS, U256::ZERO).unwrap(),
|
|
|
|
|
executor.with_state_mut(|state| state
|
|
|
|
|
.storage(HISTORY_STORAGE_ADDRESS, U256::ZERO)
|
|
|
|
|
.unwrap()),
|
|
|
|
|
U256::ZERO
|
|
|
|
|
);
|
|
|
|
|
assert!(executor
|
|
|
|
|
.state_mut()
|
|
|
|
|
assert!(executor.with_state_mut(|state| state
|
|
|
|
|
.storage(HISTORY_STORAGE_ADDRESS, U256::from(1))
|
|
|
|
|
.unwrap()
|
|
|
|
|
.is_zero());
|
|
|
|
|
.is_zero()));
|
|
|
|
|
|
|
|
|
|
// attempt to execute block 2, this should not fail
|
|
|
|
|
let header = Header {
|
|
|
|
|
@ -1210,20 +1029,24 @@ mod tests {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// the block hash of genesis and block 1 should now be in storage, but not block 2
|
|
|
|
|
assert!(executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some());
|
|
|
|
|
assert_ne!(
|
|
|
|
|
executor.state_mut().storage(HISTORY_STORAGE_ADDRESS, U256::ZERO).unwrap(),
|
|
|
|
|
U256::ZERO
|
|
|
|
|
);
|
|
|
|
|
assert_ne!(
|
|
|
|
|
executor.state_mut().storage(HISTORY_STORAGE_ADDRESS, U256::from(1)).unwrap(),
|
|
|
|
|
U256::ZERO
|
|
|
|
|
);
|
|
|
|
|
assert!(executor
|
|
|
|
|
.state_mut()
|
|
|
|
|
.with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some()));
|
|
|
|
|
assert_ne!(
|
|
|
|
|
executor.with_state_mut(|state| state
|
|
|
|
|
.storage(HISTORY_STORAGE_ADDRESS, U256::ZERO)
|
|
|
|
|
.unwrap()),
|
|
|
|
|
U256::ZERO
|
|
|
|
|
);
|
|
|
|
|
assert_ne!(
|
|
|
|
|
executor.with_state_mut(|state| state
|
|
|
|
|
.storage(HISTORY_STORAGE_ADDRESS, U256::from(1))
|
|
|
|
|
.unwrap()),
|
|
|
|
|
U256::ZERO
|
|
|
|
|
);
|
|
|
|
|
assert!(executor.with_state_mut(|state| state
|
|
|
|
|
.storage(HISTORY_STORAGE_ADDRESS, U256::from(2))
|
|
|
|
|
.unwrap()
|
|
|
|
|
.is_zero());
|
|
|
|
|
.is_zero()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|