VM tracing and JSON RPC endpoint for it. (#1169)

* Groundwork for basic VM tracing.

* RPC endpoint for VM tracing and ser/de types ready.

* Create VMTracer trait.

* Rearchitected VM tracing to reflect existing tracing.

Should more or less work now.

* Integrated VM tracing into JSONRPC.

* Fix ethcore module tests.

* Add tests for VM tracing.

* Fix consensus test code.

* Fix mock tests.

* Added VM trace information for post-execution stuff.

* Fix max-value calls and add "creates" field to getTransaction.

* Tests for VM tracing.

* Don't implement the trait with unimplemented.

* Remove invlaid comment.

* Fix tests.
This commit is contained in:
Gav Wood 2016-06-02 12:40:31 +02:00
parent 7ad9c73c75
commit b17581d7de
36 changed files with 742 additions and 172 deletions

View File

@ -34,17 +34,8 @@
"gasLimit": "0x2fefd8" "gasLimit": "0x2fefd8"
}, },
"nodes": [ "nodes": [
"enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303",
"enode://7ee7195bfac561ec938a72cd84cd1a5d2b334415263feddc325b20b5010446fc6c361297d13decab4039028fa659c1e27cca1396574b87cc7b29eea2985e97fe@108.61.197.28:30303",
"enode://933c5d5470b77537e7d9c1ee686132b5032dd3e2a096d2f64d2004df4ce9fca4ad6da5e358edcc8f81e65f047e40045600181f5fb35066e771025f6cca8e7952@46.101.114.191:30303",
"enode://ad4028ba28783d5bf58f512cb4e24a8ce980d768177c4974e1140b16b925132c947349db9ca3646752891b382dafc839a0c0716c3764c1ed9d424f09d13d01cf@148.251.220.116:30303",
"enode://c54ddaacddc7029683c80edae91015520eb2712176fbe6fdb7a5a074659270638f1266cba1731681c7cb785bceb02ca8d8b23024e3ec736fc5579f2042be97ae@54.175.255.230:30303",
"enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303",
"enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303", "enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303",
"enode://e941c58fed2709d792f552f408d2162c3d0a5597d22d1da617a9c9e6181f3251056a96adb45ae22eba70119355227298dc7e6dff805b092bae7da2f8564de422@85.25.217.23:30303", "enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303"
"enode://f4b73c9d11a780293ff0ca7afa12c67797afdc33a4797a7c2ecc5b87e455b32a8b9e9804f2004072bac38350bf82d52521d1a09590d2079705fc8357aef2bf9c@71.202.223.50:56603",
"enode://1173eea53e0cb2b8da92423e44cf4cbafbc8ea16c1558cf06e18dfc5a2fc9b140cc802a4362b4c773fb1442541e6f2a225b200bb4c1f6b347e7510a50fa4873f@104.41.138.167:30300",
"enode://1aad341327808738ad34655611f1b13293c4155dde36c8e3788128829f15cc6db2da9435f29520553d4efc134aadc50115690194ac3af519aac7a388b524811e@109.188.125.2:30303"
], ],
"accounts": { "accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },

View File

@ -436,7 +436,7 @@ impl<V> Client<V> where V: Verifier {
} }
impl<V> BlockChainClient for Client<V> where V: Verifier { impl<V> BlockChainClient for Client<V> where V: Verifier {
fn call(&self, t: &SignedTransaction) -> Result<Executed, ExecutionError> { fn call(&self, t: &SignedTransaction, vm_tracing: bool) -> Result<Executed, ExecutionError> {
let header = self.block_header(BlockID::Latest).unwrap(); let header = self.block_header(BlockID::Latest).unwrap();
let view = HeaderView::new(&header); let view = HeaderView::new(&header);
let last_hashes = self.build_last_hashes(view.hash()); let last_hashes = self.build_last_hashes(view.hash());
@ -456,10 +456,10 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
ExecutionError::TransactionMalformed(message) ExecutionError::TransactionMalformed(message)
})); }));
let balance = state.balance(&sender); let balance = state.balance(&sender);
// give the sender max balance // give the sender a decent balance
state.sub_balance(&sender, &balance); state.sub_balance(&sender, &balance);
state.add_balance(&sender, &U256::max_value()); state.add_balance(&sender, &(U256::from(1) << 200));
let options = TransactOptions { tracing: false, check_nonce: false }; let options = TransactOptions { tracing: false, vm_tracing: vm_tracing, check_nonce: false };
Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options) Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options)
} }

View File

