mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-09 15:28:01 -05:00
chore: depreacte ethexecutorbuilder (#16709)
This commit is contained in:
@@ -24,7 +24,7 @@ use reth_network_api::NetworkInfo;
|
|||||||
use reth_network_p2p::{headers::client::HeadersClient, EthBlockClient};
|
use reth_network_p2p::{headers::client::HeadersClient, EthBlockClient};
|
||||||
use reth_node_api::NodeTypesWithDBAdapter;
|
use reth_node_api::NodeTypesWithDBAdapter;
|
||||||
use reth_node_core::{args::NetworkArgs, utils::get_single_header};
|
use reth_node_core::{args::NetworkArgs, utils::get_single_header};
|
||||||
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthExecutorProvider};
|
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthEvmConfig};
|
||||||
use reth_node_events::node::NodeEvent;
|
use reth_node_events::node::NodeEvent;
|
||||||
use reth_provider::{
|
use reth_provider::{
|
||||||
providers::ProviderNodeTypes, ChainSpecProvider, ProviderFactory, StageCheckpointReader,
|
providers::ProviderNodeTypes, ChainSpecProvider, ProviderFactory, StageCheckpointReader,
|
||||||
@@ -86,7 +86,7 @@ impl<C: ChainSpecParser<ChainSpec = ChainSpec>> Command<C> {
|
|||||||
let prune_modes = config.prune.clone().map(|prune| prune.segments).unwrap_or_default();
|
let prune_modes = config.prune.clone().map(|prune| prune.segments).unwrap_or_default();
|
||||||
|
|
||||||
let (tip_tx, tip_rx) = watch::channel(B256::ZERO);
|
let (tip_tx, tip_rx) = watch::channel(B256::ZERO);
|
||||||
let executor = EthExecutorProvider::ethereum(provider_factory.chain_spec());
|
let executor = EthEvmConfig::ethereum(provider_factory.chain_spec());
|
||||||
|
|
||||||
let pipeline = Pipeline::<N>::builder()
|
let pipeline = Pipeline::<N>::builder()
|
||||||
.with_tip_sender(tip_tx)
|
.with_tip_sender(tip_tx)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use reth_network_api::NetworkInfo;
|
|||||||
use reth_network_p2p::full_block::FullBlockClient;
|
use reth_network_p2p::full_block::FullBlockClient;
|
||||||
use reth_node_api::{BlockTy, NodePrimitives};
|
use reth_node_api::{BlockTy, NodePrimitives};
|
||||||
use reth_node_core::{args::NetworkArgs, utils::get_single_header};
|
use reth_node_core::{args::NetworkArgs, utils::get_single_header};
|
||||||
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthExecutorProvider};
|
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthEvmConfig};
|
||||||
use reth_provider::{
|
use reth_provider::{
|
||||||
providers::ProviderNodeTypes, BlockNumReader, BlockWriter, ChainSpecProvider,
|
providers::ProviderNodeTypes, BlockNumReader, BlockWriter, ChainSpecProvider,
|
||||||
DatabaseProviderFactory, LatestStateProviderRef, OriginalValuesKnown, ProviderFactory,
|
DatabaseProviderFactory, LatestStateProviderRef, OriginalValuesKnown, ProviderFactory,
|
||||||
@@ -109,7 +109,7 @@ impl<C: ChainSpecParser<ChainSpec = ChainSpec>> Command<C> {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let executor_provider = EthExecutorProvider::ethereum(provider_factory.chain_spec());
|
let executor_provider = EthEvmConfig::ethereum(provider_factory.chain_spec());
|
||||||
|
|
||||||
// Initialize the fetch client
|
// Initialize the fetch client
|
||||||
info!(target: "reth::cli", target_block_number = self.to, "Downloading tip of block range");
|
info!(target: "reth::cli", target_block_number = self.to, "Downloading tip of block range");
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use reth_node_core::{
|
|||||||
args::LogArgs,
|
args::LogArgs,
|
||||||
version::{LONG_VERSION, SHORT_VERSION},
|
version::{LONG_VERSION, SHORT_VERSION},
|
||||||
};
|
};
|
||||||
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthExecutorProvider, EthereumNode};
|
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthEvmConfig, EthereumNode};
|
||||||
use reth_node_metrics::recorder::install_prometheus_recorder;
|
use reth_node_metrics::recorder::install_prometheus_recorder;
|
||||||
use reth_tracing::FileWorkerGuard;
|
use reth_tracing::FileWorkerGuard;
|
||||||
use std::{ffi::OsString, fmt, future::Future, sync::Arc};
|
use std::{ffi::OsString, fmt, future::Future, sync::Arc};
|
||||||
@@ -149,7 +149,7 @@ impl<C: ChainSpecParser<ChainSpec = ChainSpec>, Ext: clap::Args + fmt::Debug> Cl
|
|||||||
let _ = install_prometheus_recorder();
|
let _ = install_prometheus_recorder();
|
||||||
|
|
||||||
let components = |spec: Arc<C::ChainSpec>| {
|
let components = |spec: Arc<C::ChainSpec>| {
|
||||||
(EthExecutorProvider::ethereum(spec.clone()), EthBeaconConsensus::new(spec))
|
(EthEvmConfig::ethereum(spec.clone()), EthBeaconConsensus::new(spec))
|
||||||
};
|
};
|
||||||
match self.command {
|
match self.command {
|
||||||
Commands::Node(command) => runner.run_command_until_exit(|ctx| {
|
Commands::Node(command) => runner.run_command_until_exit(|ctx| {
|
||||||
|
|||||||
@@ -1,863 +0,0 @@
|
|||||||
//! Ethereum block execution strategy.
|
|
||||||
|
|
||||||
/// Helper type with backwards compatible methods to obtain Ethereum executor
|
|
||||||
/// providers.
|
|
||||||
pub type EthExecutorProvider = crate::EthEvmConfig;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::EthEvmConfig;
|
|
||||||
use alloy_consensus::{constants::ETH_TO_WEI, Header, TxLegacy};
|
|
||||||
use alloy_eips::{
|
|
||||||
eip2935::{HISTORY_SERVE_WINDOW, HISTORY_STORAGE_ADDRESS, HISTORY_STORAGE_CODE},
|
|
||||||
eip4788::{BEACON_ROOTS_ADDRESS, BEACON_ROOTS_CODE, SYSTEM_ADDRESS},
|
|
||||||
eip4895::Withdrawal,
|
|
||||||
eip7002::{WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, WITHDRAWAL_REQUEST_PREDEPLOY_CODE},
|
|
||||||
eip7685::EMPTY_REQUESTS_HASH,
|
|
||||||
};
|
|
||||||
use alloy_evm::block::BlockValidationError;
|
|
||||||
use alloy_primitives::{b256, fixed_bytes, keccak256, Bytes, TxKind, B256, U256};
|
|
||||||
use reth_chainspec::{ChainSpec, ChainSpecBuilder, EthereumHardfork, ForkCondition, MAINNET};
|
|
||||||
use reth_ethereum_primitives::{Block, BlockBody, Transaction};
|
|
||||||
use reth_evm::{execute::Executor, ConfigureEvm};
|
|
||||||
use reth_execution_types::BlockExecutionResult;
|
|
||||||
use reth_primitives_traits::{
|
|
||||||
crypto::secp256k1::public_key_to_address, Block as _, RecoveredBlock,
|
|
||||||
};
|
|
||||||
use reth_testing_utils::generators::{self, sign_tx_with_key_pair};
|
|
||||||
use revm::{
|
|
||||||
database::{CacheDB, EmptyDB, TransitionState},
|
|
||||||
primitives::address,
|
|
||||||
state::{AccountInfo, Bytecode, EvmState},
|
|
||||||
Database,
|
|
||||||
};
|
|
||||||
use std::sync::{mpsc, Arc};
|
|
||||||
|
|
||||||
fn create_database_with_beacon_root_contract() -> CacheDB<EmptyDB> {
|
|
||||||
let mut db = CacheDB::new(Default::default());
|
|
||||||
|
|
||||||
let beacon_root_contract_account = AccountInfo {
|
|
||||||
balance: U256::ZERO,
|
|
||||||
code_hash: keccak256(BEACON_ROOTS_CODE.clone()),
|
|
||||||
nonce: 1,
|
|
||||||
code: Some(Bytecode::new_raw(BEACON_ROOTS_CODE.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
db.insert_account_info(BEACON_ROOTS_ADDRESS, beacon_root_contract_account);
|
|
||||||
|
|
||||||
db
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_database_with_withdrawal_requests_contract() -> CacheDB<EmptyDB> {
|
|
||||||
let mut db = CacheDB::new(Default::default());
|
|
||||||
|
|
||||||
let withdrawal_requests_contract_account = AccountInfo {
|
|
||||||
nonce: 1,
|
|
||||||
balance: U256::ZERO,
|
|
||||||
code_hash: keccak256(WITHDRAWAL_REQUEST_PREDEPLOY_CODE.clone()),
|
|
||||||
code: Some(Bytecode::new_raw(WITHDRAWAL_REQUEST_PREDEPLOY_CODE.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
db.insert_account_info(
|
|
||||||
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
|
|
||||||
withdrawal_requests_contract_account,
|
|
||||||
);
|
|
||||||
|
|
||||||
db
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evm_config(chain_spec: Arc<ChainSpec>) -> EthEvmConfig {
|
|
||||||
EthEvmConfig::new(chain_spec)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eip_4788_non_genesis_call() {
|
|
||||||
let mut header =
|
|
||||||
Header { timestamp: 1, number: 1, excess_blob_gas: Some(0), ..Header::default() };
|
|
||||||
|
|
||||||
let db = create_database_with_beacon_root_contract();
|
|
||||||
|
|
||||||
let chain_spec = Arc::new(
|
|
||||||
ChainSpecBuilder::from(&*MAINNET)
|
|
||||||
.shanghai_activated()
|
|
||||||
.with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(1))
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let provider = evm_config(chain_spec);
|
|
||||||
|
|
||||||
let mut executor = provider.batch_executor(db);
|
|
||||||
|
|
||||||
// attempt to execute a block without parent beacon block root, expect err
|
|
||||||
let err = executor
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block {
|
|
||||||
header: header.clone(),
|
|
||||||
body: BlockBody { transactions: vec![], ommers: vec![], withdrawals: None },
|
|
||||||
},
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.expect_err(
|
|
||||||
"Executing cancun block without parent beacon block root field should fail",
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(matches!(
|
|
||||||
err.as_validation().unwrap(),
|
|
||||||
BlockValidationError::MissingParentBeaconBlockRoot
|
|
||||||
));
|
|
||||||
|
|
||||||
// fix header, set a gas limit
|
|
||||||
header.parent_beacon_block_root = Some(B256::with_last_byte(0x69));
|
|
||||||
|
|
||||||
// Now execute a block with the fixed header, ensure that it does not fail
|
|
||||||
executor
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block {
|
|
||||||
header: header.clone(),
|
|
||||||
body: BlockBody { transactions: vec![], ommers: vec![], withdrawals: None },
|
|
||||||
},
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// check the actual storage of the contract - it should be:
|
|
||||||
// * The storage value at header.timestamp % HISTORY_BUFFER_LENGTH should be
|
|
||||||
// header.timestamp
|
|
||||||
// * The storage value at header.timestamp % HISTORY_BUFFER_LENGTH + HISTORY_BUFFER_LENGTH
|
|
||||||
// // should be parent_beacon_block_root
|
|
||||||
let history_buffer_length = 8191u64;
|
|
||||||
let timestamp_index = header.timestamp % history_buffer_length;
|
|
||||||
let parent_beacon_block_root_index =
|
|
||||||
timestamp_index % history_buffer_length + history_buffer_length;
|
|
||||||
|
|
||||||
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.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));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eip_4788_no_code_cancun() {
|
|
||||||
// This test ensures that we "silently fail" when cancun is active and there is no code at
|
|
||||||
// // BEACON_ROOTS_ADDRESS
|
|
||||||
let header = Header {
|
|
||||||
timestamp: 1,
|
|
||||||
number: 1,
|
|
||||||
parent_beacon_block_root: Some(B256::with_last_byte(0x69)),
|
|
||||||
excess_blob_gas: Some(0),
|
|
||||||
..Header::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let db = CacheDB::new(EmptyDB::default());
|
|
||||||
|
|
||||||
// DON'T deploy the contract at genesis
|
|
||||||
let chain_spec = Arc::new(
|
|
||||||
ChainSpecBuilder::from(&*MAINNET)
|
|
||||||
.shanghai_activated()
|
|
||||||
.with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(1))
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let provider = evm_config(chain_spec);
|
|
||||||
|
|
||||||
// attempt to execute an empty block with parent beacon block root, this should not fail
|
|
||||||
provider
|
|
||||||
.batch_executor(db)
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block {
|
|
||||||
header,
|
|
||||||
body: BlockBody { transactions: vec![], ommers: vec![], withdrawals: None },
|
|
||||||
},
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.expect(
|
|
||||||
"Executing a block with no transactions while cancun is active should not fail",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eip_4788_empty_account_call() {
|
|
||||||
// This test ensures that we do not increment the nonce of an empty SYSTEM_ADDRESS account
|
|
||||||
// // during the pre-block call
|
|
||||||
|
|
||||||
let mut db = create_database_with_beacon_root_contract();
|
|
||||||
|
|
||||||
// insert an empty SYSTEM_ADDRESS
|
|
||||||
db.insert_account_info(SYSTEM_ADDRESS, Default::default());
|
|
||||||
|
|
||||||
let chain_spec = Arc::new(
|
|
||||||
ChainSpecBuilder::from(&*MAINNET)
|
|
||||||
.shanghai_activated()
|
|
||||||
.with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(1))
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let provider = evm_config(chain_spec);
|
|
||||||
|
|
||||||
// construct the header for block one
|
|
||||||
let header = Header {
|
|
||||||
timestamp: 1,
|
|
||||||
number: 1,
|
|
||||||
parent_beacon_block_root: Some(B256::with_last_byte(0x69)),
|
|
||||||
excess_blob_gas: Some(0),
|
|
||||||
..Header::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut executor = provider.batch_executor(db);
|
|
||||||
|
|
||||||
// attempt to execute an empty block with parent beacon block root, this should not fail
|
|
||||||
executor
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block {
|
|
||||||
header,
|
|
||||||
body: BlockBody { transactions: vec![], ommers: vec![], withdrawals: None },
|
|
||||||
},
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.expect(
|
|
||||||
"Executing a block with no transactions while cancun is active should not fail",
|
|
||||||
);
|
|
||||||
|
|
||||||
// ensure that the nonce of the system address account has not changed
|
|
||||||
let nonce =
|
|
||||||
executor.with_state_mut(|state| state.basic(SYSTEM_ADDRESS).unwrap().unwrap().nonce);
|
|
||||||
assert_eq!(nonce, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eip_4788_genesis_call() {
|
|
||||||
let db = create_database_with_beacon_root_contract();
|
|
||||||
|
|
||||||
// activate cancun at genesis
|
|
||||||
let chain_spec = Arc::new(
|
|
||||||
ChainSpecBuilder::from(&*MAINNET)
|
|
||||||
.shanghai_activated()
|
|
||||||
.with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(0))
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut header = chain_spec.genesis_header().clone();
|
|
||||||
let provider = evm_config(chain_spec);
|
|
||||||
let mut executor = provider.batch_executor(db);
|
|
||||||
|
|
||||||
// attempt to execute the genesis block with non-zero parent beacon block root, expect err
|
|
||||||
header.parent_beacon_block_root = Some(B256::with_last_byte(0x69));
|
|
||||||
let _err = executor
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block { header: header.clone(), body: Default::default() },
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.expect_err(
|
|
||||||
"Executing genesis cancun block with non-zero parent beacon block root field
|
|
||||||
should fail",
|
|
||||||
);
|
|
||||||
|
|
||||||
// fix header
|
|
||||||
header.parent_beacon_block_root = Some(B256::ZERO);
|
|
||||||
|
|
||||||
// now try to process the genesis block again, this time ensuring that a system contract
|
|
||||||
// call does not occur
|
|
||||||
executor
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block { header, body: Default::default() },
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// 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.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());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eip_4788_high_base_fee() {
|
|
||||||
// This test ensures that if we have a base fee, then we don't return an error when the
|
|
||||||
// system contract is called, due to the gas price being less than the base fee.
|
|
||||||
let header = Header {
|
|
||||||
timestamp: 1,
|
|
||||||
number: 1,
|
|
||||||
parent_beacon_block_root: Some(B256::with_last_byte(0x69)),
|
|
||||||
base_fee_per_gas: Some(u64::MAX),
|
|
||||||
excess_blob_gas: Some(0),
|
|
||||||
..Header::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let db = create_database_with_beacon_root_contract();
|
|
||||||
|
|
||||||
let chain_spec = Arc::new(
|
|
||||||
ChainSpecBuilder::from(&*MAINNET)
|
|
||||||
.shanghai_activated()
|
|
||||||
.with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(1))
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let provider = evm_config(chain_spec);
|
|
||||||
|
|
||||||
// execute header
|
|
||||||
let mut executor = provider.batch_executor(db);
|
|
||||||
|
|
||||||
// Now execute a block with the fixed header, ensure that it does not fail
|
|
||||||
executor
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block { header: header.clone(), body: Default::default() },
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// check the actual storage of the contract - it should be:
|
|
||||||
// * The storage value at header.timestamp % HISTORY_BUFFER_LENGTH should be
|
|
||||||
// header.timestamp
|
|
||||||
// * The storage value at header.timestamp % HISTORY_BUFFER_LENGTH + HISTORY_BUFFER_LENGTH
|
|
||||||
// // should be parent_beacon_block_root
|
|
||||||
let history_buffer_length = 8191u64;
|
|
||||||
let timestamp_index = header.timestamp % history_buffer_length;
|
|
||||||
let parent_beacon_block_root_index =
|
|
||||||
timestamp_index % history_buffer_length + history_buffer_length;
|
|
||||||
|
|
||||||
// get timestamp storage and compare
|
|
||||||
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.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));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a state provider with blockhashes and the EIP-2935 system contract.
|
|
||||||
fn create_database_with_block_hashes(latest_block: u64) -> CacheDB<EmptyDB> {
|
|
||||||
let mut db = CacheDB::new(Default::default());
|
|
||||||
for block_number in 0..=latest_block {
|
|
||||||
db.cache
|
|
||||||
.block_hashes
|
|
||||||
.insert(U256::from(block_number), keccak256(block_number.to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let blockhashes_contract_account = AccountInfo {
|
|
||||||
balance: U256::ZERO,
|
|
||||||
code_hash: keccak256(HISTORY_STORAGE_CODE.clone()),
|
|
||||||
code: Some(Bytecode::new_raw(HISTORY_STORAGE_CODE.clone())),
|
|
||||||
nonce: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
db.insert_account_info(HISTORY_STORAGE_ADDRESS, blockhashes_contract_account);
|
|
||||||
|
|
||||||
db
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn eip_2935_pre_fork() {
|
|
||||||
let db = create_database_with_block_hashes(1);
|
|
||||||
|
|
||||||
let chain_spec = Arc::new(
|
|
||||||
ChainSpecBuilder::from(&*MAINNET)
|
|
||||||
.shanghai_activated()
|
|
||||||
.with_fork(EthereumHardfork::Prague, ForkCondition::Never)
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let provider = evm_config(chain_spec);
|
|
||||||
let mut executor = provider.batch_executor(db);
|
|
||||||
|
|
||||||
// construct the header for block one
|
|
||||||
let header = Header { timestamp: 1, number: 1, ..Header::default() };
|
|
||||||
|
|
||||||
// attempt to execute an empty block, this should not fail
|
|
||||||
executor
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block { header, body: Default::default() },
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.expect(
|
|
||||||
"Executing a block with no transactions while Prague is active should not fail",
|
|
||||||
);
|
|
||||||
|
|
||||||
// ensure that the block hash was *not* written to storage, since this is before the fork
|
|
||||||
// was activated
|
|
||||||
//
|
|
||||||
// we load the account first, because revm expects it to be
|
|
||||||
// loaded
|
|
||||||
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()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eip_2935_fork_activation_genesis() {
|
|
||||||
let db = create_database_with_block_hashes(0);
|
|
||||||
|
|
||||||
let chain_spec = Arc::new(
|
|
||||||
ChainSpecBuilder::from(&*MAINNET)
|
|
||||||
.shanghai_activated()
|
|
||||||
.cancun_activated()
|
|
||||||
.prague_activated()
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let header = chain_spec.genesis_header().clone();
|
|
||||||
let provider = evm_config(chain_spec);
|
|
||||||
let mut executor = provider.batch_executor(db);
|
|
||||||
|
|
||||||
// attempt to execute genesis block, this should not fail
|
|
||||||
executor
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block { header, body: Default::default() },
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.expect(
|
|
||||||
"Executing a block with no transactions while Prague is active should not fail",
|
|
||||||
);
|
|
||||||
|
|
||||||
// ensure that the block hash was *not* written to storage, since there are no blocks
|
|
||||||
// preceding genesis
|
|
||||||
//
|
|
||||||
// we load the account first, because revm expects it to be
|
|
||||||
// loaded
|
|
||||||
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()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eip_2935_fork_activation_within_window_bounds() {
|
|
||||||
let fork_activation_block = (HISTORY_SERVE_WINDOW - 10) as u64;
|
|
||||||
let db = create_database_with_block_hashes(fork_activation_block);
|
|
||||||
|
|
||||||
let chain_spec = Arc::new(
|
|
||||||
ChainSpecBuilder::from(&*MAINNET)
|
|
||||||
.shanghai_activated()
|
|
||||||
.cancun_activated()
|
|
||||||
.with_fork(EthereumHardfork::Prague, ForkCondition::Timestamp(1))
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let header = Header {
|
|
||||||
parent_hash: B256::random(),
|
|
||||||
timestamp: 1,
|
|
||||||
number: fork_activation_block,
|
|
||||||
requests_hash: Some(EMPTY_REQUESTS_HASH),
|
|
||||||
excess_blob_gas: Some(0),
|
|
||||||
parent_beacon_block_root: Some(B256::random()),
|
|
||||||
..Header::default()
|
|
||||||
};
|
|
||||||
let provider = evm_config(chain_spec);
|
|
||||||
let mut executor = provider.batch_executor(db);
|
|
||||||
|
|
||||||
// attempt to execute the fork activation block, this should not fail
|
|
||||||
executor
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block { header, body: Default::default() },
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.expect(
|
|
||||||
"Executing a block with no transactions while Prague is active should not fail",
|
|
||||||
);
|
|
||||||
|
|
||||||
// the hash for the ancestor of the fork activation block should be present
|
|
||||||
assert!(executor
|
|
||||||
.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::from(fork_activation_block - 1))
|
|
||||||
.unwrap()),
|
|
||||||
U256::ZERO
|
|
||||||
);
|
|
||||||
|
|
||||||
// the hash of the block itself should not be in storage
|
|
||||||
assert!(executor.with_state_mut(|state| {
|
|
||||||
state
|
|
||||||
.storage(HISTORY_STORAGE_ADDRESS, U256::from(fork_activation_block))
|
|
||||||
.unwrap()
|
|
||||||
.is_zero()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// <https://github.com/ethereum/EIPs/pull/9144>
|
|
||||||
#[test]
|
|
||||||
fn eip_2935_fork_activation_outside_window_bounds() {
|
|
||||||
let fork_activation_block = (HISTORY_SERVE_WINDOW + 256) as u64;
|
|
||||||
let db = create_database_with_block_hashes(fork_activation_block);
|
|
||||||
|
|
||||||
let chain_spec = Arc::new(
|
|
||||||
ChainSpecBuilder::from(&*MAINNET)
|
|
||||||
.shanghai_activated()
|
|
||||||
.cancun_activated()
|
|
||||||
.with_fork(EthereumHardfork::Prague, ForkCondition::Timestamp(1))
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let provider = evm_config(chain_spec);
|
|
||||||
let mut executor = provider.batch_executor(db);
|
|
||||||
|
|
||||||
let header = Header {
|
|
||||||
parent_hash: B256::random(),
|
|
||||||
timestamp: 1,
|
|
||||||
number: fork_activation_block,
|
|
||||||
requests_hash: Some(EMPTY_REQUESTS_HASH),
|
|
||||||
excess_blob_gas: Some(0),
|
|
||||||
parent_beacon_block_root: Some(B256::random()),
|
|
||||||
..Header::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
// attempt to execute the fork activation block, this should not fail
|
|
||||||
executor
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block { header, body: Default::default() },
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.expect(
|
|
||||||
"Executing a block with no transactions while Prague is active should not fail",
|
|
||||||
);
|
|
||||||
|
|
||||||
// the hash for the ancestor of the fork activation block should be present
|
|
||||||
assert!(executor
|
|
||||||
.with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eip_2935_state_transition_inside_fork() {
|
|
||||||
let db = create_database_with_block_hashes(2);
|
|
||||||
|
|
||||||
let chain_spec = Arc::new(
|
|
||||||
ChainSpecBuilder::from(&*MAINNET)
|
|
||||||
.shanghai_activated()
|
|
||||||
.cancun_activated()
|
|
||||||
.prague_activated()
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let header = chain_spec.genesis_header().clone();
|
|
||||||
let header_hash = header.hash_slow();
|
|
||||||
|
|
||||||
let provider = evm_config(chain_spec);
|
|
||||||
let mut executor = provider.batch_executor(db);
|
|
||||||
|
|
||||||
// attempt to execute the genesis block, this should not fail
|
|
||||||
executor
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block { header, body: Default::default() },
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.expect(
|
|
||||||
"Executing a block with no transactions while Prague is active should not fail",
|
|
||||||
);
|
|
||||||
|
|
||||||
// nothing should be written as the genesis has no ancestors
|
|
||||||
//
|
|
||||||
// we load the account first, because revm expects it to be
|
|
||||||
// loaded
|
|
||||||
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()
|
|
||||||
}));
|
|
||||||
|
|
||||||
// attempt to execute block 1, this should not fail
|
|
||||||
let header = Header {
|
|
||||||
parent_hash: header_hash,
|
|
||||||
timestamp: 1,
|
|
||||||
number: 1,
|
|
||||||
requests_hash: Some(EMPTY_REQUESTS_HASH),
|
|
||||||
excess_blob_gas: Some(0),
|
|
||||||
parent_beacon_block_root: Some(B256::random()),
|
|
||||||
..Header::default()
|
|
||||||
};
|
|
||||||
let header_hash = header.hash_slow();
|
|
||||||
|
|
||||||
executor
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block { header, body: Default::default() },
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.expect(
|
|
||||||
"Executing a block with no transactions while Prague is active should not fail",
|
|
||||||
);
|
|
||||||
|
|
||||||
// the block hash of genesis should now be in storage, but not block 1
|
|
||||||
assert!(executor
|
|
||||||
.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!(executor.with_state_mut(|state| {
|
|
||||||
state.storage(HISTORY_STORAGE_ADDRESS, U256::from(1)).unwrap().is_zero()
|
|
||||||
}));
|
|
||||||
|
|
||||||
// attempt to execute block 2, this should not fail
|
|
||||||
let header = Header {
|
|
||||||
parent_hash: header_hash,
|
|
||||||
timestamp: 1,
|
|
||||||
number: 2,
|
|
||||||
requests_hash: Some(EMPTY_REQUESTS_HASH),
|
|
||||||
excess_blob_gas: Some(0),
|
|
||||||
parent_beacon_block_root: Some(B256::random()),
|
|
||||||
..Header::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
executor
|
|
||||||
.execute_one(&RecoveredBlock::new_unhashed(
|
|
||||||
Block { header, body: Default::default() },
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
.expect(
|
|
||||||
"Executing a block with no transactions while Prague is active should not fail",
|
|
||||||
);
|
|
||||||
|
|
||||||
// the block hash of genesis and block 1 should now be in storage, but not block 2
|
|
||||||
assert!(executor
|
|
||||||
.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()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eip_7002() {
|
|
||||||
let chain_spec = Arc::new(
|
|
||||||
ChainSpecBuilder::from(&*MAINNET)
|
|
||||||
.shanghai_activated()
|
|
||||||
.cancun_activated()
|
|
||||||
.prague_activated()
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut db = create_database_with_withdrawal_requests_contract();
|
|
||||||
|
|
||||||
let sender_key_pair = generators::generate_key(&mut generators::rng());
|
|
||||||
let sender_address = public_key_to_address(sender_key_pair.public_key());
|
|
||||||
|
|
||||||
db.insert_account_info(
|
|
||||||
sender_address,
|
|
||||||
AccountInfo { nonce: 1, balance: U256::from(ETH_TO_WEI), ..Default::default() },
|
|
||||||
);
|
|
||||||
|
|
||||||
// https://github.com/lightclient/sys-asm/blob/9282bdb9fd64e024e27f60f507486ffb2183cba2/test/Withdrawal.t.sol.in#L36
|
|
||||||
let validator_public_key = fixed_bytes!(
|
|
||||||
"111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
|
|
||||||
);
|
|
||||||
let withdrawal_amount = fixed_bytes!("0203040506070809");
|
|
||||||
let input: Bytes = [&validator_public_key[..], &withdrawal_amount[..]].concat().into();
|
|
||||||
assert_eq!(input.len(), 56);
|
|
||||||
|
|
||||||
let mut header = chain_spec.genesis_header().clone();
|
|
||||||
header.gas_limit = 1_500_000;
|
|
||||||
// measured
|
|
||||||
header.gas_used = 135_856;
|
|
||||||
header.receipts_root =
|
|
||||||
b256!("0xb31a3e47b902e9211c4d349af4e4c5604ce388471e79ca008907ae4616bb0ed3");
|
|
||||||
|
|
||||||
let tx = sign_tx_with_key_pair(
|
|
||||||
sender_key_pair,
|
|
||||||
Transaction::Legacy(TxLegacy {
|
|
||||||
chain_id: Some(chain_spec.chain.id()),
|
|
||||||
nonce: 1,
|
|
||||||
gas_price: header.base_fee_per_gas.unwrap().into(),
|
|
||||||
gas_limit: header.gas_used,
|
|
||||||
to: TxKind::Call(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS),
|
|
||||||
// `MIN_WITHDRAWAL_REQUEST_FEE`
|
|
||||||
value: U256::from(2),
|
|
||||||
input,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let provider = evm_config(chain_spec);
|
|
||||||
|
|
||||||
let mut executor = provider.batch_executor(db);
|
|
||||||
|
|
||||||
let BlockExecutionResult { receipts, requests, .. } = executor
|
|
||||||
.execute_one(
|
|
||||||
&Block { header, body: BlockBody { transactions: vec![tx], ..Default::default() } }
|
|
||||||
.try_into_recovered()
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let receipt = receipts.first().unwrap();
|
|
||||||
assert!(receipt.success);
|
|
||||||
|
|
||||||
// There should be exactly one entry with withdrawal requests
|
|
||||||
assert_eq!(requests.len(), 1);
|
|
||||||
assert_eq!(requests[0][0], 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn block_gas_limit_error() {
|
|
||||||
// Create a chain specification with fork conditions set for Prague
|
|
||||||
let chain_spec = Arc::new(
|
|
||||||
ChainSpecBuilder::from(&*MAINNET)
|
|
||||||
.shanghai_activated()
|
|
||||||
.with_fork(EthereumHardfork::Prague, ForkCondition::Timestamp(0))
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create a state provider with the withdrawal requests contract pre-deployed
|
|
||||||
let mut db = create_database_with_withdrawal_requests_contract();
|
|
||||||
|
|
||||||
// Generate a new key pair for the sender
|
|
||||||
let sender_key_pair = generators::generate_key(&mut generators::rng());
|
|
||||||
// Get the sender's address from the public key
|
|
||||||
let sender_address = public_key_to_address(sender_key_pair.public_key());
|
|
||||||
|
|
||||||
// Insert the sender account into the state with a nonce of 1 and a balance of 1 ETH in Wei
|
|
||||||
db.insert_account_info(
|
|
||||||
sender_address,
|
|
||||||
AccountInfo { nonce: 1, balance: U256::from(ETH_TO_WEI), ..Default::default() },
|
|
||||||
);
|
|
||||||
|
|
||||||
// Define the validator public key and withdrawal amount as fixed bytes
|
|
||||||
let validator_public_key = fixed_bytes!(
|
|
||||||
"111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
|
|
||||||
);
|
|
||||||
let withdrawal_amount = fixed_bytes!("2222222222222222");
|
|
||||||
// Concatenate the validator public key and withdrawal amount into a single byte array
|
|
||||||
let input: Bytes = [&validator_public_key[..], &withdrawal_amount[..]].concat().into();
|
|
||||||
// Ensure the input length is 56 bytes
|
|
||||||
assert_eq!(input.len(), 56);
|
|
||||||
|
|
||||||
// Create a genesis block header with a specified gas limit and gas used
|
|
||||||
let mut header = chain_spec.genesis_header().clone();
|
|
||||||
header.gas_limit = 1_500_000;
|
|
||||||
header.gas_used = 134_807;
|
|
||||||
header.receipts_root =
|
|
||||||
b256!("0xb31a3e47b902e9211c4d349af4e4c5604ce388471e79ca008907ae4616bb0ed3");
|
|
||||||
|
|
||||||
// Create a transaction with a gas limit higher than the block gas limit
|
|
||||||
let tx = sign_tx_with_key_pair(
|
|
||||||
sender_key_pair,
|
|
||||||
Transaction::Legacy(TxLegacy {
|
|
||||||
chain_id: Some(chain_spec.chain.id()),
|
|
||||||
nonce: 1,
|
|
||||||
gas_price: header.base_fee_per_gas.unwrap().into(),
|
|
||||||
gas_limit: 2_500_000, // higher than block gas limit
|
|
||||||
to: TxKind::Call(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS),
|
|
||||||
value: U256::from(1),
|
|
||||||
input,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create an executor from the state provider
|
|
||||||
let evm_config = evm_config(chain_spec);
|
|
||||||
let mut executor = evm_config.batch_executor(db);
|
|
||||||
|
|
||||||
// Execute the block and capture the result
|
|
||||||
let exec_result = executor.execute_one(
|
|
||||||
&Block { header, body: BlockBody { transactions: vec![tx], ..Default::default() } }
|
|
||||||
.try_into_recovered()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check if the execution result is an error and assert the specific error type
|
|
||||||
match exec_result {
|
|
||||||
Ok(_) => panic!("Expected block gas limit error"),
|
|
||||||
Err(err) => assert!(matches!(
|
|
||||||
*err.as_validation().unwrap(),
|
|
||||||
BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
|
|
||||||
transaction_gas_limit: 2_500_000,
|
|
||||||
block_available_gas: 1_500_000,
|
|
||||||
}
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_balance_increment_not_duplicated() {
|
|
||||||
let chain_spec = Arc::new(
|
|
||||||
ChainSpecBuilder::from(&*MAINNET)
|
|
||||||
.shanghai_activated()
|
|
||||||
.cancun_activated()
|
|
||||||
.prague_activated()
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let withdrawal_recipient = address!("0x1000000000000000000000000000000000000000");
|
|
||||||
|
|
||||||
let mut db = CacheDB::new(EmptyDB::default());
|
|
||||||
let initial_balance = 100;
|
|
||||||
db.insert_account_info(
|
|
||||||
withdrawal_recipient,
|
|
||||||
AccountInfo { balance: U256::from(initial_balance), nonce: 1, ..Default::default() },
|
|
||||||
);
|
|
||||||
|
|
||||||
let withdrawal =
|
|
||||||
Withdrawal { index: 0, validator_index: 0, address: withdrawal_recipient, amount: 1 };
|
|
||||||
|
|
||||||
let header = Header {
|
|
||||||
timestamp: 1,
|
|
||||||
number: 1,
|
|
||||||
excess_blob_gas: Some(0),
|
|
||||||
parent_beacon_block_root: Some(B256::random()),
|
|
||||||
..Header::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let block = &RecoveredBlock::new_unhashed(
|
|
||||||
Block {
|
|
||||||
header,
|
|
||||||
body: BlockBody {
|
|
||||||
transactions: vec![],
|
|
||||||
ommers: vec![],
|
|
||||||
withdrawals: Some(vec![withdrawal].into()),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
vec![],
|
|
||||||
);
|
|
||||||
|
|
||||||
let provider = evm_config(chain_spec);
|
|
||||||
let executor = provider.batch_executor(db);
|
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
|
||||||
let tx_clone = tx.clone();
|
|
||||||
|
|
||||||
let _output = executor
|
|
||||||
.execute_with_state_hook(block, move |_, state: &EvmState| {
|
|
||||||
if let Some(account) = state.get(&withdrawal_recipient) {
|
|
||||||
let _ = tx_clone.send(account.info.balance);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.expect("Block execution should succeed");
|
|
||||||
|
|
||||||
drop(tx);
|
|
||||||
let balance_changes: Vec<U256> = rx.try_iter().collect();
|
|
||||||
|
|
||||||
if let Some(final_balance) = balance_changes.last() {
|
|
||||||
let expected_final_balance = U256::from(initial_balance) + U256::from(1_000_000_000); // initial + 1 Gwei in Wei
|
|
||||||
assert_eq!(
|
|
||||||
*final_balance, expected_final_balance,
|
|
||||||
"Final balance should match expected value after withdrawal"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -44,7 +44,15 @@ use alloy_eips::{eip1559::INITIAL_BASE_FEE, eip7840::BlobParams};
|
|||||||
pub use config::{revm_spec, revm_spec_by_timestamp_and_block_number};
|
pub use config::{revm_spec, revm_spec_by_timestamp_and_block_number};
|
||||||
use reth_ethereum_forks::EthereumHardfork;
|
use reth_ethereum_forks::EthereumHardfork;
|
||||||
|
|
||||||
pub mod execute;
|
/// Helper type with backwards compatible methods to obtain Ethereum executor
|
||||||
|
/// providers.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod execute {
|
||||||
|
use crate::EthEvmConfig;
|
||||||
|
|
||||||
|
#[deprecated(note = "Use `EthEvmConfig` instead")]
|
||||||
|
pub type EthExecutorProvider = EthEvmConfig;
|
||||||
|
}
|
||||||
|
|
||||||
mod build;
|
mod build;
|
||||||
pub use build::EthBlockAssembler;
|
pub use build::EthBlockAssembler;
|
||||||
|
|||||||
825
crates/ethereum/evm/tests/execute.rs
Normal file
825
crates/ethereum/evm/tests/execute.rs
Normal file
@@ -0,0 +1,825 @@
|
|||||||
|
//! Execution tests.
|
||||||
|
|
||||||
|
use alloy_consensus::{constants::ETH_TO_WEI, Header, TxLegacy};
|
||||||
|
use alloy_eips::{
|
||||||
|
eip2935::{HISTORY_SERVE_WINDOW, HISTORY_STORAGE_ADDRESS, HISTORY_STORAGE_CODE},
|
||||||
|
eip4788::{BEACON_ROOTS_ADDRESS, BEACON_ROOTS_CODE, SYSTEM_ADDRESS},
|
||||||
|
eip4895::Withdrawal,
|
||||||
|
eip7002::{WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, WITHDRAWAL_REQUEST_PREDEPLOY_CODE},
|
||||||
|
eip7685::EMPTY_REQUESTS_HASH,
|
||||||
|
};
|
||||||
|
use alloy_evm::block::BlockValidationError;
|
||||||
|
use alloy_primitives::{b256, fixed_bytes, keccak256, Bytes, TxKind, B256, U256};
|
||||||
|
use reth_chainspec::{ChainSpecBuilder, EthereumHardfork, ForkCondition, MAINNET};
|
||||||
|
use reth_ethereum_primitives::{Block, BlockBody, Transaction};
|
||||||
|
use reth_evm::{execute::Executor, ConfigureEvm};
|
||||||
|
use reth_evm_ethereum::EthEvmConfig;
|
||||||
|
use reth_execution_types::BlockExecutionResult;
|
||||||
|
use reth_primitives_traits::{
|
||||||
|
crypto::secp256k1::public_key_to_address, Block as _, RecoveredBlock,
|
||||||
|
};
|
||||||
|
use reth_testing_utils::generators::{self, sign_tx_with_key_pair};
|
||||||
|
use revm::{
|
||||||
|
database::{CacheDB, EmptyDB, TransitionState},
|
||||||
|
primitives::address,
|
||||||
|
state::{AccountInfo, Bytecode, EvmState},
|
||||||
|
Database,
|
||||||
|
};
|
||||||
|
use std::sync::{mpsc, Arc};
|
||||||
|
|
||||||
|
fn create_database_with_beacon_root_contract() -> CacheDB<EmptyDB> {
|
||||||
|
let mut db = CacheDB::new(Default::default());
|
||||||
|
|
||||||
|
let beacon_root_contract_account = AccountInfo {
|
||||||
|
balance: U256::ZERO,
|
||||||
|
code_hash: keccak256(BEACON_ROOTS_CODE.clone()),
|
||||||
|
nonce: 1,
|
||||||
|
code: Some(Bytecode::new_raw(BEACON_ROOTS_CODE.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
db.insert_account_info(BEACON_ROOTS_ADDRESS, beacon_root_contract_account);
|
||||||
|
|
||||||
|
db
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_database_with_withdrawal_requests_contract() -> CacheDB<EmptyDB> {
|
||||||
|
let mut db = CacheDB::new(Default::default());
|
||||||
|
|
||||||
|
let withdrawal_requests_contract_account = AccountInfo {
|
||||||
|
nonce: 1,
|
||||||
|
balance: U256::ZERO,
|
||||||
|
code_hash: keccak256(WITHDRAWAL_REQUEST_PREDEPLOY_CODE.clone()),
|
||||||
|
code: Some(Bytecode::new_raw(WITHDRAWAL_REQUEST_PREDEPLOY_CODE.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
db.insert_account_info(
|
||||||
|
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
|
||||||
|
withdrawal_requests_contract_account,
|
||||||
|
);
|
||||||
|
|
||||||
|
db
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eip_4788_non_genesis_call() {
|
||||||
|
let mut header =
|
||||||
|
Header { timestamp: 1, number: 1, excess_blob_gas: Some(0), ..Header::default() };
|
||||||
|
|
||||||
|
let db = create_database_with_beacon_root_contract();
|
||||||
|
|
||||||
|
let chain_spec = Arc::new(
|
||||||
|
ChainSpecBuilder::from(&*MAINNET)
|
||||||
|
.shanghai_activated()
|
||||||
|
.with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(1))
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let provider = EthEvmConfig::new(chain_spec);
|
||||||
|
|
||||||
|
let mut executor = provider.batch_executor(db);
|
||||||
|
|
||||||
|
// attempt to execute a block without parent beacon block root, expect err
|
||||||
|
let err = executor
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block {
|
||||||
|
header: header.clone(),
|
||||||
|
body: BlockBody { transactions: vec![], ommers: vec![], withdrawals: None },
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.expect_err("Executing cancun block without parent beacon block root field should fail");
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
err.as_validation().unwrap(),
|
||||||
|
BlockValidationError::MissingParentBeaconBlockRoot
|
||||||
|
));
|
||||||
|
|
||||||
|
// fix header, set a gas limit
|
||||||
|
header.parent_beacon_block_root = Some(B256::with_last_byte(0x69));
|
||||||
|
|
||||||
|
// Now execute a block with the fixed header, ensure that it does not fail
|
||||||
|
executor
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block {
|
||||||
|
header: header.clone(),
|
||||||
|
body: BlockBody { transactions: vec![], ommers: vec![], withdrawals: None },
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// check the actual storage of the contract - it should be:
|
||||||
|
// * The storage value at header.timestamp % HISTORY_BUFFER_LENGTH should be
|
||||||
|
// header.timestamp
|
||||||
|
// * The storage value at header.timestamp % HISTORY_BUFFER_LENGTH + HISTORY_BUFFER_LENGTH //
|
||||||
|
// should be parent_beacon_block_root
|
||||||
|
let history_buffer_length = 8191u64;
|
||||||
|
let timestamp_index = header.timestamp % history_buffer_length;
|
||||||
|
let parent_beacon_block_root_index =
|
||||||
|
timestamp_index % history_buffer_length + history_buffer_length;
|
||||||
|
|
||||||
|
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.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));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eip_4788_no_code_cancun() {
|
||||||
|
// This test ensures that we "silently fail" when cancun is active and there is no code at
|
||||||
|
// // BEACON_ROOTS_ADDRESS
|
||||||
|
let header = Header {
|
||||||
|
timestamp: 1,
|
||||||
|
number: 1,
|
||||||
|
parent_beacon_block_root: Some(B256::with_last_byte(0x69)),
|
||||||
|
excess_blob_gas: Some(0),
|
||||||
|
..Header::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let db = CacheDB::new(EmptyDB::default());
|
||||||
|
|
||||||
|
// DON'T deploy the contract at genesis
|
||||||
|
let chain_spec = Arc::new(
|
||||||
|
ChainSpecBuilder::from(&*MAINNET)
|
||||||
|
.shanghai_activated()
|
||||||
|
.with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(1))
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let provider = EthEvmConfig::new(chain_spec);
|
||||||
|
|
||||||
|
// attempt to execute an empty block with parent beacon block root, this should not fail
|
||||||
|
provider
|
||||||
|
.batch_executor(db)
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block {
|
||||||
|
header,
|
||||||
|
body: BlockBody { transactions: vec![], ommers: vec![], withdrawals: None },
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.expect("Executing a block with no transactions while cancun is active should not fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eip_4788_empty_account_call() {
|
||||||
|
// This test ensures that we do not increment the nonce of an empty SYSTEM_ADDRESS account
|
||||||
|
// // during the pre-block call
|
||||||
|
|
||||||
|
let mut db = create_database_with_beacon_root_contract();
|
||||||
|
|
||||||
|
// insert an empty SYSTEM_ADDRESS
|
||||||
|
db.insert_account_info(SYSTEM_ADDRESS, Default::default());
|
||||||
|
|
||||||
|
let chain_spec = Arc::new(
|
||||||
|
ChainSpecBuilder::from(&*MAINNET)
|
||||||
|
.shanghai_activated()
|
||||||
|
.with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(1))
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let provider = EthEvmConfig::new(chain_spec);
|
||||||
|
|
||||||
|
// construct the header for block one
|
||||||
|
let header = Header {
|
||||||
|
timestamp: 1,
|
||||||
|
number: 1,
|
||||||
|
parent_beacon_block_root: Some(B256::with_last_byte(0x69)),
|
||||||
|
excess_blob_gas: Some(0),
|
||||||
|
..Header::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut executor = provider.batch_executor(db);
|
||||||
|
|
||||||
|
// attempt to execute an empty block with parent beacon block root, this should not fail
|
||||||
|
executor
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block {
|
||||||
|
header,
|
||||||
|
body: BlockBody { transactions: vec![], ommers: vec![], withdrawals: None },
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.expect("Executing a block with no transactions while cancun is active should not fail");
|
||||||
|
|
||||||
|
// ensure that the nonce of the system address account has not changed
|
||||||
|
let nonce =
|
||||||
|
executor.with_state_mut(|state| state.basic(SYSTEM_ADDRESS).unwrap().unwrap().nonce);
|
||||||
|
assert_eq!(nonce, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eip_4788_genesis_call() {
|
||||||
|
let db = create_database_with_beacon_root_contract();
|
||||||
|
|
||||||
|
// activate cancun at genesis
|
||||||
|
let chain_spec = Arc::new(
|
||||||
|
ChainSpecBuilder::from(&*MAINNET)
|
||||||
|
.shanghai_activated()
|
||||||
|
.with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(0))
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut header = chain_spec.genesis_header().clone();
|
||||||
|
let provider = EthEvmConfig::new(chain_spec);
|
||||||
|
let mut executor = provider.batch_executor(db);
|
||||||
|
|
||||||
|
// attempt to execute the genesis block with non-zero parent beacon block root, expect err
|
||||||
|
header.parent_beacon_block_root = Some(B256::with_last_byte(0x69));
|
||||||
|
let _err = executor
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block { header: header.clone(), body: Default::default() },
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.expect_err(
|
||||||
|
"Executing genesis cancun block with non-zero parent beacon block root field
|
||||||
|
should fail",
|
||||||
|
);
|
||||||
|
|
||||||
|
// fix header
|
||||||
|
header.parent_beacon_block_root = Some(B256::ZERO);
|
||||||
|
|
||||||
|
// now try to process the genesis block again, this time ensuring that a system contract
|
||||||
|
// call does not occur
|
||||||
|
executor
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block { header, body: Default::default() },
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// 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.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());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eip_4788_high_base_fee() {
|
||||||
|
// This test ensures that if we have a base fee, then we don't return an error when the
|
||||||
|
// system contract is called, due to the gas price being less than the base fee.
|
||||||
|
let header = Header {
|
||||||
|
timestamp: 1,
|
||||||
|
number: 1,
|
||||||
|
parent_beacon_block_root: Some(B256::with_last_byte(0x69)),
|
||||||
|
base_fee_per_gas: Some(u64::MAX),
|
||||||
|
excess_blob_gas: Some(0),
|
||||||
|
..Header::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let db = create_database_with_beacon_root_contract();
|
||||||
|
|
||||||
|
let chain_spec = Arc::new(
|
||||||
|
ChainSpecBuilder::from(&*MAINNET)
|
||||||
|
.shanghai_activated()
|
||||||
|
.with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(1))
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let provider = EthEvmConfig::new(chain_spec);
|
||||||
|
|
||||||
|
// execute header
|
||||||
|
let mut executor = provider.batch_executor(db);
|
||||||
|
|
||||||
|
// Now execute a block with the fixed header, ensure that it does not fail
|
||||||
|
executor
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block { header: header.clone(), body: Default::default() },
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// check the actual storage of the contract - it should be:
|
||||||
|
// * The storage value at header.timestamp % HISTORY_BUFFER_LENGTH should be
|
||||||
|
// header.timestamp
|
||||||
|
// * The storage value at header.timestamp % HISTORY_BUFFER_LENGTH + HISTORY_BUFFER_LENGTH //
|
||||||
|
// should be parent_beacon_block_root
|
||||||
|
let history_buffer_length = 8191u64;
|
||||||
|
let timestamp_index = header.timestamp % history_buffer_length;
|
||||||
|
let parent_beacon_block_root_index =
|
||||||
|
timestamp_index % history_buffer_length + history_buffer_length;
|
||||||
|
|
||||||
|
// get timestamp storage and compare
|
||||||
|
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.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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a state provider with blockhashes and the EIP-2935 system contract.
|
||||||
|
fn create_database_with_block_hashes(latest_block: u64) -> CacheDB<EmptyDB> {
|
||||||
|
let mut db = CacheDB::new(Default::default());
|
||||||
|
for block_number in 0..=latest_block {
|
||||||
|
db.cache.block_hashes.insert(U256::from(block_number), keccak256(block_number.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let blockhashes_contract_account = AccountInfo {
|
||||||
|
balance: U256::ZERO,
|
||||||
|
code_hash: keccak256(HISTORY_STORAGE_CODE.clone()),
|
||||||
|
code: Some(Bytecode::new_raw(HISTORY_STORAGE_CODE.clone())),
|
||||||
|
nonce: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
db.insert_account_info(HISTORY_STORAGE_ADDRESS, blockhashes_contract_account);
|
||||||
|
|
||||||
|
db
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn eip_2935_pre_fork() {
|
||||||
|
let db = create_database_with_block_hashes(1);
|
||||||
|
|
||||||
|
let chain_spec = Arc::new(
|
||||||
|
ChainSpecBuilder::from(&*MAINNET)
|
||||||
|
.shanghai_activated()
|
||||||
|
.with_fork(EthereumHardfork::Prague, ForkCondition::Never)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let provider = EthEvmConfig::new(chain_spec);
|
||||||
|
let mut executor = provider.batch_executor(db);
|
||||||
|
|
||||||
|
// construct the header for block one
|
||||||
|
let header = Header { timestamp: 1, number: 1, ..Header::default() };
|
||||||
|
|
||||||
|
// attempt to execute an empty block, this should not fail
|
||||||
|
executor
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block { header, body: Default::default() },
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.expect("Executing a block with no transactions while Prague is active should not fail");
|
||||||
|
|
||||||
|
// ensure that the block hash was *not* written to storage, since this is before the fork
|
||||||
|
// was activated
|
||||||
|
//
|
||||||
|
// we load the account first, because revm expects it to be
|
||||||
|
// loaded
|
||||||
|
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()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eip_2935_fork_activation_genesis() {
|
||||||
|
let db = create_database_with_block_hashes(0);
|
||||||
|
|
||||||
|
let chain_spec = Arc::new(
|
||||||
|
ChainSpecBuilder::from(&*MAINNET)
|
||||||
|
.shanghai_activated()
|
||||||
|
.cancun_activated()
|
||||||
|
.prague_activated()
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let header = chain_spec.genesis_header().clone();
|
||||||
|
let provider = EthEvmConfig::new(chain_spec);
|
||||||
|
let mut executor = provider.batch_executor(db);
|
||||||
|
|
||||||
|
// attempt to execute genesis block, this should not fail
|
||||||
|
executor
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block { header, body: Default::default() },
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.expect("Executing a block with no transactions while Prague is active should not fail");
|
||||||
|
|
||||||
|
// ensure that the block hash was *not* written to storage, since there are no blocks
|
||||||
|
// preceding genesis
|
||||||
|
//
|
||||||
|
// we load the account first, because revm expects it to be
|
||||||
|
// loaded
|
||||||
|
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()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eip_2935_fork_activation_within_window_bounds() {
|
||||||
|
let fork_activation_block = (HISTORY_SERVE_WINDOW - 10) as u64;
|
||||||
|
let db = create_database_with_block_hashes(fork_activation_block);
|
||||||
|
|
||||||
|
let chain_spec = Arc::new(
|
||||||
|
ChainSpecBuilder::from(&*MAINNET)
|
||||||
|
.shanghai_activated()
|
||||||
|
.cancun_activated()
|
||||||
|
.with_fork(EthereumHardfork::Prague, ForkCondition::Timestamp(1))
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let header = Header {
|
||||||
|
parent_hash: B256::random(),
|
||||||
|
timestamp: 1,
|
||||||
|
number: fork_activation_block,
|
||||||
|
requests_hash: Some(EMPTY_REQUESTS_HASH),
|
||||||
|
excess_blob_gas: Some(0),
|
||||||
|
parent_beacon_block_root: Some(B256::random()),
|
||||||
|
..Header::default()
|
||||||
|
};
|
||||||
|
let provider = EthEvmConfig::new(chain_spec);
|
||||||
|
let mut executor = provider.batch_executor(db);
|
||||||
|
|
||||||
|
// attempt to execute the fork activation block, this should not fail
|
||||||
|
executor
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block { header, body: Default::default() },
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.expect("Executing a block with no transactions while Prague is active should not fail");
|
||||||
|
|
||||||
|
// the hash for the ancestor of the fork activation block should be present
|
||||||
|
assert!(
|
||||||
|
executor.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::from(fork_activation_block - 1))
|
||||||
|
.unwrap()),
|
||||||
|
U256::ZERO
|
||||||
|
);
|
||||||
|
|
||||||
|
// the hash of the block itself should not be in storage
|
||||||
|
assert!(executor.with_state_mut(|state| {
|
||||||
|
state.storage(HISTORY_STORAGE_ADDRESS, U256::from(fork_activation_block)).unwrap().is_zero()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// <https://github.com/ethereum/EIPs/pull/9144>
|
||||||
|
#[test]
|
||||||
|
fn eip_2935_fork_activation_outside_window_bounds() {
|
||||||
|
let fork_activation_block = (HISTORY_SERVE_WINDOW + 256) as u64;
|
||||||
|
let db = create_database_with_block_hashes(fork_activation_block);
|
||||||
|
|
||||||
|
let chain_spec = Arc::new(
|
||||||
|
ChainSpecBuilder::from(&*MAINNET)
|
||||||
|
.shanghai_activated()
|
||||||
|
.cancun_activated()
|
||||||
|
.with_fork(EthereumHardfork::Prague, ForkCondition::Timestamp(1))
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let provider = EthEvmConfig::new(chain_spec);
|
||||||
|
let mut executor = provider.batch_executor(db);
|
||||||
|
|
||||||
|
let header = Header {
|
||||||
|
parent_hash: B256::random(),
|
||||||
|
timestamp: 1,
|
||||||
|
number: fork_activation_block,
|
||||||
|
requests_hash: Some(EMPTY_REQUESTS_HASH),
|
||||||
|
excess_blob_gas: Some(0),
|
||||||
|
parent_beacon_block_root: Some(B256::random()),
|
||||||
|
..Header::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// attempt to execute the fork activation block, this should not fail
|
||||||
|
executor
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block { header, body: Default::default() },
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.expect("Executing a block with no transactions while Prague is active should not fail");
|
||||||
|
|
||||||
|
// the hash for the ancestor of the fork activation block should be present
|
||||||
|
assert!(
|
||||||
|
executor.with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eip_2935_state_transition_inside_fork() {
|
||||||
|
let db = create_database_with_block_hashes(2);
|
||||||
|
|
||||||
|
let chain_spec = Arc::new(
|
||||||
|
ChainSpecBuilder::from(&*MAINNET)
|
||||||
|
.shanghai_activated()
|
||||||
|
.cancun_activated()
|
||||||
|
.prague_activated()
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let header = chain_spec.genesis_header().clone();
|
||||||
|
let header_hash = header.hash_slow();
|
||||||
|
|
||||||
|
let provider = EthEvmConfig::new(chain_spec);
|
||||||
|
let mut executor = provider.batch_executor(db);
|
||||||
|
|
||||||
|
// attempt to execute the genesis block, this should not fail
|
||||||
|
executor
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block { header, body: Default::default() },
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.expect("Executing a block with no transactions while Prague is active should not fail");
|
||||||
|
|
||||||
|
// nothing should be written as the genesis has no ancestors
|
||||||
|
//
|
||||||
|
// we load the account first, because revm expects it to be
|
||||||
|
// loaded
|
||||||
|
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()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// attempt to execute block 1, this should not fail
|
||||||
|
let header = Header {
|
||||||
|
parent_hash: header_hash,
|
||||||
|
timestamp: 1,
|
||||||
|
number: 1,
|
||||||
|
requests_hash: Some(EMPTY_REQUESTS_HASH),
|
||||||
|
excess_blob_gas: Some(0),
|
||||||
|
parent_beacon_block_root: Some(B256::random()),
|
||||||
|
..Header::default()
|
||||||
|
};
|
||||||
|
let header_hash = header.hash_slow();
|
||||||
|
|
||||||
|
executor
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block { header, body: Default::default() },
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.expect("Executing a block with no transactions while Prague is active should not fail");
|
||||||
|
|
||||||
|
// the block hash of genesis should now be in storage, but not block 1
|
||||||
|
assert!(
|
||||||
|
executor.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!(executor.with_state_mut(|state| {
|
||||||
|
state.storage(HISTORY_STORAGE_ADDRESS, U256::from(1)).unwrap().is_zero()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// attempt to execute block 2, this should not fail
|
||||||
|
let header = Header {
|
||||||
|
parent_hash: header_hash,
|
||||||
|
timestamp: 1,
|
||||||
|
number: 2,
|
||||||
|
requests_hash: Some(EMPTY_REQUESTS_HASH),
|
||||||
|
excess_blob_gas: Some(0),
|
||||||
|
parent_beacon_block_root: Some(B256::random()),
|
||||||
|
..Header::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
executor
|
||||||
|
.execute_one(&RecoveredBlock::new_unhashed(
|
||||||
|
Block { header, body: Default::default() },
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
.expect("Executing a block with no transactions while Prague is active should not fail");
|
||||||
|
|
||||||
|
// the block hash of genesis and block 1 should now be in storage, but not block 2
|
||||||
|
assert!(
|
||||||
|
executor.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()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eip_7002() {
|
||||||
|
let chain_spec = Arc::new(
|
||||||
|
ChainSpecBuilder::from(&*MAINNET)
|
||||||
|
.shanghai_activated()
|
||||||
|
.cancun_activated()
|
||||||
|
.prague_activated()
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut db = create_database_with_withdrawal_requests_contract();
|
||||||
|
|
||||||
|
let sender_key_pair = generators::generate_key(&mut generators::rng());
|
||||||
|
let sender_address = public_key_to_address(sender_key_pair.public_key());
|
||||||
|
|
||||||
|
db.insert_account_info(
|
||||||
|
sender_address,
|
||||||
|
AccountInfo { nonce: 1, balance: U256::from(ETH_TO_WEI), ..Default::default() },
|
||||||
|
);
|
||||||
|
|
||||||
|
// https://github.com/lightclient/sys-asm/blob/9282bdb9fd64e024e27f60f507486ffb2183cba2/test/Withdrawal.t.sol.in#L36
|
||||||
|
let validator_public_key = fixed_bytes!(
|
||||||
|
"111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
);
|
||||||
|
let withdrawal_amount = fixed_bytes!("0203040506070809");
|
||||||
|
let input: Bytes = [&validator_public_key[..], &withdrawal_amount[..]].concat().into();
|
||||||
|
assert_eq!(input.len(), 56);
|
||||||
|
|
||||||
|
let mut header = chain_spec.genesis_header().clone();
|
||||||
|
header.gas_limit = 1_500_000;
|
||||||
|
// measured
|
||||||
|
header.gas_used = 135_856;
|
||||||
|
header.receipts_root =
|
||||||
|
b256!("0xb31a3e47b902e9211c4d349af4e4c5604ce388471e79ca008907ae4616bb0ed3");
|
||||||
|
|
||||||
|
let tx = sign_tx_with_key_pair(
|
||||||
|
sender_key_pair,
|
||||||
|
Transaction::Legacy(TxLegacy {
|
||||||
|
chain_id: Some(chain_spec.chain.id()),
|
||||||
|
nonce: 1,
|
||||||
|
gas_price: header.base_fee_per_gas.unwrap().into(),
|
||||||
|
gas_limit: header.gas_used,
|
||||||
|
to: TxKind::Call(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS),
|
||||||
|
// `MIN_WITHDRAWAL_REQUEST_FEE`
|
||||||
|
value: U256::from(2),
|
||||||
|
input,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let provider = EthEvmConfig::new(chain_spec);
|
||||||
|
|
||||||
|
let mut executor = provider.batch_executor(db);
|
||||||
|
|
||||||
|
let BlockExecutionResult { receipts, requests, .. } = executor
|
||||||
|
.execute_one(
|
||||||
|
&Block { header, body: BlockBody { transactions: vec![tx], ..Default::default() } }
|
||||||
|
.try_into_recovered()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let receipt = receipts.first().unwrap();
|
||||||
|
assert!(receipt.success);
|
||||||
|
|
||||||
|
// There should be exactly one entry with withdrawal requests
|
||||||
|
assert_eq!(requests.len(), 1);
|
||||||
|
assert_eq!(requests[0][0], 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn block_gas_limit_error() {
|
||||||
|
// Create a chain specification with fork conditions set for Prague
|
||||||
|
let chain_spec = Arc::new(
|
||||||
|
ChainSpecBuilder::from(&*MAINNET)
|
||||||
|
.shanghai_activated()
|
||||||
|
.with_fork(EthereumHardfork::Prague, ForkCondition::Timestamp(0))
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create a state provider with the withdrawal requests contract pre-deployed
|
||||||
|
let mut db = create_database_with_withdrawal_requests_contract();
|
||||||
|
|
||||||
|
// Generate a new key pair for the sender
|
||||||
|
let sender_key_pair = generators::generate_key(&mut generators::rng());
|
||||||
|
// Get the sender's address from the public key
|
||||||
|
let sender_address = public_key_to_address(sender_key_pair.public_key());
|
||||||
|
|
||||||
|
// Insert the sender account into the state with a nonce of 1 and a balance of 1 ETH in Wei
|
||||||
|
db.insert_account_info(
|
||||||
|
sender_address,
|
||||||
|
AccountInfo { nonce: 1, balance: U256::from(ETH_TO_WEI), ..Default::default() },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Define the validator public key and withdrawal amount as fixed bytes
|
||||||
|
let validator_public_key = fixed_bytes!(
|
||||||
|
"111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
);
|
||||||
|
let withdrawal_amount = fixed_bytes!("2222222222222222");
|
||||||
|
// Concatenate the validator public key and withdrawal amount into a single byte array
|
||||||
|
let input: Bytes = [&validator_public_key[..], &withdrawal_amount[..]].concat().into();
|
||||||
|
// Ensure the input length is 56 bytes
|
||||||
|
assert_eq!(input.len(), 56);
|
||||||
|
|
||||||
|
// Create a genesis block header with a specified gas limit and gas used
|
||||||
|
let mut header = chain_spec.genesis_header().clone();
|
||||||
|
header.gas_limit = 1_500_000;
|
||||||
|
header.gas_used = 134_807;
|
||||||
|
header.receipts_root =
|
||||||
|
b256!("0xb31a3e47b902e9211c4d349af4e4c5604ce388471e79ca008907ae4616bb0ed3");
|
||||||
|
|
||||||
|
// Create a transaction with a gas limit higher than the block gas limit
|
||||||
|
let tx = sign_tx_with_key_pair(
|
||||||
|
sender_key_pair,
|
||||||
|
Transaction::Legacy(TxLegacy {
|
||||||
|
chain_id: Some(chain_spec.chain.id()),
|
||||||
|
nonce: 1,
|
||||||
|
gas_price: header.base_fee_per_gas.unwrap().into(),
|
||||||
|
gas_limit: 2_500_000, // higher than block gas limit
|
||||||
|
to: TxKind::Call(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS),
|
||||||
|
value: U256::from(1),
|
||||||
|
input,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create an executor from the state provider
|
||||||
|
let evm_config = EthEvmConfig::new(chain_spec);
|
||||||
|
let mut executor = evm_config.batch_executor(db);
|
||||||
|
|
||||||
|
// Execute the block and capture the result
|
||||||
|
let exec_result = executor.execute_one(
|
||||||
|
&Block { header, body: BlockBody { transactions: vec![tx], ..Default::default() } }
|
||||||
|
.try_into_recovered()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if the execution result is an error and assert the specific error type
|
||||||
|
match exec_result {
|
||||||
|
Ok(_) => panic!("Expected block gas limit error"),
|
||||||
|
Err(err) => assert!(matches!(
|
||||||
|
*err.as_validation().unwrap(),
|
||||||
|
BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
|
||||||
|
transaction_gas_limit: 2_500_000,
|
||||||
|
block_available_gas: 1_500_000,
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_balance_increment_not_duplicated() {
|
||||||
|
let chain_spec = Arc::new(
|
||||||
|
ChainSpecBuilder::from(&*MAINNET)
|
||||||
|
.shanghai_activated()
|
||||||
|
.cancun_activated()
|
||||||
|
.prague_activated()
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let withdrawal_recipient = address!("0x1000000000000000000000000000000000000000");
|
||||||
|
|
||||||
|
let mut db = CacheDB::new(EmptyDB::default());
|
||||||
|
let initial_balance = 100;
|
||||||
|
db.insert_account_info(
|
||||||
|
withdrawal_recipient,
|
||||||
|
AccountInfo { balance: U256::from(initial_balance), nonce: 1, ..Default::default() },
|
||||||
|
);
|
||||||
|
|
||||||
|
let withdrawal =
|
||||||
|
Withdrawal { index: 0, validator_index: 0, address: withdrawal_recipient, amount: 1 };
|
||||||
|
|
||||||
|
let header = Header {
|
||||||
|
timestamp: 1,
|
||||||
|
number: 1,
|
||||||
|
excess_blob_gas: Some(0),
|
||||||
|
parent_beacon_block_root: Some(B256::random()),
|
||||||
|
..Header::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let block = &RecoveredBlock::new_unhashed(
|
||||||
|
Block {
|
||||||
|
header,
|
||||||
|
body: BlockBody {
|
||||||
|
transactions: vec![],
|
||||||
|
ommers: vec![],
|
||||||
|
withdrawals: Some(vec![withdrawal].into()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
|
||||||
|
let provider = EthEvmConfig::new(chain_spec);
|
||||||
|
let executor = provider.batch_executor(db);
|
||||||
|
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let tx_clone = tx.clone();
|
||||||
|
|
||||||
|
let _output = executor
|
||||||
|
.execute_with_state_hook(block, move |_, state: &EvmState| {
|
||||||
|
if let Some(account) = state.get(&withdrawal_recipient) {
|
||||||
|
let _ = tx_clone.send(account.info.balance);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.expect("Block execution should succeed");
|
||||||
|
|
||||||
|
drop(tx);
|
||||||
|
let balance_changes: Vec<U256> = rx.try_iter().collect();
|
||||||
|
|
||||||
|
if let Some(final_balance) = balance_changes.last() {
|
||||||
|
let expected_final_balance = U256::from(initial_balance) + U256::from(1_000_000_000); // initial + 1 Gwei in Wei
|
||||||
|
assert_eq!(
|
||||||
|
*final_balance, expected_final_balance,
|
||||||
|
"Final balance should match expected value after withdrawal"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
//! Ethereum EVM support
|
//! Ethereum EVM support
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
|
#[allow(deprecated)]
|
||||||
pub use reth_evm_ethereum::execute::EthExecutorProvider;
|
pub use reth_evm_ethereum::execute::EthExecutorProvider;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use reth_evm_ethereum::{EthEvm, EthEvmConfig};
|
pub use reth_evm_ethereum::{EthEvm, EthEvmConfig};
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ use revm as _;
|
|||||||
pub use reth_ethereum_engine_primitives::EthEngineTypes;
|
pub use reth_ethereum_engine_primitives::EthEngineTypes;
|
||||||
|
|
||||||
pub mod evm;
|
pub mod evm;
|
||||||
pub use evm::{EthEvmConfig, EthExecutorProvider};
|
pub use evm::EthEvmConfig;
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
|
pub use evm::EthExecutorProvider;
|
||||||
|
|
||||||
pub use reth_ethereum_consensus as consensus;
|
pub use reth_ethereum_consensus as consensus;
|
||||||
pub mod node;
|
pub mod node;
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ mod tests {
|
|||||||
BackfillJobFactory,
|
BackfillJobFactory,
|
||||||
};
|
};
|
||||||
use reth_db_common::init::init_genesis;
|
use reth_db_common::init::init_genesis;
|
||||||
use reth_evm_ethereum::execute::EthExecutorProvider;
|
use reth_evm_ethereum::EthEvmConfig;
|
||||||
use reth_primitives_traits::crypto::secp256k1::public_key_to_address;
|
use reth_primitives_traits::crypto::secp256k1::public_key_to_address;
|
||||||
use reth_provider::{
|
use reth_provider::{
|
||||||
providers::BlockchainProvider, test_utils::create_test_provider_factory_with_chain_spec,
|
providers::BlockchainProvider, test_utils::create_test_provider_factory_with_chain_spec,
|
||||||
@@ -264,7 +264,7 @@ mod tests {
|
|||||||
|
|
||||||
let chain_spec = chain_spec(address);
|
let chain_spec = chain_spec(address);
|
||||||
|
|
||||||
let executor = EthExecutorProvider::ethereum(chain_spec.clone());
|
let executor = EthEvmConfig::ethereum(chain_spec.clone());
|
||||||
let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone());
|
let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone());
|
||||||
init_genesis(&provider_factory)?;
|
init_genesis(&provider_factory)?;
|
||||||
let blockchain_db = BlockchainProvider::new(provider_factory.clone())?;
|
let blockchain_db = BlockchainProvider::new(provider_factory.clone())?;
|
||||||
@@ -300,7 +300,7 @@ mod tests {
|
|||||||
|
|
||||||
let chain_spec = chain_spec(address);
|
let chain_spec = chain_spec(address);
|
||||||
|
|
||||||
let executor = EthExecutorProvider::ethereum(chain_spec.clone());
|
let executor = EthEvmConfig::ethereum(chain_spec.clone());
|
||||||
let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone());
|
let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone());
|
||||||
init_genesis(&provider_factory)?;
|
init_genesis(&provider_factory)?;
|
||||||
let blockchain_db = BlockchainProvider::new(provider_factory.clone())?;
|
let blockchain_db = BlockchainProvider::new(provider_factory.clone())?;
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use reth_db_common::init::init_genesis;
|
use reth_db_common::init::init_genesis;
|
||||||
use reth_evm_ethereum::execute::EthExecutorProvider;
|
use reth_evm_ethereum::EthEvmConfig;
|
||||||
use reth_primitives_traits::crypto::secp256k1::public_key_to_address;
|
use reth_primitives_traits::crypto::secp256k1::public_key_to_address;
|
||||||
use reth_provider::{
|
use reth_provider::{
|
||||||
providers::BlockchainProvider, test_utils::create_test_provider_factory_with_chain_spec,
|
providers::BlockchainProvider, test_utils::create_test_provider_factory_with_chain_spec,
|
||||||
@@ -265,7 +265,7 @@ mod tests {
|
|||||||
|
|
||||||
let chain_spec = chain_spec(address);
|
let chain_spec = chain_spec(address);
|
||||||
|
|
||||||
let executor = EthExecutorProvider::ethereum(chain_spec.clone());
|
let executor = EthEvmConfig::ethereum(chain_spec.clone());
|
||||||
let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone());
|
let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone());
|
||||||
init_genesis(&provider_factory)?;
|
init_genesis(&provider_factory)?;
|
||||||
let blockchain_db = BlockchainProvider::new(provider_factory.clone())?;
|
let blockchain_db = BlockchainProvider::new(provider_factory.clone())?;
|
||||||
@@ -302,7 +302,7 @@ mod tests {
|
|||||||
|
|
||||||
let chain_spec = chain_spec(address);
|
let chain_spec = chain_spec(address);
|
||||||
|
|
||||||
let executor = EthExecutorProvider::ethereum(chain_spec.clone());
|
let executor = EthEvmConfig::ethereum(chain_spec.clone());
|
||||||
let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone());
|
let provider_factory = create_test_provider_factory_with_chain_spec(chain_spec.clone());
|
||||||
init_genesis(&provider_factory)?;
|
init_genesis(&provider_factory)?;
|
||||||
let blockchain_db = BlockchainProvider::new(provider_factory.clone())?;
|
let blockchain_db = BlockchainProvider::new(provider_factory.clone())?;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use reth_evm::{
|
|||||||
execute::{BlockExecutionOutput, Executor},
|
execute::{BlockExecutionOutput, Executor},
|
||||||
ConfigureEvm,
|
ConfigureEvm,
|
||||||
};
|
};
|
||||||
use reth_evm_ethereum::{execute::EthExecutorProvider, EthEvmConfig};
|
use reth_evm_ethereum::EthEvmConfig;
|
||||||
use reth_node_api::FullNodePrimitives;
|
use reth_node_api::FullNodePrimitives;
|
||||||
use reth_primitives_traits::{Block as _, RecoveredBlock};
|
use reth_primitives_traits::{Block as _, RecoveredBlock};
|
||||||
use reth_provider::{
|
use reth_provider::{
|
||||||
@@ -68,7 +68,7 @@ where
|
|||||||
let provider = provider_factory.provider()?;
|
let provider = provider_factory.provider()?;
|
||||||
|
|
||||||
// Execute the block to produce a block execution output
|
// Execute the block to produce a block execution output
|
||||||
let mut block_execution_output = EthExecutorProvider::ethereum(chain_spec)
|
let mut block_execution_output = EthEvmConfig::ethereum(chain_spec)
|
||||||
.batch_executor(StateProviderDatabase::new(LatestStateProviderRef::new(&provider)))
|
.batch_executor(StateProviderDatabase::new(LatestStateProviderRef::new(&provider)))
|
||||||
.execute(block)?;
|
.execute(block)?;
|
||||||
block_execution_output.state.reverts.sort();
|
block_execution_output.state.reverts.sort();
|
||||||
|
|||||||
@@ -663,7 +663,7 @@ mod tests {
|
|||||||
use futures::{StreamExt, TryStreamExt};
|
use futures::{StreamExt, TryStreamExt};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use reth_db_common::init::init_genesis;
|
use reth_db_common::init::init_genesis;
|
||||||
use reth_evm_ethereum::{execute::EthExecutorProvider, EthEvmConfig};
|
use reth_evm_ethereum::EthEvmConfig;
|
||||||
use reth_primitives_traits::RecoveredBlock;
|
use reth_primitives_traits::RecoveredBlock;
|
||||||
use reth_provider::{
|
use reth_provider::{
|
||||||
providers::BlockchainProvider, test_utils::create_test_provider_factory, BlockReader,
|
providers::BlockchainProvider, test_utils::create_test_provider_factory, BlockReader,
|
||||||
@@ -1107,7 +1107,7 @@ mod tests {
|
|||||||
"test_exex".to_string(),
|
"test_exex".to_string(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
provider,
|
provider,
|
||||||
EthExecutorProvider::mainnet(),
|
EthEvmConfig::mainnet(),
|
||||||
wal.handle(),
|
wal.handle(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1162,7 +1162,7 @@ mod tests {
|
|||||||
"test_exex".to_string(),
|
"test_exex".to_string(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
provider,
|
provider,
|
||||||
EthExecutorProvider::mainnet(),
|
EthEvmConfig::mainnet(),
|
||||||
wal.handle(),
|
wal.handle(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1212,7 +1212,7 @@ mod tests {
|
|||||||
"test_exex".to_string(),
|
"test_exex".to_string(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
provider,
|
provider,
|
||||||
EthExecutorProvider::mainnet(),
|
EthEvmConfig::mainnet(),
|
||||||
wal.handle(),
|
wal.handle(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1255,7 +1255,7 @@ mod tests {
|
|||||||
"test_exex".to_string(),
|
"test_exex".to_string(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
provider,
|
provider,
|
||||||
EthExecutorProvider::mainnet(),
|
EthEvmConfig::mainnet(),
|
||||||
wal.handle(),
|
wal.handle(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1315,7 +1315,7 @@ mod tests {
|
|||||||
"test_exex".to_string(),
|
"test_exex".to_string(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
provider.clone(),
|
provider.clone(),
|
||||||
EthExecutorProvider::mainnet(),
|
EthEvmConfig::mainnet(),
|
||||||
wal.handle(),
|
wal.handle(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -453,7 +453,7 @@ mod tests {
|
|||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use reth_db_common::init::init_genesis;
|
use reth_db_common::init::init_genesis;
|
||||||
use reth_ethereum_primitives::Block;
|
use reth_ethereum_primitives::Block;
|
||||||
use reth_evm_ethereum::execute::EthExecutorProvider;
|
use reth_evm_ethereum::EthEvmConfig;
|
||||||
use reth_primitives_traits::Block as _;
|
use reth_primitives_traits::Block as _;
|
||||||
use reth_provider::{
|
use reth_provider::{
|
||||||
providers::BlockchainProvider, test_utils::create_test_provider_factory, BlockWriter,
|
providers::BlockchainProvider, test_utils::create_test_provider_factory, BlockWriter,
|
||||||
@@ -511,7 +511,7 @@ mod tests {
|
|||||||
let mut notifications = ExExNotificationsWithoutHead::new(
|
let mut notifications = ExExNotificationsWithoutHead::new(
|
||||||
node_head,
|
node_head,
|
||||||
provider,
|
provider,
|
||||||
EthExecutorProvider::mainnet(),
|
EthEvmConfig::mainnet(),
|
||||||
notifications_rx,
|
notifications_rx,
|
||||||
wal.handle(),
|
wal.handle(),
|
||||||
)
|
)
|
||||||
@@ -579,7 +579,7 @@ mod tests {
|
|||||||
let mut notifications = ExExNotificationsWithoutHead::new(
|
let mut notifications = ExExNotificationsWithoutHead::new(
|
||||||
node_head,
|
node_head,
|
||||||
provider,
|
provider,
|
||||||
EthExecutorProvider::mainnet(),
|
EthEvmConfig::mainnet(),
|
||||||
notifications_rx,
|
notifications_rx,
|
||||||
wal.handle(),
|
wal.handle(),
|
||||||
)
|
)
|
||||||
@@ -618,7 +618,7 @@ mod tests {
|
|||||||
provider_rw.commit()?;
|
provider_rw.commit()?;
|
||||||
let node_head_notification = ExExNotification::ChainCommitted {
|
let node_head_notification = ExExNotification::ChainCommitted {
|
||||||
new: Arc::new(
|
new: Arc::new(
|
||||||
BackfillJobFactory::new(EthExecutorProvider::mainnet(), provider.clone())
|
BackfillJobFactory::new(EthEvmConfig::mainnet(), provider.clone())
|
||||||
.backfill(node_head.number..=node_head.number)
|
.backfill(node_head.number..=node_head.number)
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| eyre::eyre!("failed to backfill"))??,
|
.ok_or_else(|| eyre::eyre!("failed to backfill"))??,
|
||||||
@@ -660,7 +660,7 @@ mod tests {
|
|||||||
let mut notifications = ExExNotificationsWithoutHead::new(
|
let mut notifications = ExExNotificationsWithoutHead::new(
|
||||||
node_head,
|
node_head,
|
||||||
provider,
|
provider,
|
||||||
EthExecutorProvider::mainnet(),
|
EthEvmConfig::mainnet(),
|
||||||
notifications_rx,
|
notifications_rx,
|
||||||
wal.handle(),
|
wal.handle(),
|
||||||
)
|
)
|
||||||
@@ -736,7 +736,7 @@ mod tests {
|
|||||||
let mut notifications = ExExNotificationsWithoutHead::new(
|
let mut notifications = ExExNotificationsWithoutHead::new(
|
||||||
node_head,
|
node_head,
|
||||||
provider,
|
provider,
|
||||||
EthExecutorProvider::mainnet(),
|
EthEvmConfig::mainnet(),
|
||||||
notifications_rx,
|
notifications_rx,
|
||||||
wal.handle(),
|
wal.handle(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
//! # use reth_downloaders::bodies::bodies::BodiesDownloaderBuilder;
|
//! # use reth_downloaders::bodies::bodies::BodiesDownloaderBuilder;
|
||||||
//! # use reth_downloaders::headers::reverse_headers::ReverseHeadersDownloaderBuilder;
|
//! # use reth_downloaders::headers::reverse_headers::ReverseHeadersDownloaderBuilder;
|
||||||
//! # use reth_network_p2p::test_utils::{TestBodiesClient, TestHeadersClient};
|
//! # use reth_network_p2p::test_utils::{TestBodiesClient, TestHeadersClient};
|
||||||
//! # use reth_evm_ethereum::execute::EthExecutorProvider;
|
|
||||||
//! # use alloy_primitives::B256;
|
//! # use alloy_primitives::B256;
|
||||||
//! # use reth_chainspec::MAINNET;
|
//! # use reth_chainspec::MAINNET;
|
||||||
//! # use reth_prune_types::PruneModes;
|
//! # use reth_prune_types::PruneModes;
|
||||||
@@ -47,7 +46,7 @@
|
|||||||
//! # provider_factory.clone()
|
//! # provider_factory.clone()
|
||||||
//! # );
|
//! # );
|
||||||
//! # let (tip_tx, tip_rx) = watch::channel(B256::default());
|
//! # let (tip_tx, tip_rx) = watch::channel(B256::default());
|
||||||
//! # let executor_provider = EthExecutorProvider::mainnet();
|
//! # let executor_provider = EthEvmConfig::mainnet();
|
||||||
//! # let static_file_producer = StaticFileProducer::new(
|
//! # let static_file_producer = StaticFileProducer::new(
|
||||||
//! # provider_factory.clone(),
|
//! # provider_factory.clone(),
|
||||||
//! # PruneModes::default()
|
//! # PruneModes::default()
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
use reth_ethereum_consensus::EthBeaconConsensus;
|
use reth_ethereum_consensus::EthBeaconConsensus;
|
||||||
use reth_ethereum_primitives::Block;
|
use reth_ethereum_primitives::Block;
|
||||||
use reth_evm_ethereum::execute::EthExecutorProvider;
|
use reth_evm_ethereum::EthEvmConfig;
|
||||||
use reth_exex::ExExManagerHandle;
|
use reth_exex::ExExManagerHandle;
|
||||||
use reth_primitives_traits::{Account, Bytecode, SealedBlock};
|
use reth_primitives_traits::{Account, Bytecode, SealedBlock};
|
||||||
use reth_provider::{
|
use reth_provider::{
|
||||||
@@ -157,7 +157,7 @@ mod tests {
|
|||||||
// Check execution and create receipts and changesets according to the pruning
|
// Check execution and create receipts and changesets according to the pruning
|
||||||
// configuration
|
// configuration
|
||||||
let mut execution_stage = ExecutionStage::new(
|
let mut execution_stage = ExecutionStage::new(
|
||||||
EthExecutorProvider::ethereum(Arc::new(
|
EthEvmConfig::ethereum(Arc::new(
|
||||||
ChainSpecBuilder::mainnet().berlin_activated().build(),
|
ChainSpecBuilder::mainnet().berlin_activated().build(),
|
||||||
)),
|
)),
|
||||||
Arc::new(EthBeaconConsensus::new(Arc::new(
|
Arc::new(EthBeaconConsensus::new(Arc::new(
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use reth_db_common::init::{insert_genesis_hashes, insert_genesis_history, insert
|
|||||||
use reth_ethereum_consensus::{validate_block_post_execution, EthBeaconConsensus};
|
use reth_ethereum_consensus::{validate_block_post_execution, EthBeaconConsensus};
|
||||||
use reth_ethereum_primitives::Block;
|
use reth_ethereum_primitives::Block;
|
||||||
use reth_evm::{execute::Executor, ConfigureEvm};
|
use reth_evm::{execute::Executor, ConfigureEvm};
|
||||||
use reth_evm_ethereum::{execute::EthExecutorProvider, EthEvmConfig};
|
use reth_evm_ethereum::EthEvmConfig;
|
||||||
use reth_primitives_traits::{RecoveredBlock, SealedBlock};
|
use reth_primitives_traits::{RecoveredBlock, SealedBlock};
|
||||||
use reth_provider::{
|
use reth_provider::{
|
||||||
test_utils::create_test_provider_factory_with_chain_spec, BlockWriter, DatabaseProviderFactory,
|
test_utils::create_test_provider_factory_with_chain_spec, BlockWriter, DatabaseProviderFactory,
|
||||||
@@ -212,7 +212,7 @@ fn run_case(case: &BlockchainTest) -> Result<(), Error> {
|
|||||||
// Decode blocks
|
// Decode blocks
|
||||||
let blocks = decode_blocks(&case.blocks)?;
|
let blocks = decode_blocks(&case.blocks)?;
|
||||||
|
|
||||||
let executor_provider = EthExecutorProvider::ethereum(chain_spec.clone());
|
let executor_provider = EthEvmConfig::ethereum(chain_spec.clone());
|
||||||
let mut parent = genesis_block;
|
let mut parent = genesis_block;
|
||||||
let mut program_inputs = Vec::new();
|
let mut program_inputs = Vec::new();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user