diff --git a/ethcore/src/action_params.rs b/ethcore/src/action_params.rs index 57100b2c5..1886c3d36 100644 --- a/ethcore/src/action_params.rs +++ b/ethcore/src/action_params.rs @@ -17,6 +17,7 @@ //! Evm input params. use common::*; use ethjson; +use types::executed::CallType; /// Transaction value #[derive(Clone, Debug)] @@ -58,7 +59,10 @@ pub struct ActionParams { /// Code being executed. pub code: Option, /// Input data. - pub data: Option + pub data: Option, + /// Type of call + pub call_type: CallType, + } impl Default for ActionParams { @@ -73,16 +77,18 @@ impl Default for ActionParams { gas_price: U256::zero(), value: ActionValue::Transfer(U256::zero()), code: None, - data: None + data: None, + call_type: CallType::None, } } } impl From for ActionParams { fn from(t: ethjson::vm::Transaction) -> Self { + let address: Address = t.address.into(); ActionParams { code_address: Address::new(), - address: t.address.into(), + address: address, sender: t.sender.into(), origin: t.origin.into(), code: Some(t.code.into()), @@ -90,6 +96,7 @@ impl From for ActionParams { gas: t.gas.into(), gas_price: t.gas_price.into(), value: ActionValue::Transfer(t.value.into()), + call_type: match address.is_zero() { true => CallType::None, false => CallType::Call }, // TODO @debris is this correct? } } } diff --git a/ethcore/src/evm/ext.rs b/ethcore/src/evm/ext.rs index 0aaa4dac6..ffc1887de 100644 --- a/ethcore/src/evm/ext.rs +++ b/ethcore/src/evm/ext.rs @@ -18,6 +18,7 @@ use util::common::*; use evm::{self, Schedule}; +use types::executed::CallType; use env_info::*; /// Result of externalities create function. @@ -69,13 +70,15 @@ pub trait Ext { /// and true if subcall was successfull. #[cfg_attr(feature="dev", allow(too_many_arguments))] fn call(&mut self, - gas: &U256, - sender_address: &Address, - receive_address: &Address, - value: Option, - data: &[u8], - code_address: &Address, - output: &mut [u8]) -> MessageCallResult; + gas: &U256, + sender_address: &Address, + receive_address: &Address, + value: Option, + data: &[u8], + code_address: &Address, + output: &mut [u8], + call_type: CallType + ) -> MessageCallResult; /// Returns code at given address fn extcode(&self, address: &Address) -> Bytes; diff --git a/ethcore/src/evm/interpreter/mod.rs b/ethcore/src/evm/interpreter/mod.rs index cf715a378..84e416c15 100644 --- a/ethcore/src/evm/interpreter/mod.rs +++ b/ethcore/src/evm/interpreter/mod.rs @@ -38,6 +38,7 @@ use self::memory::Memory; use std::marker::PhantomData; use common::*; +use types::executed::CallType; use super::instructions::{self, Instruction, InstructionInfo}; use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType}; @@ -311,16 +312,16 @@ impl Interpreter { }); // Get sender & receive addresses, check if we have balance - let (sender_address, receive_address, has_balance) = match instruction { + let (sender_address, receive_address, has_balance, call_type) = match instruction { instructions::CALL => { let has_balance = ext.balance(¶ms.address) >= value.unwrap(); - (¶ms.address, &code_address, has_balance) + (¶ms.address, &code_address, has_balance, CallType::Call) }, instructions::CALLCODE => { let has_balance = ext.balance(¶ms.address) >= value.unwrap(); - (¶ms.address, ¶ms.address, has_balance) + (¶ms.address, ¶ms.address, has_balance, CallType::CallCode) }, - instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true), + instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true, CallType::DelegateCall), _ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction)) }; @@ -335,7 +336,7 @@ impl Interpreter { // and we don't want to copy let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) }; let output = self.mem.writeable_slice(out_off, out_size); - ext.call(&call_gas.as_u256(), sender_address, receive_address, value, input, &code_address, output) + ext.call(&call_gas.as_u256(), sender_address, receive_address, value, input, &code_address, output, call_type) }; return match call_result { diff --git a/ethcore/src/evm/tests.rs b/ethcore/src/evm/tests.rs index 573ffd3b4..bdb1f1ddb 100644 --- a/ethcore/src/evm/tests.rs +++ b/ethcore/src/evm/tests.rs @@ -15,6 +15,7 @@ // along with Parity. If not, see . use common::*; +use types::executed::CallType; use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult}; use std::fmt::Debug; @@ -36,7 +37,7 @@ pub struct FakeCall { receive_address: Option
, value: Option, data: Bytes, - code_address: Option
+ code_address: Option
, } /// Fake externalities test structure. @@ -119,7 +120,9 @@ impl Ext for FakeExt { value: Option, data: &[u8], code_address: &Address, - _output: &mut [u8]) -> MessageCallResult { + _output: &mut [u8], + _call_type: CallType + ) -> MessageCallResult { self.calls.insert(FakeCall { call_type: FakeCallType::Call, diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 240ecf3c6..811087616 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -18,6 +18,7 @@ use common::*; use state::*; use engine::*; +use types::executed::CallType; use evm::{self, Ext, Factory, Finalize}; use externalities::*; use substate::*; @@ -173,6 +174,7 @@ impl<'a> Executive<'a> { value: ActionValue::Transfer(t.value), code: Some(t.data.clone()), data: None, + call_type: CallType::None, }; (self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![]) }, @@ -187,6 +189,7 @@ impl<'a> Executive<'a> { value: ActionValue::Transfer(t.value), code: self.state.code(address), data: Some(t.data.clone()), + call_type: CallType::Call, }; // TODO: move output upstream let mut out = vec![]; @@ -248,8 +251,6 @@ impl<'a> Executive<'a> { } trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info); - let delegate_call = params.code_address != params.address; - if self.engine.is_builtin(¶ms.code_address) { // if destination is builtin, try to execute it @@ -275,8 +276,7 @@ impl<'a> Executive<'a> { cost, trace_output, self.depth, - vec![], - delegate_call + vec![] ); } @@ -285,7 +285,7 @@ impl<'a> Executive<'a> { // just drain the whole gas self.state.revert_snapshot(); - tracer.trace_failed_call(trace_info, self.depth, vec![], delegate_call); + tracer.trace_failed_call(trace_info, self.depth, vec![]); Err(evm::Error::OutOfGas) } @@ -318,10 +318,9 @@ impl<'a> Executive<'a> { gas - gas_left, trace_output, self.depth, - traces, - delegate_call + traces ), - _ => tracer.trace_failed_call(trace_info, self.depth, traces, delegate_call), + _ => tracer.trace_failed_call(trace_info, self.depth, traces), }; trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate); @@ -333,7 +332,7 @@ impl<'a> Executive<'a> { // otherwise it's just a basic transaction, only do tracing, if necessary. self.state.clear_snapshot(); - tracer.trace_call(trace_info, U256::zero(), trace_output, self.depth, vec![], delegate_call); + tracer.trace_call(trace_info, U256::zero(), trace_output, self.depth, vec![]); Ok(params.gas) } } @@ -495,6 +494,7 @@ mod tests { use trace::trace; use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer}; + use types::executed::CallType; #[test] fn test_contract_address() { @@ -628,6 +628,7 @@ mod tests { params.gas = U256::from(100_000); params.code = Some(code.clone()); params.value = ActionValue::Transfer(U256::from(100)); + params.call_type = CallType::Call; let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); state.add_balance(&sender, &U256::from(100)); @@ -653,6 +654,7 @@ mod tests { value: 100.into(), gas: 100000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(55_248), diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 74bceca98..881114e4c 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -21,6 +21,7 @@ use engine::*; use executive::*; use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory}; use substate::*; +use types::executed::CallType; use trace::{Tracer, VMTracer}; /// Policy for handling output data on `RETURN` opcode. @@ -148,6 +149,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT value: ActionValue::Transfer(*value), code: Some(code.to_vec()), data: None, + call_type: CallType::None, }; self.state.inc_nonce(&self.origin_info.address); @@ -170,7 +172,8 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT value: Option, data: &[u8], code_address: &Address, - output: &mut [u8] + output: &mut [u8], + call_type: CallType ) -> MessageCallResult { trace!(target: "externalities", "call"); @@ -184,6 +187,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT gas_price: self.origin_info.gas_price, code: self.state.code(code_address), data: Some(data.to_vec()), + call_type: call_type, }; if let Some(value) = value { @@ -303,6 +307,7 @@ mod tests { use devtools::GuardedTempResult; use super::*; use trace::{NoopTracer, NoopVMTracer}; + use types::executed::CallType; fn get_test_origin() -> OriginInfo { OriginInfo { @@ -421,7 +426,9 @@ mod tests { Some(U256::from_str("0000000000000000000000000000000000000000000000000000000000150000").unwrap()), &[], &Address::new(), - &mut output); + &mut output, + CallType::Call + ); } #[test] diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 28dde2a38..dd6573a37 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -22,6 +22,7 @@ use evm; use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult}; use externalities::*; use substate::*; +use types::executed::CallType; use tests::helpers::*; use ethjson; use trace::{Tracer, NoopTracer}; @@ -109,13 +110,15 @@ impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer { } fn call(&mut self, - gas: &U256, - _sender_address: &Address, - receive_address: &Address, - value: Option, - data: &[u8], - _code_address: &Address, - _output: &mut [u8]) -> MessageCallResult { + gas: &U256, + _sender_address: &Address, + receive_address: &Address, + value: Option, + data: &[u8], + _code_address: &Address, + _output: &mut [u8], + _call_type: CallType + ) -> MessageCallResult { self.callcreates.push(CallCreate { data: data.to_vec(), destination: Some(receive_address.clone()), diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index ca19101d8..82a3682d5 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -403,6 +403,7 @@ use transaction::*; use util::log::init_log; use trace::trace; use trace::trace::{Trace}; +use types::executed::CallType; #[test] fn should_apply_create_transaction() { @@ -535,6 +536,7 @@ fn should_trace_call_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3), @@ -577,6 +579,7 @@ fn should_trace_basic_call_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(0), @@ -619,6 +622,7 @@ fn should_trace_call_transaction_to_builtin() { value: 0.into(), gas: 79_000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3000), @@ -660,6 +664,7 @@ fn should_not_trace_subcall_transaction_to_builtin() { value: 0.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(28_061), @@ -703,12 +708,28 @@ fn should_not_trace_callcode() { value: 0.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(64), + gas_used: 64.into(), output: vec![] }), - subs: vec![] + subs: vec![Trace { + depth: 1, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xa.into(), + value: 0.into(), + gas: 4096.into(), + input: vec![], + call_type: CallType::CallCode, + }), + subs: vec![], + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }], }); assert_eq!(result.trace, expected_trace); } @@ -749,12 +770,28 @@ fn should_not_trace_delegatecall() { value: 0.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(61), output: vec![] }), - subs: vec![] + subs: vec![Trace { + depth: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 32768.into(), + input: vec![], + call_type: CallType::DelegateCall, + }), + subs: vec![], + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }], }); assert_eq!(result.trace, expected_trace); } @@ -791,6 +828,7 @@ fn should_trace_failed_call_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::FailedCall, subs: vec![] @@ -834,6 +872,7 @@ fn should_trace_call_with_subcall_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(69), @@ -847,6 +886,7 @@ fn should_trace_call_with_subcall_transaction() { value: 0.into(), gas: 78934.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3), @@ -891,6 +931,7 @@ fn should_trace_call_with_basic_subcall_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(31761), @@ -904,6 +945,7 @@ fn should_trace_call_with_basic_subcall_transaction() { value: 69.into(), gas: 2300.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult::default()), subs: vec![] @@ -945,6 +987,7 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(31761), @@ -989,6 +1032,7 @@ fn should_trace_failed_subcall_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(79_000), @@ -1002,6 +1046,7 @@ fn should_trace_failed_subcall_transaction() { value: 0.into(), gas: 78934.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::FailedCall, subs: vec![] @@ -1045,6 +1090,7 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(135), @@ -1058,6 +1104,7 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { value: 0.into(), gas: 78934.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(69), @@ -1071,6 +1118,7 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { value: 0.into(), gas: 78868.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3), @@ -1118,6 +1166,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(79_000), @@ -1131,6 +1180,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() { value: 0.into(), gas: 78934.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::FailedCall, subs: vec![Trace { @@ -1141,6 +1191,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() { value: 0.into(), gas: 78868.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3), @@ -1187,6 +1238,7 @@ fn should_trace_suicide() { value: 100.into(), gas: 79000.into(), input: vec![], + call_type: CallType::Call, }), result: trace::Res::Call(trace::CallResult { gas_used: 3.into(), diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index edd7dd189..91c9da444 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -367,6 +367,7 @@ mod tests { use trace::{Config, Switch, TraceDB, Database, DatabaseExtras, ImportRequest}; use trace::{BlockTraces, Trace, Filter, LocalizedTrace, AddressesFilter}; use trace::trace::{Call, Action, Res}; + use types::executed::CallType; struct NoopExtras; @@ -492,6 +493,7 @@ mod tests { value: U256::from(3), gas: U256::from(4), input: vec![], + call_type: CallType::Call, }), result: Res::FailedCall, subs: vec![], @@ -511,6 +513,7 @@ mod tests { value: U256::from(3), gas: U256::from(4), input: vec![], + call_type: CallType::Call, }), result: Res::FailedCall, trace_address: vec![], diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index af8183c0a..83761ea23 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -40,12 +40,7 @@ impl Tracer for ExecutiveTracer { Some(vec![]) } - fn trace_call(&mut self, call: Option, gas_used: U256, output: Option, depth: usize, subs: Vec, delegate_call: bool) { - // don't trace if it's DELEGATECALL or CALLCODE. - if delegate_call { - return; - } - + fn trace_call(&mut self, call: Option, gas_used: U256, output: Option, depth: usize, subs: Vec) { let trace = Trace { depth: depth, subs: subs, @@ -72,12 +67,7 @@ impl Tracer for ExecutiveTracer { self.traces.push(trace); } - fn trace_failed_call(&mut self, call: Option, depth: usize, subs: Vec, delegate_call: bool) { - // don't trace if it's DELEGATECALL or CALLCODE. - if delegate_call { - return; - } - + fn trace_failed_call(&mut self, call: Option, depth: usize, subs: Vec) { let trace = Trace { depth: depth, subs: subs, diff --git a/ethcore/src/trace/flat.rs b/ethcore/src/trace/flat.rs index 1e39e940c..914fbaad6 100644 --- a/ethcore/src/trace/flat.rs +++ b/ethcore/src/trace/flat.rs @@ -171,6 +171,7 @@ mod tests { use util::{U256, Address}; use trace::trace::{Action, Res, CallResult, Call, Create, Trace}; use trace::BlockTraces; + use types::executed::CallType; #[test] fn test_block_from() { @@ -181,7 +182,8 @@ mod tests { to: Address::from(2), value: U256::from(3), gas: U256::from(4), - input: vec![0x5] + input: vec![0x5], + call_type: CallType::Call, }), subs: vec![ Trace { @@ -265,7 +267,8 @@ mod tests { to: 2.into(), value: 3.into(), gas: 4.into(), - input: vec![0x5] + input: vec![0x5], + call_type: CallType::Call, }), result: Res::Call(CallResult { gas_used: 10.into(), diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 67fec2b97..0e4d9f626 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -60,8 +60,7 @@ pub trait Tracer: Send { gas_used: U256, output: Option, depth: usize, - subs: Vec, - delegate_call: bool + subs: Vec ); /// Stores trace create info. @@ -76,7 +75,7 @@ pub trait Tracer: Send { ); /// Stores failed call trace. - fn trace_failed_call(&mut self, call: Option, depth: usize, subs: Vec, delegate_call: bool); + fn trace_failed_call(&mut self, call: Option, depth: usize, subs: Vec); /// Stores failed create trace. fn trace_failed_create(&mut self, create: Option, depth: usize, subs: Vec); diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index 290fb2367..0a3ec1c97 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -37,7 +37,7 @@ impl Tracer for NoopTracer { None } - fn trace_call(&mut self, call: Option, _: U256, output: Option, _: usize, _: Vec, _: bool) { + fn trace_call(&mut self, call: Option, _: U256, output: Option, _: usize, _: Vec) { assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed"); assert!(output.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed"); } @@ -47,7 +47,7 @@ impl Tracer for NoopTracer { assert!(code.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed"); } - fn trace_failed_call(&mut self, call: Option, _: usize, _: Vec, _: bool) { + fn trace_failed_call(&mut self, call: Option, _: usize, _: Vec) { assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed"); } diff --git a/ethcore/src/types/executed.rs b/ethcore/src/types/executed.rs index 293a427f7..977c2ac04 100644 --- a/ethcore/src/types/executed.rs +++ b/ethcore/src/types/executed.rs @@ -18,6 +18,7 @@ use util::numbers::*; use util::Bytes; +use util::rlp::*; use trace::{Trace, VMTrace}; use types::log_entry::LogEntry; use types::state_diff::StateDiff; @@ -26,6 +27,43 @@ use std::fmt; use std::mem; use std::collections::VecDeque; +/// The type of the call-like instruction. +#[derive(Debug, PartialEq, Clone, Binary)] +pub enum CallType { + /// Not a CALL. + None, + /// CALL. + Call, + /// CALLCODE. + CallCode, + /// DELEGATECALL. + DelegateCall, +} + +impl Encodable for CallType { + fn rlp_append(&self, s: &mut RlpStream) { + let v = match *self { + CallType::None => 0u32, + CallType::Call => 1, + CallType::CallCode => 2, + CallType::DelegateCall => 3, + }; + s.append(&v); + } +} + +impl Decodable for CallType { + fn decode(decoder: &D) -> Result where D: Decoder { + decoder.as_rlp().as_val().and_then(|v| Ok(match v { + 0u32 => CallType::None, + 1 => CallType::Call, + 2 => CallType::CallCode, + 3 => CallType::DelegateCall, + _ => return Err(DecoderError::Custom("Invalid value of CallType item")), + })) + } +} + /// Transaction execution receipt. #[derive(Debug, PartialEq, Clone, Binary)] pub struct Executed { @@ -135,3 +173,12 @@ impl fmt::Display for ExecutionError { /// Transaction execution result. pub type ExecutionResult = Result; + +#[test] +fn should_encode_and_decode_call_type() { + use util::rlp; + let original = CallType::Call; + let encoded = rlp::encode(&original); + let decoded = rlp::decode(&encoded); + assert_eq!(original, decoded); +} diff --git a/ethcore/src/types/trace_types/filter.rs b/ethcore/src/types/trace_types/filter.rs index 481cdf274..4344d1500 100644 --- a/ethcore/src/types/trace_types/filter.rs +++ b/ethcore/src/types/trace_types/filter.rs @@ -143,6 +143,7 @@ mod tests { use trace::flat::FlatTrace; use trace::{Filter, AddressesFilter}; use basic_types::LogBloom; + use types::executed::CallType; #[test] fn empty_trace_filter_bloom_possibilities() { @@ -285,6 +286,7 @@ mod tests { value: 3.into(), gas: 4.into(), input: vec![0x5], + call_type: CallType::Call, }), result: Res::FailedCall, trace_address: vec![0], diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index 2cff2240c..3736f2673 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -21,6 +21,7 @@ use util::rlp::*; use util::sha3::Hashable; use action_params::ActionParams; use basic_types::LogBloom; +use types::executed::CallType; use ipc::binary::BinaryConvertError; use std::mem; use std::collections::VecDeque; @@ -107,6 +108,8 @@ pub struct Call { pub gas: U256, /// The input data provided to the call. pub input: Bytes, + /// The type of the call. + pub call_type: CallType, } impl From for Call { @@ -117,18 +120,20 @@ impl From for Call { value: p.value.value(), gas: p.gas, input: p.data.unwrap_or_else(Vec::new), + call_type: p.call_type, } } } impl Encodable for Call { fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(5); + s.begin_list(6); s.append(&self.from); s.append(&self.to); s.append(&self.value); s.append(&self.gas); s.append(&self.input); + s.append(&self.call_type); } } @@ -141,6 +146,7 @@ impl Decodable for Call { value: try!(d.val_at(2)), gas: try!(d.val_at(3)), input: try!(d.val_at(4)), + call_type: try!(d.val_at(5)), }; Ok(res) @@ -593,6 +599,7 @@ mod tests { use util::rlp::{encode, decode}; use util::sha3::Hashable; use trace::trace::{Call, CallResult, Create, Res, Action, Trace, Suicide, CreateResult}; + use types::executed::CallType; #[test] fn traces_rlp() { @@ -603,7 +610,8 @@ mod tests { to: Address::from(2), value: U256::from(3), gas: U256::from(4), - input: vec![0x5] + input: vec![0x5], + call_type: CallType::Call, }), subs: vec![ Trace { @@ -638,7 +646,8 @@ mod tests { to: Address::from(2), value: U256::from(3), gas: U256::from(4), - input: vec![0x5] + input: vec![0x5], + call_type: CallType::Call, }), subs: vec![ Trace { diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index e32fb0e57..fd8b60720 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -21,6 +21,7 @@ use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace}; use ethcore::trace as et; use ethcore::state_diff; use ethcore::account_diff; +use ethcore::executed; use ethcore::client::Executed; use util::Uint; use v1::types::{Bytes, H160, H256, U256}; @@ -235,6 +236,34 @@ impl From for Create { } } +/// Call type. +#[derive(Debug, Serialize)] +pub enum CallType { + /// None + #[serde(rename="none")] + None, + /// Call + #[serde(rename="call")] + Call, + /// Call code + #[serde(rename="callcode")] + CallCode, + /// Delegate call + #[serde(rename="delegatecall")] + DelegateCall, +} + +impl From for CallType { + fn from(c: executed::CallType) -> Self { + match c { + executed::CallType::None => CallType::None, + executed::CallType::Call => CallType::Call, + executed::CallType::CallCode => CallType::CallCode, + executed::CallType::DelegateCall => CallType::DelegateCall, + } + } +} + /// Call response #[derive(Debug, Serialize)] pub struct Call { @@ -248,6 +277,9 @@ pub struct Call { gas: U256, /// Input data input: Bytes, + /// The type of the call. + #[serde(rename="callType")] + call_type: CallType, } impl From for Call { @@ -258,6 +290,7 @@ impl From for Call { value: c.value.into(), gas: c.gas.into(), input: c.input.into(), + call_type: c.call_type.into(), } } } @@ -500,6 +533,7 @@ mod tests { value: U256::from(6), gas: U256::from(7), input: Bytes::new(vec![0x12, 0x34]), + call_type: CallType::Call, }), result: Res::Call(CallResult { gas_used: U256::from(8), @@ -513,7 +547,7 @@ mod tests { block_hash: H256::from(14), }; let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","input":"0x1234"}},"result":{"call":{"gasUsed":"0x08","output":"0x5678"}},"traceAddress":["0x0a"],"subtraces":"0x01","transactionPosition":"0x0b","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0x0d","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); + assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","input":"0x1234","callType":{"call":[]}}},"result":{"call":{"gasUsed":"0x08","output":"0x5678"}},"traceAddress":["0x0a"],"subtraces":"0x01","transactionPosition":"0x0b","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0x0d","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); } #[test] @@ -589,6 +623,7 @@ mod tests { value: U256::from(3), gas: U256::from(4), input: vec![0x12, 0x34].into(), + call_type: CallType::Call, }), Action::Create(Create { from: H160::from(5), value: U256::from(6), @@ -597,7 +632,7 @@ mod tests { })]; let serialized = serde_json::to_string(&actions).unwrap(); - assert_eq!(serialized, r#"[{"call":{"from":"0x0000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000002","value":"0x03","gas":"0x04","input":"0x1234"}},{"create":{"from":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","init":"0x5678"}}]"#); + assert_eq!(serialized, r#"[{"call":{"from":"0x0000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000002","value":"0x03","gas":"0x04","input":"0x1234","callType":{"call":[]}}},{"create":{"from":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","init":"0x5678"}}]"#); } #[test]