@ -157,7 +157,8 @@ pub trait BlockChainClient : Sync + Send {
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>; fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
/// Makes a non-persistent transaction call. /// Makes a non-persistent transaction call.
fn call(&self, t: &SignedTransaction) -> Result<Executed, ExecutionError>; // TODO: should be able to accept blockchain location for call.
fn call(&self, t: &SignedTransaction, vm_tracing: bool) -> Result<Executed, ExecutionError>;
/// Returns EvmFactory. /// Returns EvmFactory.
fn vm_factory(&self) -> &EvmFactory; fn vm_factory(&self) -> &EvmFactory;

View File

@ -251,7 +251,7 @@ impl MiningBlockChainClient for TestBlockChainClient {
} }
impl BlockChainClient for TestBlockChainClient { impl BlockChainClient for TestBlockChainClient {
fn call(&self, _t: &SignedTransaction) -> Result<Executed, ExecutionError> { fn call(&self, _t: &SignedTransaction, _vm_tracing: bool) -> Result<Executed, ExecutionError> {
Ok(self.execution_result.read().unwrap().clone().unwrap()) Ok(self.execution_result.read().unwrap().clone().unwrap())
} }

View File

@ -105,4 +105,10 @@ pub trait Ext {
/// Increments sstore refunds count by 1. /// Increments sstore refunds count by 1.
fn inc_sstore_clears(&mut self); fn inc_sstore_clears(&mut self);
/// Prepare to trace an operation. Passthrough for the VM trace.
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
/// Trace the finalised execution of a single instruction.
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
} }

View File

@ -124,6 +124,7 @@ pub struct InstructionInfo {
pub side_effects: bool, pub side_effects: bool,
pub tier: GasPriceTier pub tier: GasPriceTier
} }
impl InstructionInfo { impl InstructionInfo {
pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo { pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo {
InstructionInfo { InstructionInfo {
@ -139,7 +140,7 @@ impl InstructionInfo {
#[cfg_attr(rustfmt, rustfmt_skip)] #[cfg_attr(rustfmt, rustfmt_skip)]
/// Return details about specific instruction /// Return details about specific instruction
pub fn get_info (instruction: Instruction) -> InstructionInfo { pub fn get_info(instruction: Instruction) -> InstructionInfo {
match instruction { match instruction {
STOP => InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero), STOP => InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero),
ADD => InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow), ADD => InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow),

View File

@ -17,8 +17,9 @@
///! Rust VM implementation ///! Rust VM implementation
use common::*; use common::*;
use trace::VMTracer;
use super::instructions as instructions; use super::instructions as instructions;
use super::instructions::Instruction; use super::instructions::{Instruction, get_info};
use std::marker::Copy; use std::marker::Copy;
use evm::{self, MessageCallResult, ContractCreateResult}; use evm::{self, MessageCallResult, ContractCreateResult};
@ -69,6 +70,8 @@ trait Stack<T> {
fn push(&mut self, elem: T); fn push(&mut self, elem: T);
/// Get number of elements on Stack /// Get number of elements on Stack
fn size(&self) -> usize; fn size(&self) -> usize;
/// Returns all data on stack.
fn peek_top(&mut self, no_of_elems: usize) -> &[T];
} }
struct VecStack<S> { struct VecStack<S> {
@ -131,6 +134,11 @@ impl<S : fmt::Display> Stack<S> for VecStack<S> {
fn size(&self) -> usize { fn size(&self) -> usize {
self.stack.len() self.stack.len()
} }
fn peek_top(&mut self, no_from_top: usize) -> &[S] {
assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist.");
&self.stack[self.stack.len() - no_from_top .. self.stack.len()]
}
} }
trait Memory { trait Memory {
@ -293,10 +301,15 @@ impl evm::Evm for Interpreter {
while reader.position < code.len() { while reader.position < code.len() {
let instruction = code[reader.position]; let instruction = code[reader.position];
reader.position += 1;
// Calculate gas cost // Calculate gas cost
let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &mut mem, &stack)); let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &mut mem, &stack));
// TODO: make compile-time removable if too much of a performance hit.
let trace_executed = ext.trace_prepare_execute(reader.position, instruction, &gas_cost);
reader.position += 1;
try!(self.verify_gas(&current_gas, &gas_cost)); try!(self.verify_gas(&current_gas, &gas_cost));
mem.expand(mem_size); mem.expand(mem_size);
current_gas = current_gas - gas_cost; //TODO: use operator -= current_gas = current_gas - gas_cost; //TODO: use operator -=
@ -311,10 +324,19 @@ impl evm::Evm for Interpreter {
); );
}); });
let (mem_written, store_written) = match trace_executed {
true => (Self::mem_written(instruction, &stack), Self::store_written(instruction, &stack)),
false => (None, None),
};
// Execute instruction // Execute instruction
let result = try!(self.exec_instruction( let result = try!(self.exec_instruction(
current_gas, &params, ext, instruction, &mut reader, &mut mem, &mut stack current_gas, &params, ext, instruction, &mut reader, &mut mem, &mut stack
)); ));
if trace_executed {
ext.trace_executed(current_gas, stack.peek_top(get_info(instruction).ret), mem_written.map(|(o, s)| (o, &(mem[o..(o + s)]))), store_written);
}
// Advance // Advance
match result { match result {
@ -485,6 +507,31 @@ impl Interpreter {
} }
} }
fn mem_written(
instruction: Instruction,
stack: &Stack<U256>
) -> Option<(usize, usize)> {
match instruction {
instructions::MSTORE | instructions::MLOAD => Some((stack.peek(0).low_u64() as usize, 32)),
instructions::MSTORE8 => Some((stack.peek(0).low_u64() as usize, 1)),
instructions::CALLDATACOPY | instructions::CODECOPY => Some((stack.peek(0).low_u64() as usize, stack.peek(2).low_u64() as usize)),
instructions::EXTCODECOPY => Some((stack.peek(1).low_u64() as usize, stack.peek(3).low_u64() as usize)),
instructions::CALL | instructions::CALLCODE => Some((stack.peek(5).low_u64() as usize, stack.peek(6).low_u64() as usize)),
instructions::DELEGATECALL => Some((stack.peek(4).low_u64() as usize, stack.peek(5).low_u64() as usize)),
_ => None,
}
}
fn store_written(
instruction: Instruction,
stack: &Stack<U256>
) -> Option<(U256, U256)> {
match instruction {
instructions::SSTORE => Some((stack.peek(0).clone(), stack.peek(1).clone())),
_ => None,
}
}
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> Result<(U256, usize), evm::Error> { fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> Result<(U256, usize), evm::Error> {
let gas_for_mem = |mem_size: U256| { let gas_for_mem = |mem_size: U256| {
let s = mem_size >> 5; let s = mem_size >> 5;
@ -833,10 +880,12 @@ impl Interpreter {
} }
} }
fn verify_instructions_requirements(&self, fn verify_instructions_requirements(
info: &instructions::InstructionInfo, &self,
stack_limit: usize, info: &instructions::InstructionInfo,
stack: &Stack<U256>) -> Result<(), evm::Error> { stack_limit: usize,
stack: &Stack<U256>
) -> Result<(), evm::Error> {
if !stack.has(info.args) { if !stack.has(info.args) {
Err(evm::Error::StackUnderflow { Err(evm::Error::StackUnderflow {
instruction: info.name, instruction: info.name,

View File

@ -16,6 +16,7 @@
//! Just in time compiler execution environment. //! Just in time compiler execution environment.
use common::*; use common::*;
use trace::VMTracer;
use evmjit; use evmjit;
use evm; use evm;

View File

@ -33,3 +33,4 @@ pub use self::evm::{Evm, Error, Result};
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult}; pub use self::ext::{Ext, ContractCreateResult, MessageCallResult};
pub use self::factory::{Factory, VMType}; pub use self::factory::{Factory, VMType};
pub use self::schedule::Schedule; pub use self::schedule::Schedule;
pub use self::instructions::get_info;

View File

@ -21,9 +21,8 @@ use engine::*;
use evm::{self, Ext, Factory}; use evm::{self, Ext, Factory};
use externalities::*; use externalities::*;
use substate::*; use substate::*;
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use crossbeam; use crossbeam;
pub use types::executed::{Executed, ExecutionResult}; pub use types::executed::{Executed, ExecutionResult};
/// Max depth to avoid stack overflow (when it's reached we start a new thread with VM) /// Max depth to avoid stack overflow (when it's reached we start a new thread with VM)
@ -43,6 +42,8 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address {
pub struct TransactOptions { pub struct TransactOptions {
/// Enable call tracing. /// Enable call tracing.
pub tracing: bool, pub tracing: bool,
/// Enable VM tracing.
pub vm_tracing: bool,
/// Check transaction nonce before execution. /// Check transaction nonce before execution.
pub check_nonce: bool, pub check_nonce: bool,
} }
@ -80,21 +81,40 @@ impl<'a> Executive<'a> {
} }
/// Creates `Externalities` from `Executive`. /// Creates `Externalities` from `Executive`.
pub fn as_externalities<'_, T>(&'_ mut self, origin_info: OriginInfo, substate: &'_ mut Substate, output: OutputPolicy<'_, '_>, tracer: &'_ mut T) -> Externalities<'_, T> where T: Tracer { pub fn as_externalities<'_, T, V>(
Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer) &'_ mut self,
origin_info: OriginInfo,
substate: &'_ mut Substate,
output: OutputPolicy<'_, '_>,
tracer: &'_ mut T,
vm_tracer: &'_ mut V
) -> Externalities<'_, T, V> where T: Tracer, V: VMTracer {
Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer, vm_tracer)
} }
/// This function should be used to execute transaction. /// This function should be used to execute transaction.
pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> { pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> {
let check = options.check_nonce; let check = options.check_nonce;
match options.tracing { match options.tracing {
true => self.transact_with_tracer(t, check, ExecutiveTracer::default()), true => match options.vm_tracing {
false => self.transact_with_tracer(t, check, NoopTracer), true => self.transact_with_tracer(t, check, ExecutiveTracer::default(), ExecutiveVMTracer::default()),
false => self.transact_with_tracer(t, check, ExecutiveTracer::default(), NoopVMTracer),
},
false => match options.vm_tracing {
true => self.transact_with_tracer(t, check, NoopTracer, ExecutiveVMTracer::default()),
false => self.transact_with_tracer(t, check, NoopTracer, NoopVMTracer),
},
} }
} }
/// Execute transaction/call with tracing enabled /// Execute transaction/call with tracing enabled
pub fn transact_with_tracer<T>(&'a mut self, t: &SignedTransaction, check_nonce: bool, mut tracer: T) -> Result<Executed, ExecutionError> where T: Tracer { pub fn transact_with_tracer<T, V>(
&'a mut self,
t: &SignedTransaction,
check_nonce: bool,
mut tracer: T,
mut vm_tracer: V
) -> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer {
let sender = try!(t.sender().map_err(|e| { let sender = try!(t.sender().map_err(|e| {
let message = format!("Transaction malformed: {:?}", e); let message = format!("Transaction malformed: {:?}", e);
ExecutionError::TransactionMalformed(message) ExecutionError::TransactionMalformed(message)
@ -154,7 +174,7 @@ impl<'a> Executive<'a> {
code: Some(t.data.clone()), code: Some(t.data.clone()),
data: None, data: None,
}; };
(self.create(params, &mut substate, &mut tracer), vec![]) (self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![])
}, },
Action::Call(ref address) => { Action::Call(ref address) => {
let params = ActionParams { let params = ActionParams {
@ -170,20 +190,26 @@ impl<'a> Executive<'a> {
}; };
// TODO: move output upstream // TODO: move output upstream
let mut out = vec![]; let mut out = vec![];
(self.call(params, &mut substate, BytesRef::Flexible(&mut out), &mut tracer), out) (self.call(params, &mut substate, BytesRef::Flexible(&mut out), &mut tracer, &mut vm_tracer), out)
} }
}; };
// finalize here! // finalize here!
Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop()))) Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain())))
} }
fn exec_vm<T>(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy, tracer: &mut T) fn exec_vm<T, V>(
-> evm::Result where T: Tracer { &mut self,
params: ActionParams,
unconfirmed_substate: &mut Substate,
output_policy: OutputPolicy,
tracer: &mut T,
vm_tracer: &mut V
) -> evm::Result where T: Tracer, V: VMTracer {
// Ordinary execution - keep VM in same thread // Ordinary execution - keep VM in same thread
if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 { if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
let vm_factory = self.vm_factory; let vm_factory = self.vm_factory;
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer); let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer);
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call); trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
return vm_factory.create().exec(params, &mut ext); return vm_factory.create().exec(params, &mut ext);
} }
@ -193,7 +219,7 @@ impl<'a> Executive<'a> {
// https://github.com/aturon/crossbeam/issues/16 // https://github.com/aturon/crossbeam/issues/16
crossbeam::scope(|scope| { crossbeam::scope(|scope| {
let vm_factory = self.vm_factory; let vm_factory = self.vm_factory;
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer); let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer);
scope.spawn(move || { scope.spawn(move || {
vm_factory.create().exec(params, &mut ext) vm_factory.create().exec(params, &mut ext)
@ -205,8 +231,14 @@ impl<'a> Executive<'a> {
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate and the output. /// Modifies the substate and the output.
/// Returns either gas_left or `evm::Error`. /// Returns either gas_left or `evm::Error`.
pub fn call<T>(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef, tracer: &mut T) pub fn call<T, V>(
-> evm::Result where T: Tracer { &mut self,
params: ActionParams,
substate: &mut Substate,
mut output: BytesRef,
tracer: &mut T,
vm_tracer: &mut V
) -> evm::Result where T: Tracer, V: VMTracer {
// backup used in case of running out of gas // backup used in case of running out of gas
self.state.snapshot(); self.state.snapshot();
@ -264,16 +296,22 @@ impl<'a> Executive<'a> {
let trace_info = tracer.prepare_trace_call(&params); let trace_info = tracer.prepare_trace_call(&params);
let mut trace_output = tracer.prepare_trace_output(); let mut trace_output = tracer.prepare_trace_output();
let mut subtracer = tracer.subtracer(); let mut subtracer = tracer.subtracer();
let gas = params.gas; let gas = params.gas;
if params.code.is_some() { if params.code.is_some() {
// part of substate that may be reverted // part of substate that may be reverted
let mut unconfirmed_substate = Substate::new(); let mut unconfirmed_substate = Substate::new();
// TODO: make ActionParams pass by ref then avoid copy altogether.
let mut subvmtracer = vm_tracer.prepare_subtrace(params.code.as_ref().expect("scope is protected by params.code.is_some condition"));
let res = { let res = {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer) self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer, &mut subvmtracer)
}; };
vm_tracer.done_subtrace(subvmtracer);
trace!(target: "executive", "res={:?}", res); trace!(target: "executive", "res={:?}", res);
let traces = subtracer.traces(); let traces = subtracer.traces();
@ -307,8 +345,13 @@ impl<'a> Executive<'a> {
/// Creates contract with given contract params. /// Creates contract with given contract params.
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate. /// Modifies the substate.
pub fn create<T>(&mut self, params: ActionParams, substate: &mut Substate, tracer: &mut T) -> evm::Result where T: pub fn create<T, V>(
Tracer { &mut self,
params: ActionParams,
substate: &mut Substate,
tracer: &mut T,
vm_tracer: &mut V
) -> evm::Result where T: Tracer, V: VMTracer {
// backup used in case of running out of gas // backup used in case of running out of gas
self.state.snapshot(); self.state.snapshot();
@ -330,10 +373,14 @@ impl<'a> Executive<'a> {
let gas = params.gas; let gas = params.gas;
let created = params.address.clone(); let created = params.address.clone();
let mut subvmtracer = vm_tracer.prepare_subtrace(&params.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed"));
let res = { let res = {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer) self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer, &mut subvmtracer)
}; };
vm_tracer.done_subtrace(subvmtracer);
match res { match res {
Ok(gas_left) => tracer.trace_create( Ok(gas_left) => tracer.trace_create(
trace_info, trace_info,
@ -351,7 +398,15 @@ impl<'a> Executive<'a> {
} }
/// Finalizes the transaction (does refunds and suicides). /// Finalizes the transaction (does refunds and suicides).
fn finalize(&mut self, t: &SignedTransaction, substate: Substate, result: evm::Result, output: Bytes, trace: Option<Trace>) -> ExecutionResult { fn finalize(
&mut self,
t: &SignedTransaction,
substate: Substate,
result: evm::Result,
output: Bytes,
trace: Option<Trace>,
vm_trace: Option<VMTrace>
) -> ExecutionResult {
let schedule = self.engine.schedule(self.info); let schedule = self.engine.schedule(self.info);
// refunds from SSTORE nonzero -> zero // refunds from SSTORE nonzero -> zero
@ -394,6 +449,7 @@ impl<'a> Executive<'a> {
contracts_created: vec![], contracts_created: vec![],
output: output, output: output,
trace: trace, trace: trace,
vm_trace: vm_trace,
}) })
}, },
_ => { _ => {
@ -406,6 +462,7 @@ impl<'a> Executive<'a> {
contracts_created: substate.contracts_created, contracts_created: substate.contracts_created,
output: output, output: output,
trace: trace, trace: trace,
vm_trace: vm_trace,
}) })
}, },
} }
@ -438,6 +495,7 @@ mod tests {
use tests::helpers::*; use tests::helpers::*;
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};
#[test] #[test]
fn test_contract_address() { fn test_contract_address() {
@ -466,7 +524,7 @@ mod tests {
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
ex.create(params, &mut substate, &mut NoopTracer).unwrap() ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_975)); assert_eq!(gas_left, U256::from(79_975));
@ -525,7 +583,7 @@ mod tests {
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
ex.create(params, &mut substate, &mut NoopTracer).unwrap() ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
}; };
assert_eq!(gas_left, U256::from(62_976)); assert_eq!(gas_left, U256::from(62_976));
@ -542,7 +600,7 @@ mod tests {
// 52 // 52
// 60 1d - push 29 // 60 1d - push 29
// 60 03 - push 3 // 60 03 - push 3
// 60 17 - push 17 // 60 17 - push 23
// f0 - create // f0 - create
// 60 00 - push 0 // 60 00 - push 0
// 55 sstore // 55 sstore
@ -578,13 +636,16 @@ mod tests {
let engine = TestEngine::new(5); let engine = TestEngine::new(5);
let mut substate = Substate::new(); let mut substate = Substate::new();
let mut tracer = ExecutiveTracer::default(); let mut tracer = ExecutiveTracer::default();
let mut vm_tracer = ExecutiveVMTracer::default();
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
let output = BytesRef::Fixed(&mut[0u8;0]); let output = BytesRef::Fixed(&mut[0u8;0]);
ex.call(params, &mut substate, output, &mut tracer).unwrap() ex.call(params, &mut substate, output, &mut tracer, &mut vm_tracer).unwrap()
}; };
assert_eq!(gas_left, U256::from(44_752));
let expected_trace = vec![ Trace { let expected_trace = vec![ Trace {
depth: 0, depth: 0,
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
@ -615,7 +676,39 @@ mod tests {
}] }]
}]; }];
assert_eq!(tracer.traces(), expected_trace); assert_eq!(tracer.traces(), expected_trace);
assert_eq!(gas_left, U256::from(44_752));
let expected_vm_trace = VMTrace {
parent_step: 0,
code: vec![124, 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85, 96, 0, 82, 96, 29, 96, 3, 96, 23, 240, 96, 0, 85],
operations: vec![
VMOperation { pc: 0, instruction: 124, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec_into![U256::from_dec_str("2589892687202724018173567190521546555304938078595079151649957320078677").unwrap()], mem_diff: None, store_diff: None }) },
VMOperation { pc: 30, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
VMOperation { pc: 32, instruction: 82, gas_cost: 6.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![0, 0, 0, 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85] }), store_diff: None }) },
VMOperation { pc: 33, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99985.into(), stack_push: vec_into![29], mem_diff: None, store_diff: None }) },
VMOperation { pc: 35, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99982.into(), stack_push: vec_into![3], mem_diff: None, store_diff: None }) },
VMOperation { pc: 37, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![23], mem_diff: None, store_diff: None }) },
VMOperation { pc: 39, instruction: 240, gas_cost: 32000.into(), executed: Some(VMExecutedOperation { gas_used: 67979.into(), stack_push: vec_into![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) },
VMOperation { pc: 40, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 64752.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
VMOperation { pc: 42, instruction: 85, gas_cost: 20000.into(), executed: Some(VMExecutedOperation { gas_used: 44752.into(), stack_push: vec_into![], mem_diff: None, store_diff: Some(StorageDiff { location: 0.into(), value: U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap() }) }) }
],
subs: vec![
VMTrace {
parent_step: 7,
code: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85],
operations: vec![
VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67976.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) },
VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67973.into(), stack_push: vec_into![16, 16], mem_diff: None, store_diff: None }) },
VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67970.into(), stack_push: vec_into![12], mem_diff: None, store_diff: None }) },
VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67967.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 67958.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) },
VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec_into![], mem_diff: None, store_diff: None }) }
],
subs: vec![]
}
]
};
assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace);
} }
evm_test!{test_create_contract: test_create_contract_jit, test_create_contract_int} evm_test!{test_create_contract: test_create_contract_jit, test_create_contract_int}
@ -650,12 +743,15 @@ mod tests {
let engine = TestEngine::new(5); let engine = TestEngine::new(5);
let mut substate = Substate::new(); let mut substate = Substate::new();
let mut tracer = ExecutiveTracer::default(); let mut tracer = ExecutiveTracer::default();
let mut vm_tracer = ExecutiveVMTracer::default();
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
ex.create(params.clone(), &mut substate, &mut tracer).unwrap() ex.create(params.clone(), &mut substate, &mut tracer, &mut vm_tracer).unwrap()
}; };
assert_eq!(gas_left, U256::from(96_776));
let expected_trace = vec![Trace { let expected_trace = vec![Trace {
depth: 0, depth: 0,
action: trace::Action::Create(trace::Create { action: trace::Action::Create(trace::Create {
@ -671,9 +767,23 @@ mod tests {
}), }),
subs: vec![] subs: vec![]
}]; }];
assert_eq!(tracer.traces(), expected_trace); assert_eq!(tracer.traces(), expected_trace);
assert_eq!(gas_left, U256::from(96_776));
let expected_vm_trace = VMTrace {
parent_step: 0,
code: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85],
operations: vec![
VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) },
VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec_into![16, 16], mem_diff: None, store_diff: None }) },
VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99991.into(), stack_push: vec_into![12], mem_diff: None, store_diff: None }) },
VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) },
VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![], mem_diff: None, store_diff: None }) }
],
subs: vec![]
};
assert_eq!(vm_tracer.drain().unwrap(), expected_vm_trace);
} }
evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int} evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int}
@ -722,7 +832,7 @@ mod tests {
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
ex.create(params, &mut substate, &mut NoopTracer).unwrap() ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
}; };
assert_eq!(gas_left, U256::from(62_976)); assert_eq!(gas_left, U256::from(62_976));
@ -774,7 +884,7 @@ mod tests {
{ {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
ex.create(params, &mut substate, &mut NoopTracer).unwrap(); ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap();
} }
assert_eq!(substate.contracts_created.len(), 1); assert_eq!(substate.contracts_created.len(), 1);
@ -835,7 +945,7 @@ mod tests {
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer).unwrap() ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap()
}; };
assert_eq!(gas_left, U256::from(73_237)); assert_eq!(gas_left, U256::from(73_237));
@ -880,7 +990,7 @@ mod tests {
let gas_left = { let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer).unwrap() ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap()
}; };
assert_eq!(gas_left, U256::from(59_870)); assert_eq!(gas_left, U256::from(59_870));
@ -913,7 +1023,7 @@ mod tests {
let executed = { let executed = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
let opts = TransactOptions { check_nonce: true, tracing: false }; let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
ex.transact(&t, opts).unwrap() ex.transact(&t, opts).unwrap()
}; };
@ -947,7 +1057,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
let opts = TransactOptions { check_nonce: true, tracing: false }; let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
ex.transact(&t, opts) ex.transact(&t, opts)
}; };
@ -979,7 +1089,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
let opts = TransactOptions { check_nonce: true, tracing: false }; let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
ex.transact(&t, opts) ex.transact(&t, opts)
}; };
@ -1013,7 +1123,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
let opts = TransactOptions { check_nonce: true, tracing: false }; let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
ex.transact(&t, opts) ex.transact(&t, opts)
}; };
@ -1047,7 +1157,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
let opts = TransactOptions { check_nonce: true, tracing: false }; let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
ex.transact(&t, opts) ex.transact(&t, opts)
}; };
@ -1082,7 +1192,7 @@ mod tests {
let result = { let result = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory); let mut ex = Executive::new(&mut state, &info, &engine, &factory);
ex.create(params, &mut substate, &mut NoopTracer) ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer)
}; };
match result { match result {

View File

@ -21,7 +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 trace::Tracer; use trace::{Tracer, VMTracer};
/// Policy for handling output data on `RETURN` opcode. /// Policy for handling output data on `RETURN` opcode.
pub enum OutputPolicy<'a, 'b> { pub enum OutputPolicy<'a, 'b> {
@ -55,7 +55,7 @@ impl OriginInfo {
} }
/// Implementation of evm Externalities. /// Implementation of evm Externalities.
pub struct Externalities<'a, T> where T: 'a + Tracer { pub struct Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
state: &'a mut State, state: &'a mut State,
env_info: &'a EnvInfo, env_info: &'a EnvInfo,
engine: &'a Engine, engine: &'a Engine,
@ -66,10 +66,10 @@ pub struct Externalities<'a, T> where T: 'a + Tracer {
schedule: Schedule, schedule: Schedule,
output: OutputPolicy<'a, 'a>, output: OutputPolicy<'a, 'a>,
tracer: &'a mut T, tracer: &'a mut T,
vm_tracer: &'a mut V,
} }
impl<'a, T> Externalities<'a, T> where T: 'a + Tracer { impl<'a, T, V> Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
#[cfg_attr(feature="dev", allow(too_many_arguments))] #[cfg_attr(feature="dev", allow(too_many_arguments))]
/// Basic `Externalities` constructor. /// Basic `Externalities` constructor.
pub fn new(state: &'a mut State, pub fn new(state: &'a mut State,
@ -81,6 +81,7 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer {
substate: &'a mut Substate, substate: &'a mut Substate,
output: OutputPolicy<'a, 'a>, output: OutputPolicy<'a, 'a>,
tracer: &'a mut T, tracer: &'a mut T,
vm_tracer: &'a mut V,
) -> Self { ) -> Self {
Externalities { Externalities {
state: state, state: state,
@ -93,11 +94,12 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer {
schedule: engine.schedule(env_info), schedule: engine.schedule(env_info),
output: output, output: output,
tracer: tracer, tracer: tracer,
vm_tracer: vm_tracer,
} }
} }
} }
impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer { impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
fn storage_at(&self, key: &H256) -> H256 { fn storage_at(&self, key: &H256) -> H256 {
self.state.storage_at(&self.origin_info.address, key) self.state.storage_at(&self.origin_info.address, key)
} }
@ -152,7 +154,7 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth); let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth);
// TODO: handle internal error separately // TODO: handle internal error separately
match ex.create(params, self.substate, self.tracer) { match ex.create(params, self.substate, self.tracer, self.vm_tracer) {
Ok(gas_left) => { Ok(gas_left) => {
self.substate.contracts_created.push(address.clone()); self.substate.contracts_created.push(address.clone());
ContractCreateResult::Created(address, gas_left) ContractCreateResult::Created(address, gas_left)
@ -190,7 +192,7 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth); let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth);
match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer) { match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer, self.vm_tracer) {
Ok(gas_left) => MessageCallResult::Success(gas_left), Ok(gas_left) => MessageCallResult::Success(gas_left),
_ => MessageCallResult::Failed _ => MessageCallResult::Failed
} }
@ -286,6 +288,14 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
fn inc_sstore_clears(&mut self) { fn inc_sstore_clears(&mut self) {
self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one(); self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one();
} }
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool {
self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost)
}
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
self.vm_tracer.trace_executed(gas_used, stack_push, mem_diff, store_diff)
}
} }
#[cfg(test)] #[cfg(test)]
@ -297,7 +307,7 @@ mod tests {
use substate::*; use substate::*;
use tests::helpers::*; use tests::helpers::*;
use super::*; use super::*;
use trace::{NoopTracer}; use trace::{NoopTracer, NoopVMTracer};
fn get_test_origin() -> OriginInfo { fn get_test_origin() -> OriginInfo {
OriginInfo { OriginInfo {
@ -349,9 +359,10 @@ mod tests {
let mut setup = TestSetup::new(); let mut setup = TestSetup::new();
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
let vm_factory = Default::default(); let vm_factory = Default::default();
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
assert_eq!(ext.env_info().number, 100); assert_eq!(ext.env_info().number, 100);
} }
@ -361,9 +372,10 @@ mod tests {
let mut setup = TestSetup::new(); let mut setup = TestSetup::new();
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
let vm_factory = Default::default(); let vm_factory = Default::default();
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
@ -383,9 +395,10 @@ mod tests {
} }
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
let vm_factory = Default::default(); let vm_factory = Default::default();
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
@ -398,9 +411,10 @@ mod tests {
let mut setup = TestSetup::new(); let mut setup = TestSetup::new();
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
let vm_factory = Default::default(); let vm_factory = Default::default();
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
let mut output = vec![]; let mut output = vec![];
@ -423,10 +437,11 @@ mod tests {
let mut setup = TestSetup::new(); let mut setup = TestSetup::new();
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
{ {
let vm_factory = Default::default(); let vm_factory = Default::default();
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
ext.log(log_topics, &log_data); ext.log(log_topics, &log_data);
} }
@ -440,10 +455,11 @@ mod tests {
let mut setup = TestSetup::new(); let mut setup = TestSetup::new();
let state = setup.state.reference_mut(); let state = setup.state.reference_mut();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
{ {
let vm_factory = Default::default(); let vm_factory = Default::default();
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer); let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
ext.suicide(&refund_account); ext.suicide(&refund_account);
} }

View File

@ -25,6 +25,7 @@ use substate::*;
use tests::helpers::*; use tests::helpers::*;
use ethjson; use ethjson;
use trace::{Tracer, NoopTracer}; use trace::{Tracer, NoopTracer};
use trace::{VMTracer, NoopVMTracer};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
struct CallCreate { struct CallCreate {
@ -48,32 +49,35 @@ impl From<ethjson::vm::Call> for CallCreate {
/// Tiny wrapper around executive externalities. /// Tiny wrapper around executive externalities.
/// Stores callcreates. /// Stores callcreates.
struct TestExt<'a, T> where T: 'a + Tracer { struct TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
ext: Externalities<'a, T>, ext: Externalities<'a, T, V>,
callcreates: Vec<CallCreate>, callcreates: Vec<CallCreate>,
contract_address: Address contract_address: Address
} }
impl<'a, T> TestExt<'a, T> where T: 'a + Tracer { impl<'a, T, V> TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
fn new(state: &'a mut State, fn new(
info: &'a EnvInfo, state: &'a mut State,
engine: &'a Engine, info: &'a EnvInfo,
vm_factory: &'a Factory, engine: &'a Engine,
depth: usize, vm_factory: &'a Factory,
origin_info: OriginInfo, depth: usize,
substate: &'a mut Substate, origin_info: OriginInfo,
output: OutputPolicy<'a, 'a>, substate: &'a mut Substate,
address: Address, output: OutputPolicy<'a, 'a>,
tracer: &'a mut T) -> Self { address: Address,
tracer: &'a mut T,
vm_tracer: &'a mut V,
) -> Self {
TestExt { TestExt {
contract_address: contract_address(&address, &state.nonce(&address)), contract_address: contract_address(&address, &state.nonce(&address)),
ext: Externalities::new(state, info, engine, vm_factory, depth, origin_info, substate, output, tracer), ext: Externalities::new(state, info, engine, vm_factory, depth, origin_info, substate, output, tracer, vm_tracer),
callcreates: vec![] callcreates: vec![]
} }
} }
} }
impl<'a, T> Ext for TestExt<'a, T> where T: Tracer { impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer {
fn storage_at(&self, key: &H256) -> H256 { fn storage_at(&self, key: &H256) -> H256 {
self.ext.storage_at(key) self.ext.storage_at(key)
} }
@ -186,6 +190,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
let mut substate = Substate::new(); let mut substate = Substate::new();
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
let mut output = vec![]; let mut output = vec![];
// execute // execute
@ -201,6 +206,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
OutputPolicy::Return(BytesRef::Flexible(&mut output), None), OutputPolicy::Return(BytesRef::Flexible(&mut output), None),
params.address.clone(), params.address.clone(),
&mut tracer, &mut tracer,
&mut vm_tracer,
); );
let evm = vm_factory.create(); let evm = vm_factory.create();
let res = evm.exec(params, &mut ex); let res = evm.exec(params, &mut ex);

View File

@ -141,3 +141,5 @@ mod tests;
mod json_tests; mod json_tests;
pub use types::*; pub use types::*;
pub use evm::get_info;
pub use executive::contract_address;

View File

@ -251,7 +251,7 @@ impl MinerService for Miner {
} }
} }
fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction) -> Result<Executed, ExecutionError> { fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, vm_tracing: bool) -> Result<Executed, ExecutionError> {
let sealing_work = self.sealing_work.lock().unwrap(); let sealing_work = self.sealing_work.lock().unwrap();
match sealing_work.peek_last_ref() { match sealing_work.peek_last_ref() {
Some(work) => { Some(work) => {
@ -277,12 +277,13 @@ impl MinerService for Miner {
// give the sender max balance // give the sender max balance
state.sub_balance(&sender, &balance); state.sub_balance(&sender, &balance);
state.add_balance(&sender, &U256::max_value()); state.add_balance(&sender, &U256::max_value());
let options = TransactOptions { tracing: false, check_nonce: false }; let options = TransactOptions { tracing: false, vm_tracing: vm_tracing, check_nonce: false };
// TODO: use vm_trace here.
Executive::new(&mut state, &env_info, self.engine(), chain.vm_factory()).transact(t, options) Executive::new(&mut state, &env_info, self.engine(), chain.vm_factory()).transact(t, options)
}, },
None => { None => {
chain.call(t) chain.call(t, vm_tracing)
} }
} }
} }

View File

@ -148,7 +148,7 @@ pub trait MinerService : Send + Sync {
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256; fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256;
/// Call into contract code using pending state. /// Call into contract code using pending state.
fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction) -> Result<Executed, ExecutionError>; fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, vm_tracing: bool) -> Result<Executed, ExecutionError>;
/// Get storage value in pending state. /// Get storage value in pending state.
fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256; fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256;

