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.
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<Bytes>,
/// Input data.
pub data: Option<Bytes>
pub data: Option<Bytes>,
/// 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<ethjson::vm::Transaction> 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<ethjson::vm::Transaction> 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?
}
}
}

View File

@ -18,6 +18,7 @@
use util::common::*;
use evm::{self, Schedule};
use types::executed::CallType;
use env_info::*;
/// Result of externalities create function.
@ -75,7 +76,9 @@ pub trait Ext {
value: Option<U256>,
data: &[u8],
code_address: &Address,
output: &mut [u8]) -> MessageCallResult;
output: &mut [u8],
call_type: CallType
) -> MessageCallResult;
/// Returns code at given address
fn extcode(&self, address: &Address) -> Bytes;

View File

@ -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<Cost: CostType> Interpreter<Cost> {
});
// 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(&params.address) >= value.unwrap();
(&params.address, &code_address, has_balance)
(&params.address, &code_address, has_balance, CallType::Call)
},
instructions::CALLCODE => {
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))
};
@ -335,7 +336,7 @@ impl<Cost: CostType> Interpreter<Cost> {
// 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 {

View File

@ -15,6 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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<Address>,
value: Option<U256>,
data: Bytes,
code_address: Option<Address>
code_address: Option<Address>,
}
/// Fake externalities test structure.
@ -119,7 +120,9 @@ impl Ext for FakeExt {
value: Option<U256>,
data: &[u8],
code_address: &Address,
_output: &mut [u8]) -> MessageCallResult {
_output: &mut [u8],
_call_type: CallType
) -> MessageCallResult {
self.calls.insert(FakeCall {
call_type: FakeCallType::Call,

View File

@ -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(&params.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),

View File

@ -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<U256>,
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]

View File

@ -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};
@ -115,7 +116,9 @@ impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer {
value: Option<U256>,
data: &[u8],
_code_address: &Address,
_output: &mut [u8]) -> MessageCallResult {
_output: &mut [u8],
_call_type: CallType
) -> MessageCallResult {
self.callcreates.push(CallCreate {
data: data.to_vec(),
destination: Some(receive_address.clone()),

View File

@ -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(),

View File

@ -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![],

View File

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

View File

@ -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(),

View File

@ -60,8 +60,7 @@ pub trait Tracer: Send {
gas_used: U256,
output: Option<Bytes>,
depth: usize,
subs: Vec<Trace>,
delegate_call: bool
subs: Vec<Trace>
);
/// Stores trace create info.
@ -76,7 +75,7 @@ pub trait Tracer: Send {
);
/// 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.
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
}
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!(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<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");
}

View File

@ -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<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.
#[derive(Debug, PartialEq, Clone, Binary)]
pub struct Executed {
@ -135,3 +173,12 @@ impl fmt::Display for ExecutionError {
/// Transaction execution result.
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::{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],

View File

@ -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<ActionParams> for Call {
@ -117,18 +120,20 @@ impl From<ActionParams> 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 {

View File

@ -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<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
#[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<trace::Call> for Call {
@ -258,6 +290,7 @@ impl From<trace::Call> 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]