From a132fefcc7d3d99556aec7ec42ff4b471db4ee1b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 2 Jun 2016 13:50:50 +0200 Subject: [PATCH 1/4] Transaction tracing for eth_call. --- ethcore/src/client/client.rs | 2 +- ethcore/src/client/mod.rs | 2 ++ ethcore/src/miner/miner.rs | 2 +- rpc/src/v1/impls/traces.rs | 29 ++++++++++++++++++++++------- rpc/src/v1/traits/traces.rs | 6 +++++- rpc/src/v1/types/mod.rs.in | 2 +- rpc/src/v1/types/trace.rs | 36 ++++++++++++++++++++++++++++++------ 7 files changed, 62 insertions(+), 17 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 6f0e5b874..da1672812 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -461,7 +461,7 @@ impl BlockChainClient for Client where V: Verifier { // give the sender a sufficient balance state.add_balance(&sender, &(needed_balance - balance)); } - let options = TransactOptions { tracing: false, vm_tracing: analytics.vm_tracing, check_nonce: false }; + let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; let mut ret = Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options); // TODO gav move this into Executive. if analytics.state_diffing { diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 0dffb1a1c..947da4904 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -52,6 +52,8 @@ use error::Error as EthError; /// Options concerning what analytics we run on the call. #[derive(Eq, PartialEq, Default, Clone, Copy, Debug)] pub struct CallAnalytics { + /// Make a transaction trace. + pub transaction_tracing: bool, /// Make a VM trace. pub vm_tracing: bool, /// Make a diff. diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 18a253ea9..4abef5554 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -276,7 +276,7 @@ impl MinerService for Miner { // give the sender max balance state.sub_balance(&sender, &balance); state.add_balance(&sender, &U256::max_value()); - let options = TransactOptions { tracing: false, vm_tracing: analytics.vm_tracing, check_nonce: false }; + let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; let mut ret = Executive::new(&mut state, &env_info, self.engine(), chain.vm_factory()).transact(t, options); // TODO gav move this into Executive. diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index b6015d498..8a1627836 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -28,7 +28,7 @@ use ethcore::state_diff::StateDiff; use ethcore::account_diff::{Diff, Existance}; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; use v1::traits::Traces; -use v1::types::{TraceFilter, Trace, BlockNumber, Index, CallRequest}; +use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest}; /// Traces api implementation. pub struct TracesClient where C: BlockChainClient, M: MinerService { @@ -156,7 +156,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: .and_then(|(filter, )| { let client = take_weak!(self.client); let traces = client.filter_traces(filter.into()); - let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(Trace::from).collect()); + let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(LocalizedTrace::from).collect()); to_value(&traces) }) } @@ -166,7 +166,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: .and_then(|(block_number,)| { let client = take_weak!(self.client); let traces = client.block_traces(block_number.into()); - let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(Trace::from).collect()); + let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(LocalizedTrace::from).collect()); to_value(&traces) }) } @@ -176,7 +176,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: .and_then(|(transaction_hash,)| { let client = take_weak!(self.client); let traces = client.transaction_traces(TransactionID::Hash(transaction_hash)); - let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(Trace::from).collect()); + let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(LocalizedTrace::from).collect()); to_value(&traces) }) } @@ -190,17 +190,32 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: address: address.into_iter().map(|i| i.value()).collect() }; let trace = client.trace(id); - let trace = trace.map(Trace::from); + let trace = trace.map(LocalizedTrace::from); to_value(&trace) }) } + fn trace_call(&self, params: Params) -> Result { + trace!(target: "jsonrpc", "trace_call: {:?}", params); + from_params(params) + .and_then(|(request,)| { + let signed = try!(self.sign_call(request)); + let r = take_weak!(self.client).call(&signed, CallAnalytics{ transaction_tracing: true, vm_tracing: false, state_diffing: false }); + if let Ok(executed) = r { + if let Some(trace) = executed.trace { + return to_value(&Trace::from(trace)); + } + } + Ok(Value::Null) + }) + } + fn vm_trace_call(&self, params: Params) -> Result { trace!(target: "jsonrpc", "vm_trace_call: {:?}", params); from_params(params) .and_then(|(request,)| { let signed = try!(self.sign_call(request)); - let r = take_weak!(self.client).call(&signed, CallAnalytics{ vm_tracing: true, state_diffing: false }); + let r = take_weak!(self.client).call(&signed, CallAnalytics{ transaction_tracing: false, vm_tracing: true, state_diffing: false }); if let Ok(executed) = r { if let Some(vm_trace) = executed.vm_trace { return Ok(vm_trace_to_object(&vm_trace)); @@ -215,7 +230,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: from_params(params) .and_then(|(request,)| { let signed = try!(self.sign_call(request)); - let r = take_weak!(self.client).call(&signed, CallAnalytics{ vm_tracing: false, state_diffing: true }); + let r = take_weak!(self.client).call(&signed, CallAnalytics{ transaction_tracing: false, vm_tracing: false, state_diffing: true }); if let Ok(executed) = r { if let Some(state_diff) = executed.state_diff { return Ok(state_diff_to_object(&state_diff)); diff --git a/rpc/src/v1/traits/traces.rs b/rpc/src/v1/traits/traces.rs index 4b58b12ac..c00b52822 100644 --- a/rpc/src/v1/traits/traces.rs +++ b/rpc/src/v1/traits/traces.rs @@ -32,11 +32,14 @@ pub trait Traces: Sized + Send + Sync + 'static { /// Returns all traces produced at given block. fn block_traces(&self, _: Params) -> Result; + /// Executes the given call and returns the transaction trace for it. + fn trace_call(&self, _: Params) -> Result; + /// Executes the given call and returns the VM trace for it. fn vm_trace_call(&self, _: Params) -> Result; /// Executes the given call and returns the diff for it. - fn state_diff_call(&self, params: Params) -> Result; + fn state_diff_call(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { @@ -46,6 +49,7 @@ pub trait Traces: Sized + Send + Sync + 'static { delegate.add_method("trace_transaction", Traces::transaction_traces); delegate.add_method("trace_block", Traces::block_traces); + delegate.add_method("trace_traceCall", Traces::trace_call); delegate.add_method("trace_vmTraceCall", Traces::vm_trace_call); delegate.add_method("trace_stateDiffCall", Traces::state_diff_call); diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index b4e82a28b..ba10fbe18 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -41,5 +41,5 @@ pub use self::transaction::Transaction; pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification}; pub use self::call_request::CallRequest; pub use self::receipt::Receipt; -pub use self::trace::Trace; +pub use self::trace::{Trace, LocalizedTrace}; pub use self::trace_filter::TraceFilter; diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index 6ea58543a..070b7d7d0 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -16,7 +16,7 @@ use util::{Address, U256, H256}; use ethcore::trace::trace; -use ethcore::trace::LocalizedTrace; +use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace}; use v1::types::Bytes; /// Create response @@ -161,7 +161,7 @@ impl From for Res { /// Trace #[derive(Debug, Serialize)] -pub struct Trace { +pub struct LocalizedTrace { /// Action action: Action, /// Result @@ -185,9 +185,9 @@ pub struct Trace { block_hash: H256, } -impl From for Trace { - fn from(t: LocalizedTrace) -> Self { - Trace { +impl From for LocalizedTrace { + fn from(t: EthLocalizedTrace) -> Self { + LocalizedTrace { action: From::from(t.action), result: From::from(t.result), trace_address: t.trace_address.into_iter().map(From::from).collect(), @@ -200,6 +200,30 @@ impl From for Trace { } } +/// Trace +#[derive(Debug, Serialize)] +pub struct Trace { + /// Depth within the call trace tree. + depth: usize, + /// Action + action: Action, + /// Result + result: Res, + /// Subtraces + subtraces: Vec, +} + +impl From for Trace { + fn from(t: EthTrace) -> Self { + Trace { + depth: t.depth.into(), + action: t.action.into(), + result: t.result.into(), + subtraces: t.subs.into_iter().map(From::from).collect(), + } + } +} + #[cfg(test)] mod tests { use serde_json; @@ -209,7 +233,7 @@ mod tests { #[test] fn test_trace_serialize() { - let t = Trace { + let t = LocalizedTrace { action: Action::Call(Call { from: Address::from(4), to: Address::from(5), From 7dc05f1bcca045fa1117559e76a64155b26b932e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 2 Jun 2016 16:30:28 +0200 Subject: [PATCH 2/4] Unify tracing interface into a single call. --- rpc/src/v1/impls/traces.rs | 49 +++++++++++++------------------------ rpc/src/v1/traits/traces.rs | 15 +++--------- 2 files changed, 20 insertions(+), 44 deletions(-) diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index 8a1627836..745f1c036 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -28,7 +28,7 @@ use ethcore::state_diff::StateDiff; use ethcore::account_diff::{Diff, Existance}; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; use v1::traits::Traces; -use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest}; +use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest, Bytes}; /// Traces api implementation. pub struct TracesClient where C: BlockChainClient, M: MinerService { @@ -195,46 +195,31 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: }) } - fn trace_call(&self, params: Params) -> Result { - trace!(target: "jsonrpc", "trace_call: {:?}", params); + fn call(&self, params: Params) -> Result { + trace!(target: "jsonrpc", "call: {:?}", params); from_params(params) - .and_then(|(request,)| { + .and_then(|(request, flags)| { + let flags: Vec = flags; + let analytics = CallAnalytics { + transaction_tracing: flags.contains(&("trace".to_owned())), + vm_tracing: flags.contains(&("vmTrace".to_owned())), + state_diffing: flags.contains(&("stateDiff".to_owned())), + }; let signed = try!(self.sign_call(request)); - let r = take_weak!(self.client).call(&signed, CallAnalytics{ transaction_tracing: true, vm_tracing: false, state_diffing: false }); + let r = take_weak!(self.client).call(&signed, analytics); if let Ok(executed) = r { + // TODO maybe add other stuff to this? + let mut ret = map!["output".to_owned() => to_value(&Bytes(executed.output)).unwrap()]; if let Some(trace) = executed.trace { - return to_value(&Trace::from(trace)); + ret.insert("trace".to_owned(), to_value(&Trace::from(trace)).unwrap()); } - } - Ok(Value::Null) - }) - } - - fn vm_trace_call(&self, params: Params) -> Result { - trace!(target: "jsonrpc", "vm_trace_call: {:?}", params); - from_params(params) - .and_then(|(request,)| { - let signed = try!(self.sign_call(request)); - let r = take_weak!(self.client).call(&signed, CallAnalytics{ transaction_tracing: false, vm_tracing: true, state_diffing: false }); - if let Ok(executed) = r { if let Some(vm_trace) = executed.vm_trace { - return Ok(vm_trace_to_object(&vm_trace)); + ret.insert("vmTrace".to_owned(), vm_trace_to_object(&vm_trace)); } - } - Ok(Value::Null) - }) - } - - fn state_diff_call(&self, params: Params) -> Result { - trace!(target: "jsonrpc", "state_diff_call: {:?}", params); - from_params(params) - .and_then(|(request,)| { - let signed = try!(self.sign_call(request)); - let r = take_weak!(self.client).call(&signed, CallAnalytics{ transaction_tracing: false, vm_tracing: false, state_diffing: true }); - if let Ok(executed) = r { if let Some(state_diff) = executed.state_diff { - return Ok(state_diff_to_object(&state_diff)); + ret.insert("stateDiff".to_owned(), state_diff_to_object(&state_diff)); } + return Ok(Value::Object(ret)) } Ok(Value::Null) }) diff --git a/rpc/src/v1/traits/traces.rs b/rpc/src/v1/traits/traces.rs index c00b52822..45fa916be 100644 --- a/rpc/src/v1/traits/traces.rs +++ b/rpc/src/v1/traits/traces.rs @@ -32,14 +32,8 @@ pub trait Traces: Sized + Send + Sync + 'static { /// Returns all traces produced at given block. fn block_traces(&self, _: Params) -> Result; - /// Executes the given call and returns the transaction trace for it. - fn trace_call(&self, _: Params) -> Result; - - /// Executes the given call and returns the VM trace for it. - fn vm_trace_call(&self, _: Params) -> Result; - - /// Executes the given call and returns the diff for it. - fn state_diff_call(&self, _: Params) -> Result; + /// Executes the given call and returns a number of possible traces for it. + fn call(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { @@ -48,10 +42,7 @@ pub trait Traces: Sized + Send + Sync + 'static { delegate.add_method("trace_get", Traces::trace); delegate.add_method("trace_transaction", Traces::transaction_traces); delegate.add_method("trace_block", Traces::block_traces); - - delegate.add_method("trace_traceCall", Traces::trace_call); - delegate.add_method("trace_vmTraceCall", Traces::vm_trace_call); - delegate.add_method("trace_stateDiffCall", Traces::state_diff_call); + delegate.add_method("trace_call", Traces::call); delegate } From 1fa8f108d920f4890f440de7af04650dd26cc782 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Jun 2016 00:24:21 +0200 Subject: [PATCH 3/4] StateDiff uses serde preprocessor. --- rpc/src/v1/impls/traces.rs | 11 +++--- rpc/src/v1/types/bytes.rs | 6 ++++ rpc/src/v1/types/mod.rs.in | 2 +- rpc/src/v1/types/trace.rs | 71 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 8 deletions(-) diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index 745f1c036..817420f3c 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -20,15 +20,12 @@ use std::sync::{Weak, Arc}; use jsonrpc_core::*; use std::collections::BTreeMap; use util::{H256, U256, FixedHash, Uint}; -use serde; use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId}; use ethcore::trace::VMTrace; use ethcore::miner::MinerService; -use ethcore::state_diff::StateDiff; -use ethcore::account_diff::{Diff, Existance}; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; use v1::traits::Traces; -use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest, Bytes}; +use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest, Bytes, StateDiff}; /// Traces api implementation. pub struct TracesClient where C: BlockChainClient, M: MinerService { @@ -108,7 +105,7 @@ fn vm_trace_to_object(t: &VMTrace) -> Value { ret.insert("ops".to_owned(), Value::Array(ops)); Value::Object(ret) } - +/* fn diff_to_object(d: &Diff) -> Value where T: serde::Serialize + Eq { let mut ret = BTreeMap::new(); match *d { @@ -149,7 +146,7 @@ fn state_diff_to_object(t: &StateDiff) -> Value { ])) }).collect::>()) } - +*/ impl Traces for TracesClient where C: BlockChainClient + 'static, M: MinerService + 'static { fn filter(&self, params: Params) -> Result { from_params::<(TraceFilter,)>(params) @@ -217,7 +214,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: ret.insert("vmTrace".to_owned(), vm_trace_to_object(&vm_trace)); } if let Some(state_diff) = executed.state_diff { - ret.insert("stateDiff".to_owned(), state_diff_to_object(&state_diff)); + ret.insert("stateDiff".to_owned(), to_value(&StateDiff::from(state_diff)).unwrap()); } return Ok(Value::Object(ret)) } diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index 76d84d0dd..1bf5deb75 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -36,6 +36,12 @@ impl Bytes { } } +impl From> for Bytes { + fn from(bytes: Vec) -> Bytes { + Bytes(bytes) + } +} + impl Serialize for Bytes { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index ba10fbe18..fc2a19987 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -41,5 +41,5 @@ pub use self::transaction::Transaction; pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification}; pub use self::call_request::CallRequest; pub use self::receipt::Receipt; -pub use self::trace::{Trace, LocalizedTrace}; +pub use self::trace::{Trace, LocalizedTrace, StateDiff}; pub use self::trace_filter::TraceFilter; diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index 070b7d7d0..ede99b448 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -14,11 +14,82 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::collections::BTreeMap; use util::{Address, U256, H256}; +use serde::{Serialize, Serializer}; use ethcore::trace::trace; use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace}; +use ethcore::state_diff; +use ethcore::account_diff; use v1::types::Bytes; +#[derive(Debug, Serialize)] +/// Aux type for Diff::Changed. +pub struct ChangedType where T: Serialize { + from: T, + to: T, +} + +#[derive(Debug, Serialize)] +/// Serde-friendly `Diff` shadow. +pub enum Diff where T: Serialize { + #[serde(rename="=")] + Same, + #[serde(rename="+")] + Born(T), + #[serde(rename="-")] + Died(T), + #[serde(rename="*")] + Changed(ChangedType), +} + +impl From> for Diff where T: Eq, U: Serialize + From { + fn from(c: account_diff::Diff) -> Self { + match c { + account_diff::Diff::Same => Diff::Same, + account_diff::Diff::Born(t) => Diff::Born(t.into()), + account_diff::Diff::Died(t) => Diff::Died(t.into()), + account_diff::Diff::Changed(t, u) => Diff::Changed(ChangedType{from: t.into(), to: u.into()}), + } + } +} + +#[derive(Debug, Serialize)] +/// Serde-friendly `AccountDiff` shadow. +pub struct AccountDiff { + pub balance: Diff, + pub nonce: Diff, + pub code: Diff, + pub storage: BTreeMap>, +} + +impl From for AccountDiff { + fn from(c: account_diff::AccountDiff) -> Self { + AccountDiff { + balance: c.balance.into(), + nonce: c.nonce.into(), + code: c.code.into(), + storage: c.storage.into_iter().map(|(k, v)| (k, v.into())).collect(), + } + } +} + +/// Serde-friendly `StateDiff` shadow. +pub struct StateDiff(BTreeMap); + +impl Serialize for StateDiff { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: Serializer { + Serialize::serialize(&self.0, serializer) + } +} + +impl From for StateDiff { + fn from(c: state_diff::StateDiff) -> Self { + StateDiff(c.0.into_iter().map(|(k, v)| (k, v.into())).collect()) + } +} + /// Create response #[derive(Debug, Serialize)] pub struct Create { From 0cb1affd48ebfe0de3094203f8e084a9ac38123d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 6 Jun 2016 15:18:38 -0700 Subject: [PATCH 4/4] Use serialize framework for VMTrace JSON. --- rpc/src/v1/impls/traces.rs | 96 +---------------------------- rpc/src/v1/types/mod.rs.in | 2 +- rpc/src/v1/types/trace.rs | 121 ++++++++++++++++++++++++++++++++++++- 3 files changed, 124 insertions(+), 95 deletions(-) diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index 817420f3c..452255383 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -19,13 +19,12 @@ use std::sync::{Weak, Arc}; use jsonrpc_core::*; use std::collections::BTreeMap; -use util::{H256, U256, FixedHash, Uint}; +use util::{H256, FixedHash}; use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId}; -use ethcore::trace::VMTrace; use ethcore::miner::MinerService; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; use v1::traits::Traces; -use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest, Bytes, StateDiff}; +use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest, Bytes, StateDiff, VMTrace}; /// Traces api implementation. pub struct TracesClient where C: BlockChainClient, M: MinerService { @@ -58,95 +57,6 @@ impl TracesClient where C: BlockChainClient, M: MinerService { } } -fn vm_trace_to_object(t: &VMTrace) -> Value { - let mut ret = BTreeMap::new(); - ret.insert("code".to_owned(), to_value(&t.code).unwrap()); - - let mut subs = t.subs.iter(); - let mut next_sub = subs.next(); - - let ops = t.operations - .iter() - .enumerate() - .map(|(i, op)| { - let mut m = map![ - "pc".to_owned() => to_value(&op.pc).unwrap(), - "cost".to_owned() => match op.gas_cost <= U256::from(!0u64) { - true => to_value(&op.gas_cost.low_u64()), - false => to_value(&op.gas_cost), - }.unwrap() - ]; - if let Some(ref ex) = op.executed { - let mut em = map![ - "used".to_owned() => to_value(&ex.gas_used.low_u64()).unwrap(), - "push".to_owned() => to_value(&ex.stack_push).unwrap() - ]; - if let Some(ref md) = ex.mem_diff { - em.insert("mem".to_owned(), Value::Object(map![ - "off".to_owned() => to_value(&md.offset).unwrap(), - "data".to_owned() => to_value(&md.data).unwrap() - ])); - } - if let Some(ref sd) = ex.store_diff { - em.insert("store".to_owned(), Value::Object(map![ - "key".to_owned() => to_value(&sd.location).unwrap(), - "val".to_owned() => to_value(&sd.value).unwrap() - ])); - } - m.insert("ex".to_owned(), Value::Object(em)); - } - if next_sub.is_some() && next_sub.unwrap().parent_step == i { - m.insert("sub".to_owned(), vm_trace_to_object(next_sub.unwrap())); - next_sub = subs.next(); - } - Value::Object(m) - }) - .collect::>(); - ret.insert("ops".to_owned(), Value::Array(ops)); - Value::Object(ret) -} -/* -fn diff_to_object(d: &Diff) -> Value where T: serde::Serialize + Eq { - let mut ret = BTreeMap::new(); - match *d { - Diff::Same => { - ret.insert("diff".to_owned(), Value::String("=".to_owned())); - } - Diff::Born(ref x) => { - ret.insert("diff".to_owned(), Value::String("+".to_owned())); - ret.insert("+".to_owned(), to_value(x).unwrap()); - } - Diff::Died(ref x) => { - ret.insert("diff".to_owned(), Value::String("-".to_owned())); - ret.insert("-".to_owned(), to_value(x).unwrap()); - } - Diff::Changed(ref from, ref to) => { - ret.insert("diff".to_owned(), Value::String("*".to_owned())); - ret.insert("-".to_owned(), to_value(from).unwrap()); - ret.insert("+".to_owned(), to_value(to).unwrap()); - } - }; - Value::Object(ret) -} - -fn state_diff_to_object(t: &StateDiff) -> Value { - Value::Object(t.iter().map(|(address, account)| { - (address.hex(), Value::Object(map![ - "existance".to_owned() => Value::String(match account.existance() { - Existance::Born => "+", - Existance::Alive => ".", - Existance::Died => "-", - }.to_owned()), - "balance".to_owned() => diff_to_object(&account.balance), - "nonce".to_owned() => diff_to_object(&account.nonce), - "code".to_owned() => diff_to_object(&account.code), - "storage".to_owned() => Value::Object(account.storage.iter().map(|(key, val)| { - (key.hex(), diff_to_object(&val)) - }).collect::>()) - ])) - }).collect::>()) -} -*/ impl Traces for TracesClient where C: BlockChainClient + 'static, M: MinerService + 'static { fn filter(&self, params: Params) -> Result { from_params::<(TraceFilter,)>(params) @@ -211,7 +121,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: ret.insert("trace".to_owned(), to_value(&Trace::from(trace)).unwrap()); } if let Some(vm_trace) = executed.vm_trace { - ret.insert("vmTrace".to_owned(), vm_trace_to_object(&vm_trace)); + ret.insert("vmTrace".to_owned(), to_value(&VMTrace::from(vm_trace)).unwrap()); } if let Some(state_diff) = executed.state_diff { ret.insert("stateDiff".to_owned(), to_value(&StateDiff::from(state_diff)).unwrap()); diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index fc2a19987..3f07bfb31 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -41,5 +41,5 @@ pub use self::transaction::Transaction; pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification}; pub use self::call_request::CallRequest; pub use self::receipt::Receipt; -pub use self::trace::{Trace, LocalizedTrace, StateDiff}; +pub use self::trace::{Trace, LocalizedTrace, StateDiff, VMTrace}; pub use self::trace_filter::TraceFilter; diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index ede99b448..5b4192886 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -15,14 +15,133 @@ // along with Parity. If not, see . use std::collections::BTreeMap; -use util::{Address, U256, H256}; +use util::{Address, U256, H256, Uint}; use serde::{Serialize, Serializer}; use ethcore::trace::trace; use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace}; +use ethcore::trace as et; use ethcore::state_diff; use ethcore::account_diff; use v1::types::Bytes; +#[derive(Debug, Serialize)] +/// A diff of some chunk of memory. +pub struct MemoryDiff { + /// Offset into memory the change begins. + pub off: usize, + /// The changed data. + pub data: Vec, +} + +impl From for MemoryDiff { + fn from(c: et::MemoryDiff) -> Self { + MemoryDiff { + off: c.offset, + data: c.data, + } + } +} + +#[derive(Debug, Serialize)] +/// A diff of some storage value. +pub struct StorageDiff { + /// Which key in storage is changed. + pub key: U256, + /// What the value has been changed to. + pub val: U256, +} + +impl From for StorageDiff { + fn from(c: et::StorageDiff) -> Self { + StorageDiff { + key: c.location, + val: c.value, + } + } +} + +#[derive(Debug, Serialize)] +/// A record of an executed VM operation. +pub struct VMExecutedOperation { + /// The total gas used. + #[serde(rename="used")] + pub used: u64, + /// The stack item placed, if any. + pub push: Vec, + /// If altered, the memory delta. + #[serde(rename="mem")] + pub mem: Option, + /// The altered storage value, if any. + #[serde(rename="store")] + pub store: Option, +} + +impl From for VMExecutedOperation { + fn from(c: et::VMExecutedOperation) -> Self { + VMExecutedOperation { + used: c.gas_used.low_u64(), + push: c.stack_push, + mem: c.mem_diff.map(From::from), + store: c.store_diff.map(From::from), + } + } +} + +#[derive(Debug, Serialize)] +/// A record of the execution of a single VM operation. +pub struct VMOperation { + /// The program counter. + pub pc: usize, + /// The gas cost for this instruction. + pub cost: u64, + /// Information concerning the execution of the operation. + pub ex: Option, + /// Subordinate trace of the CALL/CREATE if applicable. + pub sub: Option, +} + +impl From<(et::VMOperation, Option)> for VMOperation { + fn from(c: (et::VMOperation, Option)) -> Self { + VMOperation { + pc: c.0.pc, + cost: c.0.gas_cost.low_u64(), + ex: c.0.executed.map(From::from), + sub: c.1.map(From::from), + } + } +} + +#[derive(Debug, Serialize)] +/// A record of a full VM trace for a CALL/CREATE. +pub struct VMTrace { + /// The code to be executed. + pub code: Vec, + /// The operations executed. + pub ops: Vec, +} + +impl From for VMTrace { + fn from(c: et::VMTrace) -> Self { + let mut subs = c.subs.into_iter(); + let mut next_sub = subs.next(); + VMTrace { + code: c.code, + ops: c.operations + .into_iter() + .enumerate() + .map(|(i, op)| (op, { + let have_sub = next_sub.is_some() && next_sub.as_ref().unwrap().parent_step == i; + if have_sub { + let r = next_sub.clone(); + next_sub = subs.next(); + r + } else { None } + }).into()) + .collect(), + } + } +} + #[derive(Debug, Serialize)] /// Aux type for Diff::Changed. pub struct ChangedType where T: Serialize {