View File

@ -183,16 +183,14 @@ impl State {
/// Add `incr` to the balance of account `a`. /// Add `incr` to the balance of account `a`.
pub fn add_balance(&mut self, a: &Address, incr: &U256) { pub fn add_balance(&mut self, a: &Address, incr: &U256) {
let old = self.balance(a); trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a));
self.require(a, false).add_balance(incr); self.require(a, false).add_balance(incr);
trace!("state: add_balance({}, {}): {} -> {}\n", a, incr, old, self.balance(a));
} }
/// Subtract `decr` from the balance of account `a`. /// Subtract `decr` from the balance of account `a`.
pub fn sub_balance(&mut self, a: &Address, decr: &U256) { pub fn sub_balance(&mut self, a: &Address, decr: &U256) {
let old = self.balance(a); trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a));
self.require(a, false).sub_balance(decr); self.require(a, false).sub_balance(decr);
trace!("state: sub_balance({}, {}): {} -> {}\n", a, decr, old, self.balance(a));
} }
/// Subtracts `by` from the balance of `from` and adds it to that of `to`. /// Subtracts `by` from the balance of `from` and adds it to that of `to`.
@ -222,7 +220,7 @@ impl State {
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult { pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult {
// let old = self.to_pod(); // let old = self.to_pod();
let options = TransactOptions { tracing: tracing, check_nonce: true }; let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true };
let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options)); let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options));
// TODO uncomment once to_pod() works correctly. // TODO uncomment once to_pod() works correctly.

