Trace other types of calls (#1727)

* Trace through DELEGATECALL and CALLCODE

Add them to the JSON output and RLP database store.

* Fix tests.

* Fix all tests.

* Fix one more test.
This commit is contained in:
Gav Wood 2016-07-27 17:41:21 +02:00 committed by GitHub
parent edda0b2380
commit ccb62d3b55
17 changed files with 228 additions and 62 deletions

View File

@ -17,6 +17,7 @@
//! Evm input params. //! Evm input params.
use common::*; use common::*;
use ethjson; use ethjson;
use types::executed::CallType;
/// Transaction value /// Transaction value
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -58,7 +59,10 @@ pub struct ActionParams {
/// Code being executed. /// Code being executed.
pub code: Option<Bytes>, pub code: Option<Bytes>,
/// Input data. /// Input data.
pub data: Option<Bytes> pub data: Option<Bytes>,
/// Type of call
pub call_type: CallType,
} }
impl Default for ActionParams { impl Default for ActionParams {
@ -73,16 +77,18 @@ impl Default for ActionParams {
gas_price: U256::zero(), gas_price: U256::zero(),
value: ActionValue::Transfer(U256::zero()), value: ActionValue::Transfer(U256::zero()),
code: None, code: None,
data: None data: None,
call_type: CallType::None,
} }
} }
} }
impl From<ethjson::vm::Transaction> for ActionParams { impl From<ethjson::vm::Transaction> for ActionParams {
fn from(t: ethjson::vm::Transaction) -> Self { fn from(t: ethjson::vm::Transaction) -> Self {
let address: Address = t.address.into();
ActionParams { ActionParams {
code_address: Address::new(), code_address: Address::new(),
address: t.address.into(), address: address,
sender: t.sender.into(), sender: t.sender.into(),
origin: t.origin.into(), origin: t.origin.into(),
code: Some(t.code.into()), code: Some(t.code.into()),
@ -90,6 +96,7 @@ impl From<ethjson::vm::Transaction> for ActionParams {
gas: t.gas.into(), gas: t.gas.into(),
gas_price: t.gas_price.into(), gas_price: t.gas_price.into(),
value: ActionValue::Transfer(t.value.into()), value: ActionValue::Transfer(t.value.into()),
call_type: match address.is_zero() { true => CallType::None, false => CallType::Call }, // TODO @debris is this correct?
} }
} }
} }

View File

@ -18,6 +18,7 @@
use util::common::*; use util::common::*;
use evm::{self, Schedule}; use evm::{self, Schedule};
use types::executed::CallType;
use env_info::*; use env_info::*;
/// Result of externalities create function. /// Result of externalities create function.
@ -69,13 +70,15 @@ pub trait Ext {
/// and true if subcall was successfull. /// and true if subcall was successfull.
#[cfg_attr(feature="dev", allow(too_many_arguments))] #[cfg_attr(feature="dev", allow(too_many_arguments))]
fn call(&mut self, fn call(&mut self,
gas: &U256, gas: &U256,
sender_address: &Address, sender_address: &Address,
receive_address: &Address, receive_address: &Address,
value: Option<U256>, value: Option<U256>,
data: &[u8], data: &[u8],
code_address: &Address, code_address: &Address,
output: &mut [u8]) -> MessageCallResult; output: &mut [u8],
call_type: CallType
) -> MessageCallResult;
/// Returns code at given address /// Returns code at given address
fn extcode(&self, address: &Address) -> Bytes; fn extcode(&self, address: &Address) -> Bytes;

View File

@ -38,6 +38,7 @@ use self::memory::Memory;
use std::marker::PhantomData; use std::marker::PhantomData;
use common::*; use common::*;
use types::executed::CallType;
use super::instructions::{self, Instruction, InstructionInfo}; use super::instructions::{self, Instruction, InstructionInfo};
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType}; use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType};
@ -311,16 +312,16 @@ impl<Cost: CostType> Interpreter<Cost> {
}); });
// Get sender & receive addresses, check if we have balance // 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 => { instructions::CALL => {
let has_balance = ext.balance(&params.address) >= value.unwrap(); let has_balance = ext.balance(&params.address) >= value.unwrap();
(&params.address, &code_address, has_balance) (&params.address, &code_address, has_balance, CallType::Call)
}, },
instructions::CALLCODE => { instructions::CALLCODE => {
let has_balance = ext.balance(&params.address) >= value.unwrap(); let has_balance = ext.balance(&params.address) >= value.unwrap();
(&params.address, &params.address, has_balance) (&params.address, &params.address, has_balance, CallType::CallCode)
}, },
instructions::DELEGATECALL => (&params.sender, &params.address, true), instructions::DELEGATECALL => (&params.sender, &params.address, true, CallType::DelegateCall),
_ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction)) _ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction))
}; };
@ -335,7 +336,7 @@ impl<Cost: CostType> Interpreter<Cost> {
// and we don't want to copy // and we don't want to copy
let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) }; let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) };
let output = self.mem.writeable_slice(out_off, out_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 { return match call_result {

View File

@ -15,6 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use common::*; use common::*;
use types::executed::CallType;
use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult}; use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult};
use std::fmt::Debug; use std::fmt::Debug;
@ -36,7 +37,7 @@ pub struct FakeCall {
receive_address: Option<Address>, receive_address: Option<Address>,
value: Option<U256>, value: Option<U256>,
data: Bytes, data: Bytes,
code_address: Option<Address> code_address: Option<Address>,
} }
/// Fake externalities test structure. /// Fake externalities test structure.
@ -119,7 +120,9 @@ impl Ext for FakeExt {
value: Option<U256>, value: Option<U256>,
data: &[u8], data: &[u8],
code_address: &Address, code_address: &Address,
_output: &mut [u8]) -> MessageCallResult { _output: &mut [u8],
_call_type: CallType
) -> MessageCallResult {
self.calls.insert(FakeCall { self.calls.insert(FakeCall {
call_type: FakeCallType::Call, call_type: FakeCallType::Call,

View File

@ -18,6 +18,7 @@
use common::*; use common::*;
use state::*; use state::*;
use engine::*; use engine::*;
use types::executed::CallType;
use evm::{self, Ext, Factory, Finalize}; use evm::{self, Ext, Factory, Finalize};
use externalities::*; use externalities::*;
use substate::*; use substate::*;
@ -173,6 +174,7 @@ impl<'a> Executive<'a> {
value: ActionValue::Transfer(t.value), value: ActionValue::Transfer(t.value),
code: Some(t.data.clone()), code: Some(t.data.clone()),
data: None, data: None,
call_type: CallType::None,
}; };
(self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![]) (self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![])
}, },
@ -187,6 +189,7 @@ impl<'a> Executive<'a> {
value: ActionValue::Transfer(t.value), value: ActionValue::Transfer(t.value),
code: self.state.code(address), code: self.state.code(address),
data: Some(t.data.clone()), data: Some(t.data.clone()),
call_type: CallType::Call,
}; };
// TODO: move output upstream // TODO: move output upstream
let mut out = vec![]; let mut out = vec![];
@ -248,8 +251,6 @@ impl<'a> Executive<'a> {
} }
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info); trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
let delegate_call = params.code_address != params.address;
if self.engine.is_builtin(&params.code_address) { if self.engine.is_builtin(&params.code_address) {
// if destination is builtin, try to execute it // if destination is builtin, try to execute it
@ -275,8 +276,7 @@ impl<'a> Executive<'a> {
cost, cost,
trace_output, trace_output,
self.depth, self.depth,
vec![], vec![]
delegate_call
); );
} }
@ -285,7 +285,7 @@ impl<'a> Executive<'a> {
// just drain the whole gas // just drain the whole gas
self.state.revert_snapshot(); 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) Err(evm::Error::OutOfGas)
} }
@ -318,10 +318,9 @@ impl<'a> Executive<'a> {
gas - gas_left, gas - gas_left,
trace_output, trace_output,
self.depth, self.depth,
traces, traces
delegate_call
), ),
_ => 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); 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. // otherwise it's just a basic transaction, only do tracing, if necessary.
self.state.clear_snapshot(); 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) Ok(params.gas)
} }
} }
@ -495,6 +494,7 @@ mod tests {
use trace::trace; use trace::trace;
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer};
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer}; use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
use types::executed::CallType;
#[test] #[test]
fn test_contract_address() { fn test_contract_address() {
@ -628,6 +628,7 @@ mod tests {
params.gas = U256::from(100_000); params.gas = U256::from(100_000);
params.code = Some(code.clone()); params.code = Some(code.clone());
params.value = ActionValue::Transfer(U256::from(100)); params.value = ActionValue::Transfer(U256::from(100));
params.call_type = CallType::Call;
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100)); state.add_balance(&sender, &U256::from(100));
@ -653,6 +654,7 @@ mod tests {
value: 100.into(), value: 100.into(),
gas: 100000.into(), gas: 100000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(55_248), gas_used: U256::from(55_248),

View File

@ -21,6 +21,7 @@ use engine::*;
use executive::*; use executive::*;
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory}; use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
use substate::*; use substate::*;
use types::executed::CallType;
use trace::{Tracer, VMTracer}; use trace::{Tracer, VMTracer};
/// Policy for handling output data on `RETURN` opcode. /// 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), value: ActionValue::Transfer(*value),
code: Some(code.to_vec()), code: Some(code.to_vec()),
data: None, data: None,
call_type: CallType::None,
}; };
self.state.inc_nonce(&self.origin_info.address); 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<U256>, value: Option<U256>,
data: &[u8], data: &[u8],
code_address: &Address, code_address: &Address,
output: &mut [u8] output: &mut [u8],
call_type: CallType
) -> MessageCallResult { ) -> MessageCallResult {
trace!(target: "externalities", "call"); 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, gas_price: self.origin_info.gas_price,
code: self.state.code(code_address), code: self.state.code(code_address),
data: Some(data.to_vec()), data: Some(data.to_vec()),
call_type: call_type,
}; };
if let Some(value) = value { if let Some(value) = value {
@ -303,6 +307,7 @@ mod tests {
use devtools::GuardedTempResult; use devtools::GuardedTempResult;
use super::*; use super::*;
use trace::{NoopTracer, NoopVMTracer}; use trace::{NoopTracer, NoopVMTracer};
use types::executed::CallType;
fn get_test_origin() -> OriginInfo { fn get_test_origin() -> OriginInfo {
OriginInfo { OriginInfo {
@ -421,7 +426,9 @@ mod tests {
Some(U256::from_str("0000000000000000000000000000000000000000000000000000000000150000").unwrap()), Some(U256::from_str("0000000000000000000000000000000000000000000000000000000000150000").unwrap()),
&[], &[],
&Address::new(), &Address::new(),
&mut output); &mut output,
CallType::Call
);
} }
#[test] #[test]

View File

@ -22,6 +22,7 @@ use evm;
use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult}; use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult};
use externalities::*; use externalities::*;
use substate::*; use substate::*;
use types::executed::CallType;
use tests::helpers::*; use tests::helpers::*;
use ethjson; use ethjson;
use trace::{Tracer, NoopTracer}; 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, fn call(&mut self,
gas: &U256, gas: &U256,
_sender_address: &Address, _sender_address: &Address,
receive_address: &Address, receive_address: &Address,
value: Option<U256>, value: Option<U256>,
data: &[u8], data: &[u8],
_code_address: &Address, _code_address: &Address,
_output: &mut [u8]) -> MessageCallResult { _output: &mut [u8],
_call_type: CallType
) -> MessageCallResult {
self.callcreates.push(CallCreate { self.callcreates.push(CallCreate {
data: data.to_vec(), data: data.to_vec(),
destination: Some(receive_address.clone()), destination: Some(receive_address.clone()),

View File

@ -403,6 +403,7 @@ use transaction::*;
use util::log::init_log; use util::log::init_log;
use trace::trace; use trace::trace;
use trace::trace::{Trace}; use trace::trace::{Trace};
use types::executed::CallType;
#[test] #[test]
fn should_apply_create_transaction() { fn should_apply_create_transaction() {
@ -535,6 +536,7 @@ fn should_trace_call_transaction() {
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3), gas_used: U256::from(3),
@ -577,6 +579,7 @@ fn should_trace_basic_call_transaction() {
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(0), gas_used: U256::from(0),
@ -619,6 +622,7 @@ fn should_trace_call_transaction_to_builtin() {
value: 0.into(), value: 0.into(),
gas: 79_000.into(), gas: 79_000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3000), gas_used: U256::from(3000),
@ -660,6 +664,7 @@ fn should_not_trace_subcall_transaction_to_builtin() {
value: 0.into(), value: 0.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(28_061), gas_used: U256::from(28_061),
@ -703,12 +708,28 @@ fn should_not_trace_callcode() {
value: 0.into(), value: 0.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(64), gas_used: 64.into(),
output: vec![] 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); assert_eq!(result.trace, expected_trace);
} }
@ -749,12 +770,28 @@ fn should_not_trace_delegatecall() {
value: 0.into(), value: 0.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(61), gas_used: U256::from(61),
output: vec![] 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); assert_eq!(result.trace, expected_trace);
} }
@ -791,6 +828,7 @@ fn should_trace_failed_call_transaction() {
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::FailedCall, result: trace::Res::FailedCall,
subs: vec![] subs: vec![]
@ -834,6 +872,7 @@ fn should_trace_call_with_subcall_transaction() {
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(69), gas_used: U256::from(69),
@ -847,6 +886,7 @@ fn should_trace_call_with_subcall_transaction() {
value: 0.into(), value: 0.into(),
gas: 78934.into(), gas: 78934.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3), gas_used: U256::from(3),
@ -891,6 +931,7 @@ fn should_trace_call_with_basic_subcall_transaction() {
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(31761), gas_used: U256::from(31761),
@ -904,6 +945,7 @@ fn should_trace_call_with_basic_subcall_transaction() {
value: 69.into(), value: 69.into(),
gas: 2300.into(), gas: 2300.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult::default()), result: trace::Res::Call(trace::CallResult::default()),
subs: vec![] subs: vec![]
@ -945,6 +987,7 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() {
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(31761), gas_used: U256::from(31761),
@ -989,6 +1032,7 @@ fn should_trace_failed_subcall_transaction() {
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(79_000), gas_used: U256::from(79_000),
@ -1002,6 +1046,7 @@ fn should_trace_failed_subcall_transaction() {
value: 0.into(), value: 0.into(),
gas: 78934.into(), gas: 78934.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::FailedCall, result: trace::Res::FailedCall,
subs: vec![] subs: vec![]
@ -1045,6 +1090,7 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(135), gas_used: U256::from(135),
@ -1058,6 +1104,7 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
value: 0.into(), value: 0.into(),
gas: 78934.into(), gas: 78934.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(69), gas_used: U256::from(69),
@ -1071,6 +1118,7 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
value: 0.into(), value: 0.into(),
gas: 78868.into(), gas: 78868.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3), gas_used: U256::from(3),
@ -1118,6 +1166,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(79_000), gas_used: U256::from(79_000),
@ -1131,6 +1180,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
value: 0.into(), value: 0.into(),
gas: 78934.into(), gas: 78934.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::FailedCall, result: trace::Res::FailedCall,
subs: vec![Trace { subs: vec![Trace {
@ -1141,6 +1191,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
value: 0.into(), value: 0.into(),
gas: 78868.into(), gas: 78868.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(3), gas_used: U256::from(3),
@ -1187,6 +1238,7 @@ fn should_trace_suicide() {
value: 100.into(), value: 100.into(),
gas: 79000.into(), gas: 79000.into(),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: trace::Res::Call(trace::CallResult { result: trace::Res::Call(trace::CallResult {
gas_used: 3.into(), gas_used: 3.into(),

View File

@ -367,6 +367,7 @@ mod tests {
use trace::{Config, Switch, TraceDB, Database, DatabaseExtras, ImportRequest}; use trace::{Config, Switch, TraceDB, Database, DatabaseExtras, ImportRequest};
use trace::{BlockTraces, Trace, Filter, LocalizedTrace, AddressesFilter}; use trace::{BlockTraces, Trace, Filter, LocalizedTrace, AddressesFilter};
use trace::trace::{Call, Action, Res}; use trace::trace::{Call, Action, Res};
use types::executed::CallType;
struct NoopExtras; struct NoopExtras;
@ -492,6 +493,7 @@ mod tests {
value: U256::from(3), value: U256::from(3),
gas: U256::from(4), gas: U256::from(4),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: Res::FailedCall, result: Res::FailedCall,
subs: vec![], subs: vec![],
@ -511,6 +513,7 @@ mod tests {
value: U256::from(3), value: U256::from(3),
gas: U256::from(4), gas: U256::from(4),
input: vec![], input: vec![],
call_type: CallType::Call,
}), }),
result: Res::FailedCall, result: Res::FailedCall,
trace_address: vec![], trace_address: vec![],

View File

@ -40,12 +40,7 @@ impl Tracer for ExecutiveTracer {
Some(vec![]) Some(vec![])
} }
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs: Vec<Trace>, delegate_call: bool) { fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs: Vec<Trace>) {
// don't trace if it's DELEGATECALL or CALLCODE.
if delegate_call {
return;
}
let trace = Trace { let trace = Trace {
depth: depth, depth: depth,
subs: subs, subs: subs,
@ -72,12 +67,7 @@ impl Tracer for ExecutiveTracer {
self.traces.push(trace); self.traces.push(trace);
} }
fn trace_failed_call(&mut self, call: Option<Call>, depth: usize, subs: Vec<Trace>, delegate_call: bool) { fn trace_failed_call(&mut self, call: Option<Call>, depth: usize, subs: Vec<Trace>) {
// don't trace if it's DELEGATECALL or CALLCODE.
if delegate_call {
return;
}
let trace = Trace { let trace = Trace {
depth: depth, depth: depth,
subs: subs, subs: subs,

View File

@ -171,6 +171,7 @@ mod tests {
use util::{U256, Address}; use util::{U256, Address};
use trace::trace::{Action, Res, CallResult, Call, Create, Trace}; use trace::trace::{Action, Res, CallResult, Call, Create, Trace};
use trace::BlockTraces; use trace::BlockTraces;
use types::executed::CallType;
#[test] #[test]
fn test_block_from() { fn test_block_from() {
@ -181,7 +182,8 @@ mod tests {
to: Address::from(2), to: Address::from(2),
value: U256::from(3), value: U256::from(3),
gas: U256::from(4), gas: U256::from(4),
input: vec![0x5] input: vec![0x5],
call_type: CallType::Call,
}), }),
subs: vec![ subs: vec![
Trace { Trace {
@ -265,7 +267,8 @@ mod tests {
to: 2.into(), to: 2.into(),
value: 3.into(), value: 3.into(),
gas: 4.into(), gas: 4.into(),
input: vec![0x5] input: vec![0x5],
call_type: CallType::Call,
}), }),
result: Res::Call(CallResult { result: Res::Call(CallResult {
gas_used: 10.into(), gas_used: 10.into(),

View File

@ -60,8 +60,7 @@ pub trait Tracer: Send {
gas_used: U256, gas_used: U256,
output: Option<Bytes>, output: Option<Bytes>,
depth: usize, depth: usize,
subs: Vec<Trace>, subs: Vec<Trace>
delegate_call: bool
); );
/// Stores trace create info. /// Stores trace create info.
@ -76,7 +75,7 @@ pub trait Tracer: Send {
); );
/// Stores failed call trace. /// Stores failed call trace.
fn trace_failed_call(&mut self, call: Option<Call>, depth: usize, subs: Vec<Trace>, delegate_call: bool); fn trace_failed_call(&mut self, call: Option<Call>, depth: usize, subs: Vec<Trace>);
/// Stores failed create trace. /// Stores failed create trace.
fn trace_failed_create(&mut self, create: Option<Create>, depth: usize, subs: Vec<Trace>); fn trace_failed_create(&mut self, create: Option<Create>, depth: usize, subs: Vec<Trace>);

View File

@ -37,7 +37,7 @@ impl Tracer for NoopTracer {
None None
} }
fn trace_call(&mut self, call: Option<Call>, _: U256, output: Option<Bytes>, _: usize, _: Vec<Trace>, _: bool) { fn trace_call(&mut self, call: Option<Call>, _: U256, output: Option<Bytes>, _: usize, _: Vec<Trace>) {
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed"); 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"); 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"); 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<Call>, _: usize, _: Vec<Trace>, _: bool) { fn trace_failed_call(&mut self, call: Option<Call>, _: usize, _: Vec<Trace>) {
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed"); assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
} }

View File

@ -18,6 +18,7 @@
use util::numbers::*; use util::numbers::*;
use util::Bytes; use util::Bytes;
use util::rlp::*;
use trace::{Trace, VMTrace}; use trace::{Trace, VMTrace};
use types::log_entry::LogEntry; use types::log_entry::LogEntry;
use types::state_diff::StateDiff; use types::state_diff::StateDiff;
@ -26,6 +27,43 @@ use std::fmt;
use std::mem; use std::mem;
use std::collections::VecDeque; 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<D>(decoder: &D) -> Result<Self, DecoderError> 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. /// Transaction execution receipt.
#[derive(Debug, PartialEq, Clone, Binary)] #[derive(Debug, PartialEq, Clone, Binary)]
pub struct Executed { pub struct Executed {
@ -135,3 +173,12 @@ impl fmt::Display for ExecutionError {
/// Transaction execution result. /// Transaction execution result.
pub type ExecutionResult = Result<Executed, ExecutionError>; pub type ExecutionResult = Result<Executed, ExecutionError>;
#[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);
}

View File

@ -143,6 +143,7 @@ mod tests {
use trace::flat::FlatTrace; use trace::flat::FlatTrace;
use trace::{Filter, AddressesFilter}; use trace::{Filter, AddressesFilter};
use basic_types::LogBloom; use basic_types::LogBloom;
use types::executed::CallType;
#[test] #[test]
fn empty_trace_filter_bloom_possibilities() { fn empty_trace_filter_bloom_possibilities() {
@ -285,6 +286,7 @@ mod tests {
value: 3.into(), value: 3.into(),
gas: 4.into(), gas: 4.into(),
input: vec![0x5], input: vec![0x5],
call_type: CallType::Call,
}), }),
result: Res::FailedCall, result: Res::FailedCall,
trace_address: vec![0], trace_address: vec![0],

View File

@ -21,6 +21,7 @@ use util::rlp::*;
use util::sha3::Hashable; use util::sha3::Hashable;
use action_params::ActionParams; use action_params::ActionParams;
use basic_types::LogBloom; use basic_types::LogBloom;
use types::executed::CallType;
use ipc::binary::BinaryConvertError; use ipc::binary::BinaryConvertError;
use std::mem; use std::mem;
use std::collections::VecDeque; use std::collections::VecDeque;
@ -107,6 +108,8 @@ pub struct Call {
pub gas: U256, pub gas: U256,
/// The input data provided to the call. /// The input data provided to the call.
pub input: Bytes, pub input: Bytes,
/// The type of the call.
pub call_type: CallType,
} }
impl From<ActionParams> for Call { impl From<ActionParams> for Call {
@ -117,18 +120,20 @@ impl From<ActionParams> for Call {
value: p.value.value(), value: p.value.value(),
gas: p.gas, gas: p.gas,
input: p.data.unwrap_or_else(Vec::new), input: p.data.unwrap_or_else(Vec::new),
call_type: p.call_type,
} }
} }
} }
impl Encodable for Call { impl Encodable for Call {
fn rlp_append(&self, s: &mut RlpStream) { fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(5); s.begin_list(6);
s.append(&self.from); s.append(&self.from);
s.append(&self.to); s.append(&self.to);
s.append(&self.value); s.append(&self.value);
s.append(&self.gas); s.append(&self.gas);
s.append(&self.input); s.append(&self.input);
s.append(&self.call_type);
} }
} }
@ -141,6 +146,7 @@ impl Decodable for Call {
value: try!(d.val_at(2)), value: try!(d.val_at(2)),
gas: try!(d.val_at(3)), gas: try!(d.val_at(3)),
input: try!(d.val_at(4)), input: try!(d.val_at(4)),
call_type: try!(d.val_at(5)),
}; };
Ok(res) Ok(res)
@ -593,6 +599,7 @@ mod tests {
use util::rlp::{encode, decode}; use util::rlp::{encode, decode};
use util::sha3::Hashable; use util::sha3::Hashable;
use trace::trace::{Call, CallResult, Create, Res, Action, Trace, Suicide, CreateResult}; use trace::trace::{Call, CallResult, Create, Res, Action, Trace, Suicide, CreateResult};
use types::executed::CallType;
#[test] #[test]
fn traces_rlp() { fn traces_rlp() {
@ -603,7 +610,8 @@ mod tests {
to: Address::from(2), to: Address::from(2),
value: U256::from(3), value: U256::from(3),
gas: U256::from(4), gas: U256::from(4),
input: vec![0x5] input: vec![0x5],
call_type: CallType::Call,
}), }),
subs: vec![ subs: vec![
Trace { Trace {
@ -638,7 +646,8 @@ mod tests {
to: Address::from(2), to: Address::from(2),
value: U256::from(3), value: U256::from(3),
gas: U256::from(4), gas: U256::from(4),
input: vec![0x5] input: vec![0x5],
call_type: CallType::Call,
}), }),
subs: vec![ subs: vec![
Trace { Trace {

View File

@ -21,6 +21,7 @@ use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace};
use ethcore::trace as et; use ethcore::trace as et;
use ethcore::state_diff; use ethcore::state_diff;
use ethcore::account_diff; use ethcore::account_diff;
use ethcore::executed;
use ethcore::client::Executed; use ethcore::client::Executed;
use util::Uint; use util::Uint;
use v1::types::{Bytes, H160, H256, U256}; use v1::types::{Bytes, H160, H256, U256};
@ -235,6 +236,34 @@ impl From<trace::Create> 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<executed::CallType> 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 /// Call response
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct Call { pub struct Call {
@ -248,6 +277,9 @@ pub struct Call {
gas: U256, gas: U256,
/// Input data /// Input data
input: Bytes, input: Bytes,
/// The type of the call.
#[serde(rename="callType")]
call_type: CallType,
} }
impl From<trace::Call> for Call { impl From<trace::Call> for Call {
@ -258,6 +290,7 @@ impl From<trace::Call> for Call {
value: c.value.into(), value: c.value.into(),
gas: c.gas.into(), gas: c.gas.into(),
input: c.input.into(), input: c.input.into(),
call_type: c.call_type.into(),
} }
} }
} }
@ -500,6 +533,7 @@ mod tests {
value: U256::from(6), value: U256::from(6),
gas: U256::from(7), gas: U256::from(7),
input: Bytes::new(vec![0x12, 0x34]), input: Bytes::new(vec![0x12, 0x34]),
call_type: CallType::Call,
}), }),
result: Res::Call(CallResult { result: Res::Call(CallResult {
gas_used: U256::from(8), gas_used: U256::from(8),
@ -513,7 +547,7 @@ mod tests {
block_hash: H256::from(14), block_hash: H256::from(14),
}; };
let serialized = serde_json::to_string(&t).unwrap(); 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] #[test]
@ -589,6 +623,7 @@ mod tests {
value: U256::from(3), value: U256::from(3),
gas: U256::from(4), gas: U256::from(4),
input: vec![0x12, 0x34].into(), input: vec![0x12, 0x34].into(),
call_type: CallType::Call,
}), Action::Create(Create { }), Action::Create(Create {
from: H160::from(5), from: H160::from(5),
value: U256::from(6), value: U256::from(6),
@ -597,7 +632,7 @@ mod tests {
})]; })];
let serialized = serde_json::to_string(&actions).unwrap(); 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] #[test]