diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index abc497717..befe73ca8 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -908,7 +908,7 @@ impl BlockChainClient for Client { Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm) .transact(&tx, options.clone()) - .map(|r| r.trace[0].result.succeeded()) + .map(|r| r.exception.is_some()) .unwrap_or(false) }; diff --git a/ethcore/src/evm/evm.rs b/ethcore/src/evm/evm.rs index 3270b06e4..78dc8b73a 100644 --- a/ethcore/src/evm/evm.rs +++ b/ethcore/src/evm/evm.rs @@ -22,7 +22,7 @@ use action_params::ActionParams; use evm::Ext; /// Evm errors. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum Error { /// `OutOfGas` is returned when transaction execution runs out of gas. /// The state should be reverted to the state from before the diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 72427c668..a8c9596ab 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -463,8 +463,9 @@ impl<'a> Executive<'a> { match result { Err(evm::Error::Internal) => Err(ExecutionError::Internal), - Err(_) => { + Err(exception) => { Ok(Executed { + exception: Some(exception), gas: t.gas, gas_used: t.gas, refunded: U256::zero(), @@ -479,6 +480,7 @@ impl<'a> Executive<'a> { }, _ => { Ok(Executed { + exception: None, gas: t.gas, gas_used: gas_used, refunded: refunded, diff --git a/ethcore/src/types/executed.rs b/ethcore/src/types/executed.rs index 1f0ef33c7..ab7be891e 100644 --- a/ethcore/src/types/executed.rs +++ b/ethcore/src/types/executed.rs @@ -18,6 +18,7 @@ use util::{Bytes, U256, Address, U512}; use rlp::*; +use evm; use trace::{VMTrace, FlatTrace}; use types::log_entry::LogEntry; use types::state_diff::StateDiff; @@ -65,6 +66,9 @@ impl Decodable for CallType { #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "ipc", binary)] pub struct Executed { + /// True if the outer call/create resulted in an exceptional exit. + pub exception: Option, + /// Gas paid up front for execution of transaction. pub gas: U256, @@ -178,6 +182,8 @@ pub enum CallError { TransactionNotFound, /// Couldn't find requested block's state in the chain. StatePruned, + /// Couldn't find an amount of gas that didn't result in an exception. + Exceptional, /// Error executing. Execution(ExecutionError), } @@ -195,6 +201,7 @@ impl fmt::Display for CallError { let msg = match *self { TransactionNotFound => "Transaction couldn't be found in the chain".into(), StatePruned => "Couldn't find the transaction block's state in the chain".into(), + Exceptional => "An exception happened in the execution".into(), Execution(ref e) => format!("{}", e), }; diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 80a0dab9a..00f383a04 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -36,6 +36,7 @@ mod codes { pub const UNKNOWN_ERROR: i64 = -32009; pub const TRANSACTION_ERROR: i64 = -32010; pub const EXECUTION_ERROR: i64 = -32015; + pub const EXCEPTION_ERROR: i64 = -32016; pub const ACCOUNT_LOCKED: i64 = -32020; pub const PASSWORD_INVALID: i64 = -32021; pub const ACCOUNT_ERROR: i64 = -32023; @@ -130,6 +131,14 @@ pub fn state_pruned() -> Error { } } +pub fn exceptional() -> Error { + Error { + code: ErrorCode::ServerError(codes::EXCEPTION_ERROR), + message: "The execution failed due to an exception.".into(), + data: None + } +} + pub fn no_work() -> Error { Error { code: ErrorCode::ServerError(codes::NO_WORK), @@ -286,6 +295,7 @@ pub fn from_rlp_error(error: DecoderError) -> Error { pub fn from_call_error(error: CallError) -> Error { match error { CallError::StatePruned => state_pruned(), + CallError::Exceptional => exceptional(), CallError::Execution(e) => execution(e), CallError::TransactionNotFound => internal("{}, this should not be the case with eth_call, most likely a bug.", CallError::TransactionNotFound), } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 2ca4918d1..32d50e90e 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -564,6 +564,7 @@ fn rpc_eth_code() { fn rpc_eth_call_latest() { let tester = EthTester::default(); tester.client.set_execution_result(Ok(Executed { + exception: None, gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), @@ -599,6 +600,7 @@ fn rpc_eth_call_latest() { fn rpc_eth_call() { let tester = EthTester::default(); tester.client.set_execution_result(Ok(Executed { + exception: None, gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), @@ -634,6 +636,7 @@ fn rpc_eth_call() { fn rpc_eth_call_default_block() { let tester = EthTester::default(); tester.client.set_execution_result(Ok(Executed { + exception: None, gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), @@ -668,6 +671,7 @@ fn rpc_eth_call_default_block() { fn rpc_eth_estimate_gas() { let tester = EthTester::default(); tester.client.set_execution_result(Ok(Executed { + exception: None, gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), @@ -703,6 +707,7 @@ fn rpc_eth_estimate_gas() { fn rpc_eth_estimate_gas_default_block() { let tester = EthTester::default(); tester.client.set_execution_result(Ok(Executed { + exception: None, gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), diff --git a/rpc/src/v1/tests/mocked/traces.rs b/rpc/src/v1/tests/mocked/traces.rs index d12f8dbb5..864329336 100644 --- a/rpc/src/v1/tests/mocked/traces.rs +++ b/rpc/src/v1/tests/mocked/traces.rs @@ -51,6 +51,7 @@ fn io() -> Tester { block_hash: 10.into(), }]); *client.execution_result.write() = Some(Ok(Executed { + exception: None, gas: 20_000.into(), gas_used: 10_000.into(), refunded: 0.into(),