View File

@ -18,13 +18,13 @@
use util::{Bytes, Address, U256}; use util::{Bytes, Address, U256};
use action_params::ActionParams; use action_params::ActionParams;
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult}; use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
use trace::Tracer; use trace::{Tracer, VMTracer};
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
#[derive(Default)] #[derive(Default)]
pub struct ExecutiveTracer { pub struct ExecutiveTracer {
traces: Vec<Trace> traces: Vec<Trace>,
} }
impl Tracer for ExecutiveTracer { impl Tracer for ExecutiveTracer {
@ -40,8 +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: fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs: Vec<Trace>, delegate_call: bool) {
Vec<Trace>, delegate_call: bool) {
// don't trace if it's DELEGATECALL or CALLCODE. // don't trace if it's DELEGATECALL or CALLCODE.
if delegate_call { if delegate_call {
return; return;
@ -106,3 +105,46 @@ impl Tracer for ExecutiveTracer {
self.traces self.traces
} }
} }
/// Simple VM tracer. Traces all operations.
#[derive(Default)]
pub struct ExecutiveVMTracer {
data: VMTrace,
}
impl VMTracer for ExecutiveVMTracer {
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool {
self.data.operations.push(VMOperation {
pc: pc,
instruction: instruction,
gas_cost: gas_cost.clone(),
executed: None,
});
true
}
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
let ex = VMExecutedOperation {
gas_used: gas_used,
stack_push: stack_push.iter().cloned().collect(),
mem_diff: mem_diff.map(|(s, r)| MemoryDiff{ offset: s, data: r.iter().cloned().collect() }),
store_diff: store_diff.map(|(l, v)| StorageDiff{ location: l, value: v }),
};
self.data.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute").executed = Some(ex);
}
fn prepare_subtrace(&self, code: &Bytes) -> Self {
ExecutiveVMTracer { data: VMTrace {
parent_step: self.data.operations.len(),
code: code.clone(),
operations: vec![],
subs: vec![],
}}
}
fn done_subtrace(&mut self, sub: Self) {
self.data.subs.push(sub.data);
}
fn drain(mut self) -> Option<VMTrace> { self.data.subs.pop() }
}

