From 6782c3472ef77acd3d3ed567a5fac8392bbe47a0 Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 5 Mar 2025 13:47:25 +0100 Subject: [PATCH 1/4] move all default context fn to instruction --- crates/context/interface/src/context.rs | 128 +------------- .../context/interface/src/journaled_state.rs | 44 ++++- crates/context/src/journaled_state.rs | 61 +------ crates/interpreter/src/host.rs | 0 .../interpreter/src/instructions/contract.rs | 53 +++--- crates/interpreter/src/instructions/host.rs | 159 +++++++++++++----- crates/interpreter/src/lib.rs | 1 + 7 files changed, 193 insertions(+), 253 deletions(-) create mode 100644 crates/interpreter/src/host.rs 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/journaled_state.rs b/crates/context/src/journaled_state.rs index 59aca86c61..4a54e14f2c 100644 --- a/crates/context/src/journaled_state.rs +++ b/crates/context/src/journaled_state.rs @@ -1,9 +1,9 @@ -use bytecode::{Bytecode, EOF_MAGIC_BYTES, EOF_MAGIC_HASH}; +use bytecode::Bytecode; use context_interface::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 +144,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 +403,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..e69de29bb2 diff --git a/crates/interpreter/src/instructions/contract.rs b/crates/interpreter/src/instructions/contract.rs index 7e5467626b..698cae2558 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -15,7 +15,9 @@ use crate::{ InterpreterAction, InterpreterResult, }; use bytecode::eof::{Eof, EofHeader}; -use context_interface::{Cfg, CreateScheme}; +use context_interface::{ + context::StateLoad, journaled_state::AccountLoad, Cfg, CreateScheme, Journal, +}; use core::cmp::max; use primitives::{keccak256, Address, Bytes, B256, U256}; use specification::hardfork::SpecId; @@ -180,12 +182,8 @@ pub fn extcall_gas_calc( target: Address, transfers_value: bool, ) -> Option { - let Some(account_load) = host.load_account_delegated(target) else { - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); - return None; - }; + let account_load = load_delegate_account(interpreter, host, target)?; + // 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( @@ -461,12 +459,10 @@ pub fn call( return; }; - let Some(account_load) = host.load_account_delegated(to) else { - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); + let Some(account_load) = load_delegate_account(interpreter, host, to) else { return; }; + let Some(mut gas_limit) = calc_call_gas(interpreter, account_load, has_transfer, local_gas_limit) else { @@ -512,12 +508,10 @@ pub fn call_code( return; }; - let Some(mut load) = host.load_account_delegated(to) else { - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); + let Some(mut load) = load_delegate_account(interpreter, host, to) else { 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) @@ -564,12 +558,10 @@ pub fn delegate_call( return; }; - let Some(mut load) = host.load_account_delegated(to) else { - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); + let Some(mut load) = load_delegate_account(interpreter, host, to) else { 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 { @@ -596,6 +588,24 @@ pub fn delegate_call( ); } +/// Loads the delegate account and sets the instruction result to fatal error if it fails. +fn load_delegate_account( + interpreter: &mut Interpreter, + host: &mut H, + address: Address, +) -> Option> { + match host.journal().load_account_delegated(address) { + Ok(account_load) => Some(account_load), + Err(e) => { + *host.error() = Err(e); + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return None; + } + } +} + pub fn static_call( interpreter: &mut Interpreter, host: &mut H, @@ -610,10 +620,7 @@ pub fn static_call( return; }; - let Some(mut load) = host.load_account_delegated(to) else { - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); + let Some(mut load) = load_delegate_account(interpreter, host, to) else { return; }; // Set `is_empty` to false as we are not creating this account. diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index a0f1753cd6..593fbb2015 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -5,8 +5,9 @@ use crate::{ interpreter_types::{InputsTr, InterpreterTypes, LoopControl, MemoryTr, RuntimeFlag, StackTr}, Host, InstructionResult, }; +use context_interface::{Block, Database, Journal}; 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( @@ -15,7 +16,14 @@ pub fn balance( ) { popn_top!([], top, interpreter); let address = top.into_address(); - let Some(balance) = host.balance(address) else { + let Ok(balance) = host + .journal() + .load_account(address) + .map(|acc| acc.map(|a| a.info.balance)) + .map_err(|e| { + *host.error() = Err(e); + }) + else { interpreter .control .set_instruction_result(InstructionResult::FatalExternalError); @@ -45,7 +53,15 @@ pub fn selfbalance( ) { check!(interpreter, ISTANBUL); gas!(interpreter, gas::LOW); - let Some(balance) = host.balance(interpreter.input.target_address()) else { + + let Ok(balance) = host + .journal() + .load_account(interpreter.input.target_address()) + .map(|acc| acc.map(|a| a.info.balance)) + .map_err(|e| { + *host.error() = Err(e); + }) + else { interpreter .control .set_instruction_result(InstructionResult::FatalExternalError); @@ -60,11 +76,15 @@ pub fn extcodesize( ) { popn_top!([], top, interpreter); let address = top.into_address(); - let Some(code) = host.code(address) else { - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); - return; + let code = match host.journal().code(address) { + Ok(code) => code, + Err(e) => { + *host.error() = Err(e); + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; + } }; let spec_id = interpreter.runtime_flag.spec_id(); if spec_id.is_enabled_in(BERLIN) { @@ -86,11 +106,15 @@ pub fn extcodehash( check!(interpreter, CONSTANTINOPLE); popn_top!([], top, interpreter); let address = top.into_address(); - let Some(code_hash) = host.code_hash(address) else { - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); - return; + let code_hash = match host.journal().code_hash(address) { + Ok(code_hash) => code_hash, + Err(e) => { + *host.error() = Err(e); + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; + } }; let spec_id = interpreter.runtime_flag.spec_id(); if spec_id.is_enabled_in(BERLIN) { @@ -109,11 +133,15 @@ pub fn extcodecopy( ) { popn!([address, memory_offset, code_offset, len_u256], interpreter); let address = address.into_address(); - let Some(code) = host.code(address) else { - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); - return; + let code = match host.journal().code(address) { + Ok(code) => code, + Err(e) => { + *host.error() = Err(e); + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; + } }; let len = as_usize_or_fail!(interpreter, len_u256); @@ -141,14 +169,33 @@ 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 { + match host.journal().db().block_hash(requested_number) { + Ok(hash) => *number = U256::from_be_bytes(hash.0), + Err(e) => { + *host.error() = Err(e); + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; + } + } + } } pub fn sload( @@ -156,12 +203,21 @@ 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; + + let value = match host + .journal() + .sload(interpreter.input.target_address(), *index) + { + Ok(value) => value, + Err(e) => { + *host.error() = Err(e); + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; + } }; + gas!( interpreter, gas::sload_cost(interpreter.runtime_flag.spec_id(), value.is_cold) @@ -176,11 +232,19 @@ 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 - .set_instruction_result(InstructionResult::FatalExternalError); - return; + + let state_load = match host + .journal() + .sstore(interpreter.input.target_address(), index, value) + { + Ok(state_load) => state_load, + Err(e) => { + *host.error() = Err(e); + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; + } }; // EIP-1706 Disable SSTORE with gasleft lower than call stipend @@ -222,7 +286,8 @@ pub fn tstore( popn!([index, value], interpreter); - host.tstore(interpreter.input.target_address(), index, value); + host.journal() + .tstore(interpreter.input.target_address(), index, value); } /// EIP-1153: Transient storage opcodes @@ -236,7 +301,9 @@ pub fn tload( popn_top!([], index, interpreter); - *index = host.tload(interpreter.input.target_address(), *index); + *index = host + .journal() + .tload(interpreter.input.target_address(), *index); } pub fn log( @@ -274,7 +341,7 @@ pub fn log( .expect("LogData should have <=4 topics"), }; - host.log(log); + host.journal().log(log); } pub fn selfdestruct( @@ -284,11 +351,19 @@ 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 - .set_instruction_result(InstructionResult::FatalExternalError); - return; + + let res = match host + .journal() + .selfdestruct(interpreter.input.target_address(), target) + { + Ok(res) => res, + Err(e) => { + *host.error() = Err(e); + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; + } }; // EIP-3529: Reduction in refunds diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index e2cd2c3704..a909bf9475 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -24,6 +24,7 @@ pub mod interpreter; pub mod interpreter_action; pub mod interpreter_types; pub mod table; +pub mod host; // Reexport primary types. pub use context_interface::{ From bed91cdbd77561298553a18d5b27d8b8a77b6dd7 Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 5 Mar 2025 14:22:22 +0100 Subject: [PATCH 2/4] feat: remove default from context, add standalone Host --- crates/interpreter/src/host.rs | 133 ++++++++++++++++++++++++++ crates/interpreter/src/interpreter.rs | 6 +- crates/interpreter/src/table.rs | 2 +- 3 files changed, 137 insertions(+), 4 deletions(-) diff --git a/crates/interpreter/src/host.rs b/crates/interpreter/src/host.rs index e69de29bb2..51990c22da 100644 --- a/crates/interpreter/src/host.rs +++ b/crates/interpreter/src/host.rs @@ -0,0 +1,133 @@ +use context_interface::{ + context::{ContextTr, SStoreResult, SelfDestructResult, StateLoad}, + journaled_state::AccountLoad, + Block, Cfg, Database, Journal, Transaction, +}; +use primitives::{Address, Bytes, Log, B256, U256}; + +/// Host trait with all methods that can be called by the Interpreter. +/// +/// This trait is implemented for all types that have `ContextTr` trait. +pub trait Host { + /* Block */ + fn basefee(&self) -> U256; + fn blob_basefee(&self) -> U256; + fn gas_limit(&self) -> U256; + fn difficulty(&self) -> U256; + fn block_number(&self) -> U256; + fn timestamp(&self) -> U256; + fn beneficiary(&self) -> Address; + fn chain_id(&self) -> U256; + + /* Transaction */ + fn gas_price(&self) -> U256; + fn caller(&self) -> Address; + fn blob_hash(&self, number: usize) -> Option; + + /* State */ + fn block_hash(&mut self, number: u64) -> Option; + fn selfdestruct( + &mut self, + address: Address, + target: Address, + ) -> Option>; + + fn log(&mut self, log: Log); + fn sstore( + &mut self, + address: Address, + key: U256, + value: U256, + ) -> Option>; + fn sload(&mut self, address: Address, key: U256) -> Option>; + fn tstore(&mut self, address: Address, key: U256, value: U256); + fn tload(&mut self, address: Address, key: U256) -> U256; + fn load_account_delegated(&mut self, address: Address) -> Option; + fn load_account_code(&mut self, address: Address) -> Option; + fn load_account_code_hash(&mut self, address: Address) -> Option; +} + +impl Host for CTX { + fn basefee(&self) -> U256 { + U256::from(self.block().basefee()) + } + + fn blob_basefee(&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 block_number(&self) -> U256 { + U256::from(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()) + } + + fn gas_price(&self) -> U256 { + U256::from(self.tx().gas_price()) + } + + fn caller(&self) -> Address { + self.tx().caller() + } + + fn blob_hash(&self, number: usize) -> Option { + self.tx() + .blob_versioned_hashes() + .get(number) + .map(|b| U256::from_be_bytes(b.0)) + } + + fn block_hash(&mut self, number: u64) -> Option { + self.journal() + .db() + .block_hash(number) + .map_err(|e| { + *self.error() = Err(e); + () + }) + .ok() + } + + fn selfdestruct( + &mut self, + address: Address, + target: Address, + ) -> Option> { + self.journal().selfdestruct(address, target) + } + + fn selfdestruct_result(&mut self) -> Option> { + self.journal().selfdestruct_result() + } + + fn log(&mut self, log: Log) { + self.journal().log(log) + } + + fn sstore( + &mut self, + address: Address, + key: U256, + value: U256, + ) -> Option> { + self.journal().sstore(address, key, value) + } +} 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/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); From 8122924988f3f80d44eb2f1516ca80768d67bddc Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 5 Mar 2025 19:04:19 +0100 Subject: [PATCH 3/4] add rest of host functions --- crates/interpreter/src/host.rs | 179 ++++++++++++++---- .../src/instructions/block_info.rs | 26 +-- .../interpreter/src/instructions/contract.rs | 52 +++-- crates/interpreter/src/instructions/host.rs | 140 +++++--------- .../interpreter/src/instructions/tx_info.rs | 20 +- crates/interpreter/src/lib.rs | 5 +- 6 files changed, 228 insertions(+), 194 deletions(-) diff --git a/crates/interpreter/src/host.rs b/crates/interpreter/src/host.rs index 51990c22da..38bf2412d8 100644 --- a/crates/interpreter/src/host.rs +++ b/crates/interpreter/src/host.rs @@ -1,29 +1,35 @@ use context_interface::{ context::{ContextTr, SStoreResult, SelfDestructResult, StateLoad}, journaled_state::AccountLoad, - Block, Cfg, Database, Journal, Transaction, + Block, Cfg, Database, Journal, Transaction, TransactionType, }; -use primitives::{Address, Bytes, Log, B256, U256}; +use primitives::{Address, Bytes, Log, B256, BLOCK_HASH_HISTORY, U256}; -/// Host trait with all methods that can be called by the Interpreter. +use crate::instructions::utility::IntoU256; + +/// Host trait with all methods are needed by the Interpreter. /// /// This trait is implemented for all types that have `ContextTr` trait. pub trait Host { /* Block */ fn basefee(&self) -> U256; - fn blob_basefee(&self) -> U256; + fn blob_gasprice(&self) -> U256; fn gas_limit(&self) -> U256; fn difficulty(&self) -> U256; - fn block_number(&self) -> U256; + fn prevrandao(&self) -> Option; + fn block_number(&self) -> u64; fn timestamp(&self) -> U256; fn beneficiary(&self) -> Address; fn chain_id(&self) -> U256; /* Transaction */ - fn gas_price(&self) -> U256; + fn effective_gas_price(&self) -> U256; fn caller(&self) -> Address; fn blob_hash(&self, number: usize) -> Option; + /* Config */ + fn max_initcode_size(&self) -> usize; + /* State */ fn block_hash(&mut self, number: u64) -> Option; fn selfdestruct( @@ -39,20 +45,23 @@ pub trait Host { key: U256, value: U256, ) -> Option>; + fn balance(&mut self, address: Address) -> Option>; fn sload(&mut self, address: Address, key: U256) -> Option>; fn tstore(&mut self, address: Address, key: U256, value: U256); fn tload(&mut self, address: Address, key: U256) -> U256; - fn load_account_delegated(&mut self, address: Address) -> Option; - fn load_account_code(&mut self, address: Address) -> Option; - fn load_account_code_hash(&mut self, address: Address) -> Option; + fn load_account_delegated(&mut self, address: Address) -> Option>; + fn load_account_code(&mut self, address: Address) -> Option>; + 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_basefee(&self) -> U256 { + fn blob_gasprice(&self) -> U256 { U256::from(self.block().blob_gasprice().unwrap_or(0)) } @@ -64,8 +73,12 @@ impl Host for CTX { self.block().difficulty() } - fn block_number(&self) -> U256 { - U256::from(self.block().number()) + fn prevrandao(&self) -> Option { + self.block().prevrandao().map(|r| r.into_u256()) + } + + fn block_number(&self) -> u64 { + self.block().number() } fn timestamp(&self) -> U256 { @@ -80,54 +93,154 @@ impl Host for CTX { U256::from(self.cfg().chain_id()) } - fn gas_price(&self) -> U256 { - U256::from(self.tx().gas_price()) + 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() } + /* Transaction */ + fn blob_hash(&self, number: usize) -> Option { - self.tx() - .blob_versioned_hashes() + let tx = &self.tx(); + if tx.tx_type() != TransactionType::Eip4844 { + return None; + } + tx.blob_versioned_hashes() .get(number) - .map(|b| U256::from_be_bytes(b.0)) + .map(|t| U256::from_be_bytes(t.0)) + } + + /* Config */ + + fn max_initcode_size(&self) -> usize { + self.cfg().max_code_size().saturating_mul(2) + } + + /* State */ + + 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 block_hash(&mut self, number: u64) -> Option { + fn load_account_delegated(&mut self, address: Address) -> Option> { self.journal() - .db() - .block_hash(number) + .load_account_delegated(address) .map_err(|e| { *self.error() = Err(e); - () }) .ok() } - fn selfdestruct( - &mut self, - address: Address, - target: Address, - ) -> Option> { - self.journal().selfdestruct(address, target) + /// 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() } - fn selfdestruct_result(&mut self) -> Option> { - self.journal().selfdestruct_result() + /// 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() } - fn log(&mut self, log: Log) { - self.journal().log(log) + /// 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, - key: U256, + index: U256, value: U256, ) -> Option> { - self.journal().sstore(address, key, value) + 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 698cae2558..ea4c762759 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -15,9 +15,7 @@ use crate::{ InterpreterAction, InterpreterResult, }; use bytecode::eof::{Eof, EofHeader}; -use context_interface::{ - context::StateLoad, journaled_state::AccountLoad, Cfg, CreateScheme, Journal, -}; +use context_interface::CreateScheme; use core::cmp::max; use primitives::{keccak256, Address, Bytes, B256, U256}; use specification::hardfork::SpecId; @@ -182,7 +180,12 @@ pub fn extcall_gas_calc( target: Address, transfers_value: bool, ) -> Option { - let account_load = load_delegate_account(interpreter, host, target)?; + let Some(account_load) = host.load_account_delegated(target) else { + interpreter + .control + .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. @@ -386,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); @@ -459,7 +461,10 @@ pub fn call( return; }; - let Some(account_load) = load_delegate_account(interpreter, host, to) else { + let Some(account_load) = host.load_account_delegated(to) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; @@ -508,7 +513,10 @@ pub fn call_code( return; }; - let Some(mut load) = load_delegate_account(interpreter, host, to) else { + let Some(mut load) = host.load_account_delegated(to) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; @@ -558,7 +566,10 @@ pub fn delegate_call( return; }; - let Some(mut load) = load_delegate_account(interpreter, host, to) else { + let Some(mut load) = host.load_account_delegated(to) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; @@ -588,24 +599,6 @@ pub fn delegate_call( ); } -/// Loads the delegate account and sets the instruction result to fatal error if it fails. -fn load_delegate_account( - interpreter: &mut Interpreter, - host: &mut H, - address: Address, -) -> Option> { - match host.journal().load_account_delegated(address) { - Ok(account_load) => Some(account_load), - Err(e) => { - *host.error() = Err(e); - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); - return None; - } - } -} - pub fn static_call( interpreter: &mut Interpreter, host: &mut H, @@ -620,7 +613,10 @@ pub fn static_call( return; }; - let Some(mut load) = load_delegate_account(interpreter, host, to) else { + let Some(mut load) = host.load_account_delegated(to) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; // Set `is_empty` to false as we are not creating this account. diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 593fbb2015..c6a58da293 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -5,7 +5,6 @@ use crate::{ interpreter_types::{InputsTr, InterpreterTypes, LoopControl, MemoryTr, RuntimeFlag, StackTr}, Host, InstructionResult, }; -use context_interface::{Block, Database, Journal}; use core::cmp::min; use primitives::{Bytes, Log, LogData, B256, BLOCK_HASH_HISTORY, U256}; use specification::hardfork::SpecId::*; @@ -16,14 +15,7 @@ pub fn balance( ) { popn_top!([], top, interpreter); let address = top.into_address(); - let Ok(balance) = host - .journal() - .load_account(address) - .map(|acc| acc.map(|a| a.info.balance)) - .map_err(|e| { - *host.error() = Err(e); - }) - else { + let Some(balance) = host.balance(address) else { interpreter .control .set_instruction_result(InstructionResult::FatalExternalError); @@ -54,14 +46,7 @@ pub fn selfbalance( check!(interpreter, ISTANBUL); gas!(interpreter, gas::LOW); - let Ok(balance) = host - .journal() - .load_account(interpreter.input.target_address()) - .map(|acc| acc.map(|a| a.info.balance)) - .map_err(|e| { - *host.error() = Err(e); - }) - else { + let Some(balance) = host.balance(interpreter.input.target_address()) else { interpreter .control .set_instruction_result(InstructionResult::FatalExternalError); @@ -76,15 +61,11 @@ pub fn extcodesize( ) { popn_top!([], top, interpreter); let address = top.into_address(); - let code = match host.journal().code(address) { - Ok(code) => code, - Err(e) => { - *host.error() = Err(e); - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); - return; - } + let Some(code) = host.load_account_code(address) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; }; let spec_id = interpreter.runtime_flag.spec_id(); if spec_id.is_enabled_in(BERLIN) { @@ -106,15 +87,11 @@ pub fn extcodehash( check!(interpreter, CONSTANTINOPLE); popn_top!([], top, interpreter); let address = top.into_address(); - let code_hash = match host.journal().code_hash(address) { - Ok(code_hash) => code_hash, - Err(e) => { - *host.error() = Err(e); - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); - return; - } + let Some(code_hash) = host.load_account_code_hash(address) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; }; let spec_id = interpreter.runtime_flag.spec_id(); if spec_id.is_enabled_in(BERLIN) { @@ -133,15 +110,11 @@ pub fn extcodecopy( ) { popn!([address, memory_offset, code_offset, len_u256], interpreter); let address = address.into_address(); - let code = match host.journal().code(address) { - Ok(code) => code, - Err(e) => { - *host.error() = Err(e); - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); - return; - } + let Some(code) = host.load_account_code(address) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; }; let len = as_usize_or_fail!(interpreter, len_u256); @@ -171,7 +144,7 @@ pub fn blockhash( let requested_number = as_u64_saturated!(number); - let block_number = host.block().number(); + let block_number = host.block_number(); let Some(diff) = block_number.checked_sub(requested_number) else { *number = U256::ZERO; @@ -185,16 +158,13 @@ pub fn blockhash( } if diff <= BLOCK_HASH_HISTORY { - match host.journal().db().block_hash(requested_number) { - Ok(hash) => *number = U256::from_be_bytes(hash.0), - Err(e) => { - *host.error() = Err(e); - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); - return; - } - } + let Some(hash) = host.block_hash(requested_number) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; + }; + *number = U256::from_be_bytes(hash.0); } } @@ -204,18 +174,11 @@ pub fn sload( ) { popn_top!([], index, interpreter); - let value = match host - .journal() - .sload(interpreter.input.target_address(), *index) - { - Ok(value) => value, - Err(e) => { - *host.error() = Err(e); - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); - return; - } + let Some(value) = host.sload(interpreter.input.target_address(), *index) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; }; gas!( @@ -233,18 +196,11 @@ pub fn sstore( popn!([index, value], interpreter); - let state_load = match host - .journal() - .sstore(interpreter.input.target_address(), index, value) - { - Ok(state_load) => state_load, - Err(e) => { - *host.error() = Err(e); - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); - return; - } + let Some(state_load) = host.sstore(interpreter.input.target_address(), index, value) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; }; // EIP-1706 Disable SSTORE with gasleft lower than call stipend @@ -286,8 +242,7 @@ pub fn tstore( popn!([index, value], interpreter); - host.journal() - .tstore(interpreter.input.target_address(), index, value); + host.tstore(interpreter.input.target_address(), index, value); } /// EIP-1153: Transient storage opcodes @@ -301,9 +256,7 @@ pub fn tload( popn_top!([], index, interpreter); - *index = host - .journal() - .tload(interpreter.input.target_address(), *index); + *index = host.tload(interpreter.input.target_address(), *index); } pub fn log( @@ -341,7 +294,7 @@ pub fn log( .expect("LogData should have <=4 topics"), }; - host.journal().log(log); + host.log(log); } pub fn selfdestruct( @@ -352,18 +305,11 @@ pub fn selfdestruct( popn!([target], interpreter); let target = target.into_address(); - let res = match host - .journal() - .selfdestruct(interpreter.input.target_address(), target) - { - Ok(res) => res, - Err(e) => { - *host.error() = Err(e); - interpreter - .control - .set_instruction_result(InstructionResult::FatalExternalError); - return; - } + let Some(res) = host.selfdestruct(interpreter.input.target_address(), target) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); + return; }; // EIP-3529: Reduction in refunds 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/lib.rs b/crates/interpreter/src/lib.rs index a909bf9475..1846078d46 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -18,20 +18,21 @@ use serde_json as _; use walkdir as _; pub mod gas; +pub mod host; mod instruction_result; pub mod instructions; pub mod interpreter; pub mod interpreter_action; pub mod interpreter_types; pub mod table; -pub mod host; // 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, From d79b943cfee74486bdac59e60875895b16039498 Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 6 Mar 2025 12:39:01 +0100 Subject: [PATCH 4/4] cleanup --- Cargo.lock | 1 - crates/context/Cargo.toml | 3 +- crates/context/src/cfg.rs | 3 +- crates/context/src/journaled_state.rs | 6 +- crates/interpreter/src/host.rs | 80 +++++++++++++++++---------- crates/interpreter/src/lib.rs | 2 +- crates/specification/src/constants.rs | 9 +-- 7 files changed, 60 insertions(+), 44 deletions(-) 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/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 4a54e14f2c..f98c3cec82 100644 --- a/crates/context/src/journaled_state.rs +++ b/crates/context/src/journaled_state.rs @@ -1,7 +1,9 @@ use bytecode::Bytecode; -use context_interface::journaled_state::{AccountLoad, Journal, JournalCheckpoint, TransferError}; +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, HashMap, HashSet, Log, B256, KECCAK_EMPTY, PRECOMPILE3, U256, }; diff --git a/crates/interpreter/src/host.rs b/crates/interpreter/src/host.rs index 38bf2412d8..28953a9df6 100644 --- a/crates/interpreter/src/host.rs +++ b/crates/interpreter/src/host.rs @@ -3,54 +3,88 @@ use context_interface::{ journaled_state::AccountLoad, Block, Cfg, Database, Journal, Transaction, TransactionType, }; -use primitives::{Address, Bytes, Log, B256, BLOCK_HASH_HISTORY, U256}; +use primitives::{Address, Bytes, Log, B256, U256}; use crate::instructions::utility::IntoU256; -/// Host trait with all methods are needed by the Interpreter. +/// 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; - /* State */ + /* 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>; - fn balance(&mut self, address: Address) -> 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>; } @@ -93,6 +127,8 @@ impl Host for CTX { 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)) @@ -102,8 +138,6 @@ impl Host for CTX { self.tx().caller() } - /* Transaction */ - fn blob_hash(&self, number: usize) -> Option { let tx = &self.tx(); if tx.tx_type() != TransactionType::Eip4844 { @@ -120,34 +154,20 @@ impl Host for CTX { self.cfg().max_code_size().saturating_mul(2) } - /* State */ + /* Database */ 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) + 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) diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index 1846078d46..541ffdfe90 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -43,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/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;