diff --git a/src/executor/mod.rs b/src/executor/mod.rs index 8dee8150c..190ec4aa7 100644 --- a/src/executor/mod.rs +++ b/src/executor/mod.rs @@ -5,4 +5,7 @@ mod stack; -pub use self::stack::{StackExecutor, MemoryStackState, StackState, StackSubstateMetadata, StackExitKind}; +pub use self::stack::{ + StackExecutor, MemoryStackState, StackState, StackSubstateMetadata, StackExitKind, + HookedStackExecutor, Hook, +}; diff --git a/src/executor/stack/hooked.rs b/src/executor/stack/hooked.rs new file mode 100644 index 000000000..3a22c4a43 --- /dev/null +++ b/src/executor/stack/hooked.rs @@ -0,0 +1,377 @@ +use core::convert::Infallible; +use primitive_types::{U256, H256, H160}; +use sha3::{Keccak256, Digest}; +use super::{StackExecutor, StackState, StackExitKind}; +use crate::{ + ExitReason, Runtime, ExitError, Stack, Opcode, Capture, Handler, Transfer, + Context, CreateScheme, ExitSucceed, Config, gasometer, +}; + +/// Can be injected in `StackExecutor` to inspect contract execution step by +/// step. +pub trait Hook { + /// Called before the execution of a context. + fn before_loop<'config, S: StackState<'config>>( + &mut self, + executor: &StackExecutor<'config, S>, + runtime: &Runtime, + ); + + /// Called before each step. + fn before_step<'config, S: StackState<'config>>( + &mut self, + executor: &StackExecutor<'config, S>, + runtime: &Runtime, + ); + + /// Called after each step. Will not be called if runtime exited + /// from the loop. + fn after_step<'config, S: StackState<'config>>( + &mut self, + executor: &StackExecutor<'config, S>, + runtime: &Runtime, + ); + + /// Called after the execution of a context. + fn after_loop<'config, S: StackState<'config>>( + &mut self, + executor: &StackExecutor<'config, S>, + runtime: &Runtime, + reason: &ExitReason, + ); +} + +impl Hook for () { + fn before_loop<'config, S: StackState<'config>>( + &mut self, + _executor: &StackExecutor<'config, S>, + _runtime: &Runtime, + ) { + } + + fn before_step<'config, S: StackState<'config>>( + &mut self, + _executor: &StackExecutor<'config, S>, + _runtime: &Runtime, + ) { + } + + fn after_step<'config, S: StackState<'config>>( + &mut self, + _executor: &StackExecutor<'config, S>, + _runtime: &Runtime, + ) { + } + + fn after_loop<'config, S: StackState<'config>>( + &mut self, + _executor: &StackExecutor<'config, S>, + _runtime: &Runtime, + _reason: &ExitReason, + ) { + } +} + +fn hooked_execute<'config, S: StackState<'config>, H: Hook>( + executor: &mut StackExecutor<'config, S>, + runtime: &mut Runtime, + hook: &mut H, +) -> ExitReason { + hook.before_loop(executor, runtime); + + let reason = loop { + hook.before_step(executor, runtime); + + match runtime.step(executor) { + Ok(_) => {} + Err(Capture::Exit(s)) => break s, + Err(Capture::Trap(_)) => unreachable!("Trap is Infallible"), + } + + hook.after_step(executor, runtime); + }; + + hook.after_loop(executor, runtime, &reason); + + reason +} + +pub struct HookedStackExecutor<'config, S, H> { + executor: StackExecutor<'config, S>, + hook: H, +} + +impl<'config, S: StackState<'config>, H: Hook> HookedStackExecutor<'config, S, H> { + /// Create a new stack-based executor. + pub fn new(state: S, config: &'config Config, hook: H) -> Self { + Self { + executor: StackExecutor::new(state, config), + hook, + } + } + + /// Create a new stack-based executor with given precompiles. + pub fn new_with_precompile( + state: S, + config: &'config Config, + hook: H, + precompile: fn( + H160, + &[u8], + Option, + &Context, + ) -> Option, u64), ExitError>>, + ) -> Self { + Self { + executor: StackExecutor::new_with_precompile(state, config, precompile), + hook, + } + } + + /// Return a reference of the Config. + pub fn config(&self) -> &'config Config { + self.executor.config() + } + + pub fn state(&self) -> &S { + self.executor.state() + } + + pub fn state_mut(&mut self) -> &mut S { + self.executor.state_mut() + } + + pub fn into_state(self) -> S { + self.executor.into_state() + } + + /// Create a substate executor from the current executor. + pub fn enter_substate(&mut self, gas_limit: u64, is_static: bool) { + self.executor.enter_substate(gas_limit, is_static) + } + + /// Exit a substate. Panic if it results an empty substate stack. + pub fn exit_substate(&mut self, kind: StackExitKind) -> Result<(), ExitError> { + self.executor.exit_substate(kind) + } + + /// Execute the runtime until it returns. + pub fn execute(&mut self, runtime: &mut Runtime) -> ExitReason { + hooked_execute(&mut self.executor, runtime, &mut self.hook) + } + + /// Get remaining gas. + pub fn gas(&self) -> u64 { + self.executor.gas() + } + + /// Execute a `CREATE` transaction. + pub fn transact_create( + &mut self, + caller: H160, + value: U256, + init_code: Vec, + gas_limit: u64, + ) -> ExitReason { + let transaction_cost = gasometer::create_transaction_cost(&init_code); + match self.executor.state.metadata_mut().gasometer.record_transaction(transaction_cost) { + Ok(()) => (), + Err(e) => return e.into(), + } + + let hook = &mut self.hook; + match super::create_inner( + &mut self.executor, + caller, + CreateScheme::Legacy { caller }, + value, + init_code, + Some(gas_limit), + false, + |executor, runtime| { + hooked_execute(executor, runtime, hook) + } + ) { + Capture::Exit((s, _, _)) => s, + Capture::Trap(_) => unreachable!(), + } + } + + /// Execute a `CREATE2` transaction. + pub fn transact_create2( + &mut self, + caller: H160, + value: U256, + init_code: Vec, + salt: H256, + gas_limit: u64, + ) -> ExitReason { + let transaction_cost = gasometer::create_transaction_cost(&init_code); + match self.executor.state.metadata_mut().gasometer.record_transaction(transaction_cost) { + Ok(()) => (), + Err(e) => return e.into(), + } + let code_hash = H256::from_slice(Keccak256::digest(&init_code).as_slice()); + + let hook = &mut self.hook; + match super::create_inner( + &mut self.executor, + caller, + CreateScheme::Create2 { caller, code_hash, salt }, + value, + init_code, + Some(gas_limit), + false, + |executor, runtime| { + hooked_execute(executor, runtime, hook) + } + ) { + Capture::Exit((s, _, _)) => s, + Capture::Trap(_) => unreachable!(), + } + } + + /// Execute a `CALL` transaction. + pub fn transact_call( + &mut self, + caller: H160, + address: H160, + value: U256, + data: Vec, + gas_limit: u64, + ) -> (ExitReason, Vec) { + let transaction_cost = gasometer::call_transaction_cost(&data); + match self.executor.state.metadata_mut().gasometer.record_transaction(transaction_cost) { + Ok(()) => (), + Err(e) => return (e.into(), Vec::new()), + } + + self.executor.state.inc_nonce(caller); + + let context = Context { + caller, + address, + apparent_value: value, + }; + + let hook = &mut self.hook; + match super::call_inner( + &mut self.executor, + address, + Some(Transfer { + source: caller, + target: address, + value + }), + data, + Some(gas_limit), + false, + false, + false, + context, + |executor, runtime| { + hooked_execute(executor, runtime, hook) + } + ) { + Capture::Exit((s, v)) => (s, v), + Capture::Trap(_) => unreachable!(), + } + } + + /// Get used gas for the current executor, given the price. + pub fn used_gas(&self) -> u64 { + self.executor.used_gas() + } + + /// Get fee needed for the current executor, given the price. + pub fn fee(&self, price: U256) -> U256 { + self.executor.fee(price) + } + + /// Get account nonce. + pub fn nonce(&self, address: H160) -> U256 { + self.executor.nonce(address) + } + + /// Get the create address from given scheme. + pub fn create_address(&self, scheme: CreateScheme) -> H160 { + self.executor.create_address(scheme) + } +} + +impl<'config, S: StackState<'config>, H: Hook> Handler for HookedStackExecutor<'config, S, H> { + type CreateInterrupt = Infallible; + type CreateFeedback = Infallible; + type CallInterrupt = Infallible; + type CallFeedback = Infallible; + + fn balance(&self, address: H160) -> U256 { self.executor.balance(address) } + fn code_size(&self, address: H160) -> U256 { self.executor.code_size(address) } + fn code_hash(&self, address: H160) -> H256 { self.executor.code_hash(address) } + fn code(&self, address: H160) -> Vec { self.executor.code(address) } + fn storage(&self, address: H160, index: H256) -> H256 { self.executor.storage(address, index) } + fn original_storage(&self, address: H160, index: H256) -> H256 { self.executor.original_storage(address, index) } + fn exists(&self, address: H160) -> bool { self.executor.exists(address) } + fn gas_left(&self) -> U256 { self.executor.gas_left() } + fn gas_price(&self) -> U256 { self.executor.gas_price() } + fn origin(&self) -> H160 { self.executor.origin() } + fn block_hash(&self, number: U256) -> H256 { self.executor.block_hash(number) } + fn block_number(&self) -> U256 { self.executor.block_number() } + fn block_coinbase(&self) -> H160 { self.executor.block_coinbase() } + fn block_timestamp(&self) -> U256 { self.executor.block_timestamp() } + fn block_difficulty(&self) -> U256 { self.executor.block_difficulty() } + fn block_gas_limit(&self) -> U256 { self.executor.block_gas_limit() } + fn chain_id(&self) -> U256 { self.executor.chain_id() } + fn deleted(&self, address: H160) -> bool { self.executor.deleted(address) } + + fn set_storage(&mut self, address: H160, index: H256, value: H256) -> Result<(), ExitError> { + self.executor.set_storage(address, index, value) + } + + fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError> { + self.executor.log(address, topics, data) + } + + fn mark_delete(&mut self, address: H160, target: H160) -> Result<(), ExitError> { + self.executor.mark_delete(address, target) + } + + fn create( + &mut self, + caller: H160, + scheme: CreateScheme, + value: U256, + init_code: Vec, + target_gas: Option, + ) -> Capture<(ExitReason, Option, Vec), Self::CreateInterrupt> { + let hook = &mut self.hook; + super::create_inner(&mut self.executor, caller, scheme, value, init_code, target_gas, true, |executor, runtime| { + hooked_execute(executor, runtime, hook) + }) + } + + fn call( + &mut self, + code_address: H160, + transfer: Option, + input: Vec, + target_gas: Option, + is_static: bool, + context: Context, + ) -> Capture<(ExitReason, Vec), Self::CallInterrupt> { + let hook = &mut self.hook; + super::call_inner(&mut self.executor, code_address, transfer, input, target_gas, is_static, true, true, context, |executor, runtime| { + hooked_execute(executor, runtime, hook) + }) + } + + #[inline] + fn pre_validate( + &mut self, + context: &Context, + opcode: Opcode, + stack: &Stack, + ) -> Result<(), ExitError> { + self.executor.pre_validate(context, opcode, stack) + } +} diff --git a/src/executor/stack/mod.rs b/src/executor/stack/mod.rs index 8ff7ac508..4af93ad74 100644 --- a/src/executor/stack/mod.rs +++ b/src/executor/stack/mod.rs @@ -1,6 +1,8 @@ mod state; +mod hooked; pub use self::state::{MemoryStackSubstate, MemoryStackState, StackState}; +pub use self::hooked::{HookedStackExecutor, Hook}; use core::{convert::Infallible, cmp::min}; use alloc::{rc::Rc, vec::Vec}; @@ -104,13 +106,6 @@ impl<'config, S: StackState<'config>> StackExecutor<'config, S> { Self::new_with_precompile(state, config, no_precompile) } - /// Return a reference of the Config. - pub fn config( - &self - ) -> &'config Config { - self.config - } - /// Create a new stack-based executor with given precompiles. pub fn new_with_precompile( state: S, @@ -124,6 +119,11 @@ impl<'config, S: StackState<'config>> StackExecutor<'config, S> { } } + /// Return a reference of the Config. + pub fn config(&self) -> &'config Config { + self.config + } + pub fn state(&self) -> &S { &self.state } @@ -184,13 +184,17 @@ impl<'config, S: StackState<'config>> StackExecutor<'config, S> { Err(e) => return e.into(), } - match self.create_inner( + match create_inner( + self, caller, CreateScheme::Legacy { caller }, value, init_code, Some(gas_limit), false, + |executor, runtime| { + executor.execute(runtime) + } ) { Capture::Exit((s, _, _)) => s, Capture::Trap(_) => unreachable!(), @@ -213,13 +217,17 @@ impl<'config, S: StackState<'config>> StackExecutor<'config, S> { } let code_hash = H256::from_slice(Keccak256::digest(&init_code).as_slice()); - match self.create_inner( + match create_inner( + self, caller, CreateScheme::Create2 { caller, code_hash, salt }, value, init_code, Some(gas_limit), false, + |executor, runtime| { + executor.execute(runtime) + } ) { Capture::Exit((s, _, _)) => s, Capture::Trap(_) => unreachable!(), @@ -249,30 +257,38 @@ impl<'config, S: StackState<'config>> StackExecutor<'config, S> { apparent_value: value, }; - match self.call_inner(address, Some(Transfer { - source: caller, - target: address, - value - }), data, Some(gas_limit), false, false, false, context) { + match call_inner( + self, + address, + Some(Transfer { + source: caller, + target: address, + value + }), + data, + Some(gas_limit), + false, + false, + false, + context, + |executor, runtime| { + executor.execute(runtime) + } + ) { Capture::Exit((s, v)) => (s, v), Capture::Trap(_) => unreachable!(), } } /// Get used gas for the current executor, given the price. - pub fn used_gas( - &self, - ) -> u64 { + pub fn used_gas(&self) -> u64 { self.state.metadata().gasometer.total_used_gas() - min(self.state.metadata().gasometer.total_used_gas() / 2, self.state.metadata().gasometer.refunded_gas() as u64) } /// Get fee needed for the current executor, given the price. - pub fn fee( - &self, - price: U256, - ) -> U256 { + pub fn fee(&self, price: U256) -> U256 { let used_gas = self.used_gas(); U256::from(used_gas) * price } @@ -305,268 +321,274 @@ impl<'config, S: StackState<'config>> StackExecutor<'config, S> { }, } } +} - fn create_inner( - &mut self, - caller: H160, - scheme: CreateScheme, - value: U256, - init_code: Vec, - target_gas: Option, - take_l64: bool, - ) -> Capture<(ExitReason, Option, Vec), Infallible> { - macro_rules! try_or_fail { - ( $e:expr ) => { - match $e { - Ok(v) => v, - Err(e) => return Capture::Exit((e.into(), None, Vec::new())), - } +fn create_inner<'config, S: StackState<'config>, FExecute>( + executor: &mut StackExecutor<'config, S>, + caller: H160, + scheme: CreateScheme, + value: U256, + init_code: Vec, + target_gas: Option, + take_l64: bool, + fexecute: FExecute, +) -> Capture<(ExitReason, Option, Vec), Infallible> where + FExecute: FnOnce(&mut StackExecutor<'config, S>, &mut Runtime) -> ExitReason, +{ + macro_rules! try_or_fail { + ( $e:expr ) => { + match $e { + Ok(v) => v, + Err(e) => return Capture::Exit((e.into(), None, Vec::new())), } } + } - fn l64(gas: u64) -> u64 { - gas - gas / 64 - } + fn l64(gas: u64) -> u64 { + gas - gas / 64 + } - if let Some(depth) = self.state.metadata().depth { - if depth > self.config.call_stack_limit { - return Capture::Exit((ExitError::CallTooDeep.into(), None, Vec::new())) - } + if let Some(depth) = executor.state.metadata().depth { + if depth > executor.config.call_stack_limit { + return Capture::Exit((ExitError::CallTooDeep.into(), None, Vec::new())) } + } - if self.balance(caller) < value { - return Capture::Exit((ExitError::OutOfFund.into(), None, Vec::new())) - } + if executor.balance(caller) < value { + return Capture::Exit((ExitError::OutOfFund.into(), None, Vec::new())) + } - let after_gas = if take_l64 && self.config.call_l64_after_gas { - if self.config.estimate { - let initial_after_gas = self.state.metadata().gasometer.gas(); - let diff = initial_after_gas - l64(initial_after_gas); - try_or_fail!(self.state.metadata_mut().gasometer.record_cost(diff)); - self.state.metadata().gasometer.gas() - } else { - l64(self.state.metadata().gasometer.gas()) - } + let after_gas = if take_l64 && executor.config.call_l64_after_gas { + if executor.config.estimate { + let initial_after_gas = executor.state.metadata().gasometer.gas(); + let diff = initial_after_gas - l64(initial_after_gas); + try_or_fail!(executor.state.metadata_mut().gasometer.record_cost(diff)); + executor.state.metadata().gasometer.gas() } else { - self.state.metadata().gasometer.gas() - }; - - let target_gas = target_gas.unwrap_or(after_gas); + l64(executor.state.metadata().gasometer.gas()) + } + } else { + executor.state.metadata().gasometer.gas() + }; - let gas_limit = min(after_gas, target_gas); - try_or_fail!( - self.state.metadata_mut().gasometer.record_cost(gas_limit) - ); + let target_gas = target_gas.unwrap_or(after_gas); - let address = self.create_address(scheme); - self.state.inc_nonce(caller); + let gas_limit = min(after_gas, target_gas); + try_or_fail!( + executor.state.metadata_mut().gasometer.record_cost(gas_limit) + ); - self.enter_substate(gas_limit, false); + let address = executor.create_address(scheme); + executor.state.inc_nonce(caller); - { - if self.code_size(address) != U256::zero() { - let _ = self.exit_substate(StackExitKind::Failed); - return Capture::Exit((ExitError::CreateCollision.into(), None, Vec::new())) - } - - if self.nonce(address) > U256::zero() { - let _ = self.exit_substate(StackExitKind::Failed); - return Capture::Exit((ExitError::CreateCollision.into(), None, Vec::new())) - } + executor.enter_substate(gas_limit, false); - self.state.reset_storage(address); + { + if executor.code_size(address) != U256::zero() { + let _ = executor.exit_substate(StackExitKind::Failed); + return Capture::Exit((ExitError::CreateCollision.into(), None, Vec::new())) } - let context = Context { - address, - caller, - apparent_value: value, - }; - let transfer = Transfer { - source: caller, - target: address, - value, - }; - match self.state.transfer(transfer) { - Ok(()) => (), - Err(e) => { - let _ = self.exit_substate(StackExitKind::Reverted); - return Capture::Exit((ExitReason::Error(e), None, Vec::new())) - }, + if executor.nonce(address) > U256::zero() { + let _ = executor.exit_substate(StackExitKind::Failed); + return Capture::Exit((ExitError::CreateCollision.into(), None, Vec::new())) } - if self.config.create_increase_nonce { - self.state.inc_nonce(address); - } - - let mut runtime = Runtime::new( - Rc::new(init_code), - Rc::new(Vec::new()), - context, - self.config, - ); - - let reason = self.execute(&mut runtime); - log::debug!(target: "evm", "Create execution using address {}: {:?}", address, reason); - - match reason { - ExitReason::Succeed(s) => { - let out = runtime.machine().return_value(); - - if let Some(limit) = self.config.create_contract_limit { - if out.len() > limit { - self.state.metadata_mut().gasometer.fail(); - let _ = self.exit_substate(StackExitKind::Failed); - return Capture::Exit((ExitError::CreateContractLimit.into(), None, Vec::new())) - } + executor.state.reset_storage(address); + } + + let context = Context { + address, + caller, + apparent_value: value, + }; + let transfer = Transfer { + source: caller, + target: address, + value, + }; + match executor.state.transfer(transfer) { + Ok(()) => (), + Err(e) => { + let _ = executor.exit_substate(StackExitKind::Reverted); + return Capture::Exit((ExitReason::Error(e), None, Vec::new())) + }, + } + + if executor.config.create_increase_nonce { + executor.state.inc_nonce(address); + } + + let mut runtime = Runtime::new( + Rc::new(init_code), + Rc::new(Vec::new()), + context, + executor.config, + ); + + let reason = fexecute(executor, &mut runtime); + log::debug!(target: "evm", "Create execution using address {}: {:?}", address, reason); + + match reason { + ExitReason::Succeed(s) => { + let out = runtime.machine().return_value(); + + if let Some(limit) = executor.config.create_contract_limit { + if out.len() > limit { + executor.state.metadata_mut().gasometer.fail(); + let _ = executor.exit_substate(StackExitKind::Failed); + return Capture::Exit((ExitError::CreateContractLimit.into(), None, Vec::new())) } + } - match self.state.metadata_mut().gasometer.record_deposit(out.len()) { - Ok(()) => { - let e = self.exit_substate(StackExitKind::Succeeded); - self.state.set_code(address, out); - try_or_fail!(e); - Capture::Exit((ExitReason::Succeed(s), Some(address), Vec::new())) - }, - Err(e) => { - let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Error(e), None, Vec::new())) - }, - } - }, - ExitReason::Error(e) => { - self.state.metadata_mut().gasometer.fail(); - let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Error(e), None, Vec::new())) - }, - ExitReason::Revert(e) => { - let _ = self.exit_substate(StackExitKind::Reverted); - Capture::Exit((ExitReason::Revert(e), None, runtime.machine().return_value())) - }, - ExitReason::Fatal(e) => { - self.state.metadata_mut().gasometer.fail(); - let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Fatal(e), None, Vec::new())) - }, - } + match executor.state.metadata_mut().gasometer.record_deposit(out.len()) { + Ok(()) => { + let e = executor.exit_substate(StackExitKind::Succeeded); + executor.state.set_code(address, out); + try_or_fail!(e); + Capture::Exit((ExitReason::Succeed(s), Some(address), Vec::new())) + }, + Err(e) => { + let _ = executor.exit_substate(StackExitKind::Failed); + Capture::Exit((ExitReason::Error(e), None, Vec::new())) + }, + } + }, + ExitReason::Error(e) => { + executor.state.metadata_mut().gasometer.fail(); + let _ = executor.exit_substate(StackExitKind::Failed); + Capture::Exit((ExitReason::Error(e), None, Vec::new())) + }, + ExitReason::Revert(e) => { + let _ = executor.exit_substate(StackExitKind::Reverted); + Capture::Exit((ExitReason::Revert(e), None, runtime.machine().return_value())) + }, + ExitReason::Fatal(e) => { + executor.state.metadata_mut().gasometer.fail(); + let _ = executor.exit_substate(StackExitKind::Failed); + Capture::Exit((ExitReason::Fatal(e), None, Vec::new())) + }, } +} - fn call_inner( - &mut self, - code_address: H160, - transfer: Option, - input: Vec, - target_gas: Option, - is_static: bool, - take_l64: bool, - take_stipend: bool, - context: Context, - ) -> Capture<(ExitReason, Vec), Infallible> { - macro_rules! try_or_fail { - ( $e:expr ) => { - match $e { - Ok(v) => v, - Err(e) => return Capture::Exit((e.into(), Vec::new())), - } +fn call_inner<'config, S: StackState<'config>, FExecute>( + executor: &mut StackExecutor<'config, S>, + code_address: H160, + transfer: Option, + input: Vec, + target_gas: Option, + is_static: bool, + take_l64: bool, + take_stipend: bool, + context: Context, + fexecute: FExecute, +) -> Capture<(ExitReason, Vec), Infallible> where + FExecute: FnOnce(&mut StackExecutor<'config, S>, &mut Runtime) -> ExitReason, +{ + macro_rules! try_or_fail { + ( $e:expr ) => { + match $e { + Ok(v) => v, + Err(e) => return Capture::Exit((e.into(), Vec::new())), } } + } - fn l64(gas: u64) -> u64 { - gas - gas / 64 - } + fn l64(gas: u64) -> u64 { + gas - gas / 64 + } - let after_gas = if take_l64 && self.config.call_l64_after_gas { - if self.config.estimate { - let initial_after_gas = self.state.metadata().gasometer.gas(); - let diff = initial_after_gas - l64(initial_after_gas); - try_or_fail!(self.state.metadata_mut().gasometer.record_cost(diff)); - self.state.metadata().gasometer.gas() - } else { - l64(self.state.metadata().gasometer.gas()) - } + let after_gas = if take_l64 && executor.config.call_l64_after_gas { + if executor.config.estimate { + let initial_after_gas = executor.state.metadata().gasometer.gas(); + let diff = initial_after_gas - l64(initial_after_gas); + try_or_fail!(executor.state.metadata_mut().gasometer.record_cost(diff)); + executor.state.metadata().gasometer.gas() } else { - self.state.metadata().gasometer.gas() - }; + l64(executor.state.metadata().gasometer.gas()) + } + } else { + executor.state.metadata().gasometer.gas() + }; - let target_gas = target_gas.unwrap_or(after_gas); - let mut gas_limit = min(target_gas, after_gas); + let target_gas = target_gas.unwrap_or(after_gas); + let mut gas_limit = min(target_gas, after_gas); - try_or_fail!( - self.state.metadata_mut().gasometer.record_cost(gas_limit) - ); + try_or_fail!( + executor.state.metadata_mut().gasometer.record_cost(gas_limit) + ); - if let Some(transfer) = transfer.as_ref() { - if take_stipend && transfer.value != U256::zero() { - gas_limit = gas_limit.saturating_add(self.config.call_stipend); - } + if let Some(transfer) = transfer.as_ref() { + if take_stipend && transfer.value != U256::zero() { + gas_limit = gas_limit.saturating_add(executor.config.call_stipend); } + } - let code = self.code(code_address); + let code = executor.code(code_address); - self.enter_substate(gas_limit, is_static); - self.state.touch(context.address); + executor.enter_substate(gas_limit, is_static); + executor.state.touch(context.address); - if let Some(depth) = self.state.metadata().depth { - if depth > self.config.call_stack_limit { - let _ = self.exit_substate(StackExitKind::Reverted); - return Capture::Exit((ExitError::CallTooDeep.into(), Vec::new())) - } - } - - if let Some(transfer) = transfer { - match self.state.transfer(transfer) { - Ok(()) => (), - Err(e) => { - let _ = self.exit_substate(StackExitKind::Reverted); - return Capture::Exit((ExitReason::Error(e), Vec::new())) - }, - } + if let Some(depth) = executor.state.metadata().depth { + if depth > executor.config.call_stack_limit { + let _ = executor.exit_substate(StackExitKind::Reverted); + return Capture::Exit((ExitError::CallTooDeep.into(), Vec::new())) } + } - if let Some(ret) = (self.precompile)(code_address, &input, Some(gas_limit), &context) { - return match ret { - Ok((s, out, cost)) => { - let _ = self.state.metadata_mut().gasometer.record_cost(cost); - let _ = self.exit_substate(StackExitKind::Succeeded); - Capture::Exit((ExitReason::Succeed(s), out)) - }, - Err(e) => { - let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Error(e), Vec::new())) - }, - } + if let Some(transfer) = transfer { + match executor.state.transfer(transfer) { + Ok(()) => (), + Err(e) => { + let _ = executor.exit_substate(StackExitKind::Reverted); + return Capture::Exit((ExitReason::Error(e), Vec::new())) + }, } + } - let mut runtime = Runtime::new( - Rc::new(code), - Rc::new(input), - context, - self.config, - ); - - let reason = self.execute(&mut runtime); - log::debug!(target: "evm", "Call execution using address {}: {:?}", code_address, reason); - - match reason { - ExitReason::Succeed(s) => { - let _ = self.exit_substate(StackExitKind::Succeeded); - Capture::Exit((ExitReason::Succeed(s), runtime.machine().return_value())) + if let Some(ret) = (executor.precompile)(code_address, &input, Some(gas_limit), &context) { + return match ret { + Ok((s, out, cost)) => { + let _ = executor.state.metadata_mut().gasometer.record_cost(cost); + let _ = executor.exit_substate(StackExitKind::Succeeded); + Capture::Exit((ExitReason::Succeed(s), out)) }, - ExitReason::Error(e) => { - let _ = self.exit_substate(StackExitKind::Failed); + Err(e) => { + let _ = executor.exit_substate(StackExitKind::Failed); Capture::Exit((ExitReason::Error(e), Vec::new())) }, - ExitReason::Revert(e) => { - let _ = self.exit_substate(StackExitKind::Reverted); - Capture::Exit((ExitReason::Revert(e), runtime.machine().return_value())) - }, - ExitReason::Fatal(e) => { - self.state.metadata_mut().gasometer.fail(); - let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Fatal(e), Vec::new())) - }, } } + + let mut runtime = Runtime::new( + Rc::new(code), + Rc::new(input), + context, + executor.config, + ); + + let reason = fexecute(executor, &mut runtime); + log::debug!(target: "evm", "Call execution using address {}: {:?}", code_address, reason); + + match reason { + ExitReason::Succeed(s) => { + let _ = executor.exit_substate(StackExitKind::Succeeded); + Capture::Exit((ExitReason::Succeed(s), runtime.machine().return_value())) + }, + ExitReason::Error(e) => { + let _ = executor.exit_substate(StackExitKind::Failed); + Capture::Exit((ExitReason::Error(e), Vec::new())) + }, + ExitReason::Revert(e) => { + let _ = executor.exit_substate(StackExitKind::Reverted); + Capture::Exit((ExitReason::Revert(e), runtime.machine().return_value())) + }, + ExitReason::Fatal(e) => { + executor.state.metadata_mut().gasometer.fail(); + let _ = executor.exit_substate(StackExitKind::Failed); + Capture::Exit((ExitReason::Fatal(e), Vec::new())) + }, + } } impl<'config, S: StackState<'config>> Handler for StackExecutor<'config, S> { @@ -661,7 +683,9 @@ impl<'config, S: StackState<'config>> Handler for StackExecutor<'config, S> { init_code: Vec, target_gas: Option, ) -> Capture<(ExitReason, Option, Vec), Self::CreateInterrupt> { - self.create_inner(caller, scheme, value, init_code, target_gas, true) + create_inner(self, caller, scheme, value, init_code, target_gas, true, |executor, runtime| { + executor.execute(runtime) + }) } fn call( @@ -673,7 +697,9 @@ impl<'config, S: StackState<'config>> Handler for StackExecutor<'config, S> { is_static: bool, context: Context, ) -> Capture<(ExitReason, Vec), Self::CallInterrupt> { - self.call_inner(code_address, transfer, input, target_gas, is_static, true, true, context) + call_inner(self, code_address, transfer, input, target_gas, is_static, true, true, context, |executor, runtime| { + executor.execute(runtime) + }) } #[inline] @@ -683,8 +709,6 @@ impl<'config, S: StackState<'config>> Handler for StackExecutor<'config, S> { opcode: Opcode, stack: &Stack ) -> Result<(), ExitError> { - // log::trace!(target: "evm", "Running opcode: {:?}, Pre gas-left: {:?}", opcode, gasometer.gas()); - if let Some(cost) = gasometer::static_opcode_cost(opcode) { self.state.metadata_mut().gasometer.record_cost(cost)?; } else {