View File

@ -31,9 +31,9 @@ pub use self::block::BlockTraces;
pub use self::config::{Config, Switch}; pub use self::config::{Config, Switch};
pub use self::db::TraceDB; pub use self::db::TraceDB;
pub use self::error::Error; pub use self::error::Error;
pub use types::trace_types::trace::Trace; pub use types::trace_types::trace::{Trace, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
pub use self::noop_tracer::NoopTracer; pub use self::noop_tracer::{NoopTracer, NoopVMTracer};
pub use self::executive_tracer::ExecutiveTracer; pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer};
pub use types::trace_types::filter::{Filter, AddressesFilter}; pub use types::trace_types::filter::{Filter, AddressesFilter};
pub use self::import::ImportRequest; pub use self::import::ImportRequest;
pub use self::localized::LocalizedTrace; pub use self::localized::LocalizedTrace;
@ -81,13 +81,32 @@ pub trait Tracer: Send {
/// 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>);
/// Spawn subracer which will be used to trace deeper levels of execution. /// Spawn subtracer which will be used to trace deeper levels of execution.
fn subtracer(&self) -> Self where Self: Sized; fn subtracer(&self) -> Self where Self: Sized;
/// Consumes self and returns all traces. /// Consumes self and returns all traces.
fn traces(self) -> Vec<Trace>; fn traces(self) -> Vec<Trace>;
} }
/// Used by executive to build VM traces.
pub trait VMTracer: Send {
/// Trace the preparation to execute a single instruction.
/// @returns true if `trace_executed` should be called.
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
/// Trace the finalised execution of a single instruction.
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn prepare_subtrace(&self, code: &Bytes) -> Self where Self: Sized;
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn done_subtrace(&mut self, sub: Self) where Self: Sized;
/// Consumes self and returns the VM trace.
fn drain(self) -> Option<VMTrace>;
}
/// `DbExtras` provides an interface to query extra data which is not stored in tracesdb, /// `DbExtras` provides an interface to query extra data which is not stored in tracesdb,
/// but necessary to work correctly. /// but necessary to work correctly.
pub trait DatabaseExtras { pub trait DatabaseExtras {

View File

@ -18,8 +18,8 @@
use util::{Bytes, Address, U256}; use util::{Bytes, Address, U256};
use action_params::ActionParams; use action_params::ActionParams;
use trace::Tracer; use trace::{Tracer, VMTracer};
use trace::trace::{Trace, Call, Create}; use trace::trace::{Trace, Call, Create, VMTrace};
/// Nonoperative tracer. Does not trace anything. /// Nonoperative tracer. Does not trace anything.
pub struct NoopTracer; pub struct NoopTracer;
@ -63,3 +63,23 @@ impl Tracer for NoopTracer {
vec![] vec![]
} }
} }
/// Nonoperative VM tracer. Does not trace anything.
pub struct NoopVMTracer;
impl VMTracer for NoopVMTracer {
/// Trace the preparation to execute a single instruction.
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
/// Trace the finalised execution of a single instruction.
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn prepare_subtrace(&self, _code: &Bytes) -> Self { NoopVMTracer }
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn done_subtrace(&mut self, _sub: Self) {}
/// Consumes self and returns all VM traces.
fn drain(self) -> Option<VMTrace> { None }
}

