diff --git a/Cargo.lock b/Cargo.lock index b056d715b1..9820c7f05a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -137,8 +137,9 @@ dependencies = [ [[package]] name = "alloy-eip7928" -version = "0.2.0" -source = "git+https://github.com/rakita/alloy-eips.git?rev=734beaf#734beafea409bdd87b3a4e33ac72ac68654bb8dd" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6adac476434bf024279164dcdca299309f0c7d1e3557024eb7a83f8d9d01c6b5" dependencies = [ "alloy-primitives", "alloy-rlp", diff --git a/Cargo.toml b/Cargo.toml index 867a136394..e2f10307c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ ee-tests = { path = "crates/ee-tests", package = "revm-ee-tests", version = "0.1 # alloy alloy-eip2930 = { version = "0.2.1", default-features = false } alloy-eip7702 = { version = "0.6.1", default-features = false } -alloy-eip7928 = { version = "0.2.0", default-features = false, git = "https://github.com/rakita/alloy-eips.git", rev = "734beaf" } +alloy-eip7928 = { version = "0.3.0", default-features = false } alloy-primitives = { version = "1.5.0", default-features = false } # alloy in examples, revme or feature flagged. diff --git a/bins/revme/src/cmd/blockchaintest.rs b/bins/revme/src/cmd/blockchaintest.rs index 1ddc3da260..928043dd9c 100644 --- a/bins/revme/src/cmd/blockchaintest.rs +++ b/bins/revme/src/cmd/blockchaintest.rs @@ -481,7 +481,7 @@ fn print_error_with_state( // Print configuration environment eprintln!("\n📋 Configuration Environment:"); - eprintln!(" Spec ID: {:?}", debug_info.cfg_env.spec); + eprintln!(" Spec ID: {:?}", debug_info.cfg_env.spec()); eprintln!(" Chain ID: {}", debug_info.cfg_env.chain_id); eprintln!( " Limit contract code size: {:?}", @@ -714,7 +714,7 @@ fn execute_blockchain_test( // Setup configuration based on fork let spec_id = fork_to_spec_id(test_case.network); let mut cfg = CfgEnv::default(); - cfg.spec = spec_id; + cfg.set_spec_and_mainnet_gas_params(spec_id); // Genesis block is not used yet. let mut parent_block_hash = Some(test_case.genesis_block_header.hash); @@ -764,7 +764,7 @@ fn execute_blockchain_test( // Create EVM context for each transaction to ensure fresh state access let evm_context = Context::mainnet() .with_block(&block_env) - .with_cfg(&cfg) + .with_cfg(cfg.clone()) .with_db(&mut state); // Build and execute with EVM - always use inspector when JSON output is enabled diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index 183f599c70..52e2c8efcb 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -2,10 +2,7 @@ use crate::cmd::statetest::merkle_trie::{compute_test_roots, TestValidationResul use indicatif::{ProgressBar, ProgressDrawTarget}; use revm::{ context::{block::BlockEnv, cfg::CfgEnv, tx::TxEnv}, - context_interface::{ - result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction}, - Cfg, - }, + context_interface::result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction}, database::{self, bal::EvmDatabaseError}, database_interface::EmptyDB, inspector::{inspectors::TracerEip3155, InspectCommitEvm}, @@ -340,12 +337,12 @@ pub fn execute_test_suite( continue; } - cfg.spec = spec_name.to_spec_id(); + cfg.set_spec_and_mainnet_gas_params(spec_name.to_spec_id()); // Configure max blobs per spec - if cfg.spec.is_enabled_in(SpecId::OSAKA) { + if cfg.spec().is_enabled_in(SpecId::OSAKA) { cfg.set_max_blobs_per_tx(6); - } else if cfg.spec.is_enabled_in(SpecId::PRAGUE) { + } else if cfg.spec().is_enabled_in(SpecId::PRAGUE) { cfg.set_max_blobs_per_tx(9); } else { cfg.set_max_blobs_per_tx(6); @@ -421,7 +418,8 @@ pub fn execute_test_suite( fn execute_single_test(ctx: TestExecutionContext) -> Result<(), TestErrorKind> { // Prepare state let mut cache = ctx.cache_state.clone(); - cache.set_state_clear_flag(ctx.cfg.spec.is_enabled_in(SpecId::SPURIOUS_DRAGON)); + let spec = ctx.cfg.spec(); + cache.set_state_clear_flag(spec.is_enabled_in(SpecId::SPURIOUS_DRAGON)); let mut state = database::State::builder() .with_cached_prestate(cache) .with_bundle_update() @@ -430,7 +428,7 @@ fn execute_single_test(ctx: TestExecutionContext) -> Result<(), TestErrorKind> { let evm_context = Context::mainnet() .with_block(ctx.block) .with_tx(ctx.tx) - .with_cfg(ctx.cfg) + .with_cfg(ctx.cfg.clone()) .with_db(&mut state); // Execute @@ -457,7 +455,7 @@ fn execute_single_test(ctx: TestExecutionContext) -> Result<(), TestErrorKind> { ctx.name, &exec_result, db, - ctx.cfg.spec(), + *ctx.cfg.spec(), ctx.print_json_outcome, ) } @@ -467,7 +465,7 @@ fn debug_failed_test(ctx: DebugContext) { // Re-run with tracing let mut cache = ctx.cache_state.clone(); - cache.set_state_clear_flag(ctx.cfg.spec.is_enabled_in(SpecId::SPURIOUS_DRAGON)); + cache.set_state_clear_flag(ctx.cfg.spec().is_enabled_in(SpecId::SPURIOUS_DRAGON)); let mut state = database::State::builder() .with_cached_prestate(cache) .with_bundle_update() @@ -477,7 +475,7 @@ fn debug_failed_test(ctx: DebugContext) { .with_db(&mut state) .with_block(ctx.block) .with_tx(ctx.tx) - .with_cfg(ctx.cfg) + .with_cfg(ctx.cfg.clone()) .build_mainnet_with_inspector(TracerEip3155::buffered(stderr()).without_summary()); let exec_result = evm.inspect_tx_commit(ctx.tx); @@ -489,7 +487,7 @@ fn debug_failed_test(ctx: DebugContext) { "\nState after:\n{}", evm.ctx.journaled_state.database.cache.pretty_print() ); - println!("\nSpecification: {:?}", ctx.cfg.spec); + println!("\nSpecification: {:?}", ctx.cfg.spec()); println!("\nTx: {:#?}", ctx.tx); println!("Block: {:#?}", ctx.block); println!("Cfg: {:#?}", ctx.cfg); diff --git a/crates/context/interface/src/cfg.rs b/crates/context/interface/src/cfg.rs index 11e5d99659..cb107693d6 100644 --- a/crates/context/interface/src/cfg.rs +++ b/crates/context/interface/src/cfg.rs @@ -1,4 +1,10 @@ //! Configuration for the EVM. Containing [`SpecId`]. + +pub mod gas; +pub mod gas_params; + +pub use gas_params::{GasId, GasParams}; + use auto_impl::auto_impl; use core::{fmt::Debug, hash::Hash}; use primitives::{hardfork::SpecId, Address, TxKind, U256}; @@ -66,6 +72,9 @@ pub trait Cfg { /// Returns the limit in bytes for the memory buffer. fn memory_limit(&self) -> u64; + + /// Returns the gas params for the EVM. + fn gas_params(&self) -> &GasParams; } /// What bytecode analysis to perform diff --git a/crates/context/interface/src/cfg/gas.rs b/crates/context/interface/src/cfg/gas.rs new file mode 100644 index 0000000000..5b122b7657 --- /dev/null +++ b/crates/context/interface/src/cfg/gas.rs @@ -0,0 +1,298 @@ +//! Gas constants and functions for gas calculation. + +use crate::{transaction::AccessListItemTr as _, Transaction, TransactionType}; +use primitives::{eip7702, hardfork::SpecId, U256}; + +/// Gas cost for operations that consume zero gas. +pub const ZERO: u64 = 0; +/// Base gas cost for basic operations. +pub const BASE: u64 = 2; + +/// Gas cost for very low-cost operations. +pub const VERYLOW: u64 = 3; +/// Gas cost for DATALOADN instruction. +pub const DATA_LOADN_GAS: u64 = 3; + +/// Gas cost for conditional jump instructions. +pub const CONDITION_JUMP_GAS: u64 = 4; +/// Gas cost for RETF instruction. +pub const RETF_GAS: u64 = 3; +/// Gas cost for DATALOAD instruction. +pub const DATA_LOAD_GAS: u64 = 4; + +/// Gas cost for low-cost operations. +pub const LOW: u64 = 5; +/// Gas cost for medium-cost operations. +pub const MID: u64 = 8; +/// Gas cost for high-cost operations. +pub const HIGH: u64 = 10; +/// Gas cost for JUMPDEST instruction. +pub const JUMPDEST: u64 = 1; +/// Gas cost for REFUND SELFDESTRUCT instruction. +pub const SELFDESTRUCT_REFUND: i64 = 24000; +/// Gas cost for CREATE instruction. +pub const CREATE: u64 = 32000; +/// Additional gas cost when a call transfers value. +pub const CALLVALUE: u64 = 9000; +/// Gas cost for creating a new account. +pub const NEWACCOUNT: u64 = 25000; +/// Base gas cost for EXP instruction. +pub const EXP: u64 = 10; +/// Gas cost per word for memory operations. +pub const MEMORY: u64 = 3; +/// Base gas cost for LOG instructions. +pub const LOG: u64 = 375; +/// Gas cost per byte of data in LOG instructions. +pub const LOGDATA: u64 = 8; +/// Gas cost per topic in LOG instructions. +pub const LOGTOPIC: u64 = 375; +/// Base gas cost for KECCAK256 instruction. +pub const KECCAK256: u64 = 30; +/// Gas cost per word for KECCAK256 instruction. +pub const KECCAK256WORD: u64 = 6; +/// Gas cost per word for copy operations. +pub const COPY: u64 = 3; +/// Gas cost for BLOCKHASH instruction. +pub const BLOCKHASH: u64 = 20; +/// Gas cost per byte for code deposit during contract creation. +pub const CODEDEPOSIT: u64 = 200; + +/// EIP-1884: Repricing for trie-size-dependent opcodes +pub const ISTANBUL_SLOAD_GAS: u64 = 800; +/// Gas cost for SSTORE when setting a storage slot from zero to non-zero. +pub const SSTORE_SET: u64 = 20000; +/// Gas cost for SSTORE when modifying an existing non-zero storage slot. +pub const SSTORE_RESET: u64 = 5000; +/// Gas refund for SSTORE when clearing a storage slot (setting to zero). +pub const REFUND_SSTORE_CLEARS: i64 = 15000; + +/// The standard cost of calldata token. +pub const STANDARD_TOKEN_COST: u64 = 4; +/// The cost of a non-zero byte in calldata. +pub const NON_ZERO_BYTE_DATA_COST: u64 = 68; +/// The multiplier for a non zero byte in calldata. +pub const NON_ZERO_BYTE_MULTIPLIER: u64 = NON_ZERO_BYTE_DATA_COST / STANDARD_TOKEN_COST; +/// The cost of a non-zero byte in calldata adjusted by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028). +pub const NON_ZERO_BYTE_DATA_COST_ISTANBUL: u64 = 16; +/// The multiplier for a non zero byte in calldata adjusted by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028). +pub const NON_ZERO_BYTE_MULTIPLIER_ISTANBUL: u64 = + NON_ZERO_BYTE_DATA_COST_ISTANBUL / STANDARD_TOKEN_COST; +/// The cost floor per token as defined by EIP-2028. +pub const TOTAL_COST_FLOOR_PER_TOKEN: u64 = 10; + +/// Gas cost for EOF CREATE instruction. +pub const EOF_CREATE_GAS: u64 = 32000; + +// Berlin EIP-2929/EIP-2930 constants +/// Gas cost for accessing an address in the access list (EIP-2930). +pub const ACCESS_LIST_ADDRESS: u64 = 2400; +/// Gas cost for accessing a storage key in the access list (EIP-2930). +pub const ACCESS_LIST_STORAGE_KEY: u64 = 1900; + +/// Gas cost for SLOAD when accessing a cold storage slot (EIP-2929). +pub const COLD_SLOAD_COST: u64 = 2100; +/// Gas cost for accessing a cold account (EIP-2929). +pub const COLD_ACCOUNT_ACCESS_COST: u64 = 2600; +/// Additional gas cost for accessing a cold account. +pub const COLD_ACCOUNT_ACCESS_COST_ADDITIONAL: u64 = + COLD_ACCOUNT_ACCESS_COST - WARM_STORAGE_READ_COST; +/// Gas cost for reading from a warm storage slot (EIP-2929). +pub const WARM_STORAGE_READ_COST: u64 = 100; +/// Gas cost for SSTORE reset operation on a warm storage slot. +pub const WARM_SSTORE_RESET: u64 = SSTORE_RESET - COLD_SLOAD_COST; + +/// EIP-3860 : Limit and meter initcode +pub const INITCODE_WORD_COST: u64 = 2; + +/// Gas stipend provided to the recipient of a CALL with value transfer. +pub const CALL_STIPEND: u64 = 2300; + +#[inline] +pub(crate) const fn log2floor(value: U256) -> u64 { + let mut l: u64 = 256; + let mut i = 3; + loop { + if value.as_limbs()[i] == 0u64 { + l -= 64; + } else { + l -= value.as_limbs()[i].leading_zeros() as u64; + if l == 0 { + return l; + } else { + return l - 1; + } + } + if i == 0 { + break; + } + i -= 1; + } + l +} + +/// Calculate the cost of buffer per word. +#[inline] +pub const fn cost_per_word(len: usize, multiple: u64) -> Option { + multiple.checked_mul(num_words(len) as u64) +} + +/// EIP-3860: Limit and meter initcode +/// +/// Apply extra gas cost of 2 for every 32-byte chunk of initcode. +/// +/// This cannot overflow as the initcode length is assumed to be checked. +#[inline] +pub const fn initcode_cost(len: usize) -> u64 { + let Some(cost) = cost_per_word(len, INITCODE_WORD_COST) else { + panic!("initcode cost overflow") + }; + cost +} + +/// Memory expansion cost calculation for a given number of words. +#[inline] +pub const fn memory_gas(num_words: usize, linear_cost: u64, quadratic_cost: u64) -> u64 { + let num_words = num_words as u64; + linear_cost + .saturating_mul(num_words) + .saturating_add(num_words.saturating_mul(num_words) / quadratic_cost) +} + +/// Init and floor gas from transaction +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct InitialAndFloorGas { + /// Initial gas for transaction. + pub initial_gas: u64, + /// If transaction is a Call and Prague is enabled + /// floor_gas is at least amount of gas that is going to be spent. + pub floor_gas: u64, +} + +impl InitialAndFloorGas { + /// Create a new InitialAndFloorGas instance. + #[inline] + pub const fn new(initial_gas: u64, floor_gas: u64) -> Self { + Self { + initial_gas, + floor_gas, + } + } +} + +/// Initial gas that is deducted for transaction to be included. +/// Initial gas contains initial stipend gas, gas for access list and input data. +/// +/// # Returns +/// +/// - Intrinsic gas +/// - Number of tokens in calldata +pub fn calculate_initial_tx_gas( + spec_id: SpecId, + input: &[u8], + is_create: bool, + access_list_accounts: u64, + access_list_storages: u64, + authorization_list_num: u64, +) -> InitialAndFloorGas { + let mut gas = InitialAndFloorGas::default(); + + // Initdate stipend + let tokens_in_calldata = get_tokens_in_calldata(input, spec_id.is_enabled_in(SpecId::ISTANBUL)); + + gas.initial_gas += tokens_in_calldata * STANDARD_TOKEN_COST; + + // Get number of access list account and storages. + gas.initial_gas += access_list_accounts * ACCESS_LIST_ADDRESS; + gas.initial_gas += access_list_storages * ACCESS_LIST_STORAGE_KEY; + + // Base stipend + gas.initial_gas += if is_create { + if spec_id.is_enabled_in(SpecId::HOMESTEAD) { + // EIP-2: Homestead Hard-fork Changes + 53000 + } else { + 21000 + } + } else { + 21000 + }; + + // EIP-3860: Limit and meter initcode + // Init code stipend for bytecode analysis + if spec_id.is_enabled_in(SpecId::SHANGHAI) && is_create { + gas.initial_gas += initcode_cost(input.len()) + } + + // EIP-7702 + if spec_id.is_enabled_in(SpecId::PRAGUE) { + gas.initial_gas += authorization_list_num * eip7702::PER_EMPTY_ACCOUNT_COST; + + // Calculate gas floor for EIP-7623 + gas.floor_gas = calc_tx_floor_cost(tokens_in_calldata); + } + + gas +} + +/// Initial gas that is deducted for transaction to be included. +/// Initial gas contains initial stipend gas, gas for access list and input data. +/// +/// # Returns +/// +/// - Intrinsic gas +/// - Number of tokens in calldata +pub fn calculate_initial_tx_gas_for_tx(tx: impl Transaction, spec: SpecId) -> InitialAndFloorGas { + let mut accounts = 0; + let mut storages = 0; + // legacy is only tx type that does not have access list. + if tx.tx_type() != TransactionType::Legacy { + (accounts, storages) = tx + .access_list() + .map(|al| { + al.fold((0, 0), |(mut num_accounts, mut num_storage_slots), item| { + num_accounts += 1; + num_storage_slots += item.storage_slots().count(); + + (num_accounts, num_storage_slots) + }) + }) + .unwrap_or_default(); + } + + calculate_initial_tx_gas( + spec, + tx.input(), + tx.kind().is_create(), + accounts as u64, + storages as u64, + tx.authorization_list_len() as u64, + ) +} + +/// Retrieve the total number of tokens in calldata. +#[inline] +pub fn get_tokens_in_calldata(input: &[u8], is_istanbul: bool) -> u64 { + let zero_data_len = input.iter().filter(|v| **v == 0).count() as u64; + let non_zero_data_len = input.len() as u64 - zero_data_len; + let non_zero_data_multiplier = if is_istanbul { + // EIP-2028: Transaction data gas cost reduction + NON_ZERO_BYTE_MULTIPLIER_ISTANBUL + } else { + NON_ZERO_BYTE_MULTIPLIER + }; + zero_data_len + non_zero_data_len * non_zero_data_multiplier +} + +/// Calculate the transaction cost floor as specified in EIP-7623. +#[inline] +pub fn calc_tx_floor_cost(tokens_in_calldata: u64) -> u64 { + tokens_in_calldata * TOTAL_COST_FLOOR_PER_TOKEN + 21_000 +} + +/// Returns number of words what would fit to provided number of bytes, +/// i.e. it rounds up the number bytes to number of words. +#[inline] +pub const fn num_words(len: usize) -> usize { + len.div_ceil(32) +} diff --git a/crates/interpreter/src/gas/params.rs b/crates/context/interface/src/cfg/gas_params.rs similarity index 96% rename from crates/interpreter/src/gas/params.rs rename to crates/context/interface/src/cfg/gas_params.rs index 93bd8c9a80..68e195eb49 100644 --- a/crates/interpreter/src/gas/params.rs +++ b/crates/context/interface/src/cfg/gas_params.rs @@ -1,10 +1,12 @@ //! Gas table for dynamic gas constants. use crate::{ - gas::{self, log2floor, ISTANBUL_SLOAD_GAS, SSTORE_RESET, SSTORE_SET, WARM_SSTORE_RESET}, - num_words, + cfg::gas::{ + self, log2floor, num_words, ISTANBUL_SLOAD_GAS, SSTORE_RESET, SSTORE_SET, WARM_SSTORE_RESET, + }, + context::SStoreResult, }; -use context_interface::context::SStoreResult; +use core::hash::{Hash, Hasher}; use primitives::{ hardfork::SpecId::{self}, U256, @@ -12,7 +14,7 @@ use primitives::{ use std::sync::Arc; /// Gas table for dynamic gas constants. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone)] pub struct GasParams { /// Table of gas costs for operations table: Arc<[u64; 256]>, @@ -20,6 +22,30 @@ pub struct GasParams { ptr: *const u64, } +impl PartialEq for GasParams { + fn eq(&self, other: &GasParams) -> bool { + self.table == other.table + } +} + +impl Hash for GasParams { + fn hash(&self, hasher: &mut H) { + self.table.hash(hasher); + } +} + +/// Pointer points to Arc so it is safe to send across threads +unsafe impl Send for GasParams {} +/// Pointer points to Arc so it is safe to access +unsafe impl Sync for GasParams {} + +impl core::fmt::Debug for GasParams { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "GasParams {{ table: {:?} }}", self.table) + } +} + +impl Eq for GasParams {} #[cfg(feature = "serde")] mod serde { use super::{Arc, GasParams}; @@ -80,7 +106,7 @@ impl GasParams { /// Use to override default gas cost /// /// ```rust - /// use revm_interpreter::gas::params::{GasParams, GasId}; + /// use revm_context_interface::cfg::gas_params::{GasParams, GasId}; /// use primitives::hardfork::SpecId; /// /// let mut gas_table = GasParams::new_spec(SpecId::default()); @@ -509,7 +535,7 @@ impl GasId { /// # Examples /// /// ``` - /// use revm_interpreter::gas::params::GasId; + /// use revm_context_interface::cfg::gas_params::GasId; /// /// assert_eq!(GasId::exp_byte_gas().name(), "exp_byte_gas"); /// assert_eq!(GasId::memory_linear_cost().name(), "memory_linear_cost"); @@ -563,7 +589,7 @@ impl GasId { /// # Examples /// /// ``` - /// use revm_interpreter::gas::params::GasId; + /// use revm_context_interface::cfg::gas_params::GasId; /// /// assert_eq!(GasId::from_name("exp_byte_gas"), Some(GasId::exp_byte_gas())); /// assert_eq!(GasId::from_name("memory_linear_cost"), Some(GasId::memory_linear_cost())); diff --git a/crates/context/interface/src/host.rs b/crates/context/interface/src/host.rs index 833346696a..e4efb22081 100644 --- a/crates/context/interface/src/host.rs +++ b/crates/context/interface/src/host.rs @@ -1,11 +1,12 @@ //! Host interface for external blockchain state access. use crate::{ + cfg::GasParams, context::{SStoreResult, SelfDestructResult, StateLoad}, journaled_state::{AccountInfoLoad, AccountLoad}, }; use auto_impl::auto_impl; -use primitives::{Address, Bytes, Log, StorageKey, StorageValue, B256, U256}; +use primitives::{hardfork::SpecId, Address, Bytes, Log, StorageKey, StorageValue, B256, U256}; use state::Bytecode; /// Error that can happen when loading account info. @@ -60,6 +61,9 @@ pub trait Host { /// Max initcode size, calls `ContextTr::cfg().max_code_size().saturating_mul(2)` fn max_initcode_size(&self) -> usize; + /// Gas params contains the dynamic gas constants for the EVM. + fn gas_params(&self) -> &GasParams; + /* Database */ /// Block hash, calls `ContextTr::journal_mut().db().block_hash(number)` @@ -200,8 +204,19 @@ pub trait Host { } /// Dummy host that implements [`Host`] trait and returns all default values. -#[derive(Debug)] -pub struct DummyHost; +#[derive(Default, Debug)] +pub struct DummyHost { + gas_params: GasParams, +} + +impl DummyHost { + /// Create a new dummy host with the given spec. + pub fn new(spec: SpecId) -> Self { + Self { + gas_params: GasParams::new_spec(spec), + } + } +} impl Host for DummyHost { fn basefee(&self) -> U256 { @@ -216,6 +231,10 @@ impl Host for DummyHost { U256::ZERO } + fn gas_params(&self) -> &GasParams { + &self.gas_params + } + fn difficulty(&self) -> U256 { U256::ZERO } diff --git a/crates/context/interface/src/journaled_state/account.rs b/crates/context/interface/src/journaled_state/account.rs index ae5691f7a0..ecba313060 100644 --- a/crates/context/interface/src/journaled_state/account.rs +++ b/crates/context/interface/src/journaled_state/account.rs @@ -173,9 +173,18 @@ impl<'a, DB: Database, ENTRY: JournalEntryTr> JournaledAccount<'a, DB, ENTRY> { Entry::Occupied(occ) => { let slot = occ.into_mut(); // skip load if account is cold. - let is_cold = slot.is_cold_transaction_id(self.transaction_id); - if is_cold && skip_cold_load { - return Err(JournalLoadError::ColdLoadSkipped); + let mut is_cold = false; + if slot.is_cold_transaction_id(self.transaction_id) { + // is storage cold + is_cold = self + .access_list + .get(&self.address) + .and_then(|v| v.get(&key)) + .is_none(); + + if is_cold && skip_cold_load { + return Err(JournalLoadError::ColdLoadSkipped); + } } slot.mark_warm_with_transaction_id(self.transaction_id); (slot, is_cold) diff --git a/crates/context/src/cfg.rs b/crates/context/src/cfg.rs index 2aea1efcf6..5a320ea660 100644 --- a/crates/context/src/cfg.rs +++ b/crates/context/src/cfg.rs @@ -1,12 +1,26 @@ //! This module contains [`CfgEnv`] and implements [`Cfg`] trait for it. pub use context_interface::Cfg; +use context_interface::cfg::GasParams; use primitives::{eip170, eip3860, eip7825, hardfork::SpecId}; + /// EVM configuration #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, Eq, PartialEq)] #[non_exhaustive] pub struct CfgEnv { + /// Specification for EVM represent the hardfork + /// + /// [`CfgEnv::new_with_spec`] is going to set both gas params and spec. + /// + /// As GasParams is spec dependent, it is recommended to use one of following function to set both of them. + /// [`CfgEnv::set_spec_and_mainnet_gas_params`], [`CfgEnv::with_mainnet_gas_params`], [`CfgEnv::with_mainnet_gas_params`] + pub spec: SPEC, + + /// Gas params for the EVM. Use [`CfgEnv::set_gas_params`] to set the gas params. + /// If gas_params was not set it will be set to the default gas params for the spec. + pub gas_params: GasParams, + /// Chain ID of the EVM. Used in CHAINID opcode and transaction's chain ID check. /// /// Chain ID is introduced EIP-155. @@ -17,8 +31,6 @@ pub struct CfgEnv { /// If set to `false`, the transaction's chain ID check will be skipped. pub tx_chain_id_check: bool, - /// Specification for EVM represent the hardfork - pub spec: SPEC, /// Contract code size limit override. /// /// If None, the limit will be determined by the SpecId (EIP-170 or EIP-7907) at runtime. @@ -127,28 +139,9 @@ impl CfgEnv { } } -impl + Copy> CfgEnv { - /// Returns the blob base fee update fraction from [CfgEnv::blob_base_fee_update_fraction]. - /// - /// If this field is not set, return the default value for the spec. - /// - /// Default values for Cancun is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN`] - /// and for Prague is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE`]. - pub fn blob_base_fee_update_fraction(&mut self) -> u64 { - self.blob_base_fee_update_fraction.unwrap_or_else(|| { - let spec: SpecId = self.spec.into(); - if spec.is_enabled_in(SpecId::PRAGUE) { - primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE - } else { - primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN - } - }) - } -} - impl CfgEnv { /// Create new `CfgEnv` with default values and specified spec. - pub fn new_with_spec(spec: SPEC) -> Self { + pub fn new_with_spec_and_gas_params(spec: SPEC, gas_params: GasParams) -> Self { Self { chain_id: 1, tx_chain_id_check: true, @@ -159,6 +152,7 @@ impl CfgEnv { max_blobs_per_tx: None, tx_gas_limit_cap: None, blob_base_fee_update_fraction: None, + gas_params, #[cfg(feature = "memory_limit")] memory_limit: (1 << 32) - 1, #[cfg(feature = "optional_balance_check")] @@ -180,12 +174,37 @@ impl CfgEnv { } } + /// Returns the spec for the `CfgEnv`. + #[inline] + pub fn spec(&self) -> &SPEC { + &self.spec + } + /// Consumes `self` and returns a new `CfgEnv` with the specified chain ID. pub fn with_chain_id(mut self, chain_id: u64) -> Self { self.chain_id = chain_id; self } + /// Sets the gas params for the `CfgEnv`. + #[inline] + pub fn with_gas_params(mut self, gas_params: GasParams) -> Self { + self.set_gas_params(gas_params); + self + } + + /// Sets the spec for the `CfgEnv`. + #[inline] + pub fn set_spec(&mut self, spec: SPEC) { + self.spec = spec; + } + + /// Sets the gas params for the `CfgEnv`. + #[inline] + pub fn set_gas_params(&mut self, gas_params: GasParams) { + self.gas_params = gas_params; + } + /// Enables the transaction's chain ID check. pub fn enable_tx_chain_id_check(mut self) -> Self { self.tx_chain_id_check = true; @@ -198,8 +217,33 @@ impl CfgEnv { self } + /// Sets the spec for the `CfgEnv`. + #[inline] + #[deprecated( + since = "0.1.0", + note = "Use [`CfgEnv::with_spec_and_mainnet_gas_params`] instead" + )] + pub fn with_spec(mut self, spec: SPEC) -> Self { + self.spec = spec; + self + } + + /// Sets the spec for the `CfgEnv` and the gas params to the mainnet gas params. + pub fn with_spec_and_mainnet_gas_params + Clone>( + self, + spec: OSPEC, + ) -> CfgEnv { + self.with_spec_and_gas_params(spec.clone(), GasParams::new_spec(spec.into())) + } + /// Consumes `self` and returns a new `CfgEnv` with the specified spec. - pub fn with_spec>(self, spec: OSPEC) -> CfgEnv { + /// + /// Resets the gas params override function as it is generic over SPEC. + pub fn with_spec_and_gas_params + Clone>( + self, + spec: OSPEC, + gas_params: GasParams, + ) -> CfgEnv { CfgEnv { chain_id: self.chain_id, tx_chain_id_check: self.tx_chain_id_check, @@ -210,6 +254,7 @@ impl CfgEnv { tx_gas_limit_cap: self.tx_gas_limit_cap, max_blobs_per_tx: self.max_blobs_per_tx, blob_base_fee_update_fraction: self.blob_base_fee_update_fraction, + gas_params, #[cfg(feature = "memory_limit")] memory_limit: self.memory_limit, #[cfg(feature = "optional_balance_check")] @@ -269,7 +314,49 @@ impl CfgEnv { } } -impl + Copy> Cfg for CfgEnv { +impl + Clone> CfgEnv { + /// Returns the blob base fee update fraction from [CfgEnv::blob_base_fee_update_fraction]. + /// + /// If this field is not set, return the default value for the spec. + /// + /// Default values for Cancun is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN`] + /// and for Prague is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE`]. + pub fn blob_base_fee_update_fraction(&mut self) -> u64 { + self.blob_base_fee_update_fraction.unwrap_or_else(|| { + let spec: SpecId = self.spec.clone().into(); + if spec.is_enabled_in(SpecId::PRAGUE) { + primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE + } else { + primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN + } + }) + } + + /// Create new `CfgEnv` with default values and specified spec. + /// It will create a new gas params based on mainnet spec. + /// + /// Internally it will call [`CfgEnv::new_with_spec_and_gas_params`] with the mainnet gas params. + pub fn new_with_spec(spec: SPEC) -> Self { + Self::new_with_spec_and_gas_params(spec.clone(), GasParams::new_spec(spec.into())) + } + + /// Sets the gas params for the `CfgEnv` to the mainnet gas params. + /// + /// If spec gets changed, calling this function would use this spec to set the mainnetF gas params. + pub fn with_mainnet_gas_params(mut self) -> Self { + self.set_gas_params(GasParams::new_spec(self.spec.clone().into())); + self + } + + /// Sets the spec for the `CfgEnv` and the gas params to the mainnet gas params. + #[inline] + pub fn set_spec_and_mainnet_gas_params(&mut self, spec: SPEC) { + self.set_spec(spec.clone()); + self.set_gas_params(GasParams::new_spec(spec.into())); + } +} + +impl + Clone> Cfg for CfgEnv { type Spec = SPEC; #[inline] @@ -279,7 +366,7 @@ impl + Copy> Cfg for CfgEnv { #[inline] fn spec(&self) -> Self::Spec { - self.spec + self.spec.clone() } #[inline] @@ -290,7 +377,7 @@ impl + Copy> Cfg for CfgEnv { #[inline] fn tx_gas_limit_cap(&self) -> u64 { self.tx_gas_limit_cap - .unwrap_or(if self.spec.into().is_enabled_in(SpecId::OSAKA) { + .unwrap_or(if self.spec.clone().into().is_enabled_in(SpecId::OSAKA) { eip7825::TX_GAS_LIMIT_CAP } else { u64::MAX @@ -410,11 +497,19 @@ impl + Copy> Cfg for CfgEnv { } } } + + #[inline] + fn gas_params(&self) -> &GasParams { + &self.gas_params + } } -impl Default for CfgEnv { +impl> Default for CfgEnv { fn default() -> Self { - Self::new_with_spec(SPEC::default()) + Self::new_with_spec_and_gas_params( + SPEC::default(), + GasParams::new_spec(SPEC::default().into()), + ) } } diff --git a/crates/context/src/context.rs b/crates/context/src/context.rs index 5bf1832fa3..de226f3342 100644 --- a/crates/context/src/context.rs +++ b/crates/context/src/context.rs @@ -1,6 +1,7 @@ //! This module contains [`Context`] struct and implements [`ContextTr`] trait for it. use crate::{block::BlockEnv, cfg::CfgEnv, journal::Journal, tx::TxEnv, LocalContext}; use context_interface::{ + cfg::GasParams, context::{ContextError, ContextSetters, SStoreResult, SelfDestructResult, StateLoad}, host::LoadError, journaled_state::AccountInfoLoad, @@ -133,7 +134,7 @@ impl< JOURNAL: JournalTr, CHAIN: Default, LOCAL: LocalContextTr + Default, - SPEC: Default + Copy + Into, + SPEC: Default + Into + Clone, > Context, DB, JOURNAL, CHAIN, LOCAL> { /// Creates a new context with a new database type. @@ -141,14 +142,11 @@ impl< /// This will create a new [`Journal`] object. pub fn new(db: DB, spec: SPEC) -> Self { let mut journaled_state = JOURNAL::new(db); - journaled_state.set_spec_id(spec.into()); + journaled_state.set_spec_id(spec.clone().into()); Self { tx: TX::default(), block: BLOCK::default(), - cfg: CfgEnv { - spec, - ..Default::default() - }, + cfg: CfgEnv::new_with_spec(spec), local: LOCAL::default(), journaled_state, chain: Default::default(), @@ -452,6 +450,11 @@ impl< self.block().prevrandao().map(|r| r.into()) } + #[inline] + fn gas_params(&self) -> &GasParams { + self.cfg().gas_params() + } + fn block_number(&self) -> U256 { self.block().number() } diff --git a/crates/ee-tests/src/op_revm_tests.rs b/crates/ee-tests/src/op_revm_tests.rs index 1e44ad1f67..525a199a8a 100644 --- a/crates/ee-tests/src/op_revm_tests.rs +++ b/crates/ee-tests/src/op_revm_tests.rs @@ -51,7 +51,7 @@ fn test_deposit_tx() { .source_hash(revm::primitives::B256::from([1u8; 32])) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::HOLOCENE); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::HOLOCENE)); let mut evm = ctx.build_op(); @@ -83,7 +83,7 @@ fn test_halted_deposit_tx() { .source_hash(revm::primitives::B256::from([1u8; 32])) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::HOLOCENE) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::HOLOCENE)) .with_db(BenchmarkDB::new_bytecode(Bytecode::new_legacy( [opcode::POP].into(), ))); @@ -127,7 +127,7 @@ fn p256verify_test_tx( ) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID) + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)) } #[test] @@ -160,7 +160,7 @@ fn test_halted_tx_call_p256verify() { ) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -196,7 +196,7 @@ fn bn254_pair_test_tx( ) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = spec) + .with_cfg(CfgEnv::new_with_spec(spec)) } #[test] @@ -253,7 +253,7 @@ fn test_halted_tx_call_bls12_381_g1_add_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); @@ -290,7 +290,7 @@ fn test_halted_tx_call_bls12_381_g1_add_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -339,7 +339,7 @@ fn g1_msm_test_tx( l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID) + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)) } #[test] @@ -369,7 +369,7 @@ fn test_halted_tx_call_bls12_381_g1_msm_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -416,7 +416,7 @@ fn test_halted_tx_call_bls12_381_g1_msm_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -474,7 +474,7 @@ fn test_halted_tx_call_bls12_381_g2_add_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); @@ -511,7 +511,7 @@ fn test_halted_tx_call_bls12_381_g2_add_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); @@ -561,7 +561,7 @@ fn g2_msm_test_tx( l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID) + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)) } #[test] @@ -591,7 +591,7 @@ fn test_halted_tx_call_bls12_381_g2_msm_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -638,7 +638,7 @@ fn test_halted_tx_call_bls12_381_g2_msm_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -707,7 +707,7 @@ fn bl12_381_pairing_test_tx( l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)) } #[test] @@ -734,7 +734,7 @@ fn test_halted_tx_call_bls12_381_pairing_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -778,7 +778,7 @@ fn test_halted_tx_call_bls12_381_pairing_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -842,7 +842,7 @@ fn test_halted_tx_call_bls12_381_map_fp_to_g1_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -884,7 +884,7 @@ fn test_halted_tx_call_bls12_381_map_fp_to_g1_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -926,7 +926,7 @@ fn test_halted_tx_call_bls12_381_map_fp2_to_g2_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -967,7 +967,7 @@ fn test_l1block_load_for_pre_regolith() { .modify_chain_chained(|l1_block| { l1_block.l2_block = None; }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx .with_db( @@ -1006,7 +1006,7 @@ fn test_halted_tx_call_bls12_381_map_fp2_to_g2_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); diff --git a/crates/ee-tests/src/revm_tests.rs b/crates/ee-tests/src/revm_tests.rs index b89459ebfc..fc66a77dfe 100644 --- a/crates/ee-tests/src/revm_tests.rs +++ b/crates/ee-tests/src/revm_tests.rs @@ -3,7 +3,7 @@ use crate::TestdataConfig; use revm::{ bytecode::opcode, - context::{ContextTr, TxEnv}, + context::{CfgEnv, ContextTr, TxEnv}, database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}, primitives::{address, b256, hardfork::SpecId, Bytes, TxKind, KECCAK_EMPTY, U256}, state::{AccountStatus, Bytecode}, @@ -38,7 +38,7 @@ const SELFDESTRUCT_BYTECODE: &[u8] = &[ #[test] fn test_selfdestruct_multi_tx() { let mut evm = Context::mainnet() - .modify_cfg_chained(|cfg| cfg.spec = SpecId::BERLIN) + .with_cfg(CfgEnv::new_with_spec(SpecId::BERLIN)) .with_db(BenchmarkDB::new_bytecode(Bytecode::new_legacy( SELFDESTRUCT_BYTECODE.into(), ))) @@ -85,7 +85,7 @@ fn test_selfdestruct_multi_tx() { fn test_multi_tx_create() { let mut evm = Context::mainnet() .modify_cfg_chained(|cfg| { - cfg.spec = SpecId::BERLIN; + cfg.set_spec_and_mainnet_gas_params(SpecId::BERLIN); cfg.disable_nonce_check = true; }) .with_db(BenchmarkDB::new_bytecode(Bytecode::new())) @@ -217,7 +217,7 @@ fn deployment_contract(bytes: &[u8]) -> Bytes { #[test] fn test_frame_stack_index() { let mut evm = Context::mainnet() - .modify_cfg_chained(|cfg| cfg.spec = SpecId::BERLIN) + .with_cfg(CfgEnv::new_with_spec(SpecId::BERLIN)) .with_db(BenchmarkDB::new_bytecode(Bytecode::new_legacy( SELFDESTRUCT_BYTECODE.into(), ))) diff --git a/crates/handler/src/evm.rs b/crates/handler/src/evm.rs index 8607f48dce..0ad3130f0c 100644 --- a/crates/handler/src/evm.rs +++ b/crates/handler/src/evm.rs @@ -183,13 +183,7 @@ where let ctx = &mut self.ctx; let precompiles = &mut self.precompiles; - let res = Self::Frame::init_with_context( - new_frame, - ctx, - precompiles, - frame_input, - self.instruction.gas_params(), - )?; + let res = Self::Frame::init_with_context(new_frame, ctx, precompiles, frame_input)?; Ok(res.map_frame(|token| { if is_first_init { diff --git a/crates/handler/src/frame.rs b/crates/handler/src/frame.rs index 479e4093c1..2b3ced2c1b 100644 --- a/crates/handler/src/frame.rs +++ b/crates/handler/src/frame.rs @@ -4,6 +4,7 @@ use crate::{ }; use context::result::FromStringError; use context_interface::{ + cfg::gas::CODEDEPOSIT, context::ContextError, journaled_state::{account::JournaledAccountTr, JournalCheckpoint, JournalTr}, local::{FrameToken, OutFrame}, @@ -12,7 +13,6 @@ use context_interface::{ use core::cmp::min; use derive_where::derive_where; use interpreter::{ - gas::{self, params::GasParams}, interpreter::{EthInterpreter, ExtBytecode}, interpreter_action::FrameInit, interpreter_types::ReturnData, @@ -114,7 +114,6 @@ impl EthFrame { spec_id: SpecId, gas_limit: u64, checkpoint: JournalCheckpoint, - gas_params: GasParams, ) { let Self { data: data_ref, @@ -128,9 +127,7 @@ impl EthFrame { *input_ref = input; *depth_ref = depth; *is_finished_ref = false; - interpreter.clear( - memory, bytecode, inputs, is_static, spec_id, gas_limit, gas_params, - ); + interpreter.clear(memory, bytecode, inputs, is_static, spec_id, gas_limit); *checkpoint_ref = checkpoint; } @@ -147,7 +144,6 @@ impl EthFrame { depth: usize, memory: SharedMemory, inputs: Box, - gas_params: GasParams, ) -> Result, ERROR> { let gas = Gas::new(inputs.gas_limit); let return_result = |instruction_result: InstructionResult| { @@ -247,7 +243,6 @@ impl EthFrame { ctx.cfg().spec().into(), gas_limit, checkpoint, - gas_params, ); Ok(ItemOrResult::Item(this.consume())) } @@ -263,7 +258,6 @@ impl EthFrame { depth: usize, memory: SharedMemory, inputs: Box, - gas_params: GasParams, ) -> Result, ERROR> { let spec = context.cfg().spec().into(); let return_error = |e| { @@ -350,7 +344,6 @@ impl EthFrame { spec, gas_limit, checkpoint, - gas_params, ); Ok(ItemOrResult::Item(this.consume())) } @@ -364,7 +357,6 @@ impl EthFrame { ctx: &mut CTX, precompiles: &mut PRECOMPILES, frame_init: FrameInit, - gas_params: GasParams, ) -> Result< ItemOrResult, ContextError<<::Db as Database>::Error>, @@ -378,11 +370,9 @@ impl EthFrame { match frame_input { FrameInput::Call(inputs) => { - Self::make_call_frame(this, ctx, precompiles, depth, memory, inputs, gas_params) - } - FrameInput::Create(inputs) => { - Self::make_create_frame(this, ctx, depth, memory, inputs, gas_params) + Self::make_call_frame(this, ctx, precompiles, depth, memory, inputs) } + FrameInput::Create(inputs) => Self::make_create_frame(this, ctx, depth, memory, inputs), FrameInput::Empty => unreachable!(), } } @@ -579,7 +569,7 @@ pub fn return_create( interpreter_result.result = InstructionResult::CreateContractSizeLimit; return; } - let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT; + let gas_for_code = interpreter_result.output.len() as u64 * CODEDEPOSIT; if !interpreter_result.gas.record_cost(gas_for_code) { // Record code deposit gas cost and check if we are out of gas. // EIP-2 point 3: If contract creation does not have enough gas to pay for the diff --git a/crates/handler/src/handler.rs b/crates/handler/src/handler.rs index 96b1911345..6530e1e64d 100644 --- a/crates/handler/src/handler.rs +++ b/crates/handler/src/handler.rs @@ -1,4 +1,3 @@ -use crate::instructions::InstructionProvider; use crate::{ evm::FrameTr, execution, post_execution, @@ -99,7 +98,6 @@ pub trait Handler { &mut self, evm: &mut Self::Evm, ) -> Result, Self::Error> { - self.configure(evm); // Run inner handler and catch all errors to handle cleanup. match self.run_without_catch_error(evm) { Ok(output) => Ok(output), @@ -107,15 +105,6 @@ pub trait Handler { } } - /// Configure the handler: - /// * Set Instruction gas table to the spec id. - #[inline] - fn configure(&mut self, evm: &mut Self::Evm) { - let spec_id = evm.ctx().cfg().spec().into(); - // sets static gas depending on the spec id. - evm.ctx_instructions().1.set_spec(spec_id); - } - /// Runs the system call. /// /// System call is a special transaction where caller is a [`crate::SYSTEM_ADDRESS`] @@ -137,8 +126,6 @@ pub trait Handler { ) -> Result, Self::Error> { // dummy values that are not used. let init_and_floor_gas = InitialAndFloorGas::new(0, 0); - // configure the evm for system call. - self.configure(evm); // call execution and than output. match self .execution(evm, &init_and_floor_gas) diff --git a/crates/handler/src/instructions.rs b/crates/handler/src/instructions.rs index ebe5a2d319..89bcf663c0 100644 --- a/crates/handler/src/instructions.rs +++ b/crates/handler/src/instructions.rs @@ -1,6 +1,5 @@ use auto_impl::auto_impl; use interpreter::{ - gas::params::GasParams, instructions::{instruction_table_gas_changes_spec, InstructionTable}, Host, Instruction, InterpreterTypes, }; @@ -17,12 +16,6 @@ pub trait InstructionProvider { /// Returns the instruction table that is used by EvmTr to execute instructions. fn instruction_table(&self) -> &InstructionTable; - - /// Returns the gas params that is used by EvmTr to execute instructions. - fn gas_params(&self) -> GasParams; - - /// Sets the spec. Return true if the spec was changed. - fn set_spec(&mut self, spec: SpecId) -> bool; } /// Ethereum instruction contains list of mainnet instructions that is used for Interpreter execution. @@ -30,8 +23,6 @@ pub trait InstructionProvider { pub struct EthInstructions { /// Table containing instruction implementations indexed by opcode. pub instruction_table: Box>, - /// Gas params that sets gas costs for instructions. - pub gas_params: GasParams, /// Spec that is used to set gas costs for instructions. pub spec: SpecId, } @@ -43,7 +34,6 @@ where fn clone(&self) -> Self { Self { instruction_table: self.instruction_table.clone(), - gas_params: self.gas_params.clone(), spec: self.spec, } } @@ -57,23 +47,19 @@ where /// Returns `EthInstructions` with mainnet spec. pub fn new_mainnet() -> Self { let spec = SpecId::default(); - Self::new( - instruction_table_gas_changes_spec(spec), - GasParams::new_spec(spec), - spec, - ) + Self::new(instruction_table_gas_changes_spec(spec), spec) + } + + /// Returns `EthInstructions` with mainnet spec. + pub fn new_mainnet_with_spec(spec: SpecId) -> Self { + Self::new(instruction_table_gas_changes_spec(spec), spec) } /// Returns a new instance of `EthInstructions` with custom instruction table. #[inline] - pub fn new( - base_table: InstructionTable, - gas_params: GasParams, - spec: SpecId, - ) -> Self { + pub fn new(base_table: InstructionTable, spec: SpecId) -> Self { Self { instruction_table: Box::new(base_table), - gas_params, spec, } } @@ -96,20 +82,6 @@ where fn instruction_table(&self) -> &InstructionTable { &self.instruction_table } - - fn gas_params(&self) -> GasParams { - self.gas_params.clone() - } - - fn set_spec(&mut self, spec: SpecId) -> bool { - if spec == self.spec { - return false; - } - *self.instruction_table = instruction_table_gas_changes_spec(spec); - self.gas_params = GasParams::new_spec(spec); - - true - } } impl Default for EthInstructions diff --git a/crates/handler/src/mainnet_builder.rs b/crates/handler/src/mainnet_builder.rs index a640da1aca..46baa54fc8 100644 --- a/crates/handler/src/mainnet_builder.rs +++ b/crates/handler/src/mainnet_builder.rs @@ -36,11 +36,12 @@ where type Context = Self; fn build_mainnet(self) -> MainnetEvm { + let spec = self.cfg.spec().into(); Evm { ctx: self, inspector: (), - instruction: EthInstructions::default(), - precompiles: EthPrecompiles::default(), + instruction: EthInstructions::new_mainnet_with_spec(spec), + precompiles: EthPrecompiles::new(spec), frame_stack: FrameStack::new_prealloc(8), } } @@ -49,11 +50,12 @@ where self, inspector: INSP, ) -> MainnetEvm { + let spec = self.cfg.spec().into(); Evm { ctx: self, inspector, - instruction: EthInstructions::default(), - precompiles: EthPrecompiles::default(), + instruction: EthInstructions::new_mainnet_with_spec(spec), + precompiles: EthPrecompiles::new(spec), frame_stack: FrameStack::new_prealloc(8), } } @@ -99,7 +101,7 @@ mod test { let bytecode = Bytecode::new_legacy([PUSH1, 0x01, PUSH1, 0x01, SSTORE].into()); let ctx = Context::mainnet() - .modify_cfg_chained(|cfg| cfg.spec = SpecId::PRAGUE) + .modify_cfg_chained(|cfg| cfg.set_spec_and_mainnet_gas_params(SpecId::PRAGUE)) .with_db(BenchmarkDB::new_bytecode(bytecode)); let mut evm = ctx.build_mainnet(); diff --git a/crates/handler/src/precompile_provider.rs b/crates/handler/src/precompile_provider.rs index c3ac4a3e0c..dab165140f 100644 --- a/crates/handler/src/precompile_provider.rs +++ b/crates/handler/src/precompile_provider.rs @@ -44,6 +44,14 @@ pub struct EthPrecompiles { } impl EthPrecompiles { + /// Create a new precompile provider with the given spec. + pub fn new(spec: SpecId) -> Self { + Self { + precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)), + spec, + } + } + /// Returns addresses of the precompiles. pub fn warm_addresses(&self) -> Box> { Box::new(self.precompiles.addresses().cloned()) diff --git a/crates/handler/src/validation.rs b/crates/handler/src/validation.rs index a05a38031d..d038cd3f87 100644 --- a/crates/handler/src/validation.rs +++ b/crates/handler/src/validation.rs @@ -275,7 +275,7 @@ mod tests { let ctx = Context::mainnet() .modify_cfg_chained(|c| { if let Some(spec_id) = spec_id { - c.spec = spec_id; + c.set_spec_and_mainnet_gas_params(spec_id); } }) .with_db(CacheDB::::default()); diff --git a/crates/inspector/src/handler.rs b/crates/inspector/src/handler.rs index fe43a71b46..53bdf436db 100644 --- a/crates/inspector/src/handler.rs +++ b/crates/inspector/src/handler.rs @@ -42,7 +42,6 @@ where &mut self, evm: &mut Self::Evm, ) -> Result, Self::Error> { - self.configure(evm); match self.inspect_run_without_catch_error(evm) { Ok(output) => Ok(output), Err(e) => self.catch_error(evm, e), @@ -137,8 +136,6 @@ where ) -> Result, Self::Error> { // dummy values that are not used. let init_and_floor_gas = InitialAndFloorGas::new(0, 0); - // configure - self.configure(evm); // call execution with inspection and then output. match self .inspect_execution(evm, &init_and_floor_gas) diff --git a/crates/interpreter/src/gas.rs b/crates/interpreter/src/gas.rs index a8ac93a37c..68dbc24103 100644 --- a/crates/interpreter/src/gas.rs +++ b/crates/interpreter/src/gas.rs @@ -1,11 +1,8 @@ //! EVM gas calculation utilities. mod calc; -mod constants; -pub mod params; pub use calc::*; -pub use constants::*; /// Represents the state of gas during execution. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] @@ -218,10 +215,19 @@ impl MemoryGas { return None; } self.words_num = new_num; - let mut cost = crate::gas::calc::memory_gas(new_num, linear_cost, quadratic_cost); + let mut cost = memory_gas(new_num, linear_cost, quadratic_cost); core::mem::swap(&mut self.expansion_cost, &mut cost); // Safe to subtract because we know that new_len > length // Notice the swap above. Some(self.expansion_cost - cost) } } + +/// Memory expansion cost calculation for a given number of words. +#[inline] +pub const fn memory_gas(num_words: usize, linear_cost: u64, quadratic_cost: u64) -> u64 { + let num_words = num_words as u64; + linear_cost + .saturating_mul(num_words) + .saturating_add(num_words.saturating_mul(num_words) / quadratic_cost) +} diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index 06d9e6550d..ef4e79469b 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -1,30 +1,8 @@ -use super::constants::*; use crate::num_words; -use context_interface::{transaction::AccessListItemTr as _, Transaction, TransactionType}; -use primitives::{eip7702, hardfork::SpecId, U256}; - -#[inline] -pub(crate) const fn log2floor(value: U256) -> u64 { - let mut l: u64 = 256; - let mut i = 3; - loop { - if value.as_limbs()[i] == 0u64 { - l -= 64; - } else { - l -= value.as_limbs()[i].leading_zeros() as u64; - if l == 0 { - return l; - } else { - return l - 1; - } - } - if i == 0 { - break; - } - i -= 1; - } - l -} +use context_interface::{ + cfg::gas::*, transaction::AccessListItemTr as _, Transaction, TransactionType, +}; +use primitives::{eip7702, hardfork::SpecId}; /// Calculate the cost of buffer per word. #[inline] @@ -45,15 +23,6 @@ pub const fn initcode_cost(len: usize) -> u64 { cost } -/// Memory expansion cost calculation for a given number of words. -#[inline] -pub const fn memory_gas(num_words: usize, linear_cost: u64, quadratic_cost: u64) -> u64 { - let num_words = num_words as u64; - linear_cost - .saturating_mul(num_words) - .saturating_add(num_words.saturating_mul(num_words) / quadratic_cost) -} - /// Init and floor gas from transaction #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/crates/interpreter/src/gas/constants.rs b/crates/interpreter/src/gas/constants.rs deleted file mode 100644 index a4a101a985..0000000000 --- a/crates/interpreter/src/gas/constants.rs +++ /dev/null @@ -1,103 +0,0 @@ -/// Gas cost for operations that consume zero gas. -pub const ZERO: u64 = 0; -/// Base gas cost for basic operations. -pub const BASE: u64 = 2; - -/// Gas cost for very low-cost operations. -pub const VERYLOW: u64 = 3; -/// Gas cost for DATALOADN instruction. -pub const DATA_LOADN_GAS: u64 = 3; - -/// Gas cost for conditional jump instructions. -pub const CONDITION_JUMP_GAS: u64 = 4; -/// Gas cost for RETF instruction. -pub const RETF_GAS: u64 = 3; -/// Gas cost for DATALOAD instruction. -pub const DATA_LOAD_GAS: u64 = 4; - -/// Gas cost for low-cost operations. -pub const LOW: u64 = 5; -/// Gas cost for medium-cost operations. -pub const MID: u64 = 8; -/// Gas cost for high-cost operations. -pub const HIGH: u64 = 10; -/// Gas cost for JUMPDEST instruction. -pub const JUMPDEST: u64 = 1; -/// Gas cost for REFUND SELFDESTRUCT instruction. -pub const SELFDESTRUCT_REFUND: i64 = 24000; -/// Gas cost for CREATE instruction. -pub const CREATE: u64 = 32000; -/// Additional gas cost when a call transfers value. -pub const CALLVALUE: u64 = 9000; -/// Gas cost for creating a new account. -pub const NEWACCOUNT: u64 = 25000; -/// Base gas cost for EXP instruction. -pub const EXP: u64 = 10; -/// Gas cost per word for memory operations. -pub const MEMORY: u64 = 3; -/// Base gas cost for LOG instructions. -pub const LOG: u64 = 375; -/// Gas cost per byte of data in LOG instructions. -pub const LOGDATA: u64 = 8; -/// Gas cost per topic in LOG instructions. -pub const LOGTOPIC: u64 = 375; -/// Base gas cost for KECCAK256 instruction. -pub const KECCAK256: u64 = 30; -/// Gas cost per word for KECCAK256 instruction. -pub const KECCAK256WORD: u64 = 6; -/// Gas cost per word for copy operations. -pub const COPY: u64 = 3; -/// Gas cost for BLOCKHASH instruction. -pub const BLOCKHASH: u64 = 20; -/// Gas cost per byte for code deposit during contract creation. -pub const CODEDEPOSIT: u64 = 200; - -/// EIP-1884: Repricing for trie-size-dependent opcodes -pub const ISTANBUL_SLOAD_GAS: u64 = 800; -/// Gas cost for SSTORE when setting a storage slot from zero to non-zero. -pub const SSTORE_SET: u64 = 20000; -/// Gas cost for SSTORE when modifying an existing non-zero storage slot. -pub const SSTORE_RESET: u64 = 5000; -/// Gas refund for SSTORE when clearing a storage slot (setting to zero). -pub const REFUND_SSTORE_CLEARS: i64 = 15000; - -/// The standard cost of calldata token. -pub const STANDARD_TOKEN_COST: u64 = 4; -/// The cost of a non-zero byte in calldata. -pub const NON_ZERO_BYTE_DATA_COST: u64 = 68; -/// The multiplier for a non zero byte in calldata. -pub const NON_ZERO_BYTE_MULTIPLIER: u64 = NON_ZERO_BYTE_DATA_COST / STANDARD_TOKEN_COST; -/// The cost of a non-zero byte in calldata adjusted by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028). -pub const NON_ZERO_BYTE_DATA_COST_ISTANBUL: u64 = 16; -/// The multiplier for a non zero byte in calldata adjusted by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028). -pub const NON_ZERO_BYTE_MULTIPLIER_ISTANBUL: u64 = - NON_ZERO_BYTE_DATA_COST_ISTANBUL / STANDARD_TOKEN_COST; -/// The cost floor per token as defined by EIP-2028. -pub const TOTAL_COST_FLOOR_PER_TOKEN: u64 = 10; - -/// Gas cost for EOF CREATE instruction. -pub const EOF_CREATE_GAS: u64 = 32000; - -// Berlin EIP-2929/EIP-2930 constants -/// Gas cost for accessing an address in the access list (EIP-2930). -pub const ACCESS_LIST_ADDRESS: u64 = 2400; -/// Gas cost for accessing a storage key in the access list (EIP-2930). -pub const ACCESS_LIST_STORAGE_KEY: u64 = 1900; - -/// Gas cost for SLOAD when accessing a cold storage slot (EIP-2929). -pub const COLD_SLOAD_COST: u64 = 2100; -/// Gas cost for accessing a cold account (EIP-2929). -pub const COLD_ACCOUNT_ACCESS_COST: u64 = 2600; -/// Additional gas cost for accessing a cold account. -pub const COLD_ACCOUNT_ACCESS_COST_ADDITIONAL: u64 = - COLD_ACCOUNT_ACCESS_COST - WARM_STORAGE_READ_COST; -/// Gas cost for reading from a warm storage slot (EIP-2929). -pub const WARM_STORAGE_READ_COST: u64 = 100; -/// Gas cost for SSTORE reset operation on a warm storage slot. -pub const WARM_SSTORE_RESET: u64 = SSTORE_RESET - COLD_SLOAD_COST; - -/// EIP-3860 : Limit and meter initcode -pub const INITCODE_WORD_COST: u64 = 2; - -/// Gas stipend provided to the recipient of a CALL with value transfer. -pub const CALL_STIPEND: u64 = 2300; diff --git a/crates/interpreter/src/instructions.rs b/crates/interpreter/src/instructions.rs index cae338c29f..969805b583 100644 --- a/crates/interpreter/src/instructions.rs +++ b/crates/interpreter/src/instructions.rs @@ -27,9 +27,10 @@ pub mod tx_info; /// Utility functions and helpers for instruction implementation. pub mod utility; -use primitives::hardfork::SpecId; +pub use context_interface::cfg::gas::{self, *}; -use crate::{gas, interpreter_types::InterpreterTypes, Host, InstructionContext}; +use crate::{interpreter_types::InterpreterTypes, Host, InstructionContext}; +use primitives::hardfork::SpecId; /// EVM opcode function signature. #[derive(Debug)] diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index 024938ad8d..9692adce74 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -3,6 +3,7 @@ use crate::{ interpreter_types::{InterpreterTypes, StackTr}, InstructionContext, }; +use context_interface::Host; use primitives::U256; /// Implements the ADD instruction - adds two values from stack. @@ -74,11 +75,11 @@ pub fn mulmod(context: InstructionContext<'_, } /// Implements the EXP instruction - exponentiates two values from stack. -pub fn exp(context: InstructionContext<'_, H, WIRE>) { +pub fn exp(context: InstructionContext<'_, H, WIRE>) { popn_top!([op1], op2, context.interpreter); gas!( context.interpreter, - context.interpreter.gas_params.exp_cost(*op2) + context.host.gas_params().exp_cost(*op2) ); *op2 = op1.pow(*op2); } diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 170fbe56bd..57972a2de2 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -225,7 +225,7 @@ mod tests { push!(interpreter, test.value); push!(interpreter, test.shift); let context = InstructionContext { - host: &mut DummyHost, + host: &mut DummyHost::default(), interpreter: &mut interpreter, }; shl(context); @@ -308,7 +308,7 @@ mod tests { push!(interpreter, test.value); push!(interpreter, test.shift); let context = InstructionContext { - host: &mut DummyHost, + host: &mut DummyHost::default(), interpreter: &mut interpreter, }; shr(context); @@ -416,7 +416,7 @@ mod tests { push!(interpreter, test.value); push!(interpreter, test.shift); let context = InstructionContext { - host: &mut DummyHost, + host: &mut DummyHost::default(), interpreter: &mut interpreter, }; sar(context); @@ -454,7 +454,7 @@ mod tests { push!(interpreter, test.input); push!(interpreter, U256::from(test.index)); let context = InstructionContext { - host: &mut DummyHost, + host: &mut DummyHost::default(), interpreter: &mut interpreter, }; byte(context); @@ -466,7 +466,8 @@ mod tests { #[test] fn test_clz() { let mut interpreter = Interpreter::default(); - interpreter.set_spec_id(SpecId::OSAKA); + interpreter.runtime_flag.spec_id = SpecId::OSAKA; + let mut host = DummyHost::new(SpecId::OSAKA); struct TestCase { value: U256, @@ -507,7 +508,7 @@ mod tests { for test in test_cases { push!(interpreter, test.value); let context = InstructionContext { - host: &mut DummyHost, + host: &mut host, interpreter: &mut interpreter, }; clz(context); diff --git a/crates/interpreter/src/instructions/contract.rs b/crates/interpreter/src/instructions/contract.rs index dbcefcc426..b7a7cc9927 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -52,12 +52,17 @@ pub fn create( } gas!( context.interpreter, - context.interpreter.gas_params.initcode_cost(len) + context.host.gas_params().initcode_cost(len) ); } let code_offset = as_usize_or_fail!(context.interpreter, code_offset); - resize_memory!(context.interpreter, code_offset, len); + resize_memory!( + context.interpreter, + context.host.gas_params(), + code_offset, + len + ); code = Bytes::copy_from_slice( context @@ -74,14 +79,11 @@ pub fn create( // SAFETY: `len` is reasonable in size as gas for it is already deducted. gas!( context.interpreter, - context.interpreter.gas_params.create2_cost(len) + context.host.gas_params().create2_cost(len) ); CreateScheme::Create2 { salt } } else { - gas!( - context.interpreter, - context.interpreter.gas_params.create_cost() - ); + gas!(context.interpreter, context.host.gas_params().create_cost()); CreateScheme::Create }; @@ -95,10 +97,7 @@ pub fn create( .is_enabled_in(SpecId::TANGERINE) { // Take remaining gas and deduce l64 part of it. - gas_limit = context - .interpreter - .gas_params - .call_stipend_reduction(gas_limit); + gas_limit = context.host.gas_params().call_stipend_reduction(gas_limit); } gas!(context.interpreter, gas_limit); @@ -136,7 +135,8 @@ pub fn call( return; } - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter) + let Some((input, return_memory_offset)) = + get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params()) else { return; }; @@ -179,7 +179,8 @@ pub fn call_code( let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); let has_transfer = !value.is_zero(); - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter) + let Some((input, return_memory_offset)) = + get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params()) else { return; }; @@ -222,7 +223,8 @@ pub fn delegate_call( // Max gas limit is not possible in real ethereum situation. let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter) + let Some((input, return_memory_offset)) = + get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params()) else { return; }; @@ -265,7 +267,8 @@ pub fn static_call( // Max gas limit is not possible in real ethereum situation. let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter) + let Some((input, return_memory_offset)) = + get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params()) else { return; }; diff --git a/crates/interpreter/src/instructions/contract/call_helpers.rs b/crates/interpreter/src/instructions/contract/call_helpers.rs index f58ad3ae7a..47d49305fc 100644 --- a/crates/interpreter/src/instructions/contract/call_helpers.rs +++ b/crates/interpreter/src/instructions/contract/call_helpers.rs @@ -1,10 +1,9 @@ use crate::{ - gas::params::GasParams, interpreter::Interpreter, interpreter_types::{InterpreterTypes, MemoryTr, RuntimeFlag, StackTr}, InstructionContext, }; -use context_interface::{host::LoadError, Host}; +use context_interface::{cfg::GasParams, host::LoadError, Host}; use core::{cmp::min, ops::Range}; use primitives::{ hardfork::SpecId::{self, *}, @@ -16,17 +15,18 @@ use state::Bytecode; #[inline] pub fn get_memory_input_and_out_ranges( interpreter: &mut Interpreter, + gas_params: &GasParams, ) -> Option<(Range, Range)> { popn!([in_offset, in_len, out_offset, out_len], interpreter, None); - let mut in_range = resize_memory(interpreter, in_offset, in_len)?; + let mut in_range = resize_memory(interpreter, gas_params, in_offset, in_len)?; if !in_range.is_empty() { let offset = interpreter.memory.local_memory_offset(); in_range = in_range.start.saturating_add(offset)..in_range.end.saturating_add(offset); } - let ret_range = resize_memory(interpreter, out_offset, out_len)?; + let ret_range = resize_memory(interpreter, gas_params, out_offset, out_len)?; Some((in_range, ret_range)) } @@ -35,13 +35,14 @@ pub fn get_memory_input_and_out_ranges( #[inline] pub fn resize_memory( interpreter: &mut Interpreter, + gas_params: &GasParams, offset: U256, len: U256, ) -> Option> { let len = as_usize_or_fail_ret!(interpreter, len, None); let offset = if len != 0 { let offset = as_usize_or_fail_ret!(interpreter, offset, None); - resize_memory!(interpreter, offset, len, None); + resize_memory!(interpreter, gas_params, offset, len, None); offset } else { usize::MAX //unrealistic value so we are sure it is not used @@ -62,7 +63,7 @@ pub fn load_acc_and_calc_gas( if transfers_value { gas!( context.interpreter, - context.interpreter.gas_params.transfer_value_cost(), + context.host.gas_params().transfer_value_cost(), None ); } @@ -76,12 +77,13 @@ pub fn load_acc_and_calc_gas( gas!(interpreter, gas, None); let interpreter = &mut context.interpreter; + let host = &mut context.host; // EIP-150: Gas cost changes for IO-heavy operations let mut gas_limit = if interpreter.runtime_flag.spec_id().is_enabled_in(TANGERINE) { // On mainnet this will take return 63/64 of gas_limit. - let reduced_gas_limit = interpreter - .gas_params + let reduced_gas_limit = host + .gas_params() .call_stipend_reduction(interpreter.gas.remaining()); min(reduced_gas_limit, stack_gas_limit) } else { @@ -91,7 +93,7 @@ pub fn load_acc_and_calc_gas( // Add call stipend if there is value to be transferred. if transfers_value { - gas_limit = gas_limit.saturating_add(interpreter.gas_params.call_stipend()); + gas_limit = gas_limit.saturating_add(host.gas_params().call_stipend()); } Some((gas_limit, bytecode, code_hash)) @@ -107,10 +109,8 @@ pub fn load_account_delegated_handle_error( ) -> Option<(u64, Bytecode, B256)> { // move this to static gas. let remaining_gas = context.interpreter.gas.remaining(); - let gas_table = &context.interpreter.gas_params; match load_account_delegated( context.host, - gas_table, context.interpreter.runtime_flag.spec_id(), remaining_gas, to, @@ -134,7 +134,6 @@ pub fn load_account_delegated_handle_error( #[inline] pub fn load_account_delegated( host: &mut H, - gas_table: &GasParams, spec: SpecId, remaining_gas: u64, address: Address, @@ -145,7 +144,8 @@ pub fn load_account_delegated( let is_berlin = spec.is_enabled_in(SpecId::BERLIN); let is_spurious_dragon = spec.is_enabled_in(SpecId::SPURIOUS_DRAGON); - let additional_cold_cost = gas_table.cold_account_additional_cost(); + let additional_cold_cost = host.gas_params().cold_account_additional_cost(); + let warm_storage_read_cost = host.gas_params().warm_storage_read_cost(); let skip_cold_load = is_berlin && remaining_gas < additional_cold_cost; let account = host.load_account_info_skip_cold_load(address, true, skip_cold_load)?; @@ -156,14 +156,16 @@ pub fn load_account_delegated( let mut code_hash = account.code_hash(); // New account cost, as account is empty there is no delegated account and we can return early. if create_empty_account && account.is_empty { - cost += gas_table.new_account_cost(is_spurious_dragon, transfers_value); + cost += host + .gas_params() + .new_account_cost(is_spurious_dragon, transfers_value); return Ok((cost, bytecode, code_hash)); } // load delegate code if account is EIP-7702 if let Some(Bytecode::Eip7702(code)) = &account.code { // EIP-7702 is enabled after berlin hardfork. - cost += gas_table.warm_storage_read_cost(); + cost += warm_storage_read_cost; if cost > remaining_gas { return Err(LoadError::ColdLoadSkipped); } diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index 32ff53ca81..bb85393b6b 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -3,6 +3,7 @@ use crate::{ interpreter_types::{InterpreterTypes, Jumps, LoopControl, MemoryTr, RuntimeFlag, StackTr}, InstructionResult, InterpreterAction, }; +use context_interface::{cfg::GasParams, Host}; use primitives::{Bytes, U256}; use crate::InstructionContext; @@ -61,6 +62,7 @@ pub fn pc(context: InstructionContext<'_, H, /// Handles memory data retrieval and sets the return action. fn return_inner( interpreter: &mut Interpreter, + gas_params: &GasParams, instruction_result: InstructionResult, ) { popn!([offset, len], interpreter); @@ -69,7 +71,7 @@ fn return_inner( let mut output = Bytes::default(); if len != 0 { let offset = as_usize_or_fail!(interpreter, offset); - if !interpreter.resize_memory(offset, len) { + if !interpreter.resize_memory(gas_params, offset, len) { return; } output = interpreter.memory.slice_len(offset, len).to_vec().into() @@ -87,14 +89,22 @@ fn return_inner( /// Implements the RETURN instruction. /// /// Halts execution and returns data from memory. -pub fn ret(context: InstructionContext<'_, H, WIRE>) { - return_inner(context.interpreter, InstructionResult::Return); +pub fn ret(context: InstructionContext<'_, H, WIRE>) { + return_inner( + context.interpreter, + context.host.gas_params(), + InstructionResult::Return, + ); } /// EIP-140: REVERT instruction -pub fn revert(context: InstructionContext<'_, H, WIRE>) { +pub fn revert(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, BYZANTIUM); - return_inner(context.interpreter, InstructionResult::Revert); + return_inner( + context.interpreter, + context.host.gas_params(), + InstructionResult::Revert, + ); } /// Stop opcode. This opcode halts the execution. diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 489be144b6..99f5c007bd 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -120,7 +120,7 @@ pub fn extcodecopy( let len = as_usize_or_fail!(context.interpreter, len_u256); gas!( context.interpreter, - context.interpreter.gas_params.extcodecopy(len) + context.host.gas_params().extcodecopy(len) ); let mut memory_offset_usize = 0; @@ -129,7 +129,12 @@ pub fn extcodecopy( // fail on casting of memory_offset only if len is not zero. memory_offset_usize = as_usize_or_fail!(context.interpreter, memory_offset); // Resize memory to fit the code - resize_memory!(context.interpreter, memory_offset_usize, len); + resize_memory!( + context.interpreter, + context.host.gas_params(), + memory_offset_usize, + len + ); } let code = if spec_id.is_enabled_in(BERLIN) { @@ -195,10 +200,7 @@ pub fn sload(context: InstructionConte let target = context.interpreter.input.target_address(); if spec_id.is_enabled_in(BERLIN) { - let additional_cold_cost = context - .interpreter - .gas_params - .cold_storage_additional_cost(); + let additional_cold_cost = context.host.gas_params().cold_storage_additional_cost(); let skip_cold = context.interpreter.gas.remaining() < additional_cold_cost; let res = context.host.sload_skip_cold_load(target, *index, skip_cold); match res { @@ -233,7 +235,7 @@ pub fn sstore(context: InstructionCont // EIP-2200: Structured Definitions for Net Gas Metering // If gasleft is less than or equal to gas stipend, fail the current call frame with ‘out of gas’ exception. if spec_id.is_enabled_in(ISTANBUL) - && context.interpreter.gas.remaining() <= context.interpreter.gas_params.call_stipend() + && context.interpreter.gas.remaining() <= context.host.gas_params().call_stipend() { context .interpreter @@ -243,14 +245,11 @@ pub fn sstore(context: InstructionCont gas!( context.interpreter, - context.interpreter.gas_params.sstore_static_gas() + context.host.gas_params().sstore_static_gas() ); let state_load = if spec_id.is_enabled_in(BERLIN) { - let additional_cold_cost = context - .interpreter - .gas_params - .cold_storage_additional_cost(); + let additional_cold_cost = context.host.gas_params().cold_storage_additional_cost(); let skip_cold = context.interpreter.gas.remaining() < additional_cold_cost; let res = context .host @@ -272,7 +271,7 @@ pub fn sstore(context: InstructionCont // dynamic gas gas!( context.interpreter, - context.interpreter.gas_params.sstore_dynamic_gas( + context.host.gas_params().sstore_dynamic_gas( is_istanbul, &state_load.data, state_load.is_cold @@ -282,8 +281,8 @@ pub fn sstore(context: InstructionCont // refund context.interpreter.gas.record_refund( context - .interpreter - .gas_params + .host + .gas_params() .sstore_refund(is_istanbul, &state_load.data), ); } @@ -323,14 +322,14 @@ pub fn log( let len = as_usize_or_fail!(context.interpreter, len); gas!( context.interpreter, - context.interpreter.gas_params.log_cost(N as u8, len as u64) + context.host.gas_params().log_cost(N as u8, len as u64) ); let data = if len == 0 { Bytes::new() } else { let offset = as_usize_or_fail!(context.interpreter, offset); // Resize memory to fit the data - resize_memory!(context.interpreter, offset, len); + resize_memory!(context.interpreter, context.host.gas_params(), offset, len); Bytes::copy_from_slice(context.interpreter.memory.slice_len(offset, len).as_ref()) }; let Some(topics) = context.interpreter.stack.popn::() else { @@ -358,7 +357,7 @@ pub fn selfdestruct( let target = target.into_address(); let spec = context.interpreter.runtime_flag.spec_id(); - let cold_load_gas = context.interpreter.gas_params.selfdestruct_cold_cost(); + let cold_load_gas = context.host.gas_params().selfdestruct_cold_cost(); let skip_cold_load = context.interpreter.gas.remaining() < cold_load_gas; let res = match context.host.selfdestruct( @@ -381,8 +380,8 @@ pub fn selfdestruct( gas!( context.interpreter, context - .interpreter - .gas_params + .host + .gas_params() .selfdestruct_cost(should_charge_topup, res.is_cold) ); @@ -390,7 +389,7 @@ pub fn selfdestruct( context .interpreter .gas - .record_refund(context.interpreter.gas_params.selfdestruct_refund()); + .record_refund(context.host.gas_params().selfdestruct_refund()); } context.interpreter.halt(InstructionResult::SelfDestruct); diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index 30e30fd949..a45648a52f 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -51,10 +51,7 @@ macro_rules! berlin_load_account { $crate::berlin_load_account!($context, $address, $load_code, ()) }; ($context:expr, $address:expr, $load_code:expr, $ret:expr) => {{ - let cold_load_gas = $context - .interpreter - .gas_params - .cold_account_additional_cost(); + let cold_load_gas = $context.host.gas_params().cold_account_additional_cost(); let skip_cold_load = $context.interpreter.gas.remaining() < cold_load_gas; match $context .host @@ -83,14 +80,14 @@ macro_rules! berlin_load_account { #[macro_export] #[collapse_debuginfo(yes)] macro_rules! resize_memory { - ($interpreter:expr, $offset:expr, $len:expr) => { - $crate::resize_memory!($interpreter, $offset, $len, ()) + ($interpreter:expr, $gas_params:expr, $offset:expr, $len:expr) => { + $crate::resize_memory!($interpreter, $gas_params, $offset, $len, ()) }; - ($interpreter:expr, $offset:expr, $len:expr, $ret:expr) => { + ($interpreter:expr, $gas_params:expr, $offset:expr, $len:expr, $ret:expr) => { if let Err(result) = $crate::interpreter::resize_memory( &mut $interpreter.gas, &mut $interpreter.memory, - &$interpreter.gas_params, + $gas_params, $offset, $len, ) { diff --git a/crates/interpreter/src/instructions/memory.rs b/crates/interpreter/src/instructions/memory.rs index 81d7e0c46a..f6856fc634 100644 --- a/crates/interpreter/src/instructions/memory.rs +++ b/crates/interpreter/src/instructions/memory.rs @@ -1,4 +1,5 @@ use crate::interpreter_types::{InterpreterTypes, MemoryTr, RuntimeFlag, StackTr}; +use context_interface::Host; use core::cmp::max; use primitives::U256; @@ -7,10 +8,10 @@ use crate::InstructionContext; /// Implements the MLOAD instruction. /// /// Loads a 32-byte word from memory. -pub fn mload(context: InstructionContext<'_, H, WIRE>) { +pub fn mload(context: InstructionContext<'_, H, WIRE>) { popn_top!([], top, context.interpreter); let offset = as_usize_or_fail!(context.interpreter, top); - resize_memory!(context.interpreter, offset, 32); + resize_memory!(context.interpreter, context.host.gas_params(), offset, 32); *top = U256::try_from_be_slice(context.interpreter.memory.slice_len(offset, 32).as_ref()).unwrap() } @@ -18,10 +19,10 @@ pub fn mload(context: InstructionContext<'_, /// Implements the MSTORE instruction. /// /// Stores a 32-byte word to memory. -pub fn mstore(context: InstructionContext<'_, H, WIRE>) { +pub fn mstore(context: InstructionContext<'_, H, WIRE>) { popn!([offset, value], context.interpreter); let offset = as_usize_or_fail!(context.interpreter, offset); - resize_memory!(context.interpreter, offset, 32); + resize_memory!(context.interpreter, context.host.gas_params(), offset, 32); context .interpreter .memory @@ -31,10 +32,10 @@ pub fn mstore(context: InstructionContext<'_, /// Implements the MSTORE8 instruction. /// /// Stores a single byte to memory. -pub fn mstore8(context: InstructionContext<'_, H, WIRE>) { +pub fn mstore8(context: InstructionContext<'_, H, WIRE>) { popn!([offset, value], context.interpreter); let offset = as_usize_or_fail!(context.interpreter, offset); - resize_memory!(context.interpreter, offset, 1); + resize_memory!(context.interpreter, context.host.gas_params(), offset, 1); context.interpreter.memory.set(offset, &[value.byte(0)]); } @@ -51,7 +52,7 @@ pub fn msize(context: InstructionContext<'_, /// Implements the MCOPY instruction. /// /// EIP-5656: Memory copying instruction that copies memory from one location to another. -pub fn mcopy(context: InstructionContext<'_, H, WIRE>) { +pub fn mcopy(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, CANCUN); popn!([dst, src, len], context.interpreter); @@ -60,7 +61,7 @@ pub fn mcopy(context: InstructionContext<'_, // Deduce gas gas!( context.interpreter, - context.interpreter.gas_params.mcopy_cost(len) + context.host.gas_params().mcopy_cost(len) ); if len == 0 { @@ -70,7 +71,12 @@ pub fn mcopy(context: InstructionContext<'_, let dst = as_usize_or_fail!(context.interpreter, dst); let src = as_usize_or_fail!(context.interpreter, src); // Resize memory - resize_memory!(context.interpreter, max(dst, src), len); + resize_memory!( + context.interpreter, + context.host.gas_params(), + max(dst, src), + len + ); // Copy memory in place context.interpreter.memory.copy(dst, src, len); } diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs index 678dd7bf6a..1ad3bf86cc 100644 --- a/crates/interpreter/src/instructions/system.rs +++ b/crates/interpreter/src/instructions/system.rs @@ -5,6 +5,7 @@ use crate::{ }, CallInput, InstructionResult, }; +use context_interface::{cfg::GasParams, Host}; use core::ptr; use primitives::{B256, KECCAK_EMPTY, U256}; @@ -13,18 +14,20 @@ use crate::InstructionContext; /// Implements the KECCAK256 instruction. /// /// Computes Keccak-256 hash of memory data. -pub fn keccak256(context: InstructionContext<'_, H, WIRE>) { +pub fn keccak256( + context: InstructionContext<'_, H, WIRE>, +) { popn_top!([offset], top, context.interpreter); let len = as_usize_or_fail!(context.interpreter, top); gas!( context.interpreter, - context.interpreter.gas_params.keccak256_cost(len) + context.host.gas_params().keccak256_cost(len) ); let hash = if len == 0 { KECCAK_EMPTY } else { let from = as_usize_or_fail!(context.interpreter, offset); - resize_memory!(context.interpreter, from, len); + resize_memory!(context.interpreter, context.host.gas_params(), from, len); primitives::keccak256(context.interpreter.memory.slice_len(from, len).as_ref()) }; *top = hash.into(); @@ -73,11 +76,17 @@ pub fn codesize(context: InstructionContext<' /// Implements the CODECOPY instruction. /// /// Copies running contract's bytecode to memory. -pub fn codecopy(context: InstructionContext<'_, H, WIRE>) { +pub fn codecopy( + context: InstructionContext<'_, H, WIRE>, +) { popn!([memory_offset, code_offset, len], context.interpreter); let len = as_usize_or_fail!(context.interpreter, len); - let Some(memory_offset) = copy_cost_and_memory_resize(context.interpreter, memory_offset, len) - else { + let Some(memory_offset) = copy_cost_and_memory_resize( + context.interpreter, + context.host.gas_params(), + memory_offset, + len, + ) else { return; }; let code_offset = as_usize_saturated!(code_offset); @@ -148,11 +157,17 @@ pub fn callvalue(context: InstructionContext< /// Implements the CALLDATACOPY instruction. /// /// Copies input data to memory. -pub fn calldatacopy(context: InstructionContext<'_, H, WIRE>) { +pub fn calldatacopy( + context: InstructionContext<'_, H, WIRE>, +) { popn!([memory_offset, data_offset, len], context.interpreter); let len = as_usize_or_fail!(context.interpreter, len); - let Some(memory_offset) = copy_cost_and_memory_resize(context.interpreter, memory_offset, len) - else { + let Some(memory_offset) = copy_cost_and_memory_resize( + context.interpreter, + context.host.gas_params(), + memory_offset, + len, + ) else { return; }; @@ -185,7 +200,9 @@ pub fn returndatasize(context: InstructionCon } /// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY -pub fn returndatacopy(context: InstructionContext<'_, H, WIRE>) { +pub fn returndatacopy( + context: InstructionContext<'_, H, WIRE>, +) { check!(context.interpreter, BYZANTIUM); popn!([memory_offset, offset, len], context.interpreter); @@ -199,8 +216,12 @@ pub fn returndatacopy(context: InstructionCon return; } - let Some(memory_offset) = copy_cost_and_memory_resize(context.interpreter, memory_offset, len) - else { + let Some(memory_offset) = copy_cost_and_memory_resize( + context.interpreter, + context.host.gas_params(), + memory_offset, + len, + ) else { return; }; @@ -228,16 +249,17 @@ pub fn gas(context: InstructionContext<'_, H, /// Handles memory expansion and gas calculation for data copy operations. pub fn copy_cost_and_memory_resize( interpreter: &mut Interpreter, + gas_params: &GasParams, memory_offset: U256, len: usize, ) -> Option { // Safe to cast usize to u64 - gas!(interpreter, interpreter.gas_params.copy_cost(len), None); + gas!(interpreter, gas_params.copy_cost(len), None); if len == 0 { return None; } let memory_offset = as_usize_or_fail_ret!(interpreter, memory_offset, None); - resize_memory!(interpreter, memory_offset, len, None); + resize_memory!(interpreter, gas_params, memory_offset, len, None); Some(memory_offset) } diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 2bfdab36a5..698773409f 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -9,6 +9,7 @@ mod runtime_flags; mod shared_memory; mod stack; +use context_interface::cfg::GasParams; // re-exports pub use ext_bytecode::ExtBytecode; pub use input::InputsImpl; @@ -19,8 +20,8 @@ pub use stack::{Stack, STACK_LIMIT}; // imports use crate::{ - gas::params::GasParams, host::DummyHost, instruction_context::InstructionContext, - interpreter_types::*, Gas, Host, InstructionResult, InstructionTable, InterpreterAction, + host::DummyHost, instruction_context::InstructionContext, interpreter_types::*, Gas, Host, + InstructionResult, InstructionTable, InterpreterAction, }; use bytecode::Bytecode; use primitives::{hardfork::SpecId, Bytes}; @@ -29,8 +30,6 @@ use primitives::{hardfork::SpecId, Bytes}; #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Interpreter { - /// Gas table for dynamic gas constants. - pub gas_params: GasParams, /// Bytecode being executed. pub bytecode: WIRE::Bytecode, /// Gas tracking for execution costs. @@ -58,7 +57,6 @@ impl Interpreter> { is_static: bool, spec_id: SpecId, gas_limit: u64, - gas_params: GasParams, ) -> Self { Self::new_inner( Stack::new(), @@ -68,7 +66,6 @@ impl Interpreter> { is_static, spec_id, gas_limit, - gas_params, ) } @@ -91,7 +88,6 @@ impl Interpreter> { false, SpecId::default(), u64::MAX, - GasParams::default(), ) } @@ -104,12 +100,10 @@ impl Interpreter> { is_static: bool, spec_id: SpecId, gas_limit: u64, - gas_params: GasParams, ) -> Self { Self { bytecode, gas: Gas::new(gas_limit), - gas_params, stack, return_data: Default::default(), memory, @@ -130,12 +124,10 @@ impl Interpreter> { is_static: bool, spec_id: SpecId, gas_limit: u64, - gas_params: GasParams, ) { let Self { bytecode: bytecode_ref, gas, - gas_params: gas_params_ref, stack, return_data, memory: memory_ref, @@ -154,7 +146,6 @@ impl Interpreter> { *memory_ref = memory; *input_ref = input; *runtime_flag = RuntimeFlags { spec_id, is_static }; - *gas_params_ref = gas_params; *extend = EXT::default(); } @@ -163,12 +154,6 @@ impl Interpreter> { self.bytecode = ExtBytecode::new(bytecode); self } - - /// Sets the specid for the interpreter. - pub fn set_spec_id(&mut self, spec_id: SpecId) { - self.gas_params = GasParams::new_spec(spec_id); - self.runtime_flag.spec_id = spec_id; - } } impl Default for Interpreter { @@ -198,14 +183,9 @@ impl Interpreter { /// Performs EVM memory resize. #[inline] #[must_use] - pub fn resize_memory(&mut self, offset: usize, len: usize) -> bool { - if let Err(result) = resize_memory( - &mut self.gas, - &mut self.memory, - &self.gas_params, - offset, - len, - ) { + pub fn resize_memory(&mut self, gas_params: &GasParams, offset: usize, len: usize) -> bool { + if let Err(result) = resize_memory(&mut self.gas, &mut self.memory, gas_params, offset, len) + { self.halt(result); return false; } @@ -333,7 +313,7 @@ impl Interpreter { /// This uses dummy Host. #[inline] pub fn step_dummy(&mut self, instruction_table: &InstructionTable) { - self.step(instruction_table, &mut DummyHost); + self.step(instruction_table, &mut DummyHost::default()); } /// Executes the interpreter until it returns or stops. @@ -448,7 +428,6 @@ mod tests { false, SpecId::default(), u64::MAX, - GasParams::default(), ); let serialized = serde_json::to_string_pretty(&interpreter).unwrap(); @@ -486,11 +465,10 @@ fn test_mstore_big_offset_memory_oog() { false, SpecId::default(), 1000, - GasParams::default(), ); let table = instruction_table::(); - let mut host = DummyHost; + let mut host = DummyHost::default(); let action = interpreter.run_plain(&table, &mut host); assert!(action.is_return()); @@ -525,11 +503,10 @@ fn test_mstore_big_offset_memory_limit_oog() { false, SpecId::default(), 100000, - GasParams::default(), ); let table = instruction_table::(); - let mut host = DummyHost; + let mut host = DummyHost::default(); let action = interpreter.run_plain(&table, &mut host); assert!(action.is_return()); diff --git a/crates/interpreter/src/interpreter/shared_memory.rs b/crates/interpreter/src/interpreter/shared_memory.rs index abaf4ca1df..f7a7598a8c 100644 --- a/crates/interpreter/src/interpreter/shared_memory.rs +++ b/crates/interpreter/src/interpreter/shared_memory.rs @@ -1,5 +1,6 @@ use super::MemoryTr; -use crate::{gas::params::GasParams, InstructionResult}; +use crate::InstructionResult; +use context_interface::cfg::GasParams; use core::{ cell::{Ref, RefCell, RefMut}, cmp::min, diff --git a/crates/op-revm/src/evm.rs b/crates/op-revm/src/evm.rs index a93a172756..64f28a3e35 100644 --- a/crates/op-revm/src/evm.rs +++ b/crates/op-revm/src/evm.rs @@ -1,7 +1,7 @@ //! Contains the `[OpEvm]` type and its implementation of the execution EVM traits. -use crate::precompiles::OpPrecompiles; +use crate::{precompiles::OpPrecompiles, OpSpecId}; use revm::{ - context::{ContextError, ContextSetters, Evm, FrameStack}, + context::{Cfg, ContextError, ContextSetters, Evm, FrameStack}, context_interface::ContextTr, handler::{ evm::FrameTr, @@ -26,17 +26,25 @@ pub struct OpEvm< pub Evm, ); -impl OpEvm, OpPrecompiles> { +impl + Clone>>, INSP> + OpEvm, OpPrecompiles> +{ /// Create a new Optimism EVM. pub fn new(ctx: CTX, inspector: INSP) -> Self { + let spec: OpSpecId = ctx.cfg().spec().into(); Self(Evm { ctx, inspector, - instruction: EthInstructions::new_mainnet(), - precompiles: OpPrecompiles::default(), + instruction: EthInstructions::new_mainnet_with_spec(spec.into()), + precompiles: OpPrecompiles::new_with_spec(spec), frame_stack: FrameStack::new_prealloc(8), }) } + + /// Consumes self and returns the inner context. + pub fn into_context(self) -> CTX { + self.0.ctx + } } impl OpEvm { diff --git a/crates/op-revm/src/handler.rs b/crates/op-revm/src/handler.rs index ef51d2eddc..ada5a49a0f 100644 --- a/crates/op-revm/src/handler.rs +++ b/crates/op-revm/src/handler.rs @@ -464,7 +464,7 @@ mod tests { }; use alloy_primitives::uint; use revm::{ - context::{BlockEnv, Context, TxEnv}, + context::{BlockEnv, CfgEnv, Context, TxEnv}, context_interface::result::InvalidTransaction, database::InMemoryDB, database_interface::EmptyDB, @@ -511,7 +511,7 @@ mod tests { .base(TxEnv::builder().gas_limit(100)) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::BEDROCK); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::BEDROCK)); let gas = call_last_frame_return(ctx, InstructionResult::Revert, Gas::new(90)); assert_eq!(gas.remaining(), 90); @@ -527,7 +527,7 @@ mod tests { .base(TxEnv::builder().gas_limit(100)) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let gas = call_last_frame_return(ctx, InstructionResult::Stop, Gas::new(90)); assert_eq!(gas.remaining(), 90); @@ -544,7 +544,7 @@ mod tests { .source_hash(B256::from([1u8; 32])) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let mut ret_gas = Gas::new(90); ret_gas.record_refund(20); @@ -569,7 +569,7 @@ mod tests { .source_hash(B256::from([1u8; 32])) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::BEDROCK); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::BEDROCK)); let gas = call_last_frame_return(ctx, InstructionResult::Stop, Gas::new(90)); assert_eq!(gas.remaining(), 0); assert_eq!(gas.spent(), 100); @@ -586,7 +586,7 @@ mod tests { .is_system_transaction() .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::BEDROCK); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::BEDROCK)); let gas = call_last_frame_return(ctx, InstructionResult::Stop, Gas::new(90)); assert_eq!(gas.remaining(), 100); assert_eq!(gas.spent(), 0); @@ -613,7 +613,7 @@ mod tests { l1_base_fee_scalar: U256::from(1_000), ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); ctx.modify_tx(|tx| { tx.deposit.source_hash = B256::from([1u8; 32]); tx.deposit.mint = Some(10); @@ -652,7 +652,7 @@ mod tests { l2_block: Some(U256::from(0)), ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)) .with_tx( OpTransaction::builder() .base(TxEnv::builder().gas_limit(100)) @@ -725,7 +725,7 @@ mod tests { number: BLOCK_NUM, ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); @@ -812,7 +812,7 @@ mod tests { number: BLOCK_NUM, ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::JOVIAN) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::JOVIAN)) // set the operator fee to a low value .with_tx( OpTransaction::builder() @@ -880,7 +880,7 @@ mod tests { number: BLOCK_NUM, ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let mut evm = ctx.build_op(); assert_ne!(evm.ctx().chain().l2_block, Some(BLOCK_NUM)); @@ -940,7 +940,7 @@ mod tests { number: BLOCK_NUM, ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ECOTONE); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ECOTONE)); let mut evm = ctx.build_op(); assert_ne!(evm.ctx().chain().l2_block, Some(BLOCK_NUM)); @@ -1013,7 +1013,7 @@ mod tests { number: BLOCK_NUM, ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); @@ -1063,7 +1063,7 @@ mod tests { l2_block: Some(U256::from(0)), ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)) .with_tx( OpTransaction::builder() .base(TxEnv::builder().gas_limit(100)) @@ -1106,7 +1106,7 @@ mod tests { l2_block: Some(U256::from(0)), ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)) .with_tx( OpTransaction::builder() .base(TxEnv::builder().gas_limit(10)) @@ -1148,7 +1148,7 @@ mod tests { l2_block: Some(U256::from(0)), ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::JOVIAN) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::JOVIAN)) .with_tx( OpTransaction::builder() .base(TxEnv::builder().gas_limit(10)) @@ -1190,7 +1190,7 @@ mod tests { l2_block: Some(U256::from(0)), ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)) .modify_tx_chained(|tx| { tx.enveloped_tx = Some(bytes!("FACADE")); }); @@ -1221,7 +1221,7 @@ mod tests { tx.deposit.source_hash = B256::from([1u8; 32]); tx.deposit.is_system_transaction = true; }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let mut evm = ctx.build_op(); let handler = @@ -1234,7 +1234,11 @@ mod tests { )) ); - evm.ctx().modify_cfg(|cfg| cfg.spec = OpSpecId::BEDROCK); + // With BEDROCK spec. + let ctx = evm.into_context(); + let mut evm = ctx + .with_cfg(CfgEnv::new_with_spec(OpSpecId::BEDROCK)) + .build_op(); // Pre-regolith system transactions should be allowed. assert!(handler.validate_env(&mut evm).is_ok()); @@ -1247,7 +1251,7 @@ mod tests { .modify_tx_chained(|tx| { tx.deposit.source_hash = B256::from([1u8; 32]); }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let mut evm = ctx.build_op(); let handler = @@ -1263,7 +1267,7 @@ mod tests { .modify_tx_chained(|tx| { tx.deposit.source_hash = B256::from([1u8; 32]); }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let mut evm = ctx.build_op(); let handler = @@ -1280,7 +1284,7 @@ mod tests { // Set up as deposit transaction by having a deposit with source_hash tx.deposit.source_hash = B256::from([1u8; 32]); }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let mut evm = ctx.build_op(); let mut handler = @@ -1363,7 +1367,7 @@ mod tests { }) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); let handler = diff --git a/crates/op-revm/src/l1block.rs b/crates/op-revm/src/l1block.rs index 3c86904b47..325796f4da 100644 --- a/crates/op-revm/src/l1block.rs +++ b/crates/op-revm/src/l1block.rs @@ -11,11 +11,9 @@ use crate::{ OpSpecId, }; use revm::{ + context_interface::cfg::gas::{NON_ZERO_BYTE_MULTIPLIER_ISTANBUL, STANDARD_TOKEN_COST}, database_interface::Database, - interpreter::{ - gas::{get_tokens_in_calldata, NON_ZERO_BYTE_MULTIPLIER_ISTANBUL, STANDARD_TOKEN_COST}, - Gas, - }, + interpreter::{gas::get_tokens_in_calldata, Gas}, primitives::U256, }; diff --git a/crates/state/src/bal.rs b/crates/state/src/bal.rs index 3f713d87c4..bdaaa6953b 100644 --- a/crates/state/src/bal.rs +++ b/crates/state/src/bal.rs @@ -240,3 +240,5 @@ impl core::fmt::Display for BalError { } } } + +impl core::error::Error for BalError {} diff --git a/crates/statetest-types/src/test_unit.rs b/crates/statetest-types/src/test_unit.rs index c09f720fb1..ec11eb7acf 100644 --- a/crates/statetest-types/src/test_unit.rs +++ b/crates/statetest-types/src/test_unit.rs @@ -120,7 +120,7 @@ impl TestUnit { } // Set default prevrandao for merge - if cfg.spec.is_enabled_in(SpecId::MERGE) && block.prevrandao.is_none() { + if cfg.spec().is_enabled_in(SpecId::MERGE) && block.prevrandao.is_none() { block.prevrandao = Some(B256::default()); } @@ -184,8 +184,7 @@ mod tests { fn test_block_env_blob_fee_fraction_cancun() { let unit = create_test_unit_with_excess_blob_gas(0x240000); // 2,359,296 - let mut cfg = CfgEnv::default(); - cfg.spec = SpecId::CANCUN; + let mut cfg = CfgEnv::new_with_spec(SpecId::CANCUN); let block = unit.block_env(&mut cfg); @@ -208,8 +207,7 @@ mod tests { fn test_block_env_blob_fee_fraction_prague() { let unit = create_test_unit_with_excess_blob_gas(0x240000); // 2,359,296 - let mut cfg = CfgEnv::default(); - cfg.spec = SpecId::PRAGUE; + let mut cfg = CfgEnv::new_with_spec(SpecId::PRAGUE); let block = unit.block_env(&mut cfg); @@ -231,8 +229,7 @@ mod tests { fn test_block_env_blob_fee_fraction_osaka() { let unit = create_test_unit_with_excess_blob_gas(0x240000); // 2,359,296 - let mut cfg = CfgEnv::default(); - cfg.spec = SpecId::OSAKA; + let mut cfg = CfgEnv::new_with_spec(SpecId::OSAKA); let block = unit.block_env(&mut cfg); diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index 9518b3830d..8724cdc038 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -9,7 +9,7 @@ use alloy_sol_types::SolValue; use anyhow::Result; use exec::transact_erc20evm_commit; use revm::{ - context::TxEnv, + context::{CfgEnv, TxEnv}, database::{AlloyDB, BlockId, CacheDB}, database_interface::WrapDatabaseAsync, primitives::{address, hardfork::SpecId, keccak256, Address, StorageValue, TxKind, U256}, @@ -81,9 +81,7 @@ fn balance_of(address: Address, alloy_db: &mut AlloyCacheDB) -> Result Result<()> { let mut ctx = Context::mainnet() .with_db(cache_db) - .modify_cfg_chained(|cfg| { - cfg.spec = SpecId::CANCUN; - }) + .with_cfg(CfgEnv::new_with_spec(SpecId::CANCUN)) .with_tx( TxEnv::builder() .caller(from)