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:
parent
7ad9c73c75
commit
b17581d7de
@ -34,17 +34,8 @@
|
||||
"gasLimit": "0x2fefd8"
|
||||
},
|
||||
"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://e941c58fed2709d792f552f408d2162c3d0a5597d22d1da617a9c9e6181f3251056a96adb45ae22eba70119355227298dc7e6dff805b092bae7da2f8564de422@85.25.217.23:30303",
|
||||
"enode://f4b73c9d11a780293ff0ca7afa12c67797afdc33a4797a7c2ecc5b87e455b32a8b9e9804f2004072bac38350bf82d52521d1a09590d2079705fc8357aef2bf9c@71.202.223.50:56603",
|
||||
"enode://1173eea53e0cb2b8da92423e44cf4cbafbc8ea16c1558cf06e18dfc5a2fc9b140cc802a4362b4c773fb1442541e6f2a225b200bb4c1f6b347e7510a50fa4873f@104.41.138.167:30300",
|
||||
"enode://1aad341327808738ad34655611f1b13293c4155dde36c8e3788128829f15cc6db2da9435f29520553d4efc134aadc50115690194ac3af519aac7a388b524811e@109.188.125.2:30303"
|
||||
"enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
@ -436,7 +436,7 @@ impl<V> 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 view = HeaderView::new(&header);
|
||||
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)
|
||||
}));
|
||||
let balance = state.balance(&sender);
|
||||
// give the sender max balance
|
||||
// give the sender a decent balance
|
||||
state.sub_balance(&sender, &balance);
|
||||
state.add_balance(&sender, &U256::max_value());
|
||||
let options = TransactOptions { tracing: false, check_nonce: false };
|
||||
state.add_balance(&sender, &(U256::from(1) << 200));
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,8 @@ pub trait BlockChainClient : Sync + Send {
|
||||
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
|
||||
|
||||
/// 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.
|
||||
fn vm_factory(&self) -> &EvmFactory;
|
||||
|
@ -251,7 +251,7 @@ impl MiningBlockChainClient 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())
|
||||
}
|
||||
|
||||
|
@ -105,4 +105,10 @@ pub trait Ext {
|
||||
|
||||
/// Increments sstore refunds count by 1.
|
||||
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)>) {}
|
||||
}
|
||||
|
@ -124,6 +124,7 @@ pub struct InstructionInfo {
|
||||
pub side_effects: bool,
|
||||
pub tier: GasPriceTier
|
||||
}
|
||||
|
||||
impl InstructionInfo {
|
||||
pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo {
|
||||
InstructionInfo {
|
||||
|
@ -17,8 +17,9 @@
|
||||
///! Rust VM implementation
|
||||
|
||||
use common::*;
|
||||
use trace::VMTracer;
|
||||
use super::instructions as instructions;
|
||||
use super::instructions::Instruction;
|
||||
use super::instructions::{Instruction, get_info};
|
||||
use std::marker::Copy;
|
||||
use evm::{self, MessageCallResult, ContractCreateResult};
|
||||
|
||||
@ -69,6 +70,8 @@ trait Stack<T> {
|
||||
fn push(&mut self, elem: T);
|
||||
/// Get number of elements on Stack
|
||||
fn size(&self) -> usize;
|
||||
/// Returns all data on stack.
|
||||
fn peek_top(&mut self, no_of_elems: usize) -> &[T];
|
||||
}
|
||||
|
||||
struct VecStack<S> {
|
||||
@ -131,6 +134,11 @@ impl<S : fmt::Display> Stack<S> for VecStack<S> {
|
||||
fn size(&self) -> usize {
|
||||
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 {
|
||||
@ -293,10 +301,15 @@ impl evm::Evm for Interpreter {
|
||||
|
||||
while reader.position < code.len() {
|
||||
let instruction = code[reader.position];
|
||||
reader.position += 1;
|
||||
|
||||
// Calculate gas cost
|
||||
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(¤t_gas, &gas_cost));
|
||||
mem.expand(mem_size);
|
||||
current_gas = current_gas - gas_cost; //TODO: use operator -=
|
||||
@ -311,11 +324,20 @@ 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
|
||||
let result = try!(self.exec_instruction(
|
||||
current_gas, ¶ms, 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
|
||||
match result {
|
||||
InstructionResult::Ok => {},
|
||||
@ -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> {
|
||||
let gas_for_mem = |mem_size: U256| {
|
||||
let s = mem_size >> 5;
|
||||
@ -833,10 +880,12 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_instructions_requirements(&self,
|
||||
fn verify_instructions_requirements(
|
||||
&self,
|
||||
info: &instructions::InstructionInfo,
|
||||
stack_limit: usize,
|
||||
stack: &Stack<U256>) -> Result<(), evm::Error> {
|
||||
stack: &Stack<U256>
|
||||
) -> Result<(), evm::Error> {
|
||||
if !stack.has(info.args) {
|
||||
Err(evm::Error::StackUnderflow {
|
||||
instruction: info.name,
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
//! Just in time compiler execution environment.
|
||||
use common::*;
|
||||
use trace::VMTracer;
|
||||
use evmjit;
|
||||
use evm;
|
||||
|
||||
|
@ -33,3 +33,4 @@ pub use self::evm::{Evm, Error, Result};
|
||||
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult};
|
||||
pub use self::factory::{Factory, VMType};
|
||||
pub use self::schedule::Schedule;
|
||||
pub use self::instructions::get_info;
|
||||
|
@ -21,9 +21,8 @@ use engine::*;
|
||||
use evm::{self, Ext, Factory};
|
||||
use externalities::*;
|
||||
use substate::*;
|
||||
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer};
|
||||
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
|
||||
use crossbeam;
|
||||
|
||||
pub use types::executed::{Executed, ExecutionResult};
|
||||
|
||||
/// 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 {
|
||||
/// Enable call tracing.
|
||||
pub tracing: bool,
|
||||
/// Enable VM tracing.
|
||||
pub vm_tracing: bool,
|
||||
/// Check transaction nonce before execution.
|
||||
pub check_nonce: bool,
|
||||
}
|
||||
@ -80,21 +81,40 @@ impl<'a> Executive<'a> {
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer)
|
||||
pub fn as_externalities<'_, T, V>(
|
||||
&'_ 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.
|
||||
pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> {
|
||||
let check = options.check_nonce;
|
||||
match options.tracing {
|
||||
true => self.transact_with_tracer(t, check, ExecutiveTracer::default()),
|
||||
false => self.transact_with_tracer(t, check, NoopTracer),
|
||||
true => match options.vm_tracing {
|
||||
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
|
||||
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 message = format!("Transaction malformed: {:?}", e);
|
||||
ExecutionError::TransactionMalformed(message)
|
||||
@ -154,7 +174,7 @@ impl<'a> Executive<'a> {
|
||||
code: Some(t.data.clone()),
|
||||
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) => {
|
||||
let params = ActionParams {
|
||||
@ -170,20 +190,26 @@ impl<'a> Executive<'a> {
|
||||
};
|
||||
// TODO: move output upstream
|
||||
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!
|
||||
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)
|
||||
-> evm::Result where T: Tracer {
|
||||
fn exec_vm<T, V>(
|
||||
&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
|
||||
if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
|
||||
let vm_factory = self.vm_factory;
|
||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer);
|
||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer);
|
||||
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
|
||||
return vm_factory.create().exec(params, &mut ext);
|
||||
}
|
||||
@ -193,7 +219,7 @@ impl<'a> Executive<'a> {
|
||||
// https://github.com/aturon/crossbeam/issues/16
|
||||
crossbeam::scope(|scope| {
|
||||
let vm_factory = self.vm_factory;
|
||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer);
|
||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer);
|
||||
|
||||
scope.spawn(move || {
|
||||
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).
|
||||
/// Modifies the substate and the output.
|
||||
/// Returns either gas_left or `evm::Error`.
|
||||
pub fn call<T>(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef, tracer: &mut T)
|
||||
-> evm::Result where T: Tracer {
|
||||
pub fn call<T, V>(
|
||||
&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
|
||||
self.state.snapshot();
|
||||
|
||||
@ -264,16 +296,22 @@ impl<'a> Executive<'a> {
|
||||
let trace_info = tracer.prepare_trace_call(¶ms);
|
||||
let mut trace_output = tracer.prepare_trace_output();
|
||||
let mut subtracer = tracer.subtracer();
|
||||
|
||||
let gas = params.gas;
|
||||
|
||||
if params.code.is_some() {
|
||||
// part of substate that may be reverted
|
||||
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 = {
|
||||
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);
|
||||
|
||||
let traces = subtracer.traces();
|
||||
@ -307,8 +345,13 @@ impl<'a> Executive<'a> {
|
||||
/// Creates contract with given contract params.
|
||||
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
|
||||
/// Modifies the substate.
|
||||
pub fn create<T>(&mut self, params: ActionParams, substate: &mut Substate, tracer: &mut T) -> evm::Result where T:
|
||||
Tracer {
|
||||
pub fn create<T, V>(
|
||||
&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
|
||||
self.state.snapshot();
|
||||
|
||||
@ -330,10 +373,14 @@ impl<'a> Executive<'a> {
|
||||
let gas = params.gas;
|
||||
let created = params.address.clone();
|
||||
|
||||
let mut subvmtracer = vm_tracer.prepare_subtrace(¶ms.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed"));
|
||||
|
||||
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 {
|
||||
Ok(gas_left) => tracer.trace_create(
|
||||
trace_info,
|
||||
@ -351,7 +398,15 @@ impl<'a> Executive<'a> {
|
||||
}
|
||||
|
||||
/// 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);
|
||||
|
||||
// refunds from SSTORE nonzero -> zero
|
||||
@ -394,6 +449,7 @@ impl<'a> Executive<'a> {
|
||||
contracts_created: vec![],
|
||||
output: output,
|
||||
trace: trace,
|
||||
vm_trace: vm_trace,
|
||||
})
|
||||
},
|
||||
_ => {
|
||||
@ -406,6 +462,7 @@ impl<'a> Executive<'a> {
|
||||
contracts_created: substate.contracts_created,
|
||||
output: output,
|
||||
trace: trace,
|
||||
vm_trace: vm_trace,
|
||||
})
|
||||
},
|
||||
}
|
||||
@ -438,6 +495,7 @@ mod tests {
|
||||
use tests::helpers::*;
|
||||
use trace::trace;
|
||||
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer};
|
||||
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
|
||||
|
||||
#[test]
|
||||
fn test_contract_address() {
|
||||
@ -466,7 +524,7 @@ mod tests {
|
||||
|
||||
let gas_left = {
|
||||
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));
|
||||
@ -525,7 +583,7 @@ mod tests {
|
||||
|
||||
let gas_left = {
|
||||
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));
|
||||
@ -542,7 +600,7 @@ mod tests {
|
||||
// 52
|
||||
// 60 1d - push 29
|
||||
// 60 03 - push 3
|
||||
// 60 17 - push 17
|
||||
// 60 17 - push 23
|
||||
// f0 - create
|
||||
// 60 00 - push 0
|
||||
// 55 sstore
|
||||
@ -578,13 +636,16 @@ mod tests {
|
||||
let engine = TestEngine::new(5);
|
||||
let mut substate = Substate::new();
|
||||
let mut tracer = ExecutiveTracer::default();
|
||||
let mut vm_tracer = ExecutiveVMTracer::default();
|
||||
|
||||
let gas_left = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
|
||||
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 {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
@ -615,7 +676,39 @@ mod tests {
|
||||
}]
|
||||
}];
|
||||
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}
|
||||
@ -650,12 +743,15 @@ mod tests {
|
||||
let engine = TestEngine::new(5);
|
||||
let mut substate = Substate::new();
|
||||
let mut tracer = ExecutiveTracer::default();
|
||||
let mut vm_tracer = ExecutiveVMTracer::default();
|
||||
|
||||
let gas_left = {
|
||||
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 {
|
||||
depth: 0,
|
||||
action: trace::Action::Create(trace::Create {
|
||||
@ -671,9 +767,23 @@ mod tests {
|
||||
}),
|
||||
subs: vec![]
|
||||
}];
|
||||
|
||||
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}
|
||||
@ -722,7 +832,7 @@ mod tests {
|
||||
|
||||
let gas_left = {
|
||||
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));
|
||||
@ -774,7 +884,7 @@ mod tests {
|
||||
|
||||
{
|
||||
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);
|
||||
@ -835,7 +945,7 @@ mod tests {
|
||||
|
||||
let gas_left = {
|
||||
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));
|
||||
@ -880,7 +990,7 @@ mod tests {
|
||||
|
||||
let gas_left = {
|
||||
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));
|
||||
@ -913,7 +1023,7 @@ mod tests {
|
||||
|
||||
let executed = {
|
||||
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()
|
||||
};
|
||||
|
||||
@ -947,7 +1057,7 @@ mod tests {
|
||||
|
||||
let res = {
|
||||
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)
|
||||
};
|
||||
|
||||
@ -979,7 +1089,7 @@ mod tests {
|
||||
|
||||
let res = {
|
||||
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)
|
||||
};
|
||||
|
||||
@ -1013,7 +1123,7 @@ mod tests {
|
||||
|
||||
let res = {
|
||||
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)
|
||||
};
|
||||
|
||||
@ -1047,7 +1157,7 @@ mod tests {
|
||||
|
||||
let res = {
|
||||
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)
|
||||
};
|
||||
|
||||
@ -1082,7 +1192,7 @@ mod tests {
|
||||
|
||||
let result = {
|
||||
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 {
|
||||
|
@ -21,7 +21,7 @@ use engine::*;
|
||||
use executive::*;
|
||||
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
|
||||
use substate::*;
|
||||
use trace::Tracer;
|
||||
use trace::{Tracer, VMTracer};
|
||||
|
||||
/// Policy for handling output data on `RETURN` opcode.
|
||||
pub enum OutputPolicy<'a, 'b> {
|
||||
@ -55,7 +55,7 @@ impl OriginInfo {
|
||||
}
|
||||
|
||||
/// 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,
|
||||
env_info: &'a EnvInfo,
|
||||
engine: &'a Engine,
|
||||
@ -66,10 +66,10 @@ pub struct Externalities<'a, T> where T: 'a + Tracer {
|
||||
schedule: Schedule,
|
||||
output: OutputPolicy<'a, 'a>,
|
||||
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))]
|
||||
/// Basic `Externalities` constructor.
|
||||
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,
|
||||
output: OutputPolicy<'a, 'a>,
|
||||
tracer: &'a mut T,
|
||||
vm_tracer: &'a mut V,
|
||||
) -> Self {
|
||||
Externalities {
|
||||
state: state,
|
||||
@ -93,11 +94,12 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer {
|
||||
schedule: engine.schedule(env_info),
|
||||
output: output,
|
||||
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 {
|
||||
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);
|
||||
|
||||
// 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) => {
|
||||
self.substate.contracts_created.push(address.clone());
|
||||
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);
|
||||
|
||||
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),
|
||||
_ => MessageCallResult::Failed
|
||||
}
|
||||
@ -286,6 +288,14 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
|
||||
fn inc_sstore_clears(&mut self) {
|
||||
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)]
|
||||
@ -297,7 +307,7 @@ mod tests {
|
||||
use substate::*;
|
||||
use tests::helpers::*;
|
||||
use super::*;
|
||||
use trace::{NoopTracer};
|
||||
use trace::{NoopTracer, NoopVMTracer};
|
||||
|
||||
fn get_test_origin() -> OriginInfo {
|
||||
OriginInfo {
|
||||
@ -349,9 +359,10 @@ mod tests {
|
||||
let mut setup = TestSetup::new();
|
||||
let state = setup.state.reference_mut();
|
||||
let mut tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
|
||||
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);
|
||||
}
|
||||
@ -361,9 +372,10 @@ mod tests {
|
||||
let mut setup = TestSetup::new();
|
||||
let state = setup.state.reference_mut();
|
||||
let mut tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
|
||||
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());
|
||||
|
||||
@ -383,9 +395,10 @@ mod tests {
|
||||
}
|
||||
let state = setup.state.reference_mut();
|
||||
let mut tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
|
||||
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());
|
||||
|
||||
@ -398,9 +411,10 @@ mod tests {
|
||||
let mut setup = TestSetup::new();
|
||||
let state = setup.state.reference_mut();
|
||||
let mut tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
|
||||
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![];
|
||||
|
||||
@ -423,10 +437,11 @@ mod tests {
|
||||
let mut setup = TestSetup::new();
|
||||
let state = setup.state.reference_mut();
|
||||
let mut tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@ -440,10 +455,11 @@ mod tests {
|
||||
let mut setup = TestSetup::new();
|
||||
let state = setup.state.reference_mut();
|
||||
let mut tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ use substate::*;
|
||||
use tests::helpers::*;
|
||||
use ethjson;
|
||||
use trace::{Tracer, NoopTracer};
|
||||
use trace::{VMTracer, NoopVMTracer};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct CallCreate {
|
||||
@ -48,14 +49,15 @@ impl From<ethjson::vm::Call> for CallCreate {
|
||||
|
||||
/// Tiny wrapper around executive externalities.
|
||||
/// Stores callcreates.
|
||||
struct TestExt<'a, T> where T: 'a + Tracer {
|
||||
ext: Externalities<'a, T>,
|
||||
struct TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
ext: Externalities<'a, T, V>,
|
||||
callcreates: Vec<CallCreate>,
|
||||
contract_address: Address
|
||||
}
|
||||
|
||||
impl<'a, T> TestExt<'a, T> where T: 'a + Tracer {
|
||||
fn new(state: &'a mut State,
|
||||
impl<'a, T, V> TestExt<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
|
||||
fn new(
|
||||
state: &'a mut State,
|
||||
info: &'a EnvInfo,
|
||||
engine: &'a Engine,
|
||||
vm_factory: &'a Factory,
|
||||
@ -64,16 +66,18 @@ impl<'a, T> TestExt<'a, T> where T: 'a + Tracer {
|
||||
substate: &'a mut Substate,
|
||||
output: OutputPolicy<'a, 'a>,
|
||||
address: Address,
|
||||
tracer: &'a mut T) -> Self {
|
||||
tracer: &'a mut T,
|
||||
vm_tracer: &'a mut V,
|
||||
) -> Self {
|
||||
TestExt {
|
||||
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![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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 tracer = NoopTracer;
|
||||
let mut vm_tracer = NoopVMTracer;
|
||||
let mut output = vec![];
|
||||
|
||||
// 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),
|
||||
params.address.clone(),
|
||||
&mut tracer,
|
||||
&mut vm_tracer,
|
||||
);
|
||||
let evm = vm_factory.create();
|
||||
let res = evm.exec(params, &mut ex);
|
||||
|
@ -141,3 +141,5 @@ mod tests;
|
||||
mod json_tests;
|
||||
|
||||
pub use types::*;
|
||||
pub use evm::get_info;
|
||||
pub use executive::contract_address;
|
@ -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();
|
||||
match sealing_work.peek_last_ref() {
|
||||
Some(work) => {
|
||||
@ -277,12 +277,13 @@ impl MinerService for Miner {
|
||||
// give the sender max balance
|
||||
state.sub_balance(&sender, &balance);
|
||||
state.add_balance(&sender, &U256::max_value());
|
||||
let options = TransactOptions { tracing: false, 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)
|
||||
},
|
||||
None => {
|
||||
chain.call(t)
|
||||
chain.call(t, vm_tracing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ pub trait MinerService : Send + Sync {
|
||||
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256;
|
||||
|
||||
/// 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.
|
||||
fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256;
|
||||
|
@ -183,16 +183,14 @@ impl State {
|
||||
|
||||
/// Add `incr` to the balance of account `a`.
|
||||
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);
|
||||
trace!("state: add_balance({}, {}): {} -> {}\n", a, incr, old, self.balance(a));
|
||||
}
|
||||
|
||||
/// Subtract `decr` from the balance of account `a`.
|
||||
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);
|
||||
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`.
|
||||
@ -222,7 +220,7 @@ impl State {
|
||||
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult {
|
||||
// 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));
|
||||
|
||||
// TODO uncomment once to_pod() works correctly.
|
||||
|
@ -18,13 +18,13 @@
|
||||
|
||||
use util::{Bytes, Address, U256};
|
||||
use action_params::ActionParams;
|
||||
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult};
|
||||
use trace::Tracer;
|
||||
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
|
||||
use trace::{Tracer, VMTracer};
|
||||
|
||||
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
|
||||
#[derive(Default)]
|
||||
pub struct ExecutiveTracer {
|
||||
traces: Vec<Trace>
|
||||
traces: Vec<Trace>,
|
||||
}
|
||||
|
||||
impl Tracer for ExecutiveTracer {
|
||||
@ -40,8 +40,7 @@ impl Tracer for ExecutiveTracer {
|
||||
Some(vec![])
|
||||
}
|
||||
|
||||
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs:
|
||||
Vec<Trace>, delegate_call: bool) {
|
||||
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs: Vec<Trace>, delegate_call: bool) {
|
||||
// don't trace if it's DELEGATECALL or CALLCODE.
|
||||
if delegate_call {
|
||||
return;
|
||||
@ -106,3 +105,46 @@ impl Tracer for ExecutiveTracer {
|
||||
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() }
|
||||
}
|
||||
|
@ -31,9 +31,9 @@ pub use self::block::BlockTraces;
|
||||
pub use self::config::{Config, Switch};
|
||||
pub use self::db::TraceDB;
|
||||
pub use self::error::Error;
|
||||
pub use types::trace_types::trace::Trace;
|
||||
pub use self::noop_tracer::NoopTracer;
|
||||
pub use self::executive_tracer::ExecutiveTracer;
|
||||
pub use types::trace_types::trace::{Trace, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
|
||||
pub use self::noop_tracer::{NoopTracer, NoopVMTracer};
|
||||
pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer};
|
||||
pub use types::trace_types::filter::{Filter, AddressesFilter};
|
||||
pub use self::import::ImportRequest;
|
||||
pub use self::localized::LocalizedTrace;
|
||||
@ -81,13 +81,32 @@ pub trait Tracer: Send {
|
||||
/// Stores failed create 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;
|
||||
|
||||
/// Consumes self and returns all traces.
|
||||
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,
|
||||
/// but necessary to work correctly.
|
||||
pub trait DatabaseExtras {
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
use util::{Bytes, Address, U256};
|
||||
use action_params::ActionParams;
|
||||
use trace::Tracer;
|
||||
use trace::trace::{Trace, Call, Create};
|
||||
use trace::{Tracer, VMTracer};
|
||||
use trace::trace::{Trace, Call, Create, VMTrace};
|
||||
|
||||
/// Nonoperative tracer. Does not trace anything.
|
||||
pub struct NoopTracer;
|
||||
@ -63,3 +63,23 @@ impl Tracer for NoopTracer {
|
||||
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 }
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
use util::numbers::*;
|
||||
use util::Bytes;
|
||||
use trace::Trace;
|
||||
use trace::{Trace, VMTrace};
|
||||
use types::log_entry::LogEntry;
|
||||
use ipc::binary::BinaryConvertError;
|
||||
use std::fmt;
|
||||
@ -59,6 +59,8 @@ pub struct Executed {
|
||||
pub output: Bytes,
|
||||
/// The trace of this transaction.
|
||||
pub trace: Option<Trace>,
|
||||
/// The VM trace of this transaction.
|
||||
pub vm_trace: Option<VMTrace>,
|
||||
}
|
||||
|
||||
/// Result of executing the transaction.
|
||||
|
@ -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)]
|
||||
mod tests {
|
||||
use util::{Address, U256, FixedHash};
|
||||
|
@ -40,7 +40,7 @@ pub struct Transaction {
|
||||
/// To.
|
||||
pub to: MaybeEmpty<Address>,
|
||||
/// Value.
|
||||
pub value: Uint
|
||||
pub value: Uint,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -153,7 +153,7 @@ pub fn setup_rpc<T: Extendable>(server: T, deps: Arc<Dependencies>, apis: ApiSet
|
||||
}
|
||||
},
|
||||
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 => {
|
||||
server.add_delegate(TracesClient::new(&deps.client).to_delegate())
|
||||
|
@ -26,6 +26,7 @@ extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate jsonrpc_core;
|
||||
extern crate jsonrpc_http_server;
|
||||
#[macro_use]
|
||||
extern crate ethcore_util as util;
|
||||
extern crate ethcore;
|
||||
extern crate ethsync;
|
||||
|
@ -511,8 +511,8 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
|
||||
.and_then(|(request, block_number,)| {
|
||||
let signed = try!(self.sign_call(request));
|
||||
let r = match block_number {
|
||||
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed),
|
||||
BlockNumber::Latest => take_weak!(self.client).call(&signed),
|
||||
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, false),
|
||||
BlockNumber::Latest => take_weak!(self.client).call(&signed, false),
|
||||
_ => panic!("{:?}", block_number),
|
||||
};
|
||||
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,)| {
|
||||
let signed = try!(self.sign_call(request));
|
||||
let r = match block_number {
|
||||
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed),
|
||||
BlockNumber::Latest => take_weak!(self.client).call(&signed),
|
||||
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, false),
|
||||
BlockNumber::Latest => take_weak!(self.client).call(&signed, false),
|
||||
_ => return Err(Error::invalid_params()),
|
||||
};
|
||||
to_value(&r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0)))
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Ethcore-specific rpc implementation.
|
||||
use util::{U256, Address, RotatingLogger};
|
||||
use util::{U256, Address, RotatingLogger, FixedHash, Uint};
|
||||
use util::network_settings::NetworkSettings;
|
||||
use util::misc::version_data;
|
||||
use std::sync::{Arc, Weak};
|
||||
@ -23,29 +23,99 @@ use std::ops::Deref;
|
||||
use std::collections::BTreeMap;
|
||||
use jsonrpc_core::*;
|
||||
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::types::Bytes;
|
||||
use v1::types::{Bytes, CallRequest};
|
||||
|
||||
/// Ethcore implementation.
|
||||
pub struct EthcoreClient<M>
|
||||
where M: MinerService {
|
||||
pub struct EthcoreClient<C, M> where
|
||||
C: BlockChainClient,
|
||||
M: MinerService {
|
||||
|
||||
client: Weak<C>,
|
||||
miner: Weak<M>,
|
||||
logger: Arc<RotatingLogger>,
|
||||
settings: Arc<NetworkSettings>,
|
||||
}
|
||||
|
||||
impl<M> EthcoreClient<M> where M: MinerService {
|
||||
impl<C, M> EthcoreClient<C, M> where C: BlockChainClient, M: MinerService {
|
||||
/// 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 {
|
||||
client: Arc::downgrade(client),
|
||||
miner: Arc::downgrade(miner),
|
||||
logger: logger,
|
||||
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> {
|
||||
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();
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -19,13 +19,12 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::str::FromStr;
|
||||
|
||||
use ethcore::client::{MiningBlockChainClient, BlockChainClient, Client, ClientConfig};
|
||||
use ethcore::client::{BlockChainClient, Client, ClientConfig};
|
||||
use ethcore::ids::BlockID;
|
||||
use ethcore::spec::{Genesis, Spec};
|
||||
use ethcore::block::Block;
|
||||
use ethcore::views::BlockView;
|
||||
use ethcore::ethereum;
|
||||
use ethcore::transaction::{Transaction, Action};
|
||||
use ethcore::miner::{MinerService, ExternalMiner, Miner};
|
||||
use devtools::RandomTempPath;
|
||||
use util::Hashable;
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction) -> Result<Executed, ExecutionError> {
|
||||
fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction, _vm_tracing: bool) -> Result<Executed, ExecutionError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
|
@ -365,7 +365,7 @@ fn rpc_eth_pending_transaction_by_hash() {
|
||||
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#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getTransactionByHash",
|
||||
@ -430,6 +430,7 @@ fn rpc_eth_call() {
|
||||
contracts_created: vec![],
|
||||
output: vec![0x12, 0x34, 0xff],
|
||||
trace: None,
|
||||
vm_trace: None,
|
||||
});
|
||||
|
||||
let request = r#"{
|
||||
@ -463,6 +464,7 @@ fn rpc_eth_call_default_block() {
|
||||
contracts_created: vec![],
|
||||
output: vec![0x12, 0x34, 0xff],
|
||||
trace: None,
|
||||
vm_trace: None,
|
||||
});
|
||||
|
||||
let request = r#"{
|
||||
@ -495,6 +497,7 @@ fn rpc_eth_estimate_gas() {
|
||||
contracts_created: vec![],
|
||||
output: vec![0x12, 0x34, 0xff],
|
||||
trace: None,
|
||||
vm_trace: None,
|
||||
});
|
||||
|
||||
let request = r#"{
|
||||
@ -528,6 +531,7 @@ fn rpc_eth_estimate_gas_default_block() {
|
||||
contracts_created: vec![],
|
||||
output: vec![0x12, 0x34, 0xff],
|
||||
trace: None,
|
||||
vm_trace: None,
|
||||
});
|
||||
|
||||
let request = r#"{
|
||||
|
@ -18,6 +18,7 @@ use std::sync::Arc;
|
||||
use std::str::FromStr;
|
||||
use jsonrpc_core::IoHandler;
|
||||
use v1::{Ethcore, EthcoreClient};
|
||||
use ethcore::client::{TestBlockChainClient};
|
||||
use ethcore::miner::MinerService;
|
||||
use v1::tests::helpers::TestMinerService;
|
||||
use util::numbers::*;
|
||||
@ -25,10 +26,15 @@ use rustc_serialize::hex::FromHex;
|
||||
use util::log::RotatingLogger;
|
||||
use util::network_settings::NetworkSettings;
|
||||
|
||||
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
||||
let client = TestBlockChainClient::new();
|
||||
Arc::new(client)
|
||||
}
|
||||
|
||||
fn miner_service() -> Arc<TestMinerService> {
|
||||
Arc::new(TestMinerService::default())
|
||||
}
|
||||
|
||||
fn logger() -> Arc<RotatingLogger> {
|
||||
Arc::new(RotatingLogger::new("rpc=trace".to_owned()))
|
||||
}
|
||||
@ -45,14 +51,15 @@ fn settings() -> Arc<NetworkSettings> {
|
||||
})
|
||||
}
|
||||
|
||||
fn ethcore_client(miner: &Arc<TestMinerService>) -> EthcoreClient<TestMinerService> {
|
||||
EthcoreClient::new(&miner, logger(), settings())
|
||||
fn ethcore_client(client: &Arc<TestBlockChainClient>, miner: &Arc<TestMinerService>) -> EthcoreClient<TestBlockChainClient, TestMinerService> {
|
||||
EthcoreClient::new(&client, &miner, logger(), settings())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rpc_ethcore_extra_data() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -68,7 +75,8 @@ fn rpc_ethcore_default_extra_data() {
|
||||
use util::ToPretty;
|
||||
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -81,7 +89,8 @@ fn rpc_ethcore_default_extra_data() {
|
||||
#[test]
|
||||
fn rpc_ethcore_gas_floor_target() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -94,7 +103,8 @@ fn rpc_ethcore_gas_floor_target() {
|
||||
#[test]
|
||||
fn rpc_ethcore_min_gas_price() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -107,7 +117,8 @@ fn rpc_ethcore_min_gas_price() {
|
||||
#[test]
|
||||
fn rpc_ethcore_set_min_gas_price() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -121,7 +132,8 @@ fn rpc_ethcore_set_min_gas_price() {
|
||||
#[test]
|
||||
fn rpc_ethcore_set_gas_floor_target() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -135,7 +147,8 @@ fn rpc_ethcore_set_gas_floor_target() {
|
||||
#[test]
|
||||
fn rpc_ethcore_set_extra_data() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -149,7 +162,8 @@ fn rpc_ethcore_set_extra_data() {
|
||||
#[test]
|
||||
fn rpc_ethcore_set_author() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -163,10 +177,11 @@ fn rpc_ethcore_set_author() {
|
||||
#[test]
|
||||
fn rpc_ethcore_dev_logs() {
|
||||
let miner = miner_service();
|
||||
let client = blockchain_client();
|
||||
let logger = logger();
|
||||
logger.append("a".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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -179,7 +194,8 @@ fn rpc_ethcore_dev_logs() {
|
||||
#[test]
|
||||
fn rpc_ethcore_dev_logs_levels() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -191,7 +207,8 @@ fn rpc_ethcore_dev_logs_levels() {
|
||||
#[test]
|
||||
fn rpc_ethcore_set_transactions_limit() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -205,7 +222,8 @@ fn rpc_ethcore_set_transactions_limit() {
|
||||
#[test]
|
||||
fn rpc_ethcore_transactions_limit() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -218,7 +236,8 @@ fn rpc_ethcore_transactions_limit() {
|
||||
#[test]
|
||||
fn rpc_ethcore_net_chain() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -231,7 +250,8 @@ fn rpc_ethcore_net_chain() {
|
||||
#[test]
|
||||
fn rpc_ethcore_net_max_peers() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -244,7 +264,8 @@ fn rpc_ethcore_net_max_peers() {
|
||||
#[test]
|
||||
fn rpc_ethcore_net_port() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -257,7 +278,8 @@ fn rpc_ethcore_net_port() {
|
||||
#[test]
|
||||
fn rpc_ethcore_rpc_settings() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
@ -270,7 +292,8 @@ fn rpc_ethcore_rpc_settings() {
|
||||
#[test]
|
||||
fn rpc_ethcore_node_name() {
|
||||
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();
|
||||
io.add_delegate(ethcore);
|
||||
|
||||
|
@ -72,6 +72,8 @@ pub trait Ethcore: Sized + Send + Sync + 'static {
|
||||
/// Returns default extra data
|
||||
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.
|
||||
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_defaultExtraData", Ethcore::default_extra_data);
|
||||
|
||||
delegate.add_method("ethcore_vmTraceCall", Ethcore::vm_trace_call);
|
||||
|
||||
delegate
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ mod tests {
|
||||
fn test_serialize_block_transactions() {
|
||||
let t = BlockTransactions::Full(vec![Transaction::default()]);
|
||||
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 serialized = serde_json::to_string(&t).unwrap();
|
||||
|
@ -15,6 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::numbers::*;
|
||||
use ethcore::contract_address;
|
||||
use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction};
|
||||
use v1::types::{Bytes, OptionalValue};
|
||||
|
||||
@ -46,7 +47,9 @@ pub struct Transaction {
|
||||
/// Gas
|
||||
pub gas: U256,
|
||||
/// Data
|
||||
pub input: Bytes
|
||||
pub input: Bytes,
|
||||
/// Creates contract
|
||||
pub creates: OptionalValue<Address>,
|
||||
}
|
||||
|
||||
impl From<LocalizedTransaction> for Transaction {
|
||||
@ -65,7 +68,11 @@ impl From<LocalizedTransaction> for Transaction {
|
||||
value: t.value,
|
||||
gas_price: t.gas_price,
|
||||
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,
|
||||
gas_price: t.gas_price,
|
||||
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() {
|
||||
let t = Transaction::default();
|
||||
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}"#);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,13 @@ pub use vector::*;
|
||||
pub use numbers::*;
|
||||
pub use sha3::*;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! vec_into {
|
||||
( $( $x:expr ),* ) => {
|
||||
vec![ $( $x.into() ),* ]
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! hash_map {
|
||||
() => { HashMap::new() };
|
||||
|
@ -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 {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
Encodable::rlp_append(self, s)
|
||||
|
@ -408,6 +408,12 @@ impl<T> Decodable for Vec<T> where T: Decodable {
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
decoder.read_value(| bytes | {
|
||||
@ -418,18 +424,6 @@ 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 {
|
||||
($index_type:ty, $len:expr ) => (
|
||||
impl<T> Decodable for [T; $len] where T: Decodable {
|
||||
|
Loading…
Reference in New Issue
Block a user