View File

@ -18,7 +18,7 @@
use util::numbers::*; use util::numbers::*;
use util::Bytes; use util::Bytes;
use trace::Trace; use trace::{Trace, VMTrace};
use types::log_entry::LogEntry; use types::log_entry::LogEntry;
use ipc::binary::BinaryConvertError; use ipc::binary::BinaryConvertError;
use std::fmt; use std::fmt;
@ -59,6 +59,8 @@ pub struct Executed {
pub output: Bytes, pub output: Bytes,
/// The trace of this transaction. /// The trace of this transaction.
pub trace: Option<Trace>, pub trace: Option<Trace>,
/// The VM trace of this transaction.
pub vm_trace: Option<VMTrace>,
} }
/// Result of executing the transaction. /// Result of executing the transaction.

View File

@ -349,6 +349,170 @@ impl Trace {
} }
} }
#[derive(Debug, Clone, PartialEq, Binary)]
/// A diff of some chunk of memory.
pub struct MemoryDiff {
/// Offset into memory the change begins.
pub offset: usize,
/// The changed data.
pub data: Bytes,
}
impl Encodable for MemoryDiff {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(2);
s.append(&self.offset);
s.append(&self.data);
}
}
impl Decodable for MemoryDiff {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
Ok(MemoryDiff {
offset: try!(d.val_at(0)),
data: try!(d.val_at(1)),
})
}
}
#[derive(Debug, Clone, PartialEq, Binary)]
/// A diff of some storage value.
pub struct StorageDiff {
/// Which key in storage is changed.
pub location: U256,
/// What the value has been changed to.
pub value: U256,
}
impl Encodable for StorageDiff {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(2);
s.append(&self.location);
s.append(&self.value);
}
}
impl Decodable for StorageDiff {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
Ok(StorageDiff {
location: try!(d.val_at(0)),
value: try!(d.val_at(1)),
})
}
}
#[derive(Debug, Clone, PartialEq, Binary)]
/// A record of an executed VM operation.
pub struct VMExecutedOperation {
/// The total gas used.
pub gas_used: U256,
/// The stack item placed, if any.
pub stack_push: Vec<U256>,
/// If altered, the memory delta.
pub mem_diff: Option<MemoryDiff>,
/// The altered storage value, if any.
pub store_diff: Option<StorageDiff>,
}
impl Encodable for VMExecutedOperation {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.gas_used);
s.append(&self.stack_push);
s.append(&self.mem_diff);
s.append(&self.store_diff);
}
}
impl Decodable for VMExecutedOperation {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
Ok(VMExecutedOperation {
gas_used: try!(d.val_at(0)),
stack_push: try!(d.val_at(1)),
mem_diff: try!(d.val_at(2)),
store_diff: try!(d.val_at(3)),
})
}
}
#[derive(Debug, Clone, PartialEq, Binary)]
/// A record of the execution of a single VM operation.
pub struct VMOperation {
/// The program counter.
pub pc: usize,
/// The instruction executed.
pub instruction: u8,
/// The gas cost for this instruction.
pub gas_cost: U256,
/// Information concerning the execution of the operation.
pub executed: Option<VMExecutedOperation>,
}
impl Encodable for VMOperation {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.pc);
s.append(&self.instruction);
s.append(&self.gas_cost);
s.append(&self.executed);
}
}
impl Decodable for VMOperation {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let res = VMOperation {
pc: try!(d.val_at(0)),
instruction: try!(d.val_at(1)),
gas_cost: try!(d.val_at(2)),
executed: try!(d.val_at(3)),
};
Ok(res)
}
}
#[derive(Debug, Clone, PartialEq, Binary, Default)]
/// A record of a full VM trace for a CALL/CREATE.
pub struct VMTrace {
/// The step (i.e. index into operations) at which this trace corresponds.
pub parent_step: usize,
/// The code to be executed.
pub code: Bytes,
/// The operations executed.
pub operations: Vec<VMOperation>,
/// The sub traces for each interior action performed as part of this call/create.
/// Thre is a 1:1 correspondance between these and a CALL/CREATE/CALLCODE/DELEGATECALL instruction.
pub subs: Vec<VMTrace>,
}
impl Encodable for VMTrace {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.parent_step);
s.append(&self.code);
s.append(&self.operations);
s.append(&self.subs);
}
}
impl Decodable for VMTrace {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let res = VMTrace {
parent_step: try!(d.val_at(0)),
code: try!(d.val_at(1)),
operations: try!(d.val_at(2)),
subs: try!(d.val_at(3)),
};
Ok(res)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use util::{Address, U256, FixedHash}; use util::{Address, U256, FixedHash};

View File

@ -40,7 +40,7 @@ pub struct Transaction {
/// To. /// To.
pub to: MaybeEmpty<Address>, pub to: MaybeEmpty<Address>,
/// Value. /// Value.
pub value: Uint pub value: Uint,
} }
#[cfg(test)] #[cfg(test)]

View File

@ -153,7 +153,7 @@ pub fn setup_rpc<T: Extendable>(server: T, deps: Arc<Dependencies>, apis: ApiSet
} }
}, },
Api::Ethcore => { Api::Ethcore => {
server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()) server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate())
}, },
Api::Traces => { Api::Traces => {
server.add_delegate(TracesClient::new(&deps.client).to_delegate()) server.add_delegate(TracesClient::new(&deps.client).to_delegate())

View File

@ -26,6 +26,7 @@ extern crate serde;
extern crate serde_json; extern crate serde_json;
extern crate jsonrpc_core; extern crate jsonrpc_core;
extern crate jsonrpc_http_server; extern crate jsonrpc_http_server;
#[macro_use]
extern crate ethcore_util as util; extern crate ethcore_util as util;
extern crate ethcore; extern crate ethcore;
extern crate ethsync; extern crate ethsync;

View File

@ -511,8 +511,8 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
.and_then(|(request, block_number,)| { .and_then(|(request, block_number,)| {
let signed = try!(self.sign_call(request)); let signed = try!(self.sign_call(request));
let r = match block_number { let r = match block_number {
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed), BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, false),
BlockNumber::Latest => take_weak!(self.client).call(&signed), BlockNumber::Latest => take_weak!(self.client).call(&signed, false),
_ => panic!("{:?}", block_number), _ => panic!("{:?}", block_number),
}; };
to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![]))) to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![])))
@ -524,8 +524,8 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
.and_then(|(request, block_number,)| { .and_then(|(request, block_number,)| {
let signed = try!(self.sign_call(request)); let signed = try!(self.sign_call(request));
let r = match block_number { let r = match block_number {
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed), BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, false),
BlockNumber::Latest => take_weak!(self.client).call(&signed), BlockNumber::Latest => take_weak!(self.client).call(&signed, false),
_ => return Err(Error::invalid_params()), _ => return Err(Error::invalid_params()),
}; };
to_value(&r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0))) to_value(&r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0)))

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Ethcore-specific rpc implementation. //! Ethcore-specific rpc implementation.
use util::{U256, Address, RotatingLogger}; use util::{U256, Address, RotatingLogger, FixedHash, Uint};
use util::network_settings::NetworkSettings; use util::network_settings::NetworkSettings;
use util::misc::version_data; use util::misc::version_data;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
@ -23,29 +23,99 @@ use std::ops::Deref;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use jsonrpc_core::*; use jsonrpc_core::*;
use ethcore::miner::MinerService; use ethcore::miner::MinerService;
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
use ethcore::client::BlockChainClient;
use ethcore::trace::VMTrace;
use v1::traits::Ethcore; use v1::traits::Ethcore;
use v1::types::Bytes; use v1::types::{Bytes, CallRequest};
/// Ethcore implementation. /// Ethcore implementation.
pub struct EthcoreClient<M> pub struct EthcoreClient<C, M> where
where M: MinerService { C: BlockChainClient,
M: MinerService {
client: Weak<C>,
miner: Weak<M>, miner: Weak<M>,
logger: Arc<RotatingLogger>, logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>, settings: Arc<NetworkSettings>,
} }
impl<M> EthcoreClient<M> where M: MinerService { impl<C, M> EthcoreClient<C, M> where C: BlockChainClient, M: MinerService {
/// Creates new `EthcoreClient`. /// Creates new `EthcoreClient`.
pub fn new(miner: &Arc<M>, logger: Arc<RotatingLogger>, settings: Arc<NetworkSettings>) -> Self { pub fn new(client: &Arc<C>, miner: &Arc<M>, logger: Arc<RotatingLogger>, settings: Arc<NetworkSettings>) -> Self {
EthcoreClient { EthcoreClient {
client: Arc::downgrade(client),
miner: Arc::downgrade(miner), miner: Arc::downgrade(miner),
logger: logger, logger: logger,
settings: settings, settings: settings,
} }
} }
// TODO: share with eth.rs
fn sign_call(&self, request: CallRequest) -> Result<SignedTransaction, Error> {
let client = take_weak!(self.client);
let miner = take_weak!(self.miner);
let from = request.from.unwrap_or(Address::zero());
Ok(EthTransaction {
nonce: request.nonce.unwrap_or_else(|| client.latest_nonce(&from)),
action: request.to.map_or(Action::Create, Action::Call),
gas: request.gas.unwrap_or(U256::from(50_000_000)),
gas_price: request.gas_price.unwrap_or_else(|| miner.sensible_gas_price()),
value: request.value.unwrap_or_else(U256::zero),
data: request.data.map_or_else(Vec::new, |d| d.to_vec())
}.fake_sign(from))
}
} }
impl<M> Ethcore for EthcoreClient<M> where M: MinerService + 'static { 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::<Vec<_>>();
ret.insert("ops".to_owned(), Value::Array(ops));
Value::Object(ret)
}
impl<C, M> Ethcore for EthcoreClient<C, M> where C: BlockChainClient + 'static, M: MinerService + 'static {
fn set_min_gas_price(&self, params: Params) -> Result<Value, Error> { fn set_min_gas_price(&self, params: Params) -> Result<Value, Error> {
from_params::<(U256,)>(params).and_then(|(gas_price,)| { from_params::<(U256,)>(params).and_then(|(gas_price,)| {
@ -135,4 +205,19 @@ impl<M> Ethcore for EthcoreClient<M> where M: MinerService + 'static {
let version = version_data(); let version = version_data();
to_value(&Bytes::new(version)) to_value(&Bytes::new(version))
} }
fn vm_trace_call(&self, params: Params) -> Result<Value, Error> {
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, true);
if let Ok(executed) = r {
if let Some(vm_trace) = executed.vm_trace {
return Ok(vm_trace_to_object(&vm_trace));
}
}
Ok(Value::Null)
})
}
} }

