diff --git a/core/src/error.rs b/core/src/error.rs index 510d6df4a..0cd0daf72 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -154,6 +154,10 @@ pub enum ExitError { /// Other normal errors. #[cfg_attr(feature = "with-codec", codec(index = 13))] Other(Cow<'static, str>), + + /// Init code exceeds limit (runtime). + #[cfg_attr(feature = "with-codec", codec(index = 7))] + InitCodeLimit, } impl From for ExitReason { diff --git a/core/src/eval/mod.rs b/core/src/eval/mod.rs index 954ca83de..519f5d925 100644 --- a/core/src/eval/mod.rs +++ b/core/src/eval/mod.rs @@ -176,6 +176,10 @@ fn eval_jumpdest(_state: &mut Machine, _opcode: Opcode, _position: usize) -> Con Control::Continue(1) } +fn eval_push0(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { + self::misc::push(state, 0, position) +} + fn eval_push1(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { self::misc::push(state, 1, position) } @@ -494,6 +498,7 @@ pub fn eval(state: &mut Machine, opcode: Opcode, position: usize) -> Control { table[Opcode::MSIZE.as_usize()] = eval_msize as _; table[Opcode::JUMPDEST.as_usize()] = eval_jumpdest as _; + table[Opcode::PUSH0.as_usize()] = eval_push0 as _; table[Opcode::PUSH1.as_usize()] = eval_push1 as _; table[Opcode::PUSH2.as_usize()] = eval_push2 as _; table[Opcode::PUSH3.as_usize()] = eval_push3 as _; diff --git a/core/src/lib.rs b/core/src/lib.rs index 5bda37eea..c32d52878 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -66,6 +66,22 @@ impl Machine { pub fn position(&self) -> &Result { &self.position } + /// Return a reference of the return range. + pub fn return_range(&self) -> &Range { + &self.return_range + } + /// Return a reference of the valids. + pub fn valids(&self) -> &Valids { + &self.valids + } + /// Return a reference of the return range. + pub fn code(&mut self) -> Vec { + self.code.to_vec() + } + /// Return a reference of the valids. + pub fn data(&self) -> Vec { + self.data.to_vec() + } /// Create a new machine with given code and data. pub fn new( @@ -87,6 +103,27 @@ impl Machine { } } + /// Create a new machine with some previous state + pub fn create_from_state( + code: Rc>, + data: Rc>, + position: Result, + return_range: Range, + valids: Valids, + memory: Memory, + stack: Stack, + ) -> Self { + Self { + data, + code, + position, + return_range, + valids, + memory, + stack, + } + } + /// Explicit exit of the machine. Further step will return error. pub fn exit(&mut self, reason: ExitReason) { self.position = Err(reason); diff --git a/core/src/opcode.rs b/core/src/opcode.rs index c35a6ec5e..30ca094c5 100644 --- a/core/src/opcode.rs +++ b/core/src/opcode.rs @@ -1,3 +1,5 @@ +use std::fmt; + /// Opcode enum. One-to-one corresponding to an `u8` value. #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg_attr( @@ -95,6 +97,7 @@ impl Opcode { pub const JUMPDEST: Opcode = Opcode(0x5b); /// `PUSHn` + pub const PUSH0: Opcode = Opcode(0x5f); pub const PUSH1: Opcode = Opcode(0x60); pub const PUSH2: Opcode = Opcode(0x61); pub const PUSH3: Opcode = Opcode(0x62); @@ -269,3 +272,157 @@ impl Opcode { self.0 as usize } } + +impl fmt::Display for Opcode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + //write!(f, "{}", "urgh...") + let op = match self { + &Opcode::STOP => "STOP", + &Opcode::ADD => "ADD", + &Opcode::MUL => "MUL", + &Opcode::SUB => "SUB", + &Opcode::DIV => "DIV", + &Opcode::SDIV => "SDIV", + &Opcode::MOD => "MOD", + &Opcode::SMOD => "SMOD", + &Opcode::ADDMOD => "ADDMOD", + &Opcode::MULMOD => "MULMOD", + &Opcode::EXP => "EXP", + &Opcode::SIGNEXTEND => "SIGNEXTEND", + &Opcode::LT => "LT", + &Opcode::GT => "GT", + &Opcode::SLT => "SLT", + &Opcode::SGT => "SGT", + &Opcode::EQ => "EQ", + &Opcode::ISZERO => "ISZERO", + &Opcode::AND => "AND", + &Opcode::OR => "OR", + &Opcode::XOR => "XOR", + &Opcode::NOT => "NOT", + &Opcode::BYTE => "BYTE", + &Opcode::CALLDATALOAD => "CALLDATALOAD", + &Opcode::CALLDATASIZE => "CALLDATASIZE", + &Opcode::CALLDATACOPY => "CALLDATACOPY", + &Opcode::CODESIZE => "CODESIZE", + &Opcode::CODECOPY => "CODECOPY", + &Opcode::SHL => "SHL", + &Opcode::SHR => "SHR", + &Opcode::SAR => "SAR", + &Opcode::POP => "POP", + &Opcode::MLOAD => "MLOAD", + &Opcode::MSTORE => "MSTORE", + &Opcode::MSTORE8 => "MSTORE8", + &Opcode::JUMP => "JUMP", + &Opcode::JUMPI => "JUMPI", + &Opcode::PC => "PC", + &Opcode::MSIZE => "MSIZE", + &Opcode::JUMPDEST => "JUMPDEST", + &Opcode::PUSH1 => "PUSH1", + &Opcode::PUSH2 => "PUSH2", + &Opcode::PUSH3 => "PUSH3", + &Opcode::PUSH4 => "PUSH4", + &Opcode::PUSH5 => "PUSH5", + &Opcode::PUSH6 => "PUSH6", + &Opcode::PUSH7 => "PUSH7", + &Opcode::PUSH8 => "PUSH8", + &Opcode::PUSH9 => "PUSH9", + &Opcode::PUSH10 => "PUSH10", + &Opcode::PUSH11 => "PUSH11", + &Opcode::PUSH12 => "PUSH12", + &Opcode::PUSH13 => "PUSH13", + &Opcode::PUSH14 => "PUSH14", + &Opcode::PUSH15 => "PUSH15", + &Opcode::PUSH16 => "PUSH16", + &Opcode::PUSH17 => "PUSH17", + &Opcode::PUSH18 => "PUSH18", + &Opcode::PUSH19 => "PUSH19", + &Opcode::PUSH20 => "PUSH20", + &Opcode::PUSH21 => "PUSH21", + &Opcode::PUSH22 => "PUSH22", + &Opcode::PUSH23 => "PUSH23", + &Opcode::PUSH24 => "PUSH24", + &Opcode::PUSH25 => "PUSH25", + &Opcode::PUSH26 => "PUSH26", + &Opcode::PUSH27 => "PUSH27", + &Opcode::PUSH28 => "PUSH28", + &Opcode::PUSH29 => "PUSH29", + &Opcode::PUSH30 => "PUSH30", + &Opcode::PUSH31 => "PUSH31", + &Opcode::PUSH32 => "PUSH32", + &Opcode::DUP1 => "DUP1", + &Opcode::DUP2 => "DUP2", + &Opcode::DUP3 => "DUP3", + &Opcode::DUP4 => "DUP4", + &Opcode::DUP5 => "DUP5", + &Opcode::DUP6 => "DUP6", + &Opcode::DUP7 => "DUP7", + &Opcode::DUP8 => "DUP8", + &Opcode::DUP9 => "DUP9", + &Opcode::DUP10 => "DUP10", + &Opcode::DUP11 => "DUP11", + &Opcode::DUP12 => "DUP12", + &Opcode::DUP13 => "DUP13", + &Opcode::DUP14 => "DUP14", + &Opcode::DUP15 => "DUP15", + &Opcode::DUP16 => "DUP16", + &Opcode::SWAP1 => "SWAP1", + &Opcode::SWAP2 => "SWAP2", + &Opcode::SWAP3 => "SWAP3", + &Opcode::SWAP4 => "SWAP4", + &Opcode::SWAP5 => "SWAP5", + &Opcode::SWAP6 => "SWAP6", + &Opcode::SWAP7 => "SWAP7", + &Opcode::SWAP8 => "SWAP8", + &Opcode::SWAP9 => "SWAP9", + &Opcode::SWAP10 => "SWAP10", + &Opcode::SWAP11 => "SWAP11", + &Opcode::SWAP12 => "SWAP12", + &Opcode::SWAP13 => "SWAP13", + &Opcode::SWAP14 => "SWAP14", + &Opcode::SWAP15 => "SWAP15", + &Opcode::SWAP16 => "SWAP16", + &Opcode::RETURN => "RETURN", + &Opcode::REVERT => "REVERT", + &Opcode::INVALID => "INVALID", + &Opcode::EOFMAGIC => "EOFMAGIC", + &Opcode::SHA3 => "SHA3", + &Opcode::ADDRESS => "ADDRESS", + &Opcode::BALANCE => "BALANCE", + &Opcode::SELFBALANCE => "SELFBALANCE", + &Opcode::BASEFEE => "BASEFEE", + &Opcode::ORIGIN => "ORIGIN", + &Opcode::CALLER => "CALLER", + &Opcode::CALLVALUE => "CALLVALUE", + &Opcode::GASPRICE => "GASPRICE", + &Opcode::EXTCODESIZE => "EXTCODESIZE", + &Opcode::EXTCODECOPY => "EXTCODECOPY", + &Opcode::EXTCODEHASH => "EXTCODEHASH", + &Opcode::RETURNDATASIZE => "RETURNDATASIZE", + &Opcode::RETURNDATACOPY => "RETURNDATACOPY", + &Opcode::BLOCKHASH => "BLOCKHASH", + &Opcode::COINBASE => "COINBASE", + &Opcode::TIMESTAMP => "TIMESTAMP", + &Opcode::NUMBER => "NUMBER", + &Opcode::DIFFICULTY => "DIFFICULTY", + &Opcode::GASLIMIT => "GASLIMIT", + &Opcode::SLOAD => "SLOAD", + &Opcode::SSTORE => "SSTORE", + &Opcode::GAS => "GAS", + &Opcode::LOG0 => "LOG0", + &Opcode::LOG1 => "LOG1", + &Opcode::LOG2 => "LOG2", + &Opcode::LOG3 => "LOG3", + &Opcode::LOG4 => "LOG4", + &Opcode::CREATE => "CREATE", + &Opcode::CREATE2 => "CREATE2", + &Opcode::CALL => "CALL", + &Opcode::CALLCODE => "CALLCODE", + &Opcode::DELEGATECALL => "DELEGATECALL", + &Opcode::STATICCALL => "STATICCALL", + &Opcode::SUICIDE => "SUICIDE", + &Opcode::CHAINID => "CHAINID", + _ => "UNKNOWNOP", + }; + write!(f, "{}", op) + } +} diff --git a/gasometer/src/lib.rs b/gasometer/src/lib.rs index 564a2d3e0..d41924201 100644 --- a/gasometer/src/lib.rs +++ b/gasometer/src/lib.rs @@ -238,12 +238,17 @@ impl<'config> Gasometer<'config> { non_zero_data_len, access_list_address_len, access_list_storage_len, + initcode_cost, } => { - self.config.gas_transaction_create + let mut cost = self.config.gas_transaction_create + zero_data_len as u64 * self.config.gas_transaction_zero_data + non_zero_data_len as u64 * self.config.gas_transaction_non_zero_data + access_list_address_len as u64 * self.config.gas_access_list_address - + access_list_storage_len as u64 * self.config.gas_access_list_storage_key + + access_list_storage_len as u64 * self.config.gas_access_list_storage_key; + if self.config.max_initcode_size.is_some() { + cost += initcode_cost; + } + cost } }; @@ -293,15 +298,21 @@ pub fn create_transaction_cost(data: &[u8], access_list: &[(H160, Vec)]) - let zero_data_len = data.iter().filter(|v| **v == 0).count(); let non_zero_data_len = data.len() - zero_data_len; let (access_list_address_len, access_list_storage_len) = count_access_list(access_list); + let initcode_cost = init_code_cost(data); TransactionCost::Create { zero_data_len, non_zero_data_len, access_list_address_len, access_list_storage_len, + initcode_cost, } } +pub fn init_code_cost(data: &[u8]) -> u64 { + 2 * (data.len() as u64 / 32) +} + /// Counts the number of addresses and storage keys in the access list fn count_access_list(access_list: &[(H160, Vec)]) -> (usize, usize) { let access_list_address_len = access_list.len(); @@ -610,6 +621,8 @@ pub fn dynamic_opcode_cost( } } + Opcode::PUSH0 if config.has_push0 => GasCost::Base, + _ => GasCost::Invalid(opcode), }; @@ -1021,6 +1034,8 @@ pub enum TransactionCost { access_list_address_len: usize, /// Total number of storage keys in transaction access list (see EIP-2930) access_list_storage_len: usize, + /// Cost of initcode = 2 * ceil(len(initcode) / 32) (see EIP-3860) + initcode_cost: u64, }, } diff --git a/runtime/src/eval/system.rs b/runtime/src/eval/system.rs index 81ee5a642..382109e86 100644 --- a/runtime/src/eval/system.rs +++ b/runtime/src/eval/system.rs @@ -8,6 +8,14 @@ use core::cmp::min; use primitive_types::{H256, U256}; use sha3::{Digest, Keccak256}; +pub fn u8_vec_to_hex(to_convert: &Vec) -> String { + let mut hex_string = String::new(); + for byte in to_convert { + hex_string.push_str(&format!("{:02x}", byte)); + } + hex_string +} + pub fn sha3(runtime: &mut Runtime) -> Control { pop_u256!(runtime, from, len); @@ -258,6 +266,12 @@ pub fn log(runtime: &mut Runtime, n: u8, handler: &mut H) -> Control pub fn suicide(runtime: &mut Runtime, handler: &mut H) -> Control { pop!(runtime, target); + event!(TransactSuicide { + address: runtime.context.address, + target: target.into(), + balance: handler.balance(runtime.context.address), + }); + match handler.mark_delete(runtime.context.address, target.into()) { Ok(()) => (), Err(e) => return Control::Exit(e.into()), @@ -295,9 +309,23 @@ pub fn create(runtime: &mut Runtime, is_create2: bool, handler: &mut } }; + event!(TransactCreate { + call_type: &(if is_create2 { + "CREATE2".to_string() + } else { + "CREATE".to_string() + }), + address: runtime.context.address, + target: handler.get_create_address(scheme).into(), + balance: value, + is_create2: is_create2, + input: &format!("0x{:}", u8_vec_to_hex(&code)), + }); + match handler.create(runtime.context.address, scheme, value, code, None) { Capture::Exit((reason, address, return_data)) => { runtime.return_data_buffer = return_data; + let create_address: H256 = address.map(|a| a.into()).unwrap_or_default(); match reason { @@ -399,6 +427,20 @@ pub fn call(runtime: &mut Runtime, scheme: CallScheme, handler: &mut None }; + let target = if scheme == CallScheme::CallCode { + runtime.context.address + } else { + to.into() + }; + + event!(TransactTransfer { + call_type: &format!("{:?}", scheme).to_uppercase(), + address: runtime.context.address, + target: target.into(), + balance: value, + input: &format!("0x{:}", u8_vec_to_hex(&input)), + }); + match handler.call( to.into(), transfer, @@ -406,6 +448,8 @@ pub fn call(runtime: &mut Runtime, scheme: CallScheme, handler: &mut gas, scheme == CallScheme::StaticCall, context, + out_offset, + out_len, ) { Capture::Exit((reason, return_data)) => { runtime.return_data_buffer = return_data; diff --git a/runtime/src/handler.rs b/runtime/src/handler.rs index 4c3aea80a..ef1e9d481 100644 --- a/runtime/src/handler.rs +++ b/runtime/src/handler.rs @@ -88,6 +88,10 @@ pub trait Handler { init_code: Vec, target_gas: Option, ) -> Capture<(ExitReason, Option, Vec), Self::CreateInterrupt>; + + /// Calculate the create or create2 address + fn get_create_address(&mut self, scheme: CreateScheme) -> H160; + /// Feed in create feedback. fn create_feedback(&mut self, _feedback: Self::CreateFeedback) -> Result<(), ExitError> { Ok(()) @@ -101,6 +105,8 @@ pub trait Handler { target_gas: Option, is_static: bool, context: Context, + memory_offset: U256, + offset_len: U256, ) -> Capture<(ExitReason, Vec), Self::CallInterrupt>; /// Feed in call feedback. fn call_feedback(&mut self, _feedback: Self::CallFeedback) -> Result<(), ExitError> { diff --git a/runtime/src/interrupt.rs b/runtime/src/interrupt.rs index 1742fd86d..f7d06cb66 100644 --- a/runtime/src/interrupt.rs +++ b/runtime/src/interrupt.rs @@ -1,4 +1,4 @@ -use crate::{ExitFatal, Handler, Runtime}; +use crate::{Handler, Runtime}; /// Interrupt resolution. pub enum Resolve<'a, 'config, H: Handler> { @@ -9,6 +9,7 @@ pub enum Resolve<'a, 'config, H: Handler> { } /// Create interrupt resolution. +#[allow(dead_code)] pub struct ResolveCreate<'a, 'config> { runtime: &'a mut Runtime<'config>, } @@ -19,16 +20,8 @@ impl<'a, 'config> ResolveCreate<'a, 'config> { } } -impl<'a, 'config> Drop for ResolveCreate<'a, 'config> { - fn drop(&mut self) { - self.runtime.status = Err(ExitFatal::UnhandledInterrupt.into()); - self.runtime - .machine - .exit(ExitFatal::UnhandledInterrupt.into()); - } -} - /// Call interrupt resolution. +#[allow(dead_code)] pub struct ResolveCall<'a, 'config> { runtime: &'a mut Runtime<'config>, } @@ -38,12 +31,3 @@ impl<'a, 'config> ResolveCall<'a, 'config> { Self { runtime } } } - -impl<'a, 'config> Drop for ResolveCall<'a, 'config> { - fn drop(&mut self) { - self.runtime.status = Err(ExitFatal::UnhandledInterrupt.into()); - self.runtime - .machine - .exit(ExitFatal::UnhandledInterrupt.into()); - } -} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5882c3f76..035484be9 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -131,11 +131,31 @@ impl<'config> Runtime<'config> { } } + pub fn new_from_state(machine: Machine, context: Context, config: &'config Config) -> Self { + Self { + machine, + status: Ok(()), + return_data_buffer: Vec::new(), + context, + _config: config, + } + } + /// Get a reference to the machine. pub fn machine(&self) -> &Machine { &self.machine } + /// Get a reference to the machine. + pub fn machine_mut(&mut self) -> &mut Machine { + &mut self.machine + } + + /// Get a reference to the return buffer + pub fn return_data_buffer(&mut self) -> &mut Vec { + &mut self.return_data_buffer + } + /// Get a reference to the execution context. pub fn context(&self) -> &Context { &self.context @@ -215,6 +235,8 @@ pub struct Config { pub decrease_clears_refund: bool, /// EIP-3541 pub disallow_executable_format: bool, + /// EIP-3651 + pub warm_coinbase_address: bool, /// Whether to throw out of gas error when /// CALL/CALLCODE/DELEGATECALL requires more than maximum amount /// of gas. @@ -233,6 +255,8 @@ pub struct Config { pub call_stack_limit: usize, /// Create contract limit. pub create_contract_limit: Option, + /// EIP-3860, maximum size limit of init_code. + pub max_initcode_size: Option, /// Call stipend. pub call_stipend: u64, /// Has delegate call. @@ -253,6 +277,8 @@ pub struct Config { pub has_ext_code_hash: bool, /// Has ext block fee. See [EIP-3198](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3198.md) pub has_base_fee: bool, + /// Has PUSH0 opcode. See [EIP-3855](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3855.md) + pub has_push0: bool, /// Whether the gasometer is running in estimate mode. pub estimate: bool, } @@ -287,6 +313,7 @@ impl Config { increase_state_access_gas: false, decrease_clears_refund: false, disallow_executable_format: false, + warm_coinbase_address: false, err_on_call_with_more_gas: true, empty_considered_exists: true, create_increase_nonce: false, @@ -295,6 +322,7 @@ impl Config { memory_limit: usize::MAX, call_stack_limit: 1024, create_contract_limit: None, + max_initcode_size: None, call_stipend: 2300, has_delegate_call: false, has_create2: false, @@ -305,6 +333,7 @@ impl Config { has_self_balance: false, has_ext_code_hash: false, has_base_fee: false, + has_push0: false, estimate: false, } } @@ -338,6 +367,7 @@ impl Config { increase_state_access_gas: false, decrease_clears_refund: false, disallow_executable_format: false, + warm_coinbase_address: false, err_on_call_with_more_gas: false, empty_considered_exists: false, create_increase_nonce: true, @@ -346,6 +376,7 @@ impl Config { memory_limit: usize::MAX, call_stack_limit: 1024, create_contract_limit: Some(0x6000), + max_initcode_size: None, call_stipend: 2300, has_delegate_call: true, has_create2: true, @@ -356,6 +387,7 @@ impl Config { has_self_balance: true, has_ext_code_hash: true, has_base_fee: false, + has_push0: false, estimate: false, } } @@ -370,6 +402,11 @@ impl Config { Self::config_with_derived_values(DerivedConfigInputs::london()) } + /// Shanghai hard fork configuration. + pub const fn shanghai() -> Config { + Self::config_with_derived_values(DerivedConfigInputs::shanghai()) + } + const fn config_with_derived_values(inputs: DerivedConfigInputs) -> Config { let DerivedConfigInputs { gas_storage_read_warm, @@ -377,7 +414,10 @@ impl Config { gas_access_list_storage_key, decrease_clears_refund, has_base_fee, + has_push0, disallow_executable_format, + warm_coinbase_address, + max_initcode_size, } = inputs; // See https://eips.ethereum.org/EIPS/eip-2929 @@ -419,6 +459,7 @@ impl Config { increase_state_access_gas: true, decrease_clears_refund, disallow_executable_format, + warm_coinbase_address, err_on_call_with_more_gas: false, empty_considered_exists: false, create_increase_nonce: true, @@ -427,6 +468,7 @@ impl Config { memory_limit: usize::MAX, call_stack_limit: 1024, create_contract_limit: Some(0x6000), + max_initcode_size, call_stipend: 2300, has_delegate_call: true, has_create2: true, @@ -437,6 +479,7 @@ impl Config { has_self_balance: true, has_ext_code_hash: true, has_base_fee, + has_push0, estimate: false, } } @@ -450,7 +493,10 @@ struct DerivedConfigInputs { gas_access_list_storage_key: u64, decrease_clears_refund: bool, has_base_fee: bool, + has_push0: bool, disallow_executable_format: bool, + warm_coinbase_address: bool, + max_initcode_size: Option, } impl DerivedConfigInputs { @@ -461,7 +507,10 @@ impl DerivedConfigInputs { gas_access_list_storage_key: 1900, decrease_clears_refund: false, has_base_fee: false, + has_push0: false, disallow_executable_format: false, + warm_coinbase_address: false, + max_initcode_size: None, } } @@ -472,7 +521,24 @@ impl DerivedConfigInputs { gas_access_list_storage_key: 1900, decrease_clears_refund: true, has_base_fee: true, + has_push0: false, + disallow_executable_format: true, + warm_coinbase_address: false, + max_initcode_size: None, + } + } + + const fn shanghai() -> Self { + Self { + gas_storage_read_warm: 100, + gas_sload_cold: 2100, + gas_access_list_storage_key: 1900, + decrease_clears_refund: true, + has_base_fee: true, + has_push0: true, disallow_executable_format: true, + warm_coinbase_address: true, + max_initcode_size: Some(0xC000), } } } diff --git a/runtime/src/tracing.rs b/runtime/src/tracing.rs index 4f17f5619..4d5ff38a8 100644 --- a/runtime/src/tracing.rs +++ b/runtime/src/tracing.rs @@ -1,7 +1,7 @@ //! Allows to listen to runtime events. use crate::{Capture, Context, ExitReason, Memory, Opcode, Stack, Trap}; -use primitive_types::{H160, H256}; +use primitive_types::{H160, H256, U256}; environmental::environmental!(listener: dyn EventListener + 'static); @@ -32,6 +32,26 @@ pub enum Event<'a> { index: H256, value: H256, }, + TransactSuicide { + address: H160, + target: H160, + balance: U256, + }, + TransactTransfer { + call_type: &'a String, + address: H160, + target: H160, + balance: U256, + input: &'a String, + }, + TransactCreate { + call_type: &'a String, + address: H160, + target: H160, + balance: U256, + is_create2: bool, + input: &'a String, + }, } // Expose `listener::with` to the crate only. diff --git a/src/backend/memory.rs b/src/backend/memory.rs index 7382a5e22..6eac4a190 100644 --- a/src/backend/memory.rs +++ b/src/backend/memory.rs @@ -142,6 +142,14 @@ impl<'vicinity> Backend for MemoryBackend<'vicinity> { .unwrap_or_default() } + fn code_as_json(&self, address: H160) -> Vec { + self.code(address) + } + + fn init_data_as_json(&self, _address: H160) -> Vec { + Vec::new() + } + fn storage(&self, address: H160, index: H256) -> H256 { self.state .get(&address) @@ -152,6 +160,10 @@ impl<'vicinity> Backend for MemoryBackend<'vicinity> { fn original_storage(&self, address: H160, index: H256) -> Option { Some(self.storage(address, index)) } + + fn substate_as_json(&self, _address: H160, _vname: &str, _indices: &[String]) -> Vec { + Vec::new() + } } impl<'vicinity> ApplyBackend for MemoryBackend<'vicinity> { diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 1a503cbcc..3e425b60f 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -79,10 +79,16 @@ pub trait Backend { fn basic(&self, address: H160) -> Basic; /// Get account code. fn code(&self, address: H160) -> Vec; - /// Get storage value of address at index. + /// Get account code formatted as json (if possible) + fn code_as_json(&self, address: H160) -> Vec; + /// Get contract init data formatted as json (if possible) + fn init_data_as_json(&self, address: H160) -> Vec; + /// Get storage value of address at inasex. fn storage(&self, address: H160, index: H256) -> H256; /// Get original storage value of address at index, if available. fn original_storage(&self, address: H160, index: H256) -> Option; + /// Get contract substate with given array of indices + fn substate_as_json(&self, address: H160, vname: &str, indices: &[String]) -> Vec; } /// EVM backend that can apply changes. diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 87062974b..47041afd0 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -227,10 +227,17 @@ pub trait StackState<'config>: Backend { } } +/// Data returned by a precompile on success. +#[derive(Debug, Eq, PartialEq, Clone)] +pub enum PrecompileOutputType { + Exit(ExitSucceed), + Trap, +} + /// Data returned by a precompile on success. #[derive(Debug, Eq, PartialEq, Clone)] pub struct PrecompileOutput { - pub exit_status: ExitSucceed, + pub output_type: PrecompileOutputType, pub output: Vec, } @@ -278,20 +285,23 @@ pub trait PrecompileHandle { /// Record a log. fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError>; - /// Retreive the code address (what is the address of the precompile being called). + /// Retrieve the code address (what is the address of the precompile being called). fn code_address(&self) -> H160; - /// Retreive the input data the precompile is called with. + /// Retrieve the input data the precompile is called with. fn input(&self) -> &[u8]; - /// Retreive the context in which the precompile is executed. + /// Retrieve the context in which the precompile is executed. fn context(&self) -> &Context; /// Is the precompile call is done statically. fn is_static(&self) -> bool; - /// Retreive the gas limit of this call. + /// Retrieve the gas limit of this call. fn gas_limit(&self) -> Option; + + /// Retrieve backend + fn backend(&self) -> &dyn Backend; } /// A precompile result. @@ -328,8 +338,13 @@ impl PrecompileSet for () { /// * Is static /// /// In case of success returns the output and the cost. -pub type PrecompileFn = - fn(&[u8], Option, &Context, bool) -> Result<(PrecompileOutput, u64), PrecompileFailure>; +pub type PrecompileFn = fn( + &[u8], + Option, + &Context, + &dyn Backend, + bool, +) -> Result<(PrecompileOutput, u64), PrecompileFailure>; impl PrecompileSet for BTreeMap { fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { @@ -340,8 +355,9 @@ impl PrecompileSet for BTreeMap { let gas_limit = handle.gas_limit(); let context = handle.context(); let is_static = handle.is_static(); + let backend = handle.backend(); - match (*precompile)(input, gas_limit, context, is_static) { + match (*precompile)(input, gas_limit, context, backend, is_static) { Ok((output, cost)) => { handle.record_cost(cost)?; Ok(output) @@ -441,6 +457,23 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> gasometer.record_transaction(transaction_cost) } + fn maybe_record_init_code_cost(&mut self, init_code: &[u8]) -> Result<(), ExitError> { + if let Some(limit) = self.config.max_initcode_size { + // EIP-3860 + if init_code.len() > limit { + self.state.metadata_mut().gasometer.fail(); + let _ = self.exit_substate(StackExitKind::Failed); + return Err(ExitError::OutOfGas); + } + return self + .state + .metadata_mut() + .gasometer + .record_cost(gasometer::init_code_cost(init_code)); + } + Ok(()) + } + /// Execute a `CREATE` transaction. pub fn transact_create( &mut self, @@ -458,6 +491,14 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> address: self.create_address(CreateScheme::Legacy { caller }), }); + if let Some(limit) = self.config.max_initcode_size { + if init_code.len() > limit { + self.state.metadata_mut().gasometer.fail(); + let _ = self.exit_substate(StackExitKind::Failed); + return emit_exit!(ExitError::InitCodeLimit.into(), Vec::new()); + } + } + if let Err(e) = self.record_create_transaction_cost(&init_code, &access_list) { return emit_exit!(e.into(), Vec::new()); } @@ -486,6 +527,14 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> gas_limit: u64, access_list: Vec<(H160, Vec)>, // See EIP-2930 ) -> (ExitReason, Vec) { + if let Some(limit) = self.config.max_initcode_size { + if init_code.len() > limit { + self.state.metadata_mut().gasometer.fail(); + let _ = self.exit_substate(StackExitKind::Failed); + return emit_exit!(ExitError::InitCodeLimit.into(), Vec::new()); + } + } + let code_hash = H256::from_slice(Keccak256::digest(&init_code).as_slice()); event!(TransactCreate2 { caller, @@ -554,8 +603,16 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> // Initialize initial addresses for EIP-2929 if self.config.increase_state_access_gas { - let addresses = core::iter::once(caller).chain(core::iter::once(address)); - self.state.metadata_mut().access_addresses(addresses); + if self.config.warm_coinbase_address { + // Warm coinbase address for EIP-3651 + let addresses = core::iter::once(caller) + .chain(core::iter::once(address)) + .chain(core::iter::once(self.block_coinbase())); + self.state.metadata_mut().access_addresses(addresses); + } else { + let addresses = core::iter::once(caller).chain(core::iter::once(address)); + self.state.metadata_mut().access_addresses(addresses); + } self.initialize_with_access_list(access_list); } @@ -921,11 +978,20 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> }) { return match result { Ok(PrecompileOutput { - exit_status, + output_type, output, }) => { - let _ = self.exit_substate(StackExitKind::Succeeded); - Capture::Exit((ExitReason::Succeed(exit_status), output)) + if let PrecompileOutputType::Exit(exit_status) = output_type { + let _ = self.exit_substate(StackExitKind::Succeeded); + Capture::Exit((ExitReason::Succeed(exit_status), output)) + } else { + Capture::Exit(( + ExitReason::Error(ExitError::Other(std::borrow::Cow::Borrowed( + "Unsupported", + ))), + output, + )) + } } Err(PrecompileFailure::Error { exit_status }) => { let _ = self.exit_substate(StackExitKind::Failed); @@ -1104,6 +1170,12 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler init_code: Vec, target_gas: Option, ) -> Capture<(ExitReason, Option, Vec), Self::CreateInterrupt> { + if let Err(e) = self.maybe_record_init_code_cost(&init_code) { + let reason: ExitReason = e.into(); + emit_exit!(reason.clone()); + return Capture::Exit((reason, None, Vec::new())); + } + self.create_inner(caller, scheme, value, init_code, target_gas, true) } @@ -1116,6 +1188,12 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler init_code: Vec, target_gas: Option, ) -> Capture<(ExitReason, Option, Vec), Self::CreateInterrupt> { + if let Err(e) = self.maybe_record_init_code_cost(&init_code) { + let reason: ExitReason = e.into(); + emit_exit!(reason.clone()); + return Capture::Exit((reason, None, Vec::new())); + } + let capture = self.create_inner(caller, scheme, value, init_code, target_gas, true); if let Capture::Exit((ref reason, _, ref return_value)) = capture { @@ -1125,6 +1203,10 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler capture } + fn get_create_address(&mut self, scheme: CreateScheme) -> H160 { + self.create_address(scheme) + } + #[cfg(not(feature = "tracing"))] fn call( &mut self, @@ -1134,6 +1216,8 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler target_gas: Option, is_static: bool, context: Context, + _memory_offset: U256, + _offset_len: U256, ) -> Capture<(ExitReason, Vec), Self::CallInterrupt> { self.call_inner( code_address, @@ -1156,6 +1240,8 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler target_gas: Option, is_static: bool, context: Context, + _memory_offset: U256, + _offset_len: U256, ) -> Capture<(ExitReason, Vec), Self::CallInterrupt> { let capture = self.call_inner( code_address, @@ -1215,13 +1301,13 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler } } -struct StackExecutorHandle<'inner, 'config, 'precompiles, S, P> { - executor: &'inner mut StackExecutor<'config, 'precompiles, S, P>, - code_address: H160, - input: &'inner [u8], - gas_limit: Option, - context: &'inner Context, - is_static: bool, +pub struct StackExecutorHandle<'inner, 'config, 'precompiles, S, P> { + pub executor: &'inner mut StackExecutor<'config, 'precompiles, S, P>, + pub code_address: H160, + pub input: &'inner [u8], + pub gas_limit: Option, + pub context: &'inner Context, + pub is_static: bool, } impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> PrecompileHandle @@ -1283,6 +1369,8 @@ impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Pr gas_limit, is_static, context.clone(), + U256::default(), + U256::default(), ) { Capture::Exit((s, v)) => (s, v), Capture::Trap(_) => unreachable!("Trap is infaillible since StackExecutor is sync"), @@ -1308,17 +1396,17 @@ impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Pr Handler::log(self.executor, address, topics, data) } - /// Retreive the code address (what is the address of the precompile being called). + /// Retrieve the code address (what is the address of the precompile being called). fn code_address(&self) -> H160 { self.code_address } - /// Retreive the input data the precompile is called with. + /// Retrieve the input data the precompile is called with. fn input(&self) -> &[u8] { self.input } - /// Retreive the context in which the precompile is executed. + /// Retrieve the context in which the precompile is executed. fn context(&self) -> &Context { self.context } @@ -1328,8 +1416,12 @@ impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Pr self.is_static } - /// Retreive the gas limit of this call. + /// Retrieve the gas limit of this call. fn gas_limit(&self) -> Option { self.gas_limit } + + fn backend(&self) -> &dyn Backend { + &self.executor.state + } } diff --git a/src/executor/stack/memory.rs b/src/executor/stack/memory.rs index 0ec07eabf..d859046cd 100644 --- a/src/executor/stack/memory.rs +++ b/src/executor/stack/memory.rs @@ -38,6 +38,23 @@ impl<'config> MemoryStackSubstate<'config> { } } + pub fn from_state( + metadata: StackSubstateMetadata<'config>, + logs: Vec, + accounts: BTreeMap, + storages: BTreeMap<(H160, H256), H256>, + deletes: BTreeSet, + ) -> Self { + Self { + metadata, + parent: None, + logs, + accounts, + storages, + deletes, + } + } + pub fn logs(&self) -> &[Log] { &self.logs } @@ -46,6 +63,18 @@ impl<'config> MemoryStackSubstate<'config> { &mut self.logs } + pub fn accounts(&self) -> &BTreeMap { + &self.accounts + } + + pub fn storages(&self) -> &BTreeMap<(H160, H256), H256> { + &self.storages + } + + pub fn deletes(&self) -> &BTreeSet { + &self.deletes + } + pub fn metadata(&self) -> &StackSubstateMetadata<'config> { &self.metadata } @@ -448,6 +477,14 @@ impl<'backend, 'config, B: Backend> Backend for MemoryStackState<'backend, 'conf .unwrap_or_else(|| self.backend.code(address)) } + fn code_as_json(&self, address: H160) -> Vec { + self.backend.code_as_json(address) + } + + fn init_data_as_json(&self, address: H160) -> Vec { + self.backend.init_data_as_json(address) + } + fn storage(&self, address: H160, key: H256) -> H256 { self.substate .known_storage(address, key) @@ -461,6 +498,10 @@ impl<'backend, 'config, B: Backend> Backend for MemoryStackState<'backend, 'conf self.backend.original_storage(address, key) } + + fn substate_as_json(&self, address: H160, vname: &str, indices: &[String]) -> Vec { + self.backend.substate_as_json(address, vname, indices) + } } impl<'backend, 'config, B: Backend> StackState<'config> for MemoryStackState<'backend, 'config, B> { @@ -555,6 +596,14 @@ impl<'backend, 'config, B: Backend> MemoryStackState<'backend, 'config, B> { } } + pub fn new_with_substate(substate: MemoryStackSubstate<'config>, backend: &'backend B) -> Self { + Self { backend, substate } + } + + pub fn substate(&self) -> &MemoryStackSubstate { + &self.substate + } + /// Returns a mutable reference to an account given its address pub fn account_mut(&mut self, address: H160) -> &mut MemoryStackAccount { self.substate.account_mut(address, self.backend) diff --git a/src/executor/stack/mod.rs b/src/executor/stack/mod.rs index 5bb448a7a..46b5ee309 100644 --- a/src/executor/stack/mod.rs +++ b/src/executor/stack/mod.rs @@ -6,8 +6,9 @@ mod executor; mod memory; pub use self::executor::{ - Accessed, PrecompileFailure, PrecompileFn, PrecompileHandle, PrecompileOutput, PrecompileSet, - StackExecutor, StackExitKind, StackState, StackSubstateMetadata, + Accessed, PrecompileFailure, PrecompileFn, PrecompileHandle, PrecompileOutput, + PrecompileOutputType, PrecompileSet, StackExecutor, StackExecutorHandle, StackExitKind, + StackState, StackSubstateMetadata, }; pub use self::memory::{MemoryStackAccount, MemoryStackState, MemoryStackSubstate}; diff --git a/src/lib.rs b/src/lib.rs index 6b1e85819..51cf252fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ extern crate alloc; pub use evm_core::*; pub use evm_gasometer as gasometer; +pub use evm_runtime as runtime; pub use evm_runtime::*; #[cfg(feature = "tracing")]