diff --git a/Cargo.lock b/Cargo.lock index 975279d732..26844a7d1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3302,7 +3302,6 @@ dependencies = [ "revm-context-interface", "revm-database", "revm-database-interface", - "revm-interpreter", "revm-primitives", "revm-specification", "revm-state", diff --git a/crates/context/Cargo.toml b/crates/context/Cargo.toml index 1844f5b096..e873989bc7 100644 --- a/crates/context/Cargo.toml +++ b/crates/context/Cargo.toml @@ -23,18 +23,17 @@ all = "warn" [dependencies] # revm -interpreter.workspace = true context-interface.workspace = true primitives.workspace = true database-interface.workspace = true state.workspace = true specification.workspace = true bytecode.workspace = true -auto_impl.workspace = true # misc derive-where.workspace = true cfg-if.workspace = true +auto_impl.workspace = true # Optional serde = { workspace = true, features = ["derive", "rc"], optional = true } diff --git a/crates/context/interface/src/context.rs b/crates/context/interface/src/context.rs index 99164602f1..e3cfbafbac 100644 --- a/crates/context/interface/src/context.rs +++ b/crates/context/interface/src/context.rs @@ -1,7 +1,7 @@ pub use crate::journaled_state::StateLoad; -use crate::{journaled_state::AccountLoad, Block, Cfg, Database, Journal, Transaction}; +use crate::{Block, Cfg, Database, Journal, Transaction}; use auto_impl::auto_impl; -use primitives::{Address, Bytes, Log, B256, BLOCK_HASH_HISTORY, U256}; +use primitives::U256; #[auto_impl(&mut, Box)] pub trait ContextTr { @@ -22,130 +22,6 @@ pub trait ContextTr { fn chain(&mut self) -> &mut Self::Chain; fn error(&mut self) -> &mut Result<(), ::Error>; fn tx_journal(&mut self) -> (&mut Self::Tx, &mut Self::Journal); - // default implementationHost calls interface - - /// Gets the block hash of the given block `number`. - fn block_hash(&mut self, requested_number: u64) -> Option { - let block_number = self.block().number(); - - let Some(diff) = block_number.checked_sub(requested_number) else { - return Some(B256::ZERO); - }; - - // blockhash should push zero if number is same as current block number. - if diff == 0 { - return Some(B256::ZERO); - } - - if diff <= BLOCK_HASH_HISTORY { - return self - .journal() - .db() - .block_hash(requested_number) - .map_err(|e| { - *self.error() = Err(e); - }) - .ok(); - } - - Some(B256::ZERO) - } - - fn load_account_delegated(&mut self, address: Address) -> Option> { - self.journal() - .load_account_delegated(address) - .map_err(|e| { - *self.error() = Err(e); - }) - .ok() - } - - /// Gets balance of `address` and if the account is cold. - fn balance(&mut self, address: Address) -> Option> { - self.journal() - .load_account(address) - .map(|acc| acc.map(|a| a.info.balance)) - .map_err(|e| { - *self.error() = Err(e); - }) - .ok() - } - - /// Gets code of `address` and if the account is cold. - fn code(&mut self, address: Address) -> Option> { - self.journal() - .code(address) - .map_err(|e| { - *self.error() = Err(e); - }) - .ok() - } - - /// Gets code hash of `address` and if the account is cold. - fn code_hash(&mut self, address: Address) -> Option> { - self.journal() - .code_hash(address) - .map_err(|e| { - *self.error() = Err(e); - }) - .ok() - } - - /// Gets storage value of `address` at `index` and if the account is cold. - fn sload(&mut self, address: Address, index: U256) -> Option> { - self.journal() - .sload(address, index) - .map_err(|e| { - *self.error() = Err(e); - }) - .ok() - } - - /// Sets storage value of account address at index. - /// - /// Returns [`StateLoad`] with [`SStoreResult`] that contains original/new/old storage value. - fn sstore( - &mut self, - address: Address, - index: U256, - value: U256, - ) -> Option> { - self.journal() - .sstore(address, index, value) - .map_err(|e| { - *self.error() = Err(e); - }) - .ok() - } - - /// Gets the transient storage value of `address` at `index`. - fn tload(&mut self, address: Address, index: U256) -> U256 { - self.journal().tload(address, index) - } - - /// Sets the transient storage value of `address` at `index`. - fn tstore(&mut self, address: Address, index: U256, value: U256) { - self.journal().tstore(address, index, value) - } - - /// Emits a log owned by `address` with given `LogData`. - fn log(&mut self, log: Log) { - self.journal().log(log); - } - - /// Marks `address` to be deleted, with funds transferred to `target`. - fn selfdestruct( - &mut self, - address: Address, - target: Address, - ) -> Option> { - self.journal() - .selfdestruct(address, target) - .map_err(|e| { - *self.error() = Err(e); - }) - .ok() - } } /// Represents the result of an `sstore` operation. diff --git a/crates/context/interface/src/journaled_state.rs b/crates/context/interface/src/journaled_state.rs index 88725bda67..9675a12d49 100644 --- a/crates/context/interface/src/journaled_state.rs +++ b/crates/context/interface/src/journaled_state.rs @@ -3,7 +3,10 @@ use core::ops::{Deref, DerefMut}; use database_interface::Database; use primitives::{Address, Bytes, HashSet, Log, B256, U256}; use specification::hardfork::SpecId; -use state::{Account, Bytecode}; +use state::{ + bytecode::{EOF_MAGIC_BYTES, EOF_MAGIC_HASH}, + Account, Bytecode, +}; pub trait Journal { type Database: Database; @@ -108,15 +111,50 @@ pub trait Journal { self.set_code_with_hash(address, code, hash); } + /// Returns account code bytes and if address is cold loaded. + /// + /// In case of EOF account it will return `EOF_MAGIC` (0xEF00) as code. + #[inline] fn code( &mut self, address: Address, - ) -> Result, ::Error>; + ) -> Result, ::Error> { + let a = self.load_account_code(address)?; + // SAFETY: Safe to unwrap as load_code will insert code if it is empty. + let code = a.info.code.as_ref().unwrap(); + + let code = if code.is_eof() { + EOF_MAGIC_BYTES.clone() + } else { + code.original_bytes() + }; + + Ok(StateLoad::new(code, a.is_cold)) + } + /// Gets code hash of account. + /// + /// In case of EOF account it will return `EOF_MAGIC_HASH` + /// (the hash of `0xEF00`). fn code_hash( &mut self, address: Address, - ) -> Result, ::Error>; + ) -> Result, ::Error> { + let acc = self.load_account_code(address)?; + if acc.is_empty() { + return Ok(StateLoad::new(B256::ZERO, acc.is_cold)); + } + // SAFETY: Safe to unwrap as load_code will insert code if it is empty. + let code = acc.info.code.as_ref().unwrap(); + + let hash = if code.is_eof() { + EOF_MAGIC_HASH + } else { + acc.info.code_hash + }; + + Ok(StateLoad::new(hash, acc.is_cold)) + } /// Called at the end of the transaction to clean all residue data from journal. fn clear(&mut self); diff --git a/crates/context/src/cfg.rs b/crates/context/src/cfg.rs index a11d31b157..03df6aca8b 100644 --- a/crates/context/src/cfg.rs +++ b/crates/context/src/cfg.rs @@ -1,7 +1,6 @@ pub use context_interface::Cfg; -use interpreter::MAX_CODE_SIZE; -use specification::hardfork::SpecId; +use specification::{eip170::MAX_CODE_SIZE, hardfork::SpecId}; use std::{vec, vec::Vec}; /// EVM configuration diff --git a/crates/context/src/journaled_state.rs b/crates/context/src/journaled_state.rs index 59aca86c61..f98c3cec82 100644 --- a/crates/context/src/journaled_state.rs +++ b/crates/context/src/journaled_state.rs @@ -1,9 +1,11 @@ -use bytecode::{Bytecode, EOF_MAGIC_BYTES, EOF_MAGIC_HASH}; -use context_interface::journaled_state::{AccountLoad, Journal, JournalCheckpoint, TransferError}; +use bytecode::Bytecode; +use context_interface::{ + context::{SStoreResult, SelfDestructResult, StateLoad}, + journaled_state::{AccountLoad, Journal, JournalCheckpoint, TransferError}, +}; use database_interface::Database; -use interpreter::{SStoreResult, SelfDestructResult, StateLoad}; use primitives::{ - hash_map::Entry, Address, Bytes, HashMap, HashSet, Log, B256, KECCAK_EMPTY, PRECOMPILE3, U256, + hash_map::Entry, Address, HashMap, HashSet, Log, B256, KECCAK_EMPTY, PRECOMPILE3, U256, }; use specification::hardfork::{SpecId, SpecId::*}; use state::{Account, EvmState, EvmStorageSlot, TransientStorage}; @@ -144,20 +146,6 @@ impl Journal for JournaledState { self.spec = spec_id; } - fn code( - &mut self, - address: Address, - ) -> Result, ::Error> { - self.code(address) - } - - fn code_hash( - &mut self, - address: Address, - ) -> Result, ::Error> { - self.code_hash(address) - } - fn transfer( &mut self, from: &Address, @@ -417,49 +405,6 @@ impl JournaledState { Ok(None) } - /// Returns account code bytes and if address is cold loaded. - /// - /// In case of EOF account it will return `EOF_MAGIC` (0xEF00) as code. - #[inline] - pub fn code(&mut self, address: Address) -> Result, ::Error> { - let a = self.load_account_code(address)?; - // SAFETY: Safe to unwrap as load_code will insert code if it is empty. - let code = a.info.code.as_ref().unwrap(); - - let code = if code.is_eof() { - EOF_MAGIC_BYTES.clone() - } else { - code.original_bytes() - }; - - Ok(StateLoad::new(code, a.is_cold)) - } - - /// Gets code hash of address. - /// - /// In case of EOF account it will return `EOF_MAGIC_HASH` - /// (the hash of `0xEF00`). - #[inline] - pub fn code_hash( - &mut self, - address: Address, - ) -> Result, ::Error> { - let acc = self.load_account_code(address)?; - if acc.is_empty() { - return Ok(StateLoad::new(B256::ZERO, acc.is_cold)); - } - // SAFETY: Safe to unwrap as load_code will insert code if it is empty. - let code = acc.info.code.as_ref().unwrap(); - - let hash = if code.is_eof() { - EOF_MAGIC_HASH - } else { - acc.info.code_hash - }; - - Ok(StateLoad::new(hash, acc.is_cold)) - } - /// Creates account or returns false if collision is detected. /// /// There are few steps done: diff --git a/crates/interpreter/src/host.rs b/crates/interpreter/src/host.rs new file mode 100644 index 0000000000..28953a9df6 --- /dev/null +++ b/crates/interpreter/src/host.rs @@ -0,0 +1,266 @@ +use context_interface::{ + context::{ContextTr, SStoreResult, SelfDestructResult, StateLoad}, + journaled_state::AccountLoad, + Block, Cfg, Database, Journal, Transaction, TransactionType, +}; +use primitives::{Address, Bytes, Log, B256, U256}; + +use crate::instructions::utility::IntoU256; + +/// Host trait with all methods that are needed by the Interpreter. +/// +/// This trait is implemented for all types that have `ContextTr` trait. +/// +/// There are few groups of functions which are Block, Transaction, Config, Database and Journal functions. +pub trait Host { + /* Block */ + + /// Block basefee, calls ContextTr::block().basefee() + fn basefee(&self) -> U256; + /// Block blob gasprice, calls `ContextTr::block().blob_gasprice()` + fn blob_gasprice(&self) -> U256; + /// Block gas limit, calls ContextTr::block().gas_limit() + fn gas_limit(&self) -> U256; + /// Block difficulty, calls ContextTr::block().difficulty() + fn difficulty(&self) -> U256; + /// Block prevrandao, calls ContextTr::block().prevrandao() + fn prevrandao(&self) -> Option; + /// Block number, calls ContextTr::block().number() + fn block_number(&self) -> u64; + /// Block timestamp, calls ContextTr::block().timestamp() + fn timestamp(&self) -> U256; + /// Block beneficiary, calls ContextTr::block().beneficiary() + fn beneficiary(&self) -> Address; + /// Chain id, calls ContextTr::cfg().chain_id() + fn chain_id(&self) -> U256; + + /* Transaction */ + + /// Transaction effective gas price, calls `ContextTr::tx().effective_gas_price(basefee as u128)` + fn effective_gas_price(&self) -> U256; + /// Transaction caller, calls `ContextTr::tx().caller()` + fn caller(&self) -> Address; + /// Transaction blob hash, calls `ContextTr::tx().blob_hash(number)` + fn blob_hash(&self, number: usize) -> Option; + + /* Config */ + + /// Max initcode size, calls `ContextTr::cfg().max_code_size().saturating_mul(2)` + fn max_initcode_size(&self) -> usize; + + /* Database */ + + /// Block hash, calls `ContextTr::journal().db().block_hash(number)` + fn block_hash(&mut self, number: u64) -> Option; + + /* Journal */ + + /// Load account delegated, calls `ContextTr::journal().load_account_delegated(address)` + fn selfdestruct( + &mut self, + address: Address, + target: Address, + ) -> Option>; + + /// Log, calls `ContextTr::journal().log(log)` + fn log(&mut self, log: Log); + /// Sstore, calls `ContextTr::journal().sstore(address, key, value)` + fn sstore( + &mut self, + address: Address, + key: U256, + value: U256, + ) -> Option>; + + /// Sload, calls `ContextTr::journal().sload(address, key)` + fn sload(&mut self, address: Address, key: U256) -> Option>; + /// Tstore, calls `ContextTr::journal().tstore(address, key, value)` + fn tstore(&mut self, address: Address, key: U256, value: U256); + /// Tload, calls `ContextTr::journal().tload(address, key)` + fn tload(&mut self, address: Address, key: U256) -> U256; + /// Balance, calls `ContextTr::journal().load_account(address)` + fn balance(&mut self, address: Address) -> Option>; + /// Load account delegated, calls `ContextTr::journal().load_account_delegated(address)` + fn load_account_delegated(&mut self, address: Address) -> Option>; + /// Load account code, calls `ContextTr::journal().load_account_code(address)` + fn load_account_code(&mut self, address: Address) -> Option>; + /// Load account code hash, calls `ContextTr::journal().code_hash(address)` + fn load_account_code_hash(&mut self, address: Address) -> Option>; +} + +impl Host for CTX { + /* Block */ + + fn basefee(&self) -> U256 { + U256::from(self.block().basefee()) + } + + fn blob_gasprice(&self) -> U256 { + U256::from(self.block().blob_gasprice().unwrap_or(0)) + } + + fn gas_limit(&self) -> U256 { + U256::from(self.block().gas_limit()) + } + + fn difficulty(&self) -> U256 { + self.block().difficulty() + } + + fn prevrandao(&self) -> Option { + self.block().prevrandao().map(|r| r.into_u256()) + } + + fn block_number(&self) -> u64 { + self.block().number() + } + + fn timestamp(&self) -> U256 { + U256::from(self.block().timestamp()) + } + + fn beneficiary(&self) -> Address { + self.block().beneficiary() + } + + fn chain_id(&self) -> U256 { + U256::from(self.cfg().chain_id()) + } + + /* Transaction */ + + fn effective_gas_price(&self) -> U256 { + let basefee = self.block().basefee(); + U256::from(self.tx().effective_gas_price(basefee as u128)) + } + + fn caller(&self) -> Address { + self.tx().caller() + } + + fn blob_hash(&self, number: usize) -> Option { + let tx = &self.tx(); + if tx.tx_type() != TransactionType::Eip4844 { + return None; + } + tx.blob_versioned_hashes() + .get(number) + .map(|t| U256::from_be_bytes(t.0)) + } + + /* Config */ + + fn max_initcode_size(&self) -> usize { + self.cfg().max_code_size().saturating_mul(2) + } + + /* Database */ + + fn block_hash(&mut self, requested_number: u64) -> Option { + self.journal() + .db() + .block_hash(requested_number) + .map_err(|e| { + *self.error() = Err(e); + }) + .ok() + } + + /* Journal */ + + fn load_account_delegated(&mut self, address: Address) -> Option> { + self.journal() + .load_account_delegated(address) + .map_err(|e| { + *self.error() = Err(e); + }) + .ok() + } + + /// Gets balance of `address` and if the account is cold. + fn balance(&mut self, address: Address) -> Option> { + self.journal() + .load_account(address) + .map(|acc| acc.map(|a| a.info.balance)) + .map_err(|e| { + *self.error() = Err(e); + }) + .ok() + } + + /// Gets code of `address` and if the account is cold. + fn load_account_code(&mut self, address: Address) -> Option> { + self.journal() + .code(address) + .map_err(|e| { + *self.error() = Err(e); + }) + .ok() + } + + /// Gets code hash of `address` and if the account is cold. + fn load_account_code_hash(&mut self, address: Address) -> Option> { + self.journal() + .code_hash(address) + .map_err(|e| { + *self.error() = Err(e); + }) + .ok() + } + + /// Gets storage value of `address` at `index` and if the account is cold. + fn sload(&mut self, address: Address, index: U256) -> Option> { + self.journal() + .sload(address, index) + .map_err(|e| { + *self.error() = Err(e); + }) + .ok() + } + + /// Sets storage value of account address at index. + /// + /// Returns [`StateLoad`] with [`SStoreResult`] that contains original/new/old storage value. + fn sstore( + &mut self, + address: Address, + index: U256, + value: U256, + ) -> Option> { + self.journal() + .sstore(address, index, value) + .map_err(|e| { + *self.error() = Err(e); + }) + .ok() + } + + /// Gets the transient storage value of `address` at `index`. + fn tload(&mut self, address: Address, index: U256) -> U256 { + self.journal().tload(address, index) + } + + /// Sets the transient storage value of `address` at `index`. + fn tstore(&mut self, address: Address, index: U256, value: U256) { + self.journal().tstore(address, index, value) + } + + /// Emits a log owned by `address` with given `LogData`. + fn log(&mut self, log: Log) { + self.journal().log(log); + } + + /// Marks `address` to be deleted, with funds transferred to `target`. + fn selfdestruct( + &mut self, + address: Address, + target: Address, + ) -> Option> { + self.journal() + .selfdestruct(address, target) + .map_err(|e| { + *self.error() = Err(e); + }) + .ok() + } +} diff --git a/crates/interpreter/src/instructions/block_info.rs b/crates/interpreter/src/instructions/block_info.rs index 28fd23ee8a..be83de7cae 100644 --- a/crates/interpreter/src/instructions/block_info.rs +++ b/crates/interpreter/src/instructions/block_info.rs @@ -1,11 +1,9 @@ use crate::{ gas, - instructions::utility::IntoU256, interpreter::Interpreter, interpreter_types::{InterpreterTypes, LoopControl, RuntimeFlag, StackTr}, Host, }; -use context_interface::{Block, Cfg}; use primitives::U256; use specification::hardfork::SpecId::*; @@ -16,7 +14,7 @@ pub fn chainid( ) { check!(interpreter, ISTANBUL); gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(host.cfg().chain_id())); + push!(interpreter, U256::from(host.chain_id())); } pub fn coinbase( @@ -24,7 +22,7 @@ pub fn coinbase( host: &mut H, ) { gas!(interpreter, gas::BASE); - push!(interpreter, host.block().beneficiary().into_word().into()); + push!(interpreter, host.beneficiary().into_word().into()); } pub fn timestamp( @@ -32,7 +30,7 @@ pub fn timestamp( host: &mut H, ) { gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(host.block().timestamp())); + push!(interpreter, U256::from(host.timestamp())); } pub fn block_number( @@ -40,7 +38,7 @@ pub fn block_number( host: &mut H, ) { gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(host.block().number())); + push!(interpreter, U256::from(host.block_number())); } pub fn difficulty( @@ -50,12 +48,9 @@ pub fn difficulty( gas!(interpreter, gas::BASE); if interpreter.runtime_flag.spec_id().is_enabled_in(MERGE) { // Unwrap is safe as this fields is checked in validation handler. - push!( - interpreter, - (host.block().prevrandao().unwrap()).into_u256() - ); + push!(interpreter, host.prevrandao().unwrap()); } else { - push!(interpreter, host.block().difficulty()); + push!(interpreter, host.difficulty()); } } @@ -64,7 +59,7 @@ pub fn gaslimit( host: &mut H, ) { gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(host.block().gas_limit())); + push!(interpreter, U256::from(host.gas_limit())); } /// EIP-3198: BASEFEE opcode @@ -74,7 +69,7 @@ pub fn basefee( ) { check!(interpreter, LONDON); gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(host.block().basefee())); + push!(interpreter, U256::from(host.basefee())); } /// EIP-7516: BLOBBASEFEE opcode @@ -84,8 +79,5 @@ pub fn blob_basefee( ) { check!(interpreter, CANCUN); gas!(interpreter, gas::BASE); - push!( - interpreter, - U256::from(host.block().blob_gasprice().unwrap_or_default()) - ); + push!(interpreter, U256::from(host.blob_gasprice())); } diff --git a/crates/interpreter/src/instructions/contract.rs b/crates/interpreter/src/instructions/contract.rs index 7e5467626b..ea4c762759 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -15,7 +15,7 @@ use crate::{ InterpreterAction, InterpreterResult, }; use bytecode::eof::{Eof, EofHeader}; -use context_interface::{Cfg, CreateScheme}; +use context_interface::CreateScheme; use core::cmp::max; use primitives::{keccak256, Address, Bytes, B256, U256}; use specification::hardfork::SpecId; @@ -186,6 +186,7 @@ pub fn extcall_gas_calc( .set_instruction_result(InstructionResult::FatalExternalError); return None; }; + // account_load.is_empty will be accounted if there is transfer value // Berlin can be hardcoded as extcall came after berlin. let call_cost = gas::call_cost( @@ -388,8 +389,7 @@ pub fn create( .is_enabled_in(SpecId::SHANGHAI) { // Limit is set as double of max contract bytecode size - let max_initcode_size = host.cfg().max_code_size().saturating_mul(2); - if len > max_initcode_size { + if len > host.max_initcode_size() { interpreter .control .set_instruction_result(InstructionResult::CreateInitCodeSizeLimit); @@ -467,6 +467,7 @@ pub fn call( .set_instruction_result(InstructionResult::FatalExternalError); return; }; + let Some(mut gas_limit) = calc_call_gas(interpreter, account_load, has_transfer, local_gas_limit) else { @@ -518,6 +519,7 @@ pub fn call_code( .set_instruction_result(InstructionResult::FatalExternalError); return; }; + // Set `is_empty` to false as we are not creating this account. load.is_empty = false; let Some(mut gas_limit) = calc_call_gas(interpreter, load, !value.is_zero(), local_gas_limit) @@ -570,6 +572,7 @@ pub fn delegate_call( .set_instruction_result(InstructionResult::FatalExternalError); return; }; + // Set is_empty to false as we are not creating this account. load.is_empty = false; let Some(gas_limit) = calc_call_gas(interpreter, load, false, local_gas_limit) else { diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index a0f1753cd6..c6a58da293 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -6,7 +6,7 @@ use crate::{ Host, InstructionResult, }; use core::cmp::min; -use primitives::{Bytes, Log, LogData, B256, U256}; +use primitives::{Bytes, Log, LogData, B256, BLOCK_HASH_HISTORY, U256}; use specification::hardfork::SpecId::*; pub fn balance( @@ -45,6 +45,7 @@ pub fn selfbalance( ) { check!(interpreter, ISTANBUL); gas!(interpreter, gas::LOW); + let Some(balance) = host.balance(interpreter.input.target_address()) else { interpreter .control @@ -60,7 +61,7 @@ pub fn extcodesize( ) { popn_top!([], top, interpreter); let address = top.into_address(); - let Some(code) = host.code(address) else { + let Some(code) = host.load_account_code(address) else { interpreter .control .set_instruction_result(InstructionResult::FatalExternalError); @@ -86,7 +87,7 @@ pub fn extcodehash( check!(interpreter, CONSTANTINOPLE); popn_top!([], top, interpreter); let address = top.into_address(); - let Some(code_hash) = host.code_hash(address) else { + let Some(code_hash) = host.load_account_code_hash(address) else { interpreter .control .set_instruction_result(InstructionResult::FatalExternalError); @@ -109,7 +110,7 @@ pub fn extcodecopy( ) { popn!([address, memory_offset, code_offset, len_u256], interpreter); let address = address.into_address(); - let Some(code) = host.code(address) else { + let Some(code) = host.load_account_code(address) else { interpreter .control .set_instruction_result(InstructionResult::FatalExternalError); @@ -141,14 +142,30 @@ pub fn blockhash( gas!(interpreter, gas::BLOCKHASH); popn_top!([], number, interpreter); - let number_u64 = as_u64_saturated!(number); - let Some(hash) = host.block_hash(number_u64) else { - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); + let requested_number = as_u64_saturated!(number); + + let block_number = host.block_number(); + + let Some(diff) = block_number.checked_sub(requested_number) else { + *number = U256::ZERO; return; }; - *number = U256::from_be_bytes(hash.0); + + // blockhash should push zero if number is same as current block number. + if diff == 0 { + *number = U256::ZERO; + return; + } + + if diff <= BLOCK_HASH_HISTORY { + let Some(hash) = host.block_hash(requested_number) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; + }; + *number = U256::from_be_bytes(hash.0); + } } pub fn sload( @@ -156,12 +173,14 @@ pub fn sload( host: &mut H, ) { popn_top!([], index, interpreter); + let Some(value) = host.sload(interpreter.input.target_address(), *index) else { interpreter .control .set_instruction_result(InstructionResult::FatalExternalError); return; }; + gas!( interpreter, gas::sload_cost(interpreter.runtime_flag.spec_id(), value.is_cold) @@ -176,6 +195,7 @@ pub fn sstore( require_non_staticcall!(interpreter); popn!([index, value], interpreter); + let Some(state_load) = host.sstore(interpreter.input.target_address(), index, value) else { interpreter .control @@ -284,6 +304,7 @@ pub fn selfdestruct( require_non_staticcall!(interpreter); popn!([target], interpreter); let target = target.into_address(); + let Some(res) = host.selfdestruct(interpreter.input.target_address(), target) else { interpreter .control diff --git a/crates/interpreter/src/instructions/tx_info.rs b/crates/interpreter/src/instructions/tx_info.rs index c90d9c4c0f..f295daece1 100644 --- a/crates/interpreter/src/instructions/tx_info.rs +++ b/crates/interpreter/src/instructions/tx_info.rs @@ -4,7 +4,6 @@ use crate::{ interpreter_types::{InterpreterTypes, LoopControl, RuntimeFlag, StackTr}, Host, }; -use context_interface::{Block, Transaction, TransactionType}; use primitives::U256; pub fn gasprice( @@ -12,11 +11,7 @@ pub fn gasprice( host: &mut H, ) { gas!(interpreter, gas::BASE); - let basefee = host.block().basefee(); - push!( - interpreter, - U256::from(host.tx().effective_gas_price(basefee as u128)) - ); + push!(interpreter, U256::from(host.effective_gas_price())); } pub fn origin( @@ -24,7 +19,7 @@ pub fn origin( host: &mut H, ) { gas!(interpreter, gas::BASE); - push!(interpreter, host.tx().caller().into_word().into()); + push!(interpreter, host.caller().into_word().into()); } // EIP-4844: Shard Blob Transactions @@ -36,14 +31,5 @@ pub fn blob_hash( gas!(interpreter, gas::VERYLOW); popn_top!([], index, interpreter); let i = as_usize_saturated!(index); - let tx = &host.tx(); - *index = if tx.tx_type() == TransactionType::Eip4844 { - tx.blob_versioned_hashes() - .get(i) - .cloned() - .map(|b| U256::from_be_bytes(*b)) - .unwrap_or(U256::ZERO) - } else { - U256::ZERO - }; + *index = host.blob_hash(i).unwrap_or_default(); } diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 3156baad8a..62a95c284e 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -87,7 +87,7 @@ impl InterpreterTypes for EthInterpreter { type Extend = EXT; } -impl CustomInstruction for Instruction { +impl CustomInstruction for Instruction { type Wire = IW; type Host = H; @@ -107,7 +107,7 @@ impl Interpreter { /// /// Internally it will increment instruction pointer by one. #[inline] - pub(crate) fn step( + pub(crate) fn step( &mut self, instruction_table: &[Instruction; 256], host: &mut H, @@ -148,7 +148,7 @@ impl Interpreter { } /// Executes the interpreter until it returns or stops. - pub fn run_plain( + pub fn run_plain( &mut self, instruction_table: &InstructionTable, host: &mut H, diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index e2cd2c3704..541ffdfe90 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -18,6 +18,7 @@ use serde_json as _; use walkdir as _; pub mod gas; +pub mod host; mod instruction_result; pub mod instructions; pub mod interpreter; @@ -28,9 +29,10 @@ pub mod table; // Reexport primary types. pub use context_interface::{ context::{SStoreResult, SelfDestructResult, StateLoad}, - ContextTr as Host, CreateScheme, + CreateScheme, }; pub use gas::{Gas, InitialAndFloorGas}; +pub use host::Host; pub use instruction_result::*; pub use interpreter::{ num_words, InputsImpl, Interpreter, InterpreterResult, MemoryGetter, SharedMemory, Stack, @@ -41,5 +43,5 @@ pub use interpreter_action::{ EOFCreateKind, FrameInput, InterpreterAction, }; pub use interpreter_types::InterpreterTypes; -pub use specification::constants::{MAX_CODE_SIZE, MAX_INITCODE_SIZE}; +pub use specification::{constants::MAX_INITCODE_SIZE, eip170::MAX_CODE_SIZE}; pub use table::Instruction; diff --git a/crates/interpreter/src/table.rs b/crates/interpreter/src/table.rs index 2f578669c6..2723f72429 100644 --- a/crates/interpreter/src/table.rs +++ b/crates/interpreter/src/table.rs @@ -19,7 +19,7 @@ pub type CustomInstructionTable = [IT; 256]; pub trait CustomInstruction { type Wire: InterpreterTypes; - type Host; + type Host: ?Sized; fn exec(&self, interpreter: &mut Interpreter, host: &mut Self::Host); diff --git a/crates/specification/src/constants.rs b/crates/specification/src/constants.rs index ae5b9c9e4b..14a0765648 100644 --- a/crates/specification/src/constants.rs +++ b/crates/specification/src/constants.rs @@ -1,15 +1,12 @@ +use super::eip170; + /// EVM interpreter stack limit pub const STACK_LIMIT: usize = 1024; -/// EIP-170: Contract code size limit -/// -/// By default the limit is `0x6000` (~25kb) -pub const MAX_CODE_SIZE: usize = 0x6000; - /// EIP-3860: Limit and meter initcode /// /// Limit of maximum initcode size is `2 * MAX_CODE_SIZE`. -pub const MAX_INITCODE_SIZE: usize = 2 * MAX_CODE_SIZE; +pub const MAX_INITCODE_SIZE: usize = 2 * eip170::MAX_CODE_SIZE; /// EVM call stack limit pub const CALL_STACK_LIMIT: u64 = 1024;