View File

@ -19,13 +19,12 @@ use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use std::str::FromStr; use std::str::FromStr;
use ethcore::client::{MiningBlockChainClient, BlockChainClient, Client, ClientConfig}; use ethcore::client::{BlockChainClient, Client, ClientConfig};
use ethcore::ids::BlockID; use ethcore::ids::BlockID;
use ethcore::spec::{Genesis, Spec}; use ethcore::spec::{Genesis, Spec};
use ethcore::block::Block; use ethcore::block::Block;
use ethcore::views::BlockView; use ethcore::views::BlockView;
use ethcore::ethereum; use ethcore::ethereum;
use ethcore::transaction::{Transaction, Action};
use ethcore::miner::{MinerService, ExternalMiner, Miner}; use ethcore::miner::{MinerService, ExternalMiner, Miner};
use devtools::RandomTempPath; use devtools::RandomTempPath;
use util::Hashable; use util::Hashable;

View File

@ -202,7 +202,7 @@ impl MinerService for TestMinerService {
self.latest_closed_block.lock().unwrap().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.balance(address).clone()) self.latest_closed_block.lock().unwrap().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.balance(address).clone())
} }
fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction) -> Result<Executed, ExecutionError> { fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction, _vm_tracing: bool) -> Result<Executed, ExecutionError> {
unimplemented!(); unimplemented!();
} }

View File

@ -365,7 +365,7 @@ fn rpc_eth_pending_transaction_by_hash() {
tester.miner.pending_transactions.lock().unwrap().insert(H256::zero(), tx); tester.miner.pending_transactions.lock().unwrap().insert(H256::zero(), tx);
} }
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#; let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#;
let request = r#"{ let request = r#"{
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "eth_getTransactionByHash", "method": "eth_getTransactionByHash",
@ -430,6 +430,7 @@ fn rpc_eth_call() {
contracts_created: vec![], contracts_created: vec![],
output: vec![0x12, 0x34, 0xff], output: vec![0x12, 0x34, 0xff],
trace: None, trace: None,
vm_trace: None,
}); });
let request = r#"{ let request = r#"{
@ -463,6 +464,7 @@ fn rpc_eth_call_default_block() {
contracts_created: vec![], contracts_created: vec![],
output: vec![0x12, 0x34, 0xff], output: vec![0x12, 0x34, 0xff],
trace: None, trace: None,
vm_trace: None,
}); });
let request = r#"{ let request = r#"{
@ -495,6 +497,7 @@ fn rpc_eth_estimate_gas() {
contracts_created: vec![], contracts_created: vec![],
output: vec![0x12, 0x34, 0xff], output: vec![0x12, 0x34, 0xff],
trace: None, trace: None,
vm_trace: None,
}); });
let request = r#"{ let request = r#"{
@ -528,6 +531,7 @@ fn rpc_eth_estimate_gas_default_block() {
contracts_created: vec![], contracts_created: vec![],
output: vec![0x12, 0x34, 0xff], output: vec![0x12, 0x34, 0xff],
trace: None, trace: None,
vm_trace: None,
}); });
let request = r#"{ let request = r#"{

View File

