#![doc(
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
use core::convert::Infallible;
use alloc::{sync::Arc, vec::Vec};
use alloy_consensus::Header;
use alloy_primitives::{Address, Bytes, TxKind, U256};
use reth_chainspec::{ChainSpec, Head};
use reth_evm::{ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes};
use reth_primitives::{transaction::FillTxEnv, TransactionSigned};
use revm_primitives::{
AnalysisKind, BlobExcessGasAndPrice, BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, Env, SpecId, TxEnv,
};
mod config;
use alloy_eips::eip1559::INITIAL_BASE_FEE;
pub use config::{revm_spec, revm_spec_by_timestamp_after_merge};
use reth_ethereum_forks::EthereumHardfork;
pub mod execute;
pub mod dao_fork;
pub mod eip6110;
#[derive(Debug, Clone)]
pub struct EthEvmConfig {
chain_spec: Arc<ChainSpec>,
}
impl EthEvmConfig {
pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
Self { chain_spec }
}
pub fn chain_spec(&self) -> &ChainSpec {
&self.chain_spec
}
}
impl ConfigureEvmEnv for EthEvmConfig {
type Header = Header;
type Error = Infallible;
fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) {
transaction.fill_tx_env(tx_env, sender);
}
fn fill_tx_env_system_contract_call(
&self,
env: &mut Env,
caller: Address,
contract: Address,
data: Bytes,
) {
#[allow(clippy::needless_update)] let tx = TxEnv {
caller,
transact_to: TxKind::Call(contract),
nonce: None,
gas_limit: 30_000_000,
value: U256::ZERO,
data,
gas_price: U256::ZERO,
chain_id: None,
gas_priority_fee: None,
access_list: Vec::new(),
blob_hashes: Vec::new(),
max_fee_per_blob_gas: None,
..Default::default()
};
env.tx = tx;
env.block.gas_limit = U256::from(env.tx.gas_limit);
env.block.basefee = U256::ZERO;
}
fn fill_cfg_env(
&self,
cfg_env: &mut CfgEnvWithHandlerCfg,
header: &Header,
total_difficulty: U256,
) {
let spec_id = config::revm_spec(
self.chain_spec(),
&Head {
number: header.number,
timestamp: header.timestamp,
difficulty: header.difficulty,
total_difficulty,
hash: Default::default(),
},
);
cfg_env.chain_id = self.chain_spec.chain().id();
cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse;
cfg_env.handler_cfg.spec_id = spec_id;
}
fn next_cfg_and_block_env(
&self,
parent: &Self::Header,
attributes: NextBlockEnvAttributes,
) -> Result<(CfgEnvWithHandlerCfg, BlockEnv), Self::Error> {
let cfg = CfgEnv::default().with_chain_id(self.chain_spec.chain().id());
let spec_id = revm_spec_by_timestamp_after_merge(&self.chain_spec, attributes.timestamp);
let blob_excess_gas_and_price = parent
.next_block_excess_blob_gas()
.or_else(|| (spec_id == SpecId::CANCUN).then_some(0))
.map(BlobExcessGasAndPrice::new);
let mut basefee = parent.next_block_base_fee(
self.chain_spec.base_fee_params_at_timestamp(attributes.timestamp),
);
let mut gas_limit = U256::from(parent.gas_limit);
if self.chain_spec.fork(EthereumHardfork::London).transitions_at_block(parent.number + 1) {
let elasticity_multiplier = self
.chain_spec
.base_fee_params_at_timestamp(attributes.timestamp)
.elasticity_multiplier;
gas_limit *= U256::from(elasticity_multiplier);
basefee = Some(INITIAL_BASE_FEE)
}
let block_env = BlockEnv {
number: U256::from(parent.number + 1),
coinbase: attributes.suggested_fee_recipient,
timestamp: U256::from(attributes.timestamp),
difficulty: U256::ZERO,
prevrandao: Some(attributes.prev_randao),
gas_limit,
basefee: basefee.map(U256::from).unwrap_or_default(),
blob_excess_gas_and_price,
};
Ok((CfgEnvWithHandlerCfg::new_with_spec_id(cfg, spec_id), block_env))
}
}
impl ConfigureEvm for EthEvmConfig {
type DefaultExternalContext<'a> = ();
fn default_external_context<'a>(&self) -> Self::DefaultExternalContext<'a> {}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_consensus::{constants::KECCAK_EMPTY, Header};
use alloy_genesis::Genesis;
use alloy_primitives::{B256, U256};
use reth_chainspec::{Chain, ChainSpec, MAINNET};
use reth_evm::execute::ProviderError;
use reth_primitives::revm_primitives::{BlockEnv, CfgEnv, SpecId};
use reth_revm::{
db::{CacheDB, EmptyDBTyped},
inspectors::NoOpInspector,
JournaledState,
};
use revm_primitives::{CfgEnvWithHandlerCfg, EnvWithHandlerCfg, HandlerCfg};
use std::collections::HashSet;
#[test]
fn test_fill_cfg_and_block_env() {
let mut cfg_env = CfgEnvWithHandlerCfg::new_with_spec_id(CfgEnv::default(), SpecId::LATEST);
let mut block_env = BlockEnv::default();
let header = Header::default();
let chain_spec = ChainSpec::builder()
.chain(Chain::mainnet())
.genesis(Genesis::default())
.london_activated()
.paris_activated()
.shanghai_activated()
.build();
let total_difficulty = U256::ZERO;
EthEvmConfig::new(Arc::new(chain_spec.clone())).fill_cfg_and_block_env(
&mut cfg_env,
&mut block_env,
&header,
total_difficulty,
);
assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
}
#[test]
#[allow(clippy::needless_update)]
fn test_evm_configure() {
let evm_config = EthEvmConfig::new(MAINNET.clone());
let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
let evm = evm_config.evm(db);
assert_eq!(evm.context.evm.inner.env, Box::default());
assert_eq!(
evm.context.evm.inner.journaled_state,
JournaledState::new(SpecId::LATEST, HashSet::default())
);
assert!(evm.context.evm.inner.db.accounts.is_empty());
assert!(evm.context.evm.inner.db.block_hashes.is_empty());
assert_eq!(evm.context.evm.inner.db.contracts.len(), 2);
assert!(evm.context.evm.inner.db.contracts.contains_key(&KECCAK_EMPTY));
assert!(evm.context.evm.inner.db.contracts.contains_key(&B256::ZERO));
assert!(evm.context.evm.inner.db.logs.is_empty());
assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
}
#[test]
#[allow(clippy::needless_update)]
fn test_evm_with_env_default_spec() {
let evm_config = EthEvmConfig::new(MAINNET.clone());
let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
let env_with_handler = EnvWithHandlerCfg::default();
let evm = evm_config.evm_with_env(db, env_with_handler.clone());
assert_eq!(evm.context.evm.env, env_with_handler.env);
assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
}
#[test]
#[allow(clippy::needless_update)]
fn test_evm_with_env_custom_cfg() {
let evm_config = EthEvmConfig::new(MAINNET.clone());
let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
let cfg = CfgEnv::default().with_chain_id(111);
let env_with_handler = EnvWithHandlerCfg {
env: Box::new(Env {
cfg: cfg.clone(),
block: BlockEnv::default(),
tx: TxEnv::default(),
}),
handler_cfg: Default::default(),
};
let evm = evm_config.evm_with_env(db, env_with_handler);
assert_eq!(evm.context.evm.inner.env.cfg, cfg);
assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
}
#[test]
#[allow(clippy::needless_update)]
fn test_evm_with_env_custom_block_and_tx() {
let evm_config = EthEvmConfig::new(MAINNET.clone());
let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
let block = BlockEnv {
basefee: U256::from(1000),
gas_limit: U256::from(10_000_000),
number: U256::from(42),
..Default::default()
};
let tx = TxEnv { gas_limit: 5_000_000, gas_price: U256::from(50), ..Default::default() };
let env_with_handler = EnvWithHandlerCfg {
env: Box::new(Env { cfg: CfgEnv::default(), block, tx }),
handler_cfg: Default::default(),
};
let evm = evm_config.evm_with_env(db, env_with_handler.clone());
assert_eq!(evm.context.evm.env.block, env_with_handler.env.block);
assert_eq!(evm.context.evm.env.tx, env_with_handler.env.tx);
assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
}
#[test]
#[allow(clippy::needless_update)]
fn test_evm_with_spec_id() {
let evm_config = EthEvmConfig::new(MAINNET.clone());
let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
let handler_cfg = HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() };
let env_with_handler = EnvWithHandlerCfg { env: Box::new(Env::default()), handler_cfg };
let evm = evm_config.evm_with_env(db, env_with_handler);
assert_eq!(evm.handler.spec_id(), SpecId::CONSTANTINOPLE);
assert_eq!(
evm.handler.cfg,
HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() }
);
}
#[test]
#[allow(clippy::needless_update)]
fn test_evm_with_inspector() {
let evm_config = EthEvmConfig::new(MAINNET.clone());
let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
let noop = NoOpInspector;
let evm = evm_config.evm_with_inspector(db, noop);
assert_eq!(evm.context.external, noop);
assert_eq!(evm.context.evm.inner.env, Box::default());
assert_eq!(
evm.context.evm.inner.journaled_state,
JournaledState::new(SpecId::LATEST, HashSet::default())
);
assert!(evm.context.evm.inner.db.accounts.is_empty());
assert!(evm.context.evm.inner.db.block_hashes.is_empty());
assert_eq!(evm.context.evm.inner.db.contracts.len(), 2);
assert!(evm.context.evm.inner.db.contracts.contains_key(&KECCAK_EMPTY));
assert!(evm.context.evm.inner.db.contracts.contains_key(&B256::ZERO));
assert!(evm.context.evm.inner.db.logs.is_empty());
assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
}
#[test]
#[allow(clippy::needless_update)]
fn test_evm_with_env_and_default_inspector() {
let evm_config = EthEvmConfig::new(MAINNET.clone());
let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
let env_with_handler = EnvWithHandlerCfg::default();
let evm =
evm_config.evm_with_env_and_inspector(db, env_with_handler.clone(), NoOpInspector);
assert_eq!(evm.context.evm.env, env_with_handler.env);
assert_eq!(evm.context.external, NoOpInspector);
assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
}
#[test]
#[allow(clippy::needless_update)]
fn test_evm_with_env_inspector_and_custom_cfg() {
let evm_config = EthEvmConfig::new(MAINNET.clone());
let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
let cfg = CfgEnv::default().with_chain_id(111);
let block = BlockEnv::default();
let tx = TxEnv::default();
let env_with_handler = EnvWithHandlerCfg {
env: Box::new(Env { cfg: cfg.clone(), block, tx }),
handler_cfg: Default::default(),
};
let evm = evm_config.evm_with_env_and_inspector(db, env_with_handler, NoOpInspector);
assert_eq!(evm.context.evm.env.cfg, cfg);
assert_eq!(evm.context.external, NoOpInspector);
assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
}
#[test]
#[allow(clippy::needless_update)]
fn test_evm_with_env_inspector_and_custom_block_tx() {
let evm_config = EthEvmConfig::new(MAINNET.clone());
let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
let block = BlockEnv {
basefee: U256::from(1000),
gas_limit: U256::from(10_000_000),
number: U256::from(42),
..Default::default()
};
let tx = TxEnv { gas_limit: 5_000_000, gas_price: U256::from(50), ..Default::default() };
let env_with_handler = EnvWithHandlerCfg {
env: Box::new(Env { cfg: CfgEnv::default(), block, tx }),
handler_cfg: Default::default(),
};
let evm =
evm_config.evm_with_env_and_inspector(db, env_with_handler.clone(), NoOpInspector);
assert_eq!(evm.context.evm.env.block, env_with_handler.env.block);
assert_eq!(evm.context.evm.env.tx, env_with_handler.env.tx);
assert_eq!(evm.context.external, NoOpInspector);
assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
}
#[test]
#[allow(clippy::needless_update)]
fn test_evm_with_env_inspector_and_spec_id() {
let evm_config = EthEvmConfig::new(MAINNET.clone());
let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
let handler_cfg = HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() };
let env_with_handler = EnvWithHandlerCfg { env: Box::new(Env::default()), handler_cfg };
let evm =
evm_config.evm_with_env_and_inspector(db, env_with_handler.clone(), NoOpInspector);
assert_eq!(evm.handler.spec_id(), SpecId::CONSTANTINOPLE);
assert_eq!(evm.context.evm.env, env_with_handler.env);
assert_eq!(evm.context.external, NoOpInspector);
assert_eq!(
evm.handler.cfg,
HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() }
);
}
}