From cd16828fefa022cffd7a34cb467ce71d0ad47c86 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 29 May 2016 00:58:52 +0200 Subject: [PATCH] Integrated VM tracing into JSONRPC. --- ethcore/src/evm/instructions.rs | 3 +- ethcore/src/evm/mod.rs | 1 + ethcore/src/executive.rs | 2 +- ethcore/src/lib.rs | 1 + ethcore/src/trace/executive_tracer.rs | 2 +- rpc/src/v1/impls/ethcore.rs | 43 +++++++++++++++++++++++---- 6 files changed, 44 insertions(+), 8 deletions(-) diff --git a/ethcore/src/evm/instructions.rs b/ethcore/src/evm/instructions.rs index 6a1a06ba9..c313726f1 100644 --- a/ethcore/src/evm/instructions.rs +++ b/ethcore/src/evm/instructions.rs @@ -124,6 +124,7 @@ pub struct InstructionInfo { pub side_effects: bool, pub tier: GasPriceTier } + impl InstructionInfo { pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo { InstructionInfo { @@ -139,7 +140,7 @@ impl InstructionInfo { #[cfg_attr(rustfmt, rustfmt_skip)] /// Return details about specific instruction -pub fn get_info (instruction: Instruction) -> InstructionInfo { +pub fn get_info(instruction: Instruction) -> InstructionInfo { match instruction { STOP => InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero), ADD => InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow), diff --git a/ethcore/src/evm/mod.rs b/ethcore/src/evm/mod.rs index b7816b99c..06ce5e7e8 100644 --- a/ethcore/src/evm/mod.rs +++ b/ethcore/src/evm/mod.rs @@ -33,3 +33,4 @@ pub use self::evm::{Evm, Error, Result}; pub use self::ext::{Ext, ContractCreateResult, MessageCallResult}; pub use self::factory::{Factory, VMType}; pub use self::schedule::Schedule; +pub use self::instructions::get_info; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index f2be36df4..057c1e67a 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -196,7 +196,7 @@ impl<'a> Executive<'a> { }; // finalize here! - Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain()))) + Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain().and_then(|mut i| i.subs.pop())))) } fn exec_vm( diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 1d4ddadbc..4e1a5be56 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -139,3 +139,4 @@ mod tests; mod json_tests; pub use types::*; +pub use evm::get_info; diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index 34d8a6936..b51fbaa71 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -106,7 +106,7 @@ impl Tracer for ExecutiveTracer { } } -/// Simple VM tracer. Traces all operations. Ignores delegatecalls. +/// Simple VM tracer. Traces all operations. #[derive(Default)] pub struct ExecutiveVMTracer { data: VMTrace, diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index 4412cdac6..8ed4f0fec 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -24,7 +24,9 @@ use std::collections::BTreeMap; use jsonrpc_core::*; use ethminer::{MinerService}; use ethcore::client::{BlockChainClient}; +use ethcore::trace::VMTrace; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; +use ethcore::get_info; use v1::traits::Ethcore; use v1::types::{Bytes, CallRequest}; @@ -66,6 +68,34 @@ impl EthcoreClient 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(), + "inst".to_owned() => to_value(&get_info(op.instruction).name).unwrap(), + "gas_cost".to_owned() => to_value(&op.gas_cost).unwrap(), + "stack".to_owned() => to_value(&op.stack).unwrap() + ]; + 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) +} + impl Ethcore for EthcoreClient where C: BlockChainClient + 'static, M: MinerService + 'static { fn set_min_gas_price(&self, params: Params) -> Result { @@ -162,11 +192,14 @@ impl Ethcore for EthcoreClient where C: BlockChainClient + 'static, from_params(params) .and_then(|(request,)| { let signed = try!(self.sign_call(request)); - let _ = take_weak!(self.client).call(&signed, true); - // TODO: construct JSON trace from _.vm_trace. - let mut ret = Vec::new(); - ret.push(Value::Object(map!["foo".to_owned() => Value::String("var".to_owned())])); - Ok(Value::Array(ret)) + let r = take_weak!(self.client).call(&signed, true); + trace!(target: "jsonrpc", "returned {:?}", r); + if let Ok(executed) = r { + if let Some(vm_trace) = executed.vm_trace { + return Ok(vm_trace_to_object(&vm_trace)); + } + } + Ok(Value::Null) }) } }