@ -18,6 +18,7 @@ use std::sync::Arc;
use std::str::FromStr; use std::str::FromStr;
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
use v1::{Ethcore, EthcoreClient}; use v1::{Ethcore, EthcoreClient};
use ethcore::client::{TestBlockChainClient};
use ethcore::miner::MinerService; use ethcore::miner::MinerService;
use v1::tests::helpers::TestMinerService; use v1::tests::helpers::TestMinerService;
use util::numbers::*; use util::numbers::*;
@ -25,10 +26,15 @@ use rustc_serialize::hex::FromHex;
use util::log::RotatingLogger; use util::log::RotatingLogger;
use util::network_settings::NetworkSettings; use util::network_settings::NetworkSettings;
fn blockchain_client() -> Arc<TestBlockChainClient> {
let client = TestBlockChainClient::new();
Arc::new(client)
}
fn miner_service() -> Arc<TestMinerService> { fn miner_service() -> Arc<TestMinerService> {
Arc::new(TestMinerService::default()) Arc::new(TestMinerService::default())
} }
fn logger() -> Arc<RotatingLogger> { fn logger() -> Arc<RotatingLogger> {
Arc::new(RotatingLogger::new("rpc=trace".to_owned())) Arc::new(RotatingLogger::new("rpc=trace".to_owned()))
} }
@ -45,14 +51,15 @@ fn settings() -> Arc<NetworkSettings> {
}) })
} }
fn ethcore_client(miner: &Arc<TestMinerService>) -> EthcoreClient<TestMinerService> { fn ethcore_client(client: &Arc<TestBlockChainClient>, miner: &Arc<TestMinerService>) -> EthcoreClient<TestBlockChainClient, TestMinerService> {
EthcoreClient::new(&miner, logger(), settings()) EthcoreClient::new(&client, &miner, logger(), settings())
} }
#[test] #[test]
fn rpc_ethcore_extra_data() { fn rpc_ethcore_extra_data() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -68,7 +75,8 @@ fn rpc_ethcore_default_extra_data() {
use util::ToPretty; use util::ToPretty;
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -81,7 +89,8 @@ fn rpc_ethcore_default_extra_data() {
#[test] #[test]
fn rpc_ethcore_gas_floor_target() { fn rpc_ethcore_gas_floor_target() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -94,7 +103,8 @@ fn rpc_ethcore_gas_floor_target() {
#[test] #[test]
fn rpc_ethcore_min_gas_price() { fn rpc_ethcore_min_gas_price() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -107,7 +117,8 @@ fn rpc_ethcore_min_gas_price() {
#[test] #[test]
fn rpc_ethcore_set_min_gas_price() { fn rpc_ethcore_set_min_gas_price() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -121,7 +132,8 @@ fn rpc_ethcore_set_min_gas_price() {
#[test] #[test]
fn rpc_ethcore_set_gas_floor_target() { fn rpc_ethcore_set_gas_floor_target() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -135,7 +147,8 @@ fn rpc_ethcore_set_gas_floor_target() {
#[test] #[test]
fn rpc_ethcore_set_extra_data() { fn rpc_ethcore_set_extra_data() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -149,7 +162,8 @@ fn rpc_ethcore_set_extra_data() {
#[test] #[test]
fn rpc_ethcore_set_author() { fn rpc_ethcore_set_author() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -163,10 +177,11 @@ fn rpc_ethcore_set_author() {
#[test] #[test]
fn rpc_ethcore_dev_logs() { fn rpc_ethcore_dev_logs() {
let miner = miner_service(); let miner = miner_service();
let client = blockchain_client();
let logger = logger(); let logger = logger();
logger.append("a".to_owned()); logger.append("a".to_owned());
logger.append("b".to_owned()); logger.append("b".to_owned());
let ethcore = EthcoreClient::new(&miner, logger.clone(), settings()).to_delegate(); let ethcore = EthcoreClient::new(&client, &miner, logger.clone(), settings()).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -179,7 +194,8 @@ fn rpc_ethcore_dev_logs() {
#[test] #[test]
fn rpc_ethcore_dev_logs_levels() { fn rpc_ethcore_dev_logs_levels() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -191,7 +207,8 @@ fn rpc_ethcore_dev_logs_levels() {
#[test] #[test]
fn rpc_ethcore_set_transactions_limit() { fn rpc_ethcore_set_transactions_limit() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -205,7 +222,8 @@ fn rpc_ethcore_set_transactions_limit() {
#[test] #[test]
fn rpc_ethcore_transactions_limit() { fn rpc_ethcore_transactions_limit() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -218,7 +236,8 @@ fn rpc_ethcore_transactions_limit() {
#[test] #[test]
fn rpc_ethcore_net_chain() { fn rpc_ethcore_net_chain() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -231,7 +250,8 @@ fn rpc_ethcore_net_chain() {
#[test] #[test]
fn rpc_ethcore_net_max_peers() { fn rpc_ethcore_net_max_peers() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -244,7 +264,8 @@ fn rpc_ethcore_net_max_peers() {
#[test] #[test]
fn rpc_ethcore_net_port() { fn rpc_ethcore_net_port() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -257,7 +278,8 @@ fn rpc_ethcore_net_port() {
#[test] #[test]
fn rpc_ethcore_rpc_settings() { fn rpc_ethcore_rpc_settings() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);
@ -270,7 +292,8 @@ fn rpc_ethcore_rpc_settings() {
#[test] #[test]
fn rpc_ethcore_node_name() { fn rpc_ethcore_node_name() {
let miner = miner_service(); let miner = miner_service();
let ethcore = ethcore_client(&miner).to_delegate(); let client = blockchain_client();
let ethcore = ethcore_client(&client, &miner).to_delegate();
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(ethcore); io.add_delegate(ethcore);

View File

@ -72,6 +72,8 @@ pub trait Ethcore: Sized + Send + Sync + 'static {
/// Returns default extra data /// Returns default extra data
fn default_extra_data(&self, _: Params) -> Result<Value, Error>; fn default_extra_data(&self, _: Params) -> Result<Value, Error>;
/// Executes the given call and returns the VM trace for it.
fn vm_trace_call(&self, _: Params) -> Result<Value, Error>;
/// Should be used to convert object to io delegate. /// Should be used to convert object to io delegate.
fn to_delegate(self) -> IoDelegate<Self> { fn to_delegate(self) -> IoDelegate<Self> {
@ -95,6 +97,8 @@ pub trait Ethcore: Sized + Send + Sync + 'static {
delegate.add_method("ethcore_nodeName", Ethcore::node_name); delegate.add_method("ethcore_nodeName", Ethcore::node_name);
delegate.add_method("ethcore_defaultExtraData", Ethcore::default_extra_data); delegate.add_method("ethcore_defaultExtraData", Ethcore::default_extra_data);
delegate.add_method("ethcore_vmTraceCall", Ethcore::vm_trace_call);
delegate delegate
} }
} }

View File

@ -103,7 +103,7 @@ mod tests {
fn test_serialize_block_transactions() { fn test_serialize_block_transactions() {
let t = BlockTransactions::Full(vec![Transaction::default()]); let t = BlockTransactions::Full(vec![Transaction::default()]);
let serialized = serde_json::to_string(&t).unwrap(); let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x"}]"#); assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}]"#);
let t = BlockTransactions::Hashes(vec![H256::default()]); let t = BlockTransactions::Hashes(vec![H256::default()]);
let serialized = serde_json::to_string(&t).unwrap(); let serialized = serde_json::to_string(&t).unwrap();

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 util::numbers::*; use util::numbers::*;
use ethcore::contract_address;
use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction}; use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction};
use v1::types::{Bytes, OptionalValue}; use v1::types::{Bytes, OptionalValue};
@ -46,7 +47,9 @@ pub struct Transaction {
/// Gas /// Gas
pub gas: U256, pub gas: U256,
/// Data /// Data
pub input: Bytes pub input: Bytes,
/// Creates contract
pub creates: OptionalValue<Address>,
} }
impl From<LocalizedTransaction> for Transaction { impl From<LocalizedTransaction> for Transaction {
@ -65,7 +68,11 @@ impl From<LocalizedTransaction> for Transaction {
value: t.value, value: t.value,
gas_price: t.gas_price, gas_price: t.gas_price,
gas: t.gas, gas: t.gas,
input: Bytes::new(t.data.clone()) input: Bytes::new(t.data.clone()),
creates: match t.action {
Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)),
Action::Call(_) => OptionalValue::Null,
},
} }
} }
} }
@ -86,7 +93,11 @@ impl From<SignedTransaction> for Transaction {
value: t.value, value: t.value,
gas_price: t.gas_price, gas_price: t.gas_price,
gas: t.gas, gas: t.gas,
input: Bytes::new(t.data.clone()) input: Bytes::new(t.data.clone()),
creates: match t.action {
Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)),
Action::Call(_) => OptionalValue::Null,
},
} }
} }
} }
@ -100,7 +111,7 @@ mod tests {
fn test_transaction_serialize() { fn test_transaction_serialize() {
let t = Transaction::default(); let t = Transaction::default();
let serialized = serde_json::to_string(&t).unwrap(); let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x"}"#); assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}"#);
} }
} }

View File

@ -24,6 +24,13 @@ pub use vector::*;
pub use numbers::*; pub use numbers::*;
pub use sha3::*; pub use sha3::*;
#[macro_export]
macro_rules! vec_into {
( $( $x:expr ),* ) => {
vec![ $( $x.into() ),* ]
}
}
#[macro_export] #[macro_export]
macro_rules! hash_map { macro_rules! hash_map {
() => { HashMap::new() }; () => { HashMap::new() };

View File

@ -338,6 +338,18 @@ impl<T> Encodable for Vec<T> where T: Encodable {
} }
} }
impl<T> Encodable for Option<T> where T: Encodable {
fn rlp_append(&self, s: &mut RlpStream) {
match *self {
None => { s.begin_list(0); },
Some(ref x) => {
s.begin_list(1);
s.append_internal(x);
}
}
}
}
impl<T> RlpEncodable for T where T: Encodable { impl<T> RlpEncodable for T where T: Encodable {
fn rlp_append(&self, s: &mut RlpStream) { fn rlp_append(&self, s: &mut RlpStream) {
Encodable::rlp_append(self, s) Encodable::rlp_append(self, s)

View File

@ -395,7 +395,7 @@ impl<'a> Decoder for BasicDecoder<'a> {
} }
impl<T> Decodable for T where T: FromBytes { impl<T> Decodable for T where T: FromBytes {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder { fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.read_value(| bytes | { decoder.read_value(| bytes | {
Ok(try!(T::from_bytes(bytes))) Ok(try!(T::from_bytes(bytes)))
}) })
@ -403,13 +403,19 @@ impl<T> Decodable for T where T: FromBytes {
} }
impl<T> Decodable for Vec<T> where T: Decodable { impl<T> Decodable for Vec<T> where T: Decodable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder { fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect() decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect()
} }
} }
impl<T> Decodable for Option<T> where T: Decodable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect::<Result<Vec<_>, DecoderError>>().map(|mut a| a.pop())
}
}
impl Decodable for Vec<u8> { impl Decodable for Vec<u8> {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder { fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.read_value(| bytes | { decoder.read_value(| bytes | {
let mut res = vec![]; let mut res = vec![];
res.extend_from_slice(bytes); res.extend_from_slice(bytes);
@ -418,22 +424,10 @@ impl Decodable for Vec<u8> {
} }
} }
impl<T> Decodable for Option<T> where T: Decodable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.read_value(| bytes | {
let res = match bytes.len() {
0 => None,
_ => Some(try!(T::decode(decoder)))
};
Ok(res)
})
}
}
macro_rules! impl_array_decodable { macro_rules! impl_array_decodable {
($index_type:ty, $len:expr ) => ( ($index_type:ty, $len:expr ) => (
impl<T> Decodable for [T; $len] where T: Decodable { impl<T> Decodable for [T; $len] where T: Decodable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder { fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let decoders = decoder.as_rlp(); let decoders = decoder.as_rlp();
let mut result: [T; $len] = unsafe { ::std::mem::uninitialized() }; let mut result: [T; $len] = unsafe { ::std::mem::uninitialized() };
@ -466,7 +460,7 @@ impl_array_decodable_recursive!(
); );
impl<T> RlpDecodable for T where T: Decodable { impl<T> RlpDecodable for T where T: Decodable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder { fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
Decodable::decode(decoder) Decodable::decode(decoder)
} }
} }
@ -489,7 +483,7 @@ impl FromBytes for DecodableU8 {
} }
impl RlpDecodable for u8 { impl RlpDecodable for u8 {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder { fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let u: DecodableU8 = try!(Decodable::decode(decoder)); let u: DecodableU8 = try!(Decodable::decode(decoder));
Ok(u.0) Ok(u.0)
} }