Reformat the source code
This commit is contained in:
@@ -19,21 +19,21 @@
|
||||
/// Traces config.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Config {
|
||||
/// Indicates if tracing should be enabled or not.
|
||||
/// If it's None, it will be automatically configured.
|
||||
pub enabled: bool,
|
||||
/// Preferef cache-size.
|
||||
pub pref_cache_size: usize,
|
||||
/// Max cache-size.
|
||||
pub max_cache_size: usize,
|
||||
/// Indicates if tracing should be enabled or not.
|
||||
/// If it's None, it will be automatically configured.
|
||||
pub enabled: bool,
|
||||
/// Preferef cache-size.
|
||||
pub pref_cache_size: usize,
|
||||
/// Max cache-size.
|
||||
pub max_cache_size: usize,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
enabled: false,
|
||||
pref_cache_size: 15 * 1024 * 1024,
|
||||
max_cache_size: 20 * 1024 * 1024,
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
enabled: false,
|
||||
pref_cache_size: 15 * 1024 * 1024,
|
||||
max_cache_size: 20 * 1024 * 1024,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,306 +16,349 @@
|
||||
|
||||
//! Simple executive tracer.
|
||||
|
||||
use std::cmp::min;
|
||||
use ethereum_types::{U256, Address};
|
||||
use vm::{Error as VmError, ActionParams};
|
||||
use ethereum_types::{Address, U256};
|
||||
use log::{debug, warn};
|
||||
use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide, Reward, RewardType};
|
||||
use trace::{Tracer, VMTracer, FlatTrace};
|
||||
use std::cmp::min;
|
||||
use trace::{
|
||||
trace::{
|
||||
Action, Call, CallResult, Create, CreateResult, MemoryDiff, Res, Reward, RewardType,
|
||||
StorageDiff, Suicide, VMExecutedOperation, VMOperation, VMTrace,
|
||||
},
|
||||
FlatTrace, Tracer, VMTracer,
|
||||
};
|
||||
use vm::{ActionParams, Error as VmError};
|
||||
|
||||
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
|
||||
#[derive(Default)]
|
||||
pub struct ExecutiveTracer {
|
||||
traces: Vec<FlatTrace>,
|
||||
index_stack: Vec<usize>,
|
||||
vecindex_stack: Vec<usize>,
|
||||
sublen_stack: Vec<usize>,
|
||||
skip_one: bool,
|
||||
traces: Vec<FlatTrace>,
|
||||
index_stack: Vec<usize>,
|
||||
vecindex_stack: Vec<usize>,
|
||||
sublen_stack: Vec<usize>,
|
||||
skip_one: bool,
|
||||
}
|
||||
|
||||
impl Tracer for ExecutiveTracer {
|
||||
type Output = FlatTrace;
|
||||
type Output = FlatTrace;
|
||||
|
||||
fn prepare_trace_call(&mut self, params: &ActionParams, depth: usize, is_builtin: bool) {
|
||||
assert!(!self.skip_one, "skip_one is used only for builtin contracts that do not have subsequent calls; in prepare_trace_call it cannot be true; qed");
|
||||
fn prepare_trace_call(&mut self, params: &ActionParams, depth: usize, is_builtin: bool) {
|
||||
assert!(!self.skip_one, "skip_one is used only for builtin contracts that do not have subsequent calls; in prepare_trace_call it cannot be true; qed");
|
||||
|
||||
if depth != 0 && is_builtin && params.value.value() == U256::zero() {
|
||||
self.skip_one = true;
|
||||
return;
|
||||
}
|
||||
if depth != 0 && is_builtin && params.value.value() == U256::zero() {
|
||||
self.skip_one = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(parentlen) = self.sublen_stack.last_mut() {
|
||||
*parentlen += 1;
|
||||
}
|
||||
if let Some(parentlen) = self.sublen_stack.last_mut() {
|
||||
*parentlen += 1;
|
||||
}
|
||||
|
||||
let trace = FlatTrace {
|
||||
trace_address: self.index_stack.clone(),
|
||||
subtraces: self.sublen_stack.last().cloned().unwrap_or(0),
|
||||
action: Action::Call(Call::from(params.clone())),
|
||||
result: Res::Call(CallResult {
|
||||
gas_used: U256::zero(),
|
||||
output: Vec::new()
|
||||
}),
|
||||
};
|
||||
self.vecindex_stack.push(self.traces.len());
|
||||
self.traces.push(trace);
|
||||
self.index_stack.push(0);
|
||||
self.sublen_stack.push(0);
|
||||
}
|
||||
let trace = FlatTrace {
|
||||
trace_address: self.index_stack.clone(),
|
||||
subtraces: self.sublen_stack.last().cloned().unwrap_or(0),
|
||||
action: Action::Call(Call::from(params.clone())),
|
||||
result: Res::Call(CallResult {
|
||||
gas_used: U256::zero(),
|
||||
output: Vec::new(),
|
||||
}),
|
||||
};
|
||||
self.vecindex_stack.push(self.traces.len());
|
||||
self.traces.push(trace);
|
||||
self.index_stack.push(0);
|
||||
self.sublen_stack.push(0);
|
||||
}
|
||||
|
||||
fn prepare_trace_create(&mut self, params: &ActionParams) {
|
||||
assert!(!self.skip_one, "skip_one is used only for builtin contracts that do not have subsequent calls; in prepare_trace_create it cannot be true; qed");
|
||||
fn prepare_trace_create(&mut self, params: &ActionParams) {
|
||||
assert!(!self.skip_one, "skip_one is used only for builtin contracts that do not have subsequent calls; in prepare_trace_create it cannot be true; qed");
|
||||
|
||||
if let Some(parentlen) = self.sublen_stack.last_mut() {
|
||||
*parentlen += 1;
|
||||
}
|
||||
if let Some(parentlen) = self.sublen_stack.last_mut() {
|
||||
*parentlen += 1;
|
||||
}
|
||||
|
||||
let trace = FlatTrace {
|
||||
trace_address: self.index_stack.clone(),
|
||||
subtraces: self.sublen_stack.last().cloned().unwrap_or(0),
|
||||
action: Action::Create(Create::from(params.clone())),
|
||||
result: Res::Create(CreateResult {
|
||||
gas_used: U256::zero(),
|
||||
code: Vec::new(),
|
||||
address: Address::default(),
|
||||
}),
|
||||
};
|
||||
self.vecindex_stack.push(self.traces.len());
|
||||
self.traces.push(trace);
|
||||
self.index_stack.push(0);
|
||||
self.sublen_stack.push(0);
|
||||
}
|
||||
let trace = FlatTrace {
|
||||
trace_address: self.index_stack.clone(),
|
||||
subtraces: self.sublen_stack.last().cloned().unwrap_or(0),
|
||||
action: Action::Create(Create::from(params.clone())),
|
||||
result: Res::Create(CreateResult {
|
||||
gas_used: U256::zero(),
|
||||
code: Vec::new(),
|
||||
address: Address::default(),
|
||||
}),
|
||||
};
|
||||
self.vecindex_stack.push(self.traces.len());
|
||||
self.traces.push(trace);
|
||||
self.index_stack.push(0);
|
||||
self.sublen_stack.push(0);
|
||||
}
|
||||
|
||||
fn done_trace_call(&mut self, gas_used: U256, output: &[u8]) {
|
||||
if self.skip_one {
|
||||
self.skip_one = false;
|
||||
return;
|
||||
}
|
||||
fn done_trace_call(&mut self, gas_used: U256, output: &[u8]) {
|
||||
if self.skip_one {
|
||||
self.skip_one = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_call before this function; vecindex_stack is never empty; qed");
|
||||
let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_call before this function; sublen_stack is never empty; qed");
|
||||
self.index_stack.pop();
|
||||
let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_call before this function; vecindex_stack is never empty; qed");
|
||||
let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_call before this function; sublen_stack is never empty; qed");
|
||||
self.index_stack.pop();
|
||||
|
||||
self.traces[vecindex].result = Res::Call(CallResult {
|
||||
gas_used,
|
||||
output: output.into(),
|
||||
});
|
||||
self.traces[vecindex].subtraces = sublen;
|
||||
self.traces[vecindex].result = Res::Call(CallResult {
|
||||
gas_used,
|
||||
output: output.into(),
|
||||
});
|
||||
self.traces[vecindex].subtraces = sublen;
|
||||
|
||||
if let Some(index) = self.index_stack.last_mut() {
|
||||
*index += 1;
|
||||
}
|
||||
}
|
||||
if let Some(index) = self.index_stack.last_mut() {
|
||||
*index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn done_trace_create(&mut self, gas_used: U256, code: &[u8], address: Address) {
|
||||
assert!(!self.skip_one, "skip_one is only set with prepare_trace_call for builtin contracts with no subsequent calls; skip_one cannot be true after the same level prepare_trace_create; qed");
|
||||
fn done_trace_create(&mut self, gas_used: U256, code: &[u8], address: Address) {
|
||||
assert!(!self.skip_one, "skip_one is only set with prepare_trace_call for builtin contracts with no subsequent calls; skip_one cannot be true after the same level prepare_trace_create; qed");
|
||||
|
||||
let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_create before this function; vecindex_stack is never empty; qed");
|
||||
let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_create before this function; sublen_stack is never empty; qed");
|
||||
self.index_stack.pop();
|
||||
let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_create before this function; vecindex_stack is never empty; qed");
|
||||
let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_create before this function; sublen_stack is never empty; qed");
|
||||
self.index_stack.pop();
|
||||
|
||||
self.traces[vecindex].result = Res::Create(CreateResult {
|
||||
gas_used, address,
|
||||
code: code.into(),
|
||||
});
|
||||
self.traces[vecindex].subtraces = sublen;
|
||||
self.traces[vecindex].result = Res::Create(CreateResult {
|
||||
gas_used,
|
||||
address,
|
||||
code: code.into(),
|
||||
});
|
||||
self.traces[vecindex].subtraces = sublen;
|
||||
|
||||
if let Some(index) = self.index_stack.last_mut() {
|
||||
*index += 1;
|
||||
}
|
||||
}
|
||||
if let Some(index) = self.index_stack.last_mut() {
|
||||
*index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn done_trace_failed(&mut self, error: &VmError) {
|
||||
if self.skip_one {
|
||||
self.skip_one = false;
|
||||
return;
|
||||
}
|
||||
fn done_trace_failed(&mut self, error: &VmError) {
|
||||
if self.skip_one {
|
||||
self.skip_one = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_create/call before this function; vecindex_stack is never empty; qed");
|
||||
let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_create/call before this function; vecindex_stack is never empty; qed");
|
||||
self.index_stack.pop();
|
||||
let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_create/call before this function; vecindex_stack is never empty; qed");
|
||||
let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_create/call before this function; vecindex_stack is never empty; qed");
|
||||
self.index_stack.pop();
|
||||
|
||||
let is_create = match self.traces[vecindex].action {
|
||||
Action::Create(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
let is_create = match self.traces[vecindex].action {
|
||||
Action::Create(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if is_create {
|
||||
self.traces[vecindex].result = Res::FailedCreate(error.into());
|
||||
} else {
|
||||
self.traces[vecindex].result = Res::FailedCall(error.into());
|
||||
}
|
||||
self.traces[vecindex].subtraces = sublen;
|
||||
if is_create {
|
||||
self.traces[vecindex].result = Res::FailedCreate(error.into());
|
||||
} else {
|
||||
self.traces[vecindex].result = Res::FailedCall(error.into());
|
||||
}
|
||||
self.traces[vecindex].subtraces = sublen;
|
||||
|
||||
if let Some(index) = self.index_stack.last_mut() {
|
||||
*index += 1;
|
||||
}
|
||||
}
|
||||
if let Some(index) = self.index_stack.last_mut() {
|
||||
*index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address) {
|
||||
if let Some(parentlen) = self.sublen_stack.last_mut() {
|
||||
*parentlen += 1;
|
||||
}
|
||||
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address) {
|
||||
if let Some(parentlen) = self.sublen_stack.last_mut() {
|
||||
*parentlen += 1;
|
||||
}
|
||||
|
||||
let trace = FlatTrace {
|
||||
subtraces: 0,
|
||||
action: Action::Suicide(Suicide { address, refund_address, balance } ),
|
||||
result: Res::None,
|
||||
trace_address: self.index_stack.clone(),
|
||||
};
|
||||
debug!(target: "trace", "Traced suicide {:?}", trace);
|
||||
self.traces.push(trace);
|
||||
let trace = FlatTrace {
|
||||
subtraces: 0,
|
||||
action: Action::Suicide(Suicide {
|
||||
address,
|
||||
refund_address,
|
||||
balance,
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: self.index_stack.clone(),
|
||||
};
|
||||
debug!(target: "trace", "Traced suicide {:?}", trace);
|
||||
self.traces.push(trace);
|
||||
|
||||
if let Some(index) = self.index_stack.last_mut() {
|
||||
*index += 1;
|
||||
}
|
||||
}
|
||||
if let Some(index) = self.index_stack.last_mut() {
|
||||
*index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType) {
|
||||
if let Some(parentlen) = self.sublen_stack.last_mut() {
|
||||
*parentlen += 1;
|
||||
}
|
||||
fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType) {
|
||||
if let Some(parentlen) = self.sublen_stack.last_mut() {
|
||||
*parentlen += 1;
|
||||
}
|
||||
|
||||
let trace = FlatTrace {
|
||||
subtraces: 0,
|
||||
action: Action::Reward(Reward { author, value, reward_type } ),
|
||||
result: Res::None,
|
||||
trace_address: self.index_stack.clone(),
|
||||
};
|
||||
debug!(target: "trace", "Traced reward {:?}", trace);
|
||||
self.traces.push(trace);
|
||||
let trace = FlatTrace {
|
||||
subtraces: 0,
|
||||
action: Action::Reward(Reward {
|
||||
author,
|
||||
value,
|
||||
reward_type,
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: self.index_stack.clone(),
|
||||
};
|
||||
debug!(target: "trace", "Traced reward {:?}", trace);
|
||||
self.traces.push(trace);
|
||||
|
||||
if let Some(index) = self.index_stack.last_mut() {
|
||||
*index += 1;
|
||||
}
|
||||
}
|
||||
if let Some(index) = self.index_stack.last_mut() {
|
||||
*index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn drain(self) -> Vec<FlatTrace> {
|
||||
self.traces
|
||||
}
|
||||
fn drain(self) -> Vec<FlatTrace> {
|
||||
self.traces
|
||||
}
|
||||
}
|
||||
|
||||
struct TraceData {
|
||||
mem_written: Option<(usize, usize)>,
|
||||
store_written: Option<(U256, U256)>,
|
||||
mem_written: Option<(usize, usize)>,
|
||||
store_written: Option<(U256, U256)>,
|
||||
}
|
||||
|
||||
/// Simple VM tracer. Traces all operations.
|
||||
pub struct ExecutiveVMTracer {
|
||||
data: VMTrace,
|
||||
depth: usize,
|
||||
trace_stack: Vec<TraceData>,
|
||||
data: VMTrace,
|
||||
depth: usize,
|
||||
trace_stack: Vec<TraceData>,
|
||||
}
|
||||
|
||||
impl ExecutiveVMTracer {
|
||||
/// Create a new top-level instance.
|
||||
pub fn toplevel() -> Self {
|
||||
ExecutiveVMTracer {
|
||||
data: VMTrace {
|
||||
parent_step: 0,
|
||||
code: vec![],
|
||||
operations: vec![Default::default()], // prefill with a single entry so that prepare_subtrace can get the parent_step
|
||||
subs: vec![],
|
||||
},
|
||||
depth: 0,
|
||||
trace_stack: vec![],
|
||||
}
|
||||
}
|
||||
/// Create a new top-level instance.
|
||||
pub fn toplevel() -> Self {
|
||||
ExecutiveVMTracer {
|
||||
data: VMTrace {
|
||||
parent_step: 0,
|
||||
code: vec![],
|
||||
operations: vec![Default::default()], // prefill with a single entry so that prepare_subtrace can get the parent_step
|
||||
subs: vec![],
|
||||
},
|
||||
depth: 0,
|
||||
trace_stack: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn with_trace_in_depth<F: Fn(&mut VMTrace)>(trace: &mut VMTrace, depth: usize, f: F) {
|
||||
if depth == 0 {
|
||||
f(trace);
|
||||
} else {
|
||||
Self::with_trace_in_depth(trace.subs.last_mut().expect("self.depth is incremented with prepare_subtrace; a subtrace is always pushed; self.depth cannot be greater than subtrace stack; qed"), depth - 1, f);
|
||||
}
|
||||
}
|
||||
fn with_trace_in_depth<F: Fn(&mut VMTrace)>(trace: &mut VMTrace, depth: usize, f: F) {
|
||||
if depth == 0 {
|
||||
f(trace);
|
||||
} else {
|
||||
Self::with_trace_in_depth(trace.subs.last_mut().expect("self.depth is incremented with prepare_subtrace; a subtrace is always pushed; self.depth cannot be greater than subtrace stack; qed"), depth - 1, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VMTracer for ExecutiveVMTracer {
|
||||
type Output = VMTrace;
|
||||
type Output = VMTrace;
|
||||
|
||||
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { true }
|
||||
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256, mem_written: Option<(usize, usize)>, store_written: Option<(U256, U256)>) {
|
||||
Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| {
|
||||
trace.operations.push(VMOperation {
|
||||
pc: pc,
|
||||
instruction: instruction,
|
||||
gas_cost: gas_cost,
|
||||
executed: None,
|
||||
});
|
||||
});
|
||||
self.trace_stack.push(TraceData { mem_written, store_written });
|
||||
}
|
||||
fn trace_prepare_execute(
|
||||
&mut self,
|
||||
pc: usize,
|
||||
instruction: u8,
|
||||
gas_cost: U256,
|
||||
mem_written: Option<(usize, usize)>,
|
||||
store_written: Option<(U256, U256)>,
|
||||
) {
|
||||
Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| {
|
||||
trace.operations.push(VMOperation {
|
||||
pc: pc,
|
||||
instruction: instruction,
|
||||
gas_cost: gas_cost,
|
||||
executed: None,
|
||||
});
|
||||
});
|
||||
self.trace_stack.push(TraceData {
|
||||
mem_written,
|
||||
store_written,
|
||||
});
|
||||
}
|
||||
|
||||
fn trace_failed(&mut self) {
|
||||
let _ = self.trace_stack.pop().expect("pushed in trace_prepare_execute; qed");
|
||||
}
|
||||
fn trace_failed(&mut self) {
|
||||
let _ = self
|
||||
.trace_stack
|
||||
.pop()
|
||||
.expect("pushed in trace_prepare_execute; qed");
|
||||
}
|
||||
|
||||
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) {
|
||||
let TraceData { mem_written, store_written } = self.trace_stack.pop().expect("pushed in trace_prepare_execute; qed");
|
||||
let mem_diff = mem_written.map(|(o, s)| {
|
||||
if o + s > mem.len() {
|
||||
warn!(target: "trace", "mem_written is out of bounds");
|
||||
}
|
||||
(o, &mem[min(mem.len(), o)..min(o + s, mem.len())])
|
||||
});
|
||||
let store_diff = store_written;
|
||||
Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| {
|
||||
let ex = VMExecutedOperation {
|
||||
gas_used: gas_used,
|
||||
stack_push: stack_push.to_vec(),
|
||||
mem_diff: mem_diff.map(|(s, r)| MemoryDiff { offset: s, data: r.to_vec() }),
|
||||
store_diff: store_diff.map(|(l, v)| StorageDiff { location: l, value: v }),
|
||||
};
|
||||
trace.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute; trace.operations cannot be empty; qed").executed = Some(ex);
|
||||
});
|
||||
}
|
||||
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) {
|
||||
let TraceData {
|
||||
mem_written,
|
||||
store_written,
|
||||
} = self
|
||||
.trace_stack
|
||||
.pop()
|
||||
.expect("pushed in trace_prepare_execute; qed");
|
||||
let mem_diff = mem_written.map(|(o, s)| {
|
||||
if o + s > mem.len() {
|
||||
warn!(target: "trace", "mem_written is out of bounds");
|
||||
}
|
||||
(o, &mem[min(mem.len(), o)..min(o + s, mem.len())])
|
||||
});
|
||||
let store_diff = store_written;
|
||||
Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| {
|
||||
let ex = VMExecutedOperation {
|
||||
gas_used: gas_used,
|
||||
stack_push: stack_push.to_vec(),
|
||||
mem_diff: mem_diff.map(|(s, r)| MemoryDiff {
|
||||
offset: s,
|
||||
data: r.to_vec(),
|
||||
}),
|
||||
store_diff: store_diff.map(|(l, v)| StorageDiff {
|
||||
location: l,
|
||||
value: v,
|
||||
}),
|
||||
};
|
||||
trace.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute; trace.operations cannot be empty; qed").executed = Some(ex);
|
||||
});
|
||||
}
|
||||
|
||||
fn prepare_subtrace(&mut self, code: &[u8]) {
|
||||
Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| {
|
||||
let parent_step = trace.operations.len() - 1; // won't overflow since we must already have pushed an operation in trace_prepare_execute.
|
||||
trace.subs.push(VMTrace {
|
||||
parent_step,
|
||||
code: code.to_vec(),
|
||||
operations: vec![],
|
||||
subs: vec![],
|
||||
});
|
||||
});
|
||||
self.depth += 1;
|
||||
}
|
||||
fn prepare_subtrace(&mut self, code: &[u8]) {
|
||||
Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| {
|
||||
let parent_step = trace.operations.len() - 1; // won't overflow since we must already have pushed an operation in trace_prepare_execute.
|
||||
trace.subs.push(VMTrace {
|
||||
parent_step,
|
||||
code: code.to_vec(),
|
||||
operations: vec![],
|
||||
subs: vec![],
|
||||
});
|
||||
});
|
||||
self.depth += 1;
|
||||
}
|
||||
|
||||
fn done_subtrace(&mut self) {
|
||||
self.depth -= 1;
|
||||
}
|
||||
fn done_subtrace(&mut self) {
|
||||
self.depth -= 1;
|
||||
}
|
||||
|
||||
fn drain(mut self) -> Option<VMTrace> { self.data.subs.pop() }
|
||||
fn drain(mut self) -> Option<VMTrace> {
|
||||
self.data.subs.pop()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_prefix_address_properly() {
|
||||
let mut tracer = ExecutiveTracer::default();
|
||||
#[test]
|
||||
fn should_prefix_address_properly() {
|
||||
let mut tracer = ExecutiveTracer::default();
|
||||
|
||||
tracer.prepare_trace_call(&ActionParams::default(), 0, false);
|
||||
tracer.prepare_trace_call(&ActionParams::default(), 1, false);
|
||||
tracer.prepare_trace_call(&ActionParams::default(), 2, false);
|
||||
tracer.done_trace_call(U256::zero(), &[]);
|
||||
tracer.prepare_trace_call(&ActionParams::default(), 2, false);
|
||||
tracer.done_trace_call(U256::zero(), &[]);
|
||||
tracer.prepare_trace_call(&ActionParams::default(), 2, false);
|
||||
tracer.done_trace_call(U256::zero(), &[]);
|
||||
tracer.done_trace_call(U256::zero(), &[]);
|
||||
tracer.done_trace_call(U256::zero(), &[]);
|
||||
tracer.prepare_trace_call(&ActionParams::default(), 0, false);
|
||||
tracer.prepare_trace_call(&ActionParams::default(), 1, false);
|
||||
tracer.prepare_trace_call(&ActionParams::default(), 2, false);
|
||||
tracer.done_trace_call(U256::zero(), &[]);
|
||||
tracer.prepare_trace_call(&ActionParams::default(), 2, false);
|
||||
tracer.done_trace_call(U256::zero(), &[]);
|
||||
tracer.prepare_trace_call(&ActionParams::default(), 2, false);
|
||||
tracer.done_trace_call(U256::zero(), &[]);
|
||||
tracer.done_trace_call(U256::zero(), &[]);
|
||||
tracer.done_trace_call(U256::zero(), &[]);
|
||||
|
||||
let drained = tracer.drain();
|
||||
assert!(drained[0].trace_address.len() == 0);
|
||||
assert_eq!(&drained[1].trace_address, &[0]);
|
||||
assert_eq!(&drained[2].trace_address, &[0, 0]);
|
||||
assert_eq!(&drained[3].trace_address, &[0, 1]);
|
||||
assert_eq!(&drained[4].trace_address, &[0, 2]);
|
||||
}
|
||||
let drained = tracer.drain();
|
||||
assert!(drained[0].trace_address.len() == 0);
|
||||
assert_eq!(&drained[1].trace_address, &[0]);
|
||||
assert_eq!(&drained[2].trace_address, &[0, 0]);
|
||||
assert_eq!(&drained[3].trace_address, &[0, 1]);
|
||||
assert_eq!(&drained[4].trace_address, &[0, 2]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,16 +22,16 @@ use trace::FlatBlockTraces;
|
||||
|
||||
/// Traces import request.
|
||||
pub struct ImportRequest {
|
||||
/// Traces to import.
|
||||
pub traces: FlatBlockTraces,
|
||||
/// Hash of traces block.
|
||||
pub block_hash: H256,
|
||||
/// Number of traces block.
|
||||
pub block_number: BlockNumber,
|
||||
/// Blocks enacted by this import.
|
||||
///
|
||||
/// They should be ordered from oldest to newest.
|
||||
pub enacted: Vec<H256>,
|
||||
/// Number of blocks retracted by this import.
|
||||
pub retracted: usize,
|
||||
/// Traces to import.
|
||||
pub traces: FlatBlockTraces,
|
||||
/// Hash of traces block.
|
||||
pub block_hash: H256,
|
||||
/// Number of traces block.
|
||||
pub block_number: BlockNumber,
|
||||
/// Blocks enacted by this import.
|
||||
///
|
||||
/// They should be ordered from oldest to newest.
|
||||
pub enacted: Vec<H256>,
|
||||
/// Number of blocks retracted by this import.
|
||||
pub retracted: usize,
|
||||
}
|
||||
|
||||
@@ -23,112 +23,136 @@ mod import;
|
||||
mod noop_tracer;
|
||||
mod types;
|
||||
|
||||
pub use self::config::Config;
|
||||
pub use self::db::TraceDB;
|
||||
pub use self::noop_tracer::{NoopTracer, NoopVMTracer};
|
||||
pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer};
|
||||
pub use self::import::ImportRequest;
|
||||
pub use self::localized::LocalizedTrace;
|
||||
pub use self::{
|
||||
config::Config,
|
||||
db::TraceDB,
|
||||
executive_tracer::{ExecutiveTracer, ExecutiveVMTracer},
|
||||
import::ImportRequest,
|
||||
localized::LocalizedTrace,
|
||||
noop_tracer::{NoopTracer, NoopVMTracer},
|
||||
};
|
||||
|
||||
pub use self::types::{filter, flat, localized, trace, Tracing};
|
||||
pub use self::types::error::Error as TraceError;
|
||||
pub use self::types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, RewardType};
|
||||
pub use self::types::flat::{FlatTrace, FlatTransactionTraces, FlatBlockTraces};
|
||||
pub use self::types::filter::{Filter, AddressesFilter};
|
||||
pub use self::types::{
|
||||
error::Error as TraceError,
|
||||
filter,
|
||||
filter::{AddressesFilter, Filter},
|
||||
flat,
|
||||
flat::{FlatBlockTraces, FlatTrace, FlatTransactionTraces},
|
||||
localized, trace,
|
||||
trace::{MemoryDiff, RewardType, StorageDiff, VMExecutedOperation, VMOperation, VMTrace},
|
||||
Tracing,
|
||||
};
|
||||
|
||||
use ethereum_types::{H256, U256, Address};
|
||||
use ethereum_types::{Address, H256, U256};
|
||||
use kvdb::DBTransaction;
|
||||
use vm::{Error as VmError, ActionParams};
|
||||
use types::BlockNumber;
|
||||
use vm::{ActionParams, Error as VmError};
|
||||
|
||||
/// This trait is used by executive to build traces.
|
||||
pub trait Tracer: Send {
|
||||
/// Data returned when draining the Tracer.
|
||||
type Output;
|
||||
/// Data returned when draining the Tracer.
|
||||
type Output;
|
||||
|
||||
/// Prepares call trace for given params. Would panic if prepare/done_trace are not balanced.
|
||||
fn prepare_trace_call(&mut self, params: &ActionParams, depth: usize, is_builtin: bool);
|
||||
/// Prepares call trace for given params. Would panic if prepare/done_trace are not balanced.
|
||||
fn prepare_trace_call(&mut self, params: &ActionParams, depth: usize, is_builtin: bool);
|
||||
|
||||
/// Prepares create trace for given params. Would panic if prepare/done_trace are not balanced.
|
||||
fn prepare_trace_create(&mut self, params: &ActionParams);
|
||||
/// Prepares create trace for given params. Would panic if prepare/done_trace are not balanced.
|
||||
fn prepare_trace_create(&mut self, params: &ActionParams);
|
||||
|
||||
/// Finishes a successful call trace. Would panic if prepare/done_trace are not balanced.
|
||||
fn done_trace_call(&mut self, gas_used: U256, output: &[u8]);
|
||||
/// Finishes a successful call trace. Would panic if prepare/done_trace are not balanced.
|
||||
fn done_trace_call(&mut self, gas_used: U256, output: &[u8]);
|
||||
|
||||
/// Finishes a successful create trace. Would panic if prepare/done_trace are not balanced.
|
||||
fn done_trace_create(&mut self, gas_used: U256, code: &[u8], address: Address);
|
||||
/// Finishes a successful create trace. Would panic if prepare/done_trace are not balanced.
|
||||
fn done_trace_create(&mut self, gas_used: U256, code: &[u8], address: Address);
|
||||
|
||||
/// Finishes a failed trace. Would panic if prepare/done_trace are not balanced.
|
||||
fn done_trace_failed(&mut self, error: &VmError);
|
||||
/// Finishes a failed trace. Would panic if prepare/done_trace are not balanced.
|
||||
fn done_trace_failed(&mut self, error: &VmError);
|
||||
|
||||
/// Stores suicide info.
|
||||
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address);
|
||||
/// Stores suicide info.
|
||||
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address);
|
||||
|
||||
/// Stores reward info.
|
||||
fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType);
|
||||
/// Stores reward info.
|
||||
fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType);
|
||||
|
||||
/// Consumes self and returns all traces.
|
||||
fn drain(self) -> Vec<Self::Output>;
|
||||
/// Consumes self and returns all traces.
|
||||
fn drain(self) -> Vec<Self::Output>;
|
||||
}
|
||||
|
||||
/// Used by executive to build VM traces.
|
||||
pub trait VMTracer: Send {
|
||||
/// Data returned when draining the VMTracer.
|
||||
type Output;
|
||||
|
||||
/// Data returned when draining the VMTracer.
|
||||
type Output;
|
||||
/// Trace the progression of interpreter to next instruction.
|
||||
/// If tracer returns `false` it won't be called again.
|
||||
/// @returns true if `trace_prepare_execute` and `trace_executed` should be called.
|
||||
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Trace the progression of interpreter to next instruction.
|
||||
/// If tracer returns `false` it won't be called again.
|
||||
/// @returns true if `trace_prepare_execute` and `trace_executed` should be called.
|
||||
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { false }
|
||||
/// Trace the preparation to execute a single valid instruction.
|
||||
fn trace_prepare_execute(
|
||||
&mut self,
|
||||
_pc: usize,
|
||||
_instruction: u8,
|
||||
_gas_cost: U256,
|
||||
_mem_written: Option<(usize, usize)>,
|
||||
_store_written: Option<(U256, U256)>,
|
||||
) {
|
||||
}
|
||||
|
||||
/// Trace the preparation to execute a single valid instruction.
|
||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, _store_written: Option<(U256, U256)>) {}
|
||||
/// Trace the execution failure of a single instruction.
|
||||
fn trace_failed(&mut self) {}
|
||||
|
||||
/// Trace the execution failure of a single instruction.
|
||||
fn trace_failed(&mut self) {}
|
||||
/// Trace the finalised execution of a single valid instruction.
|
||||
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {}
|
||||
|
||||
/// Trace the finalised execution of a single valid instruction.
|
||||
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {}
|
||||
/// Spawn subtracer which will be used to trace deeper levels of execution.
|
||||
fn prepare_subtrace(&mut self, _code: &[u8]) {}
|
||||
|
||||
/// Spawn subtracer which will be used to trace deeper levels of execution.
|
||||
fn prepare_subtrace(&mut self, _code: &[u8]) {}
|
||||
|
||||
/// Finalize subtracer.
|
||||
fn done_subtrace(&mut self) {}
|
||||
|
||||
/// Consumes self and returns the VM trace.
|
||||
fn drain(self) -> Option<Self::Output>;
|
||||
/// Finalize subtracer.
|
||||
fn done_subtrace(&mut self) {}
|
||||
|
||||
/// Consumes self and returns the VM trace.
|
||||
fn drain(self) -> Option<Self::Output>;
|
||||
}
|
||||
|
||||
/// `DbExtras` provides an interface to query extra data which is not stored in tracesdb,
|
||||
/// but necessary to work correctly.
|
||||
pub trait DatabaseExtras {
|
||||
/// Returns hash of given block number.
|
||||
fn block_hash(&self, block_number: BlockNumber) -> Option<H256>;
|
||||
/// Returns hash of given block number.
|
||||
fn block_hash(&self, block_number: BlockNumber) -> Option<H256>;
|
||||
|
||||
/// Returns hash of transaction at given position.
|
||||
fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option<H256>;
|
||||
/// Returns hash of transaction at given position.
|
||||
fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option<H256>;
|
||||
}
|
||||
|
||||
/// Db provides an interface to query tracesdb.
|
||||
pub trait Database {
|
||||
/// Returns true if tracing is enabled. Otherwise false.
|
||||
fn tracing_enabled(&self) -> bool;
|
||||
/// Returns true if tracing is enabled. Otherwise false.
|
||||
fn tracing_enabled(&self) -> bool;
|
||||
|
||||
/// Imports new block traces.
|
||||
fn import(&self, batch: &mut DBTransaction, request: ImportRequest);
|
||||
/// Imports new block traces.
|
||||
fn import(&self, batch: &mut DBTransaction, request: ImportRequest);
|
||||
|
||||
/// Returns localized trace at given position.
|
||||
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace>;
|
||||
/// Returns localized trace at given position.
|
||||
fn trace(
|
||||
&self,
|
||||
block_number: BlockNumber,
|
||||
tx_position: usize,
|
||||
trace_position: Vec<usize>,
|
||||
) -> Option<LocalizedTrace>;
|
||||
|
||||
/// Returns localized traces created by a single transaction.
|
||||
fn transaction_traces(&self, block_number: BlockNumber, tx_position: usize) -> Option<Vec<LocalizedTrace>>;
|
||||
/// Returns localized traces created by a single transaction.
|
||||
fn transaction_traces(
|
||||
&self,
|
||||
block_number: BlockNumber,
|
||||
tx_position: usize,
|
||||
) -> Option<Vec<LocalizedTrace>>;
|
||||
|
||||
/// Returns localized traces created in given block.
|
||||
fn block_traces(&self, block_number: BlockNumber) -> Option<Vec<LocalizedTrace>>;
|
||||
/// Returns localized traces created in given block.
|
||||
fn block_traces(&self, block_number: BlockNumber) -> Option<Vec<LocalizedTrace>>;
|
||||
|
||||
/// Filter traces matching given filter.
|
||||
fn filter(&self, filter: &Filter) -> Vec<LocalizedTrace>;
|
||||
/// Filter traces matching given filter.
|
||||
fn filter(&self, filter: &Filter) -> Vec<LocalizedTrace>;
|
||||
}
|
||||
|
||||
@@ -16,32 +16,38 @@
|
||||
|
||||
//! Nonoperative tracer.
|
||||
|
||||
use ethereum_types::{U256, Address};
|
||||
use vm::{Error as VmError, ActionParams};
|
||||
use trace::{Tracer, VMTracer, FlatTrace};
|
||||
use trace::trace::{VMTrace, RewardType};
|
||||
use ethereum_types::{Address, U256};
|
||||
use trace::{
|
||||
trace::{RewardType, VMTrace},
|
||||
FlatTrace, Tracer, VMTracer,
|
||||
};
|
||||
use vm::{ActionParams, Error as VmError};
|
||||
|
||||
/// Nonoperative tracer. Does not trace anything.
|
||||
pub struct NoopTracer;
|
||||
|
||||
impl Tracer for NoopTracer {
|
||||
type Output = FlatTrace;
|
||||
type Output = FlatTrace;
|
||||
|
||||
fn prepare_trace_call(&mut self, _: &ActionParams, _: usize, _: bool) { }
|
||||
fn prepare_trace_create(&mut self, _: &ActionParams) { }
|
||||
fn done_trace_call(&mut self, _: U256, _: &[u8]) { }
|
||||
fn done_trace_create(&mut self, _: U256, _: &[u8], _: Address) { }
|
||||
fn done_trace_failed(&mut self, _: &VmError) { }
|
||||
fn trace_suicide(&mut self, _: Address, _: U256, _: Address) { }
|
||||
fn trace_reward(&mut self, _: Address, _: U256, _: RewardType) { }
|
||||
fn drain(self) -> Vec<FlatTrace> { vec![] }
|
||||
fn prepare_trace_call(&mut self, _: &ActionParams, _: usize, _: bool) {}
|
||||
fn prepare_trace_create(&mut self, _: &ActionParams) {}
|
||||
fn done_trace_call(&mut self, _: U256, _: &[u8]) {}
|
||||
fn done_trace_create(&mut self, _: U256, _: &[u8], _: Address) {}
|
||||
fn done_trace_failed(&mut self, _: &VmError) {}
|
||||
fn trace_suicide(&mut self, _: Address, _: U256, _: Address) {}
|
||||
fn trace_reward(&mut self, _: Address, _: U256, _: RewardType) {}
|
||||
fn drain(self) -> Vec<FlatTrace> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
/// Nonoperative VM tracer. Does not trace anything.
|
||||
pub struct NoopVMTracer;
|
||||
|
||||
impl VMTracer for NoopVMTracer {
|
||||
type Output = VMTrace;
|
||||
type Output = VMTrace;
|
||||
|
||||
fn drain(self) -> Option<VMTrace> { None }
|
||||
fn drain(self) -> Option<VMTrace> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,139 +16,139 @@
|
||||
|
||||
//! Trace errors.
|
||||
|
||||
use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
|
||||
use std::fmt;
|
||||
use rlp::{Encodable, RlpStream, Decodable, DecoderError, Rlp};
|
||||
use vm::Error as VmError;
|
||||
|
||||
/// Trace evm errors.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Error {
|
||||
/// `OutOfGas` is returned when transaction execution runs out of gas.
|
||||
OutOfGas,
|
||||
/// `BadJumpDestination` is returned when execution tried to move
|
||||
/// to position that wasn't marked with JUMPDEST instruction
|
||||
BadJumpDestination,
|
||||
/// `BadInstructions` is returned when given instruction is not supported
|
||||
BadInstruction,
|
||||
/// `StackUnderflow` when there is not enough stack elements to execute instruction
|
||||
StackUnderflow,
|
||||
/// When execution would exceed defined Stack Limit
|
||||
OutOfStack,
|
||||
/// When builtin contract failed on input data
|
||||
BuiltIn,
|
||||
/// Returned on evm internal error. Should never be ignored during development.
|
||||
/// Likely to cause consensus issues.
|
||||
Internal,
|
||||
/// When execution tries to modify the state in static context
|
||||
MutableCallInStaticContext,
|
||||
/// Wasm error
|
||||
Wasm,
|
||||
/// Contract tried to access past the return data buffer.
|
||||
OutOfBounds,
|
||||
/// Execution has been reverted with REVERT instruction.
|
||||
Reverted,
|
||||
/// `OutOfGas` is returned when transaction execution runs out of gas.
|
||||
OutOfGas,
|
||||
/// `BadJumpDestination` is returned when execution tried to move
|
||||
/// to position that wasn't marked with JUMPDEST instruction
|
||||
BadJumpDestination,
|
||||
/// `BadInstructions` is returned when given instruction is not supported
|
||||
BadInstruction,
|
||||
/// `StackUnderflow` when there is not enough stack elements to execute instruction
|
||||
StackUnderflow,
|
||||
/// When execution would exceed defined Stack Limit
|
||||
OutOfStack,
|
||||
/// When builtin contract failed on input data
|
||||
BuiltIn,
|
||||
/// Returned on evm internal error. Should never be ignored during development.
|
||||
/// Likely to cause consensus issues.
|
||||
Internal,
|
||||
/// When execution tries to modify the state in static context
|
||||
MutableCallInStaticContext,
|
||||
/// Wasm error
|
||||
Wasm,
|
||||
/// Contract tried to access past the return data buffer.
|
||||
OutOfBounds,
|
||||
/// Execution has been reverted with REVERT instruction.
|
||||
Reverted,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a VmError> for Error {
|
||||
fn from(e: &'a VmError) -> Self {
|
||||
match *e {
|
||||
VmError::OutOfGas => Error::OutOfGas,
|
||||
VmError::BadJumpDestination { .. } => Error::BadJumpDestination,
|
||||
VmError::BadInstruction { .. } => Error::BadInstruction,
|
||||
VmError::StackUnderflow { .. } => Error::StackUnderflow,
|
||||
VmError::OutOfStack { .. } => Error::OutOfStack,
|
||||
VmError::BuiltIn { .. } => Error::BuiltIn,
|
||||
VmError::Wasm { .. } => Error::Wasm,
|
||||
VmError::Internal(_) => Error::Internal,
|
||||
VmError::MutableCallInStaticContext => Error::MutableCallInStaticContext,
|
||||
VmError::OutOfBounds => Error::OutOfBounds,
|
||||
VmError::Reverted => Error::Reverted,
|
||||
}
|
||||
}
|
||||
fn from(e: &'a VmError) -> Self {
|
||||
match *e {
|
||||
VmError::OutOfGas => Error::OutOfGas,
|
||||
VmError::BadJumpDestination { .. } => Error::BadJumpDestination,
|
||||
VmError::BadInstruction { .. } => Error::BadInstruction,
|
||||
VmError::StackUnderflow { .. } => Error::StackUnderflow,
|
||||
VmError::OutOfStack { .. } => Error::OutOfStack,
|
||||
VmError::BuiltIn { .. } => Error::BuiltIn,
|
||||
VmError::Wasm { .. } => Error::Wasm,
|
||||
VmError::Internal(_) => Error::Internal,
|
||||
VmError::MutableCallInStaticContext => Error::MutableCallInStaticContext,
|
||||
VmError::OutOfBounds => Error::OutOfBounds,
|
||||
VmError::Reverted => Error::Reverted,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VmError> for Error {
|
||||
fn from(e: VmError) -> Self {
|
||||
Error::from(&e)
|
||||
}
|
||||
fn from(e: VmError) -> Self {
|
||||
Error::from(&e)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Error::*;
|
||||
let message = match *self {
|
||||
OutOfGas => "Out of gas",
|
||||
BadJumpDestination => "Bad jump destination",
|
||||
BadInstruction => "Bad instruction",
|
||||
StackUnderflow => "Stack underflow",
|
||||
OutOfStack => "Out of stack",
|
||||
BuiltIn => "Built-in failed",
|
||||
Wasm => "Wasm runtime error",
|
||||
Internal => "Internal error",
|
||||
MutableCallInStaticContext => "Mutable Call In Static Context",
|
||||
OutOfBounds => "Out of bounds",
|
||||
Reverted => "Reverted",
|
||||
};
|
||||
message.fmt(f)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Error::*;
|
||||
let message = match *self {
|
||||
OutOfGas => "Out of gas",
|
||||
BadJumpDestination => "Bad jump destination",
|
||||
BadInstruction => "Bad instruction",
|
||||
StackUnderflow => "Stack underflow",
|
||||
OutOfStack => "Out of stack",
|
||||
BuiltIn => "Built-in failed",
|
||||
Wasm => "Wasm runtime error",
|
||||
Internal => "Internal error",
|
||||
MutableCallInStaticContext => "Mutable Call In Static Context",
|
||||
OutOfBounds => "Out of bounds",
|
||||
Reverted => "Reverted",
|
||||
};
|
||||
message.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Error {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
use self::Error::*;
|
||||
let value = match *self {
|
||||
OutOfGas => 0u8,
|
||||
BadJumpDestination => 1,
|
||||
BadInstruction => 2,
|
||||
StackUnderflow => 3,
|
||||
OutOfStack => 4,
|
||||
Internal => 5,
|
||||
BuiltIn => 6,
|
||||
MutableCallInStaticContext => 7,
|
||||
Wasm => 8,
|
||||
OutOfBounds => 9,
|
||||
Reverted => 10,
|
||||
};
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
use self::Error::*;
|
||||
let value = match *self {
|
||||
OutOfGas => 0u8,
|
||||
BadJumpDestination => 1,
|
||||
BadInstruction => 2,
|
||||
StackUnderflow => 3,
|
||||
OutOfStack => 4,
|
||||
Internal => 5,
|
||||
BuiltIn => 6,
|
||||
MutableCallInStaticContext => 7,
|
||||
Wasm => 8,
|
||||
OutOfBounds => 9,
|
||||
Reverted => 10,
|
||||
};
|
||||
|
||||
s.append_internal(&value);
|
||||
}
|
||||
s.append_internal(&value);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Error {
|
||||
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
||||
use self::Error::*;
|
||||
let value: u8 = rlp.as_val()?;
|
||||
match value {
|
||||
0 => Ok(OutOfGas),
|
||||
1 => Ok(BadJumpDestination),
|
||||
2 => Ok(BadInstruction),
|
||||
3 => Ok(StackUnderflow),
|
||||
4 => Ok(OutOfStack),
|
||||
5 => Ok(Internal),
|
||||
6 => Ok(BuiltIn),
|
||||
7 => Ok(MutableCallInStaticContext),
|
||||
8 => Ok(Wasm),
|
||||
9 => Ok(OutOfBounds),
|
||||
10 => Ok(Reverted),
|
||||
_ => Err(DecoderError::Custom("Invalid error type")),
|
||||
}
|
||||
}
|
||||
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
||||
use self::Error::*;
|
||||
let value: u8 = rlp.as_val()?;
|
||||
match value {
|
||||
0 => Ok(OutOfGas),
|
||||
1 => Ok(BadJumpDestination),
|
||||
2 => Ok(BadInstruction),
|
||||
3 => Ok(StackUnderflow),
|
||||
4 => Ok(OutOfStack),
|
||||
5 => Ok(Internal),
|
||||
6 => Ok(BuiltIn),
|
||||
7 => Ok(MutableCallInStaticContext),
|
||||
8 => Ok(Wasm),
|
||||
9 => Ok(OutOfBounds),
|
||||
10 => Ok(Reverted),
|
||||
_ => Err(DecoderError::Custom("Invalid error type")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rlp::*;
|
||||
use super::Error;
|
||||
use super::Error;
|
||||
use rlp::*;
|
||||
|
||||
#[test]
|
||||
fn encode_error() {
|
||||
let err = Error::BadJumpDestination;
|
||||
#[test]
|
||||
fn encode_error() {
|
||||
let err = Error::BadJumpDestination;
|
||||
|
||||
let mut s = RlpStream::new_list(2);
|
||||
s.append(&err);
|
||||
assert!(!s.is_finished(), "List shouldn't finished yet");
|
||||
s.append(&err);
|
||||
assert!(s.is_finished(), "List should be finished now");
|
||||
s.out();
|
||||
}
|
||||
let mut s = RlpStream::new_list(2);
|
||||
s.append(&err);
|
||||
assert!(!s.is_finished(), "List shouldn't finished yet");
|
||||
s.append(&err);
|
||||
assert!(s.is_finished(), "List should be finished now");
|
||||
s.out();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,413 +16,419 @@
|
||||
|
||||
//! Trace filters type definitions
|
||||
|
||||
use std::ops::Range;
|
||||
use ethereum_types::{Address, Bloom, BloomInput};
|
||||
use trace::flat::FlatTrace;
|
||||
use super::trace::{Action, Res};
|
||||
use ethereum_types::{Address, Bloom, BloomInput};
|
||||
use std::ops::Range;
|
||||
use trace::flat::FlatTrace;
|
||||
|
||||
/// Addresses filter.
|
||||
///
|
||||
/// Used to create bloom possibilities and match filters.
|
||||
#[derive(Debug)]
|
||||
pub struct AddressesFilter {
|
||||
list: Vec<Address>
|
||||
list: Vec<Address>,
|
||||
}
|
||||
|
||||
impl From<Vec<Address>> for AddressesFilter {
|
||||
fn from(addresses: Vec<Address>) -> Self {
|
||||
AddressesFilter { list: addresses }
|
||||
}
|
||||
fn from(addresses: Vec<Address>) -> Self {
|
||||
AddressesFilter { list: addresses }
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressesFilter {
|
||||
/// Returns true if address matches one of the searched addresses.
|
||||
pub fn matches(&self, address: &Address) -> bool {
|
||||
self.matches_all() || self.list.contains(address)
|
||||
}
|
||||
/// Returns true if address matches one of the searched addresses.
|
||||
pub fn matches(&self, address: &Address) -> bool {
|
||||
self.matches_all() || self.list.contains(address)
|
||||
}
|
||||
|
||||
/// Returns true if this address filter matches everything.
|
||||
pub fn matches_all(&self) -> bool {
|
||||
self.list.is_empty()
|
||||
}
|
||||
/// Returns true if this address filter matches everything.
|
||||
pub fn matches_all(&self) -> bool {
|
||||
self.list.is_empty()
|
||||
}
|
||||
|
||||
/// Returns blooms of this addresses filter.
|
||||
pub fn blooms(&self) -> Vec<Bloom> {
|
||||
match self.list.is_empty() {
|
||||
true => vec![Bloom::default()],
|
||||
false => self.list.iter()
|
||||
.map(|address| Bloom::from(BloomInput::Raw(address)))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
/// Returns blooms of this addresses filter.
|
||||
pub fn blooms(&self) -> Vec<Bloom> {
|
||||
match self.list.is_empty() {
|
||||
true => vec![Bloom::default()],
|
||||
false => self
|
||||
.list
|
||||
.iter()
|
||||
.map(|address| Bloom::from(BloomInput::Raw(address)))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns vector of blooms zipped with blooms of this addresses filter.
|
||||
pub fn with_blooms(&self, blooms: Vec<Bloom>) -> Vec<Bloom> {
|
||||
match self.list.is_empty() {
|
||||
true => blooms,
|
||||
false => blooms
|
||||
.into_iter()
|
||||
.flat_map(|bloom| self.list.iter()
|
||||
.map(|address| {
|
||||
let mut bloom = bloom.clone();
|
||||
bloom.accrue(BloomInput::Raw(address));
|
||||
bloom
|
||||
})
|
||||
.collect::<Vec<_>>())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
/// Returns vector of blooms zipped with blooms of this addresses filter.
|
||||
pub fn with_blooms(&self, blooms: Vec<Bloom>) -> Vec<Bloom> {
|
||||
match self.list.is_empty() {
|
||||
true => blooms,
|
||||
false => blooms
|
||||
.into_iter()
|
||||
.flat_map(|bloom| {
|
||||
self.list
|
||||
.iter()
|
||||
.map(|address| {
|
||||
let mut bloom = bloom.clone();
|
||||
bloom.accrue(BloomInput::Raw(address));
|
||||
bloom
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Traces filter.
|
||||
pub struct Filter {
|
||||
/// Block range.
|
||||
pub range: Range<usize>,
|
||||
/// Block range.
|
||||
pub range: Range<usize>,
|
||||
|
||||
/// From address filter.
|
||||
pub from_address: AddressesFilter,
|
||||
/// From address filter.
|
||||
pub from_address: AddressesFilter,
|
||||
|
||||
/// To address filter.
|
||||
pub to_address: AddressesFilter,
|
||||
/// To address filter.
|
||||
pub to_address: AddressesFilter,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
/// Returns combinations of each address.
|
||||
pub fn bloom_possibilities(&self) -> Vec<Bloom> {
|
||||
self.to_address.with_blooms(self.from_address.blooms())
|
||||
}
|
||||
/// Returns combinations of each address.
|
||||
pub fn bloom_possibilities(&self) -> Vec<Bloom> {
|
||||
self.to_address.with_blooms(self.from_address.blooms())
|
||||
}
|
||||
|
||||
/// Returns true if given trace matches the filter.
|
||||
pub fn matches(&self, trace: &FlatTrace) -> bool {
|
||||
match trace.action {
|
||||
Action::Call(ref call) => {
|
||||
let from_matches = self.from_address.matches(&call.from);
|
||||
let to_matches = self.to_address.matches(&call.to);
|
||||
from_matches && to_matches
|
||||
},
|
||||
Action::Create(ref create) => {
|
||||
let from_matches = self.from_address.matches(&create.from);
|
||||
/// Returns true if given trace matches the filter.
|
||||
pub fn matches(&self, trace: &FlatTrace) -> bool {
|
||||
match trace.action {
|
||||
Action::Call(ref call) => {
|
||||
let from_matches = self.from_address.matches(&call.from);
|
||||
let to_matches = self.to_address.matches(&call.to);
|
||||
from_matches && to_matches
|
||||
}
|
||||
Action::Create(ref create) => {
|
||||
let from_matches = self.from_address.matches(&create.from);
|
||||
|
||||
let to_matches = match trace.result {
|
||||
Res::Create(ref create_result) => self.to_address.matches(&create_result.address),
|
||||
_ => self.to_address.matches_all(),
|
||||
};
|
||||
let to_matches = match trace.result {
|
||||
Res::Create(ref create_result) => {
|
||||
self.to_address.matches(&create_result.address)
|
||||
}
|
||||
_ => self.to_address.matches_all(),
|
||||
};
|
||||
|
||||
from_matches && to_matches
|
||||
},
|
||||
Action::Suicide(ref suicide) => {
|
||||
let from_matches = self.from_address.matches(&suicide.address);
|
||||
let to_matches = self.to_address.matches(&suicide.refund_address);
|
||||
from_matches && to_matches
|
||||
},
|
||||
Action::Reward(ref reward) => {
|
||||
self.from_address.matches_all() && self.to_address.matches(&reward.author)
|
||||
},
|
||||
}
|
||||
}
|
||||
from_matches && to_matches
|
||||
}
|
||||
Action::Suicide(ref suicide) => {
|
||||
let from_matches = self.from_address.matches(&suicide.address);
|
||||
let to_matches = self.to_address.matches(&suicide.refund_address);
|
||||
from_matches && to_matches
|
||||
}
|
||||
Action::Reward(ref reward) => {
|
||||
self.from_address.matches_all() && self.to_address.matches(&reward.author)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ethereum_types::{Address, Bloom, BloomInput};
|
||||
use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide, Reward};
|
||||
use trace::flat::FlatTrace;
|
||||
use trace::{Filter, AddressesFilter, TraceError, RewardType};
|
||||
use evm::CallType;
|
||||
use ethereum_types::{Address, Bloom, BloomInput};
|
||||
use evm::CallType;
|
||||
use trace::{
|
||||
flat::FlatTrace,
|
||||
trace::{Action, Call, Create, CreateResult, Res, Reward, Suicide},
|
||||
AddressesFilter, Filter, RewardType, TraceError,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn empty_trace_filter_bloom_possibilities() {
|
||||
let filter = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![]),
|
||||
to_address: AddressesFilter::from(vec![]),
|
||||
};
|
||||
#[test]
|
||||
fn empty_trace_filter_bloom_possibilities() {
|
||||
let filter = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![]),
|
||||
to_address: AddressesFilter::from(vec![]),
|
||||
};
|
||||
|
||||
let blooms = filter.bloom_possibilities();
|
||||
assert_eq!(blooms, vec![Bloom::default()]);
|
||||
}
|
||||
let blooms = filter.bloom_possibilities();
|
||||
assert_eq!(blooms, vec![Bloom::default()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_trace_filter_bloom_possibility() {
|
||||
let filter = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(2)]),
|
||||
};
|
||||
#[test]
|
||||
fn single_trace_filter_bloom_possibility() {
|
||||
let filter = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(2)]),
|
||||
};
|
||||
|
||||
let blooms = filter.bloom_possibilities();
|
||||
assert_eq!(blooms.len(), 1);
|
||||
let blooms = filter.bloom_possibilities();
|
||||
assert_eq!(blooms.len(), 1);
|
||||
|
||||
assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(3))));
|
||||
}
|
||||
assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(3))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_from_trace_filter_bloom_possibility() {
|
||||
let filter = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![]),
|
||||
};
|
||||
#[test]
|
||||
fn only_from_trace_filter_bloom_possibility() {
|
||||
let filter = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![]),
|
||||
};
|
||||
|
||||
let blooms = filter.bloom_possibilities();
|
||||
assert_eq!(blooms.len(), 1);
|
||||
let blooms = filter.bloom_possibilities();
|
||||
assert_eq!(blooms.len(), 1);
|
||||
|
||||
assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
}
|
||||
assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_to_trace_filter_bloom_possibility() {
|
||||
let filter = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
};
|
||||
#[test]
|
||||
fn only_to_trace_filter_bloom_possibility() {
|
||||
let filter = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
};
|
||||
|
||||
let blooms = filter.bloom_possibilities();
|
||||
assert_eq!(blooms.len(), 1);
|
||||
let blooms = filter.bloom_possibilities();
|
||||
assert_eq!(blooms.len(), 1);
|
||||
|
||||
assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
}
|
||||
assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_trace_filter_bloom_possibility() {
|
||||
let filter = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1), Address::from(3)]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(2), Address::from(4)]),
|
||||
};
|
||||
#[test]
|
||||
fn multiple_trace_filter_bloom_possibility() {
|
||||
let filter = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1), Address::from(3)]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(2), Address::from(4)]),
|
||||
};
|
||||
|
||||
let blooms = filter.bloom_possibilities();
|
||||
assert_eq!(blooms.len(), 4);
|
||||
let blooms = filter.bloom_possibilities();
|
||||
assert_eq!(blooms.len(), 4);
|
||||
|
||||
assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(3))));
|
||||
assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(4))));
|
||||
assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(3))));
|
||||
assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(4))));
|
||||
|
||||
assert!(blooms[1].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(blooms[1].contains_input(BloomInput::Raw(&Address::from(4))));
|
||||
assert!(!blooms[1].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
assert!(!blooms[1].contains_input(BloomInput::Raw(&Address::from(3))));
|
||||
assert!(blooms[1].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(blooms[1].contains_input(BloomInput::Raw(&Address::from(4))));
|
||||
assert!(!blooms[1].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
assert!(!blooms[1].contains_input(BloomInput::Raw(&Address::from(3))));
|
||||
|
||||
assert!(blooms[2].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
assert!(blooms[2].contains_input(BloomInput::Raw(&Address::from(3))));
|
||||
assert!(!blooms[2].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(!blooms[2].contains_input(BloomInput::Raw(&Address::from(4))));
|
||||
assert!(blooms[2].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
assert!(blooms[2].contains_input(BloomInput::Raw(&Address::from(3))));
|
||||
assert!(!blooms[2].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(!blooms[2].contains_input(BloomInput::Raw(&Address::from(4))));
|
||||
|
||||
assert!(blooms[3].contains_input(BloomInput::Raw(&Address::from(3))));
|
||||
assert!(blooms[3].contains_input(BloomInput::Raw(&Address::from(4))));
|
||||
assert!(!blooms[3].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(!blooms[3].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
}
|
||||
assert!(blooms[3].contains_input(BloomInput::Raw(&Address::from(3))));
|
||||
assert!(blooms[3].contains_input(BloomInput::Raw(&Address::from(4))));
|
||||
assert!(!blooms[3].contains_input(BloomInput::Raw(&Address::from(1))));
|
||||
assert!(!blooms[3].contains_input(BloomInput::Raw(&Address::from(2))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_matches() {
|
||||
let f0 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![]),
|
||||
};
|
||||
#[test]
|
||||
fn filter_matches() {
|
||||
let f0 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![]),
|
||||
};
|
||||
|
||||
let f1 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(3), Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![]),
|
||||
};
|
||||
let f1 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(3), Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![]),
|
||||
};
|
||||
|
||||
let f2 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![]),
|
||||
to_address: AddressesFilter::from(vec![]),
|
||||
};
|
||||
let f2 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![]),
|
||||
to_address: AddressesFilter::from(vec![]),
|
||||
};
|
||||
|
||||
let f3 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(2)]),
|
||||
};
|
||||
let f3 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(2)]),
|
||||
};
|
||||
|
||||
let f4 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(2), Address::from(3)]),
|
||||
};
|
||||
let f4 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(2), Address::from(3)]),
|
||||
};
|
||||
|
||||
let f5 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(2), Address::from(3)]),
|
||||
};
|
||||
let f5 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(2), Address::from(3)]),
|
||||
};
|
||||
|
||||
let f6 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(4)]),
|
||||
};
|
||||
let f6 = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![Address::from(4)]),
|
||||
};
|
||||
|
||||
let trace = FlatTrace {
|
||||
action: Action::Call(Call {
|
||||
from: 1.into(),
|
||||
to: 2.into(),
|
||||
value: 3.into(),
|
||||
gas: 4.into(),
|
||||
input: vec![0x5],
|
||||
call_type: CallType::Call,
|
||||
}),
|
||||
result: Res::FailedCall(TraceError::OutOfGas),
|
||||
trace_address: vec![0].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
let trace = FlatTrace {
|
||||
action: Action::Call(Call {
|
||||
from: 1.into(),
|
||||
to: 2.into(),
|
||||
value: 3.into(),
|
||||
gas: 4.into(),
|
||||
input: vec![0x5],
|
||||
call_type: CallType::Call,
|
||||
}),
|
||||
result: Res::FailedCall(TraceError::OutOfGas),
|
||||
trace_address: vec![0].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
|
||||
assert!(f0.matches(&trace));
|
||||
assert!(f1.matches(&trace));
|
||||
assert!(f2.matches(&trace));
|
||||
assert!(f3.matches(&trace));
|
||||
assert!(f4.matches(&trace));
|
||||
assert!(f5.matches(&trace));
|
||||
assert!(!f6.matches(&trace));
|
||||
assert!(f0.matches(&trace));
|
||||
assert!(f1.matches(&trace));
|
||||
assert!(f2.matches(&trace));
|
||||
assert!(f3.matches(&trace));
|
||||
assert!(f4.matches(&trace));
|
||||
assert!(f5.matches(&trace));
|
||||
assert!(!f6.matches(&trace));
|
||||
|
||||
let trace = FlatTrace {
|
||||
action: Action::Create(Create {
|
||||
from: 1.into(),
|
||||
value: 3.into(),
|
||||
gas: 4.into(),
|
||||
init: vec![0x5],
|
||||
}),
|
||||
result: Res::Create(CreateResult {
|
||||
gas_used: 10.into(),
|
||||
code: vec![],
|
||||
address: 2.into(),
|
||||
}),
|
||||
trace_address: vec![0].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
let trace = FlatTrace {
|
||||
action: Action::Create(Create {
|
||||
from: 1.into(),
|
||||
value: 3.into(),
|
||||
gas: 4.into(),
|
||||
init: vec![0x5],
|
||||
}),
|
||||
result: Res::Create(CreateResult {
|
||||
gas_used: 10.into(),
|
||||
code: vec![],
|
||||
address: 2.into(),
|
||||
}),
|
||||
trace_address: vec![0].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
|
||||
assert!(f0.matches(&trace));
|
||||
assert!(f1.matches(&trace));
|
||||
assert!(f2.matches(&trace));
|
||||
assert!(f3.matches(&trace));
|
||||
assert!(f4.matches(&trace));
|
||||
assert!(f5.matches(&trace));
|
||||
assert!(!f6.matches(&trace));
|
||||
assert!(f0.matches(&trace));
|
||||
assert!(f1.matches(&trace));
|
||||
assert!(f2.matches(&trace));
|
||||
assert!(f3.matches(&trace));
|
||||
assert!(f4.matches(&trace));
|
||||
assert!(f5.matches(&trace));
|
||||
assert!(!f6.matches(&trace));
|
||||
|
||||
let trace = FlatTrace {
|
||||
action: Action::Suicide(Suicide {
|
||||
address: 1.into(),
|
||||
refund_address: 2.into(),
|
||||
balance: 3.into(),
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: vec![].into_iter().collect(),
|
||||
subtraces: 0
|
||||
};
|
||||
let trace = FlatTrace {
|
||||
action: Action::Suicide(Suicide {
|
||||
address: 1.into(),
|
||||
refund_address: 2.into(),
|
||||
balance: 3.into(),
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: vec![].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
|
||||
assert!(f0.matches(&trace));
|
||||
assert!(f1.matches(&trace));
|
||||
assert!(f2.matches(&trace));
|
||||
assert!(f3.matches(&trace));
|
||||
assert!(f4.matches(&trace));
|
||||
assert!(f5.matches(&trace));
|
||||
assert!(!f6.matches(&trace));
|
||||
assert!(f0.matches(&trace));
|
||||
assert!(f1.matches(&trace));
|
||||
assert!(f2.matches(&trace));
|
||||
assert!(f3.matches(&trace));
|
||||
assert!(f4.matches(&trace));
|
||||
assert!(f5.matches(&trace));
|
||||
assert!(!f6.matches(&trace));
|
||||
|
||||
let trace = FlatTrace {
|
||||
action: Action::Reward(Reward {
|
||||
author: 2.into(),
|
||||
value: 100.into(),
|
||||
reward_type: RewardType::Block,
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: vec![].into_iter().collect(),
|
||||
subtraces: 0
|
||||
};
|
||||
let trace = FlatTrace {
|
||||
action: Action::Reward(Reward {
|
||||
author: 2.into(),
|
||||
value: 100.into(),
|
||||
reward_type: RewardType::Block,
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: vec![].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
|
||||
assert!(!f0.matches(&trace));
|
||||
assert!(!f1.matches(&trace));
|
||||
assert!(f2.matches(&trace));
|
||||
assert!(f3.matches(&trace));
|
||||
assert!(f4.matches(&trace));
|
||||
assert!(!f5.matches(&trace));
|
||||
assert!(!f6.matches(&trace));
|
||||
}
|
||||
assert!(!f0.matches(&trace));
|
||||
assert!(!f1.matches(&trace));
|
||||
assert!(f2.matches(&trace));
|
||||
assert!(f3.matches(&trace));
|
||||
assert!(f4.matches(&trace));
|
||||
assert!(!f5.matches(&trace));
|
||||
assert!(!f6.matches(&trace));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_match_block_reward_fix_8070() {
|
||||
let f0 = Filter {
|
||||
range: (0..0),
|
||||
from_address: vec![1.into()].into(),
|
||||
to_address: vec![].into(),
|
||||
};
|
||||
#[test]
|
||||
fn filter_match_block_reward_fix_8070() {
|
||||
let f0 = Filter {
|
||||
range: (0..0),
|
||||
from_address: vec![1.into()].into(),
|
||||
to_address: vec![].into(),
|
||||
};
|
||||
|
||||
let f1 = Filter {
|
||||
range: (0..0),
|
||||
from_address: vec![].into(),
|
||||
to_address: vec![].into(),
|
||||
};
|
||||
let f1 = Filter {
|
||||
range: (0..0),
|
||||
from_address: vec![].into(),
|
||||
to_address: vec![].into(),
|
||||
};
|
||||
|
||||
let f2 = Filter {
|
||||
range: (0..0),
|
||||
from_address: vec![].into(),
|
||||
to_address: vec![2.into()].into(),
|
||||
};
|
||||
let f2 = Filter {
|
||||
range: (0..0),
|
||||
from_address: vec![].into(),
|
||||
to_address: vec![2.into()].into(),
|
||||
};
|
||||
|
||||
let trace = FlatTrace {
|
||||
action: Action::Reward(Reward {
|
||||
author: 2.into(),
|
||||
value: 10.into(),
|
||||
reward_type: RewardType::Block,
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: vec![0].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
let trace = FlatTrace {
|
||||
action: Action::Reward(Reward {
|
||||
author: 2.into(),
|
||||
value: 10.into(),
|
||||
reward_type: RewardType::Block,
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: vec![0].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
|
||||
assert!(!f0.matches(&trace));
|
||||
assert!(f1.matches(&trace));
|
||||
assert!(f2.matches(&trace));
|
||||
}
|
||||
assert!(!f0.matches(&trace));
|
||||
assert!(f1.matches(&trace));
|
||||
assert!(f2.matches(&trace));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_match_failed_contract_creation_fix_9822() {
|
||||
#[test]
|
||||
fn filter_match_failed_contract_creation_fix_9822() {
|
||||
let f0 = Filter {
|
||||
range: (0..0),
|
||||
from_address: vec![1.into()].into(),
|
||||
to_address: vec![].into(),
|
||||
};
|
||||
|
||||
let f0 = Filter {
|
||||
range: (0..0),
|
||||
from_address: vec![1.into()].into(),
|
||||
to_address: vec![].into(),
|
||||
};
|
||||
let f1 = Filter {
|
||||
range: (0..0),
|
||||
from_address: vec![].into(),
|
||||
to_address: vec![].into(),
|
||||
};
|
||||
|
||||
let f1 = Filter {
|
||||
range: (0..0),
|
||||
from_address: vec![].into(),
|
||||
to_address: vec![].into(),
|
||||
};
|
||||
let f2 = Filter {
|
||||
range: (0..0),
|
||||
from_address: vec![].into(),
|
||||
to_address: vec![2.into()].into(),
|
||||
};
|
||||
|
||||
let f2 = Filter {
|
||||
range: (0..0),
|
||||
from_address: vec![].into(),
|
||||
to_address: vec![2.into()].into(),
|
||||
};
|
||||
|
||||
let trace = FlatTrace {
|
||||
action: Action::Create(Create {
|
||||
from: 1.into(),
|
||||
gas: 4.into(),
|
||||
init: vec![0x5],
|
||||
value: 3.into(),
|
||||
}),
|
||||
result: Res::FailedCall(TraceError::BadInstruction),
|
||||
trace_address: vec![].into_iter().collect(),
|
||||
subtraces: 0
|
||||
};
|
||||
|
||||
assert!(f0.matches(&trace));
|
||||
assert!(f1.matches(&trace));
|
||||
assert!(!f2.matches(&trace));
|
||||
}
|
||||
let trace = FlatTrace {
|
||||
action: Action::Create(Create {
|
||||
from: 1.into(),
|
||||
gas: 4.into(),
|
||||
init: vec![0x5],
|
||||
value: 3.into(),
|
||||
}),
|
||||
result: Res::FailedCall(TraceError::BadInstruction),
|
||||
trace_address: vec![].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
|
||||
assert!(f0.matches(&trace));
|
||||
assert!(f1.matches(&trace));
|
||||
assert!(!f2.matches(&trace));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,63 +16,63 @@
|
||||
|
||||
//! Flat trace module
|
||||
|
||||
use rlp::{Rlp, RlpStream, Decodable, Encodable, DecoderError};
|
||||
use heapsize::HeapSizeOf;
|
||||
use ethereum_types::Bloom;
|
||||
use super::trace::{Action, Res};
|
||||
use ethereum_types::Bloom;
|
||||
use heapsize::HeapSizeOf;
|
||||
use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
|
||||
|
||||
/// Trace localized in vector of traces produced by a single transaction.
|
||||
///
|
||||
/// Parent and children indexes refer to positions in this vector.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct FlatTrace {
|
||||
/// Type of action performed by a transaction.
|
||||
pub action: Action,
|
||||
/// Result of this action.
|
||||
pub result: Res,
|
||||
/// Number of subtraces.
|
||||
pub subtraces: usize,
|
||||
/// Exact location of trace.
|
||||
///
|
||||
/// [index in root, index in first CALL, index in second CALL, ...]
|
||||
pub trace_address: Vec<usize>,
|
||||
/// Type of action performed by a transaction.
|
||||
pub action: Action,
|
||||
/// Result of this action.
|
||||
pub result: Res,
|
||||
/// Number of subtraces.
|
||||
pub subtraces: usize,
|
||||
/// Exact location of trace.
|
||||
///
|
||||
/// [index in root, index in first CALL, index in second CALL, ...]
|
||||
pub trace_address: Vec<usize>,
|
||||
}
|
||||
|
||||
impl FlatTrace {
|
||||
/// Returns bloom of the trace.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
self.action.bloom() | self.result.bloom()
|
||||
}
|
||||
/// Returns bloom of the trace.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
self.action.bloom() | self.result.bloom()
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapSizeOf for FlatTrace {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.trace_address.heap_size_of_children()
|
||||
}
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.trace_address.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for FlatTrace {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(4);
|
||||
s.append(&self.action);
|
||||
s.append(&self.result);
|
||||
s.append(&self.subtraces);
|
||||
s.append_list::<usize, &usize>(&self.trace_address.iter().collect::<Vec<_>>());
|
||||
}
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(4);
|
||||
s.append(&self.action);
|
||||
s.append(&self.result);
|
||||
s.append(&self.subtraces);
|
||||
s.append_list::<usize, &usize>(&self.trace_address.iter().collect::<Vec<_>>());
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for FlatTrace {
|
||||
fn decode(d: &Rlp) -> Result<Self, DecoderError> {
|
||||
let v: Vec<usize> = d.list_at(3)?;
|
||||
let res = FlatTrace {
|
||||
action: d.val_at(0)?,
|
||||
result: d.val_at(1)?,
|
||||
subtraces: d.val_at(2)?,
|
||||
trace_address: v.into_iter().collect(),
|
||||
};
|
||||
fn decode(d: &Rlp) -> Result<Self, DecoderError> {
|
||||
let v: Vec<usize> = d.list_at(3)?;
|
||||
let res = FlatTrace {
|
||||
action: d.val_at(0)?,
|
||||
result: d.val_at(1)?,
|
||||
subtraces: d.val_at(2)?,
|
||||
trace_address: v.into_iter().collect(),
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents all traces produced by a single transaction.
|
||||
@@ -80,28 +80,30 @@ impl Decodable for FlatTrace {
|
||||
pub struct FlatTransactionTraces(Vec<FlatTrace>);
|
||||
|
||||
impl From<Vec<FlatTrace>> for FlatTransactionTraces {
|
||||
fn from(v: Vec<FlatTrace>) -> Self {
|
||||
FlatTransactionTraces(v)
|
||||
}
|
||||
fn from(v: Vec<FlatTrace>) -> Self {
|
||||
FlatTransactionTraces(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapSizeOf for FlatTransactionTraces {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.0.heap_size_of_children()
|
||||
}
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.0.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
impl FlatTransactionTraces {
|
||||
/// Returns bloom of all traces in the collection.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
self.0.iter().fold(Default::default(), | bloom, trace | bloom | trace.bloom())
|
||||
}
|
||||
/// Returns bloom of all traces in the collection.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
self.0
|
||||
.iter()
|
||||
.fold(Default::default(), |bloom, trace| bloom | trace.bloom())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<FlatTrace>> for FlatTransactionTraces {
|
||||
fn into(self) -> Vec<FlatTrace> {
|
||||
self.0
|
||||
}
|
||||
fn into(self) -> Vec<FlatTrace> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents all traces produced by transactions in a single block.
|
||||
@@ -109,141 +111,145 @@ impl Into<Vec<FlatTrace>> for FlatTransactionTraces {
|
||||
pub struct FlatBlockTraces(Vec<FlatTransactionTraces>);
|
||||
|
||||
impl HeapSizeOf for FlatBlockTraces {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.0.heap_size_of_children()
|
||||
}
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.0.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<FlatTransactionTraces>> for FlatBlockTraces {
|
||||
fn from(v: Vec<FlatTransactionTraces>) -> Self {
|
||||
FlatBlockTraces(v)
|
||||
}
|
||||
fn from(v: Vec<FlatTransactionTraces>) -> Self {
|
||||
FlatBlockTraces(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl FlatBlockTraces {
|
||||
/// Returns bloom of all traces in the block.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
self.0.iter().fold(Default::default(), | bloom, tx_traces | bloom | tx_traces.bloom())
|
||||
}
|
||||
/// Returns bloom of all traces in the block.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
self.0.iter().fold(Default::default(), |bloom, tx_traces| {
|
||||
bloom | tx_traces.bloom()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<FlatTransactionTraces>> for FlatBlockTraces {
|
||||
fn into(self) -> Vec<FlatTransactionTraces> {
|
||||
self.0
|
||||
}
|
||||
fn into(self) -> Vec<FlatTransactionTraces> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rlp::*;
|
||||
use super::{FlatBlockTraces, FlatTransactionTraces, FlatTrace};
|
||||
use trace::trace::{Action, Res, CallResult, Call, Suicide, Reward};
|
||||
use evm::CallType;
|
||||
use trace::RewardType;
|
||||
use super::{FlatBlockTraces, FlatTrace, FlatTransactionTraces};
|
||||
use evm::CallType;
|
||||
use rlp::*;
|
||||
use trace::{
|
||||
trace::{Action, Call, CallResult, Res, Reward, Suicide},
|
||||
RewardType,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn encode_flat_transaction_traces() {
|
||||
let ftt = FlatTransactionTraces::from(Vec::new());
|
||||
#[test]
|
||||
fn encode_flat_transaction_traces() {
|
||||
let ftt = FlatTransactionTraces::from(Vec::new());
|
||||
|
||||
let mut s = RlpStream::new_list(2);
|
||||
s.append(&ftt);
|
||||
assert!(!s.is_finished(), "List shouldn't finished yet");
|
||||
s.append(&ftt);
|
||||
assert!(s.is_finished(), "List should be finished now");
|
||||
s.out();
|
||||
}
|
||||
let mut s = RlpStream::new_list(2);
|
||||
s.append(&ftt);
|
||||
assert!(!s.is_finished(), "List shouldn't finished yet");
|
||||
s.append(&ftt);
|
||||
assert!(s.is_finished(), "List should be finished now");
|
||||
s.out();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_flat_block_traces() {
|
||||
let fbt = FlatBlockTraces::from(Vec::new());
|
||||
#[test]
|
||||
fn encode_flat_block_traces() {
|
||||
let fbt = FlatBlockTraces::from(Vec::new());
|
||||
|
||||
let mut s = RlpStream::new_list(2);
|
||||
s.append(&fbt);
|
||||
assert!(!s.is_finished(), "List shouldn't finished yet");
|
||||
s.append(&fbt);
|
||||
assert!(s.is_finished(), "List should be finished now");
|
||||
s.out();
|
||||
}
|
||||
let mut s = RlpStream::new_list(2);
|
||||
s.append(&fbt);
|
||||
assert!(!s.is_finished(), "List shouldn't finished yet");
|
||||
s.append(&fbt);
|
||||
assert!(s.is_finished(), "List should be finished now");
|
||||
s.out();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trace_serialization() {
|
||||
// block #51921
|
||||
#[test]
|
||||
fn test_trace_serialization() {
|
||||
// block #51921
|
||||
|
||||
let flat_trace = FlatTrace {
|
||||
action: Action::Call(Call {
|
||||
from: "8dda5e016e674683241bf671cced51e7239ea2bc".parse().unwrap(),
|
||||
to: "37a5e19cc2d49f244805d5c268c0e6f321965ab9".parse().unwrap(),
|
||||
value: "3627e8f712373c0000".parse().unwrap(),
|
||||
gas: 0x03e8.into(),
|
||||
input: vec![],
|
||||
call_type: CallType::Call,
|
||||
}),
|
||||
result: Res::Call(CallResult {
|
||||
gas_used: 0.into(),
|
||||
output: vec![],
|
||||
}),
|
||||
trace_address: Default::default(),
|
||||
subtraces: 0,
|
||||
};
|
||||
let flat_trace = FlatTrace {
|
||||
action: Action::Call(Call {
|
||||
from: "8dda5e016e674683241bf671cced51e7239ea2bc".parse().unwrap(),
|
||||
to: "37a5e19cc2d49f244805d5c268c0e6f321965ab9".parse().unwrap(),
|
||||
value: "3627e8f712373c0000".parse().unwrap(),
|
||||
gas: 0x03e8.into(),
|
||||
input: vec![],
|
||||
call_type: CallType::Call,
|
||||
}),
|
||||
result: Res::Call(CallResult {
|
||||
gas_used: 0.into(),
|
||||
output: vec![],
|
||||
}),
|
||||
trace_address: Default::default(),
|
||||
subtraces: 0,
|
||||
};
|
||||
|
||||
let flat_trace1 = FlatTrace {
|
||||
action: Action::Call(Call {
|
||||
from: "3d0768da09ce77d25e2d998e6a7b6ed4b9116c2d".parse().unwrap(),
|
||||
to: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(),
|
||||
value: 0.into(),
|
||||
gas: 0x010c78.into(),
|
||||
input: vec![0x41, 0xc0, 0xe1, 0xb5],
|
||||
call_type: CallType::Call,
|
||||
}),
|
||||
result: Res::Call(CallResult {
|
||||
gas_used: 0x0127.into(),
|
||||
output: vec![],
|
||||
}),
|
||||
trace_address: Default::default(),
|
||||
subtraces: 1,
|
||||
};
|
||||
let flat_trace1 = FlatTrace {
|
||||
action: Action::Call(Call {
|
||||
from: "3d0768da09ce77d25e2d998e6a7b6ed4b9116c2d".parse().unwrap(),
|
||||
to: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(),
|
||||
value: 0.into(),
|
||||
gas: 0x010c78.into(),
|
||||
input: vec![0x41, 0xc0, 0xe1, 0xb5],
|
||||
call_type: CallType::Call,
|
||||
}),
|
||||
result: Res::Call(CallResult {
|
||||
gas_used: 0x0127.into(),
|
||||
output: vec![],
|
||||
}),
|
||||
trace_address: Default::default(),
|
||||
subtraces: 1,
|
||||
};
|
||||
|
||||
let flat_trace2 = FlatTrace {
|
||||
action: Action::Suicide(Suicide {
|
||||
address: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(),
|
||||
balance: 0.into(),
|
||||
refund_address: "3d0768da09ce77d25e2d998e6a7b6ed4b9116c2d".parse().unwrap(),
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: vec![0].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
let flat_trace2 = FlatTrace {
|
||||
action: Action::Suicide(Suicide {
|
||||
address: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(),
|
||||
balance: 0.into(),
|
||||
refund_address: "3d0768da09ce77d25e2d998e6a7b6ed4b9116c2d".parse().unwrap(),
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: vec![0].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
|
||||
let flat_trace3 = FlatTrace {
|
||||
action: Action::Reward(Reward {
|
||||
author: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(),
|
||||
value: 10.into(),
|
||||
reward_type: RewardType::Uncle,
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: vec![0].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
let flat_trace3 = FlatTrace {
|
||||
action: Action::Reward(Reward {
|
||||
author: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(),
|
||||
value: 10.into(),
|
||||
reward_type: RewardType::Uncle,
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: vec![0].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
|
||||
let flat_trace4 = FlatTrace {
|
||||
action: Action::Reward(Reward {
|
||||
author: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(),
|
||||
value: 10.into(),
|
||||
reward_type: RewardType::Block,
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: vec![0].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
let flat_trace4 = FlatTrace {
|
||||
action: Action::Reward(Reward {
|
||||
author: "412fda7643b37d436cb40628f6dbbb80a07267ed".parse().unwrap(),
|
||||
value: 10.into(),
|
||||
reward_type: RewardType::Block,
|
||||
}),
|
||||
result: Res::None,
|
||||
trace_address: vec![0].into_iter().collect(),
|
||||
subtraces: 0,
|
||||
};
|
||||
|
||||
let block_traces = FlatBlockTraces(vec![
|
||||
FlatTransactionTraces(vec![flat_trace]),
|
||||
FlatTransactionTraces(vec![flat_trace1, flat_trace2]),
|
||||
FlatTransactionTraces(vec![flat_trace3, flat_trace4])
|
||||
]);
|
||||
let block_traces = FlatBlockTraces(vec![
|
||||
FlatTransactionTraces(vec![flat_trace]),
|
||||
FlatTransactionTraces(vec![flat_trace1, flat_trace2]),
|
||||
FlatTransactionTraces(vec![flat_trace3, flat_trace4]),
|
||||
]);
|
||||
|
||||
let encoded = ::rlp::encode(&block_traces);
|
||||
let decoded = ::rlp::decode(&encoded).expect("error decoding block traces");
|
||||
assert_eq!(block_traces, decoded);
|
||||
}
|
||||
let encoded = ::rlp::encode(&block_traces);
|
||||
let decoded = ::rlp::decode(&encoded).expect("error decoding block traces");
|
||||
assert_eq!(block_traces, decoded);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,29 +16,29 @@
|
||||
|
||||
//! Localized traces type definitions
|
||||
|
||||
use ethereum_types::H256;
|
||||
use super::trace::{Action, Res};
|
||||
use ethereum_types::H256;
|
||||
use types::BlockNumber;
|
||||
|
||||
/// Localized trace.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct LocalizedTrace {
|
||||
/// Type of action performed by a transaction.
|
||||
pub action: Action,
|
||||
/// Result of this action.
|
||||
pub result: Res,
|
||||
/// Number of subtraces.
|
||||
pub subtraces: usize,
|
||||
/// Exact location of trace.
|
||||
///
|
||||
/// [index in root, index in first CALL, index in second CALL, ...]
|
||||
pub trace_address: Vec<usize>,
|
||||
/// Transaction number within the block.
|
||||
pub transaction_number: Option<usize>,
|
||||
/// Signed transaction hash.
|
||||
pub transaction_hash: Option<H256>,
|
||||
/// Block number.
|
||||
pub block_number: BlockNumber,
|
||||
/// Block hash.
|
||||
pub block_hash: H256,
|
||||
/// Type of action performed by a transaction.
|
||||
pub action: Action,
|
||||
/// Result of this action.
|
||||
pub result: Res,
|
||||
/// Number of subtraces.
|
||||
pub subtraces: usize,
|
||||
/// Exact location of trace.
|
||||
///
|
||||
/// [index in root, index in first CALL, index in second CALL, ...]
|
||||
pub trace_address: Vec<usize>,
|
||||
/// Transaction number within the block.
|
||||
pub transaction_number: Option<usize>,
|
||||
/// Signed transaction hash.
|
||||
pub transaction_hash: Option<H256>,
|
||||
/// Block number.
|
||||
pub block_number: BlockNumber,
|
||||
/// Block hash.
|
||||
pub block_hash: H256,
|
||||
}
|
||||
|
||||
@@ -19,39 +19,39 @@
|
||||
pub mod error;
|
||||
pub mod filter;
|
||||
pub mod flat;
|
||||
pub mod trace;
|
||||
pub mod localized;
|
||||
pub mod trace;
|
||||
|
||||
use self::flat::FlatTransactionTraces;
|
||||
|
||||
/// Container for block traces.
|
||||
#[derive(Clone)]
|
||||
pub enum Tracing {
|
||||
/// This variant should be used when tracing is enabled.
|
||||
Enabled(Vec<FlatTransactionTraces>),
|
||||
/// Tracing is disabled.
|
||||
Disabled,
|
||||
/// This variant should be used when tracing is enabled.
|
||||
Enabled(Vec<FlatTransactionTraces>),
|
||||
/// Tracing is disabled.
|
||||
Disabled,
|
||||
}
|
||||
|
||||
impl Tracing {
|
||||
/// Creates new instance of enabled tracing object.
|
||||
pub fn enabled() -> Self {
|
||||
Tracing::Enabled(Default::default())
|
||||
}
|
||||
/// Creates new instance of enabled tracing object.
|
||||
pub fn enabled() -> Self {
|
||||
Tracing::Enabled(Default::default())
|
||||
}
|
||||
|
||||
/// Returns true if tracing is enabled.
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
match *self {
|
||||
Tracing::Enabled(_) => true,
|
||||
Tracing::Disabled => false,
|
||||
}
|
||||
}
|
||||
/// Returns true if tracing is enabled.
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
match *self {
|
||||
Tracing::Enabled(_) => true,
|
||||
Tracing::Disabled => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Drain all traces.
|
||||
pub fn drain(self) -> Vec<FlatTransactionTraces> {
|
||||
match self {
|
||||
Tracing::Enabled(traces) => traces,
|
||||
Tracing::Disabled => vec![],
|
||||
}
|
||||
}
|
||||
/// Drain all traces.
|
||||
pub fn drain(self) -> Vec<FlatTransactionTraces> {
|
||||
match self {
|
||||
Tracing::Enabled(traces) => traces,
|
||||
Tracing::Disabled => vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,416 +16,419 @@
|
||||
|
||||
//! Tracing datatypes.
|
||||
|
||||
use ethereum_types::{U256, Address, Bloom, BloomInput};
|
||||
use bytes::Bytes;
|
||||
use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable};
|
||||
use ethereum_types::{Address, Bloom, BloomInput, U256};
|
||||
use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
|
||||
|
||||
use vm::ActionParams;
|
||||
use evm::CallType;
|
||||
use super::error::Error;
|
||||
use evm::CallType;
|
||||
use vm::ActionParams;
|
||||
|
||||
/// `Call` result.
|
||||
#[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)]
|
||||
pub struct CallResult {
|
||||
/// Gas used by call.
|
||||
pub gas_used: U256,
|
||||
/// Call Output.
|
||||
pub output: Bytes,
|
||||
/// Gas used by call.
|
||||
pub gas_used: U256,
|
||||
/// Call Output.
|
||||
pub output: Bytes,
|
||||
}
|
||||
|
||||
/// `Create` result.
|
||||
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
||||
pub struct CreateResult {
|
||||
/// Gas used by create.
|
||||
pub gas_used: U256,
|
||||
/// Code of the newly created contract.
|
||||
pub code: Bytes,
|
||||
/// Address of the newly created contract.
|
||||
pub address: Address,
|
||||
/// Gas used by create.
|
||||
pub gas_used: U256,
|
||||
/// Code of the newly created contract.
|
||||
pub code: Bytes,
|
||||
/// Address of the newly created contract.
|
||||
pub address: Address,
|
||||
}
|
||||
|
||||
impl CreateResult {
|
||||
/// Returns bloom.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
BloomInput::Raw(&self.address).into()
|
||||
}
|
||||
/// Returns bloom.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
BloomInput::Raw(&self.address).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Description of a _call_ action, either a `CALL` operation or a message transction.
|
||||
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
||||
pub struct Call {
|
||||
/// The sending account.
|
||||
pub from: Address,
|
||||
/// The destination account.
|
||||
pub to: Address,
|
||||
/// The value transferred to the destination account.
|
||||
pub value: U256,
|
||||
/// The gas available for executing the call.
|
||||
pub gas: U256,
|
||||
/// The input data provided to the call.
|
||||
pub input: Bytes,
|
||||
/// The type of the call.
|
||||
pub call_type: CallType,
|
||||
/// The sending account.
|
||||
pub from: Address,
|
||||
/// The destination account.
|
||||
pub to: Address,
|
||||
/// The value transferred to the destination account.
|
||||
pub value: U256,
|
||||
/// The gas available for executing the call.
|
||||
pub gas: U256,
|
||||
/// The input data provided to the call.
|
||||
pub input: Bytes,
|
||||
/// The type of the call.
|
||||
pub call_type: CallType,
|
||||
}
|
||||
|
||||
impl From<ActionParams> for Call {
|
||||
fn from(p: ActionParams) -> Self {
|
||||
match p.call_type {
|
||||
CallType::DelegateCall | CallType::CallCode => Call {
|
||||
from: p.address,
|
||||
to: p.code_address,
|
||||
value: p.value.value(),
|
||||
gas: p.gas,
|
||||
input: p.data.unwrap_or_else(Vec::new),
|
||||
call_type: p.call_type,
|
||||
},
|
||||
_ => Call {
|
||||
from: p.sender,
|
||||
to: p.address,
|
||||
value: p.value.value(),
|
||||
gas: p.gas,
|
||||
input: p.data.unwrap_or_else(Vec::new),
|
||||
call_type: p.call_type,
|
||||
},
|
||||
}
|
||||
}
|
||||
fn from(p: ActionParams) -> Self {
|
||||
match p.call_type {
|
||||
CallType::DelegateCall | CallType::CallCode => Call {
|
||||
from: p.address,
|
||||
to: p.code_address,
|
||||
value: p.value.value(),
|
||||
gas: p.gas,
|
||||
input: p.data.unwrap_or_else(Vec::new),
|
||||
call_type: p.call_type,
|
||||
},
|
||||
_ => Call {
|
||||
from: p.sender,
|
||||
to: p.address,
|
||||
value: p.value.value(),
|
||||
gas: p.gas,
|
||||
input: p.data.unwrap_or_else(Vec::new),
|
||||
call_type: p.call_type,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Call {
|
||||
/// Returns call action bloom.
|
||||
/// The bloom contains from and to addresses.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
let mut bloom = Bloom::default();
|
||||
bloom.accrue(BloomInput::Raw(&self.from));
|
||||
bloom.accrue(BloomInput::Raw(&self.to));
|
||||
bloom
|
||||
}
|
||||
/// Returns call action bloom.
|
||||
/// The bloom contains from and to addresses.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
let mut bloom = Bloom::default();
|
||||
bloom.accrue(BloomInput::Raw(&self.from));
|
||||
bloom.accrue(BloomInput::Raw(&self.to));
|
||||
bloom
|
||||
}
|
||||
}
|
||||
|
||||
/// Description of a _create_ action, either a `CREATE` operation or a create transction.
|
||||
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
||||
pub struct Create {
|
||||
/// The address of the creator.
|
||||
pub from: Address,
|
||||
/// The value with which the new account is endowed.
|
||||
pub value: U256,
|
||||
/// The gas available for the creation init code.
|
||||
pub gas: U256,
|
||||
/// The init code.
|
||||
pub init: Bytes,
|
||||
/// The address of the creator.
|
||||
pub from: Address,
|
||||
/// The value with which the new account is endowed.
|
||||
pub value: U256,
|
||||
/// The gas available for the creation init code.
|
||||
pub gas: U256,
|
||||
/// The init code.
|
||||
pub init: Bytes,
|
||||
}
|
||||
|
||||
impl From<ActionParams> for Create {
|
||||
fn from(p: ActionParams) -> Self {
|
||||
Create {
|
||||
from: p.sender,
|
||||
value: p.value.value(),
|
||||
gas: p.gas,
|
||||
init: p.code.map_or_else(Vec::new, |c| (*c).clone()),
|
||||
}
|
||||
}
|
||||
fn from(p: ActionParams) -> Self {
|
||||
Create {
|
||||
from: p.sender,
|
||||
value: p.value.value(),
|
||||
gas: p.gas,
|
||||
init: p.code.map_or_else(Vec::new, |c| (*c).clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Create {
|
||||
/// Returns bloom create action bloom.
|
||||
/// The bloom contains only from address.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
BloomInput::Raw(&self.from).into()
|
||||
}
|
||||
/// Returns bloom create action bloom.
|
||||
/// The bloom contains only from address.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
BloomInput::Raw(&self.from).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Reward type.
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum RewardType {
|
||||
/// Block
|
||||
Block,
|
||||
/// Uncle
|
||||
Uncle,
|
||||
/// Empty step (AuthorityRound)
|
||||
EmptyStep,
|
||||
/// A reward directly attributed by an external protocol (e.g. block reward contract)
|
||||
External,
|
||||
/// Block
|
||||
Block,
|
||||
/// Uncle
|
||||
Uncle,
|
||||
/// Empty step (AuthorityRound)
|
||||
EmptyStep,
|
||||
/// A reward directly attributed by an external protocol (e.g. block reward contract)
|
||||
External,
|
||||
}
|
||||
|
||||
impl Encodable for RewardType {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
let v = match *self {
|
||||
RewardType::Block => 0u32,
|
||||
RewardType::Uncle => 1,
|
||||
RewardType::EmptyStep => 2,
|
||||
RewardType::External => 3,
|
||||
};
|
||||
Encodable::rlp_append(&v, s);
|
||||
}
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
let v = match *self {
|
||||
RewardType::Block => 0u32,
|
||||
RewardType::Uncle => 1,
|
||||
RewardType::EmptyStep => 2,
|
||||
RewardType::External => 3,
|
||||
};
|
||||
Encodable::rlp_append(&v, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for RewardType {
|
||||
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
||||
rlp.as_val().and_then(|v| Ok(match v {
|
||||
0u32 => RewardType::Block,
|
||||
1 => RewardType::Uncle,
|
||||
2 => RewardType::EmptyStep,
|
||||
3 => RewardType::External,
|
||||
_ => return Err(DecoderError::Custom("Invalid value of RewardType item")),
|
||||
}))
|
||||
}
|
||||
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
||||
rlp.as_val().and_then(|v| {
|
||||
Ok(match v {
|
||||
0u32 => RewardType::Block,
|
||||
1 => RewardType::Uncle,
|
||||
2 => RewardType::EmptyStep,
|
||||
3 => RewardType::External,
|
||||
_ => return Err(DecoderError::Custom("Invalid value of RewardType item")),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Reward action
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Reward {
|
||||
/// Author's address.
|
||||
pub author: Address,
|
||||
/// Reward amount.
|
||||
pub value: U256,
|
||||
/// Reward type.
|
||||
pub reward_type: RewardType,
|
||||
/// Author's address.
|
||||
pub author: Address,
|
||||
/// Reward amount.
|
||||
pub value: U256,
|
||||
/// Reward type.
|
||||
pub reward_type: RewardType,
|
||||
}
|
||||
|
||||
impl Reward {
|
||||
/// Return reward action bloom.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
BloomInput::Raw(&self.author).into()
|
||||
}
|
||||
/// Return reward action bloom.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
BloomInput::Raw(&self.author).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Reward {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(3);
|
||||
s.append(&self.author);
|
||||
s.append(&self.value);
|
||||
s.append(&self.reward_type);
|
||||
}
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(3);
|
||||
s.append(&self.author);
|
||||
s.append(&self.value);
|
||||
s.append(&self.reward_type);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Reward {
|
||||
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
||||
let res = Reward {
|
||||
author: rlp.val_at(0)?,
|
||||
value: rlp.val_at(1)?,
|
||||
reward_type: rlp.val_at(2)?,
|
||||
};
|
||||
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
||||
let res = Reward {
|
||||
author: rlp.val_at(0)?,
|
||||
value: rlp.val_at(1)?,
|
||||
reward_type: rlp.val_at(2)?,
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Suicide action.
|
||||
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
||||
pub struct Suicide {
|
||||
/// Suicided address.
|
||||
pub address: Address,
|
||||
/// Suicided contract heir.
|
||||
pub refund_address: Address,
|
||||
/// Balance of the contract just before suicide.
|
||||
pub balance: U256,
|
||||
/// Suicided address.
|
||||
pub address: Address,
|
||||
/// Suicided contract heir.
|
||||
pub refund_address: Address,
|
||||
/// Balance of the contract just before suicide.
|
||||
pub balance: U256,
|
||||
}
|
||||
|
||||
impl Suicide {
|
||||
/// Return suicide action bloom.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
let mut bloom = Bloom::default();
|
||||
bloom.accrue(BloomInput::Raw(&self.address));
|
||||
bloom.accrue(BloomInput::Raw(&self.refund_address));
|
||||
bloom
|
||||
}
|
||||
/// Return suicide action bloom.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
let mut bloom = Bloom::default();
|
||||
bloom.accrue(BloomInput::Raw(&self.address));
|
||||
bloom.accrue(BloomInput::Raw(&self.refund_address));
|
||||
bloom
|
||||
}
|
||||
}
|
||||
|
||||
/// Description of an action that we trace; will be either a call or a create.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Action {
|
||||
/// It's a call action.
|
||||
Call(Call),
|
||||
/// It's a create action.
|
||||
Create(Create),
|
||||
/// Suicide.
|
||||
Suicide(Suicide),
|
||||
/// Reward
|
||||
Reward(Reward),
|
||||
/// It's a call action.
|
||||
Call(Call),
|
||||
/// It's a create action.
|
||||
Create(Create),
|
||||
/// Suicide.
|
||||
Suicide(Suicide),
|
||||
/// Reward
|
||||
Reward(Reward),
|
||||
}
|
||||
|
||||
impl Encodable for Action {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(2);
|
||||
match *self {
|
||||
Action::Call(ref call) => {
|
||||
s.append(&0u8);
|
||||
s.append(call);
|
||||
},
|
||||
Action::Create(ref create) => {
|
||||
s.append(&1u8);
|
||||
s.append(create);
|
||||
},
|
||||
Action::Suicide(ref suicide) => {
|
||||
s.append(&2u8);
|
||||
s.append(suicide);
|
||||
},
|
||||
Action::Reward(ref reward) => {
|
||||
s.append(&3u8);
|
||||
s.append(reward);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(2);
|
||||
match *self {
|
||||
Action::Call(ref call) => {
|
||||
s.append(&0u8);
|
||||
s.append(call);
|
||||
}
|
||||
Action::Create(ref create) => {
|
||||
s.append(&1u8);
|
||||
s.append(create);
|
||||
}
|
||||
Action::Suicide(ref suicide) => {
|
||||
s.append(&2u8);
|
||||
s.append(suicide);
|
||||
}
|
||||
Action::Reward(ref reward) => {
|
||||
s.append(&3u8);
|
||||
s.append(reward);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Action {
|
||||
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
||||
let action_type: u8 = rlp.val_at(0)?;
|
||||
match action_type {
|
||||
0 => rlp.val_at(1).map(Action::Call),
|
||||
1 => rlp.val_at(1).map(Action::Create),
|
||||
2 => rlp.val_at(1).map(Action::Suicide),
|
||||
3 => rlp.val_at(1).map(Action::Reward),
|
||||
_ => Err(DecoderError::Custom("Invalid action type.")),
|
||||
}
|
||||
}
|
||||
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
||||
let action_type: u8 = rlp.val_at(0)?;
|
||||
match action_type {
|
||||
0 => rlp.val_at(1).map(Action::Call),
|
||||
1 => rlp.val_at(1).map(Action::Create),
|
||||
2 => rlp.val_at(1).map(Action::Suicide),
|
||||
3 => rlp.val_at(1).map(Action::Reward),
|
||||
_ => Err(DecoderError::Custom("Invalid action type.")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Action {
|
||||
/// Returns action bloom.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
match *self {
|
||||
Action::Call(ref call) => call.bloom(),
|
||||
Action::Create(ref create) => create.bloom(),
|
||||
Action::Suicide(ref suicide) => suicide.bloom(),
|
||||
Action::Reward(ref reward) => reward.bloom(),
|
||||
}
|
||||
}
|
||||
/// Returns action bloom.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
match *self {
|
||||
Action::Call(ref call) => call.bloom(),
|
||||
Action::Create(ref create) => create.bloom(),
|
||||
Action::Suicide(ref suicide) => suicide.bloom(),
|
||||
Action::Reward(ref reward) => reward.bloom(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of the performed action.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Res {
|
||||
/// Successful call action result.
|
||||
Call(CallResult),
|
||||
/// Successful create action result.
|
||||
Create(CreateResult),
|
||||
/// Failed call.
|
||||
FailedCall(Error),
|
||||
/// Failed create.
|
||||
FailedCreate(Error),
|
||||
/// None
|
||||
None,
|
||||
/// Successful call action result.
|
||||
Call(CallResult),
|
||||
/// Successful create action result.
|
||||
Create(CreateResult),
|
||||
/// Failed call.
|
||||
FailedCall(Error),
|
||||
/// Failed create.
|
||||
FailedCreate(Error),
|
||||
/// None
|
||||
None,
|
||||
}
|
||||
|
||||
impl Encodable for Res {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
match *self {
|
||||
Res::Call(ref call) => {
|
||||
s.begin_list(2);
|
||||
s.append(&0u8);
|
||||
s.append(call);
|
||||
},
|
||||
Res::Create(ref create) => {
|
||||
s.begin_list(2);
|
||||
s.append(&1u8);
|
||||
s.append(create);
|
||||
},
|
||||
Res::FailedCall(ref err) => {
|
||||
s.begin_list(2);
|
||||
s.append(&2u8);
|
||||
s.append(err);
|
||||
},
|
||||
Res::FailedCreate(ref err) => {
|
||||
s.begin_list(2);
|
||||
s.append(&3u8);
|
||||
s.append(err);
|
||||
},
|
||||
Res::None => {
|
||||
s.begin_list(1);
|
||||
s.append(&4u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
match *self {
|
||||
Res::Call(ref call) => {
|
||||
s.begin_list(2);
|
||||
s.append(&0u8);
|
||||
s.append(call);
|
||||
}
|
||||
Res::Create(ref create) => {
|
||||
s.begin_list(2);
|
||||
s.append(&1u8);
|
||||
s.append(create);
|
||||
}
|
||||
Res::FailedCall(ref err) => {
|
||||
s.begin_list(2);
|
||||
s.append(&2u8);
|
||||
s.append(err);
|
||||
}
|
||||
Res::FailedCreate(ref err) => {
|
||||
s.begin_list(2);
|
||||
s.append(&3u8);
|
||||
s.append(err);
|
||||
}
|
||||
Res::None => {
|
||||
s.begin_list(1);
|
||||
s.append(&4u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Res {
|
||||
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
||||
let action_type: u8 = rlp.val_at(0)?;
|
||||
match action_type {
|
||||
0 => rlp.val_at(1).map(Res::Call),
|
||||
1 => rlp.val_at(1).map(Res::Create),
|
||||
2 => rlp.val_at(1).map(Res::FailedCall),
|
||||
3 => rlp.val_at(1).map(Res::FailedCreate),
|
||||
4 => Ok(Res::None),
|
||||
_ => Err(DecoderError::Custom("Invalid result type.")),
|
||||
}
|
||||
}
|
||||
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
||||
let action_type: u8 = rlp.val_at(0)?;
|
||||
match action_type {
|
||||
0 => rlp.val_at(1).map(Res::Call),
|
||||
1 => rlp.val_at(1).map(Res::Create),
|
||||
2 => rlp.val_at(1).map(Res::FailedCall),
|
||||
3 => rlp.val_at(1).map(Res::FailedCreate),
|
||||
4 => Ok(Res::None),
|
||||
_ => Err(DecoderError::Custom("Invalid result type.")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Res {
|
||||
/// Returns result bloom.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
match *self {
|
||||
Res::Create(ref create) => create.bloom(),
|
||||
Res::Call(_) | Res::FailedCall(_) | Res::FailedCreate(_) | Res::None => Default::default(),
|
||||
}
|
||||
}
|
||||
/// Returns result bloom.
|
||||
pub fn bloom(&self) -> Bloom {
|
||||
match *self {
|
||||
Res::Create(ref create) => create.bloom(),
|
||||
Res::Call(_) | Res::FailedCall(_) | Res::FailedCreate(_) | Res::None => {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Did this call fail?
|
||||
pub fn succeeded(&self) -> bool {
|
||||
match *self {
|
||||
Res::Call(_) | Res::Create(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
/// Did this call fail?
|
||||
pub fn succeeded(&self) -> bool {
|
||||
match *self {
|
||||
Res::Call(_) | Res::Create(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
||||
/// 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,
|
||||
/// Offset into memory the change begins.
|
||||
pub offset: usize,
|
||||
/// The changed data.
|
||||
pub data: Bytes,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
||||
/// 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,
|
||||
/// Which key in storage is changed.
|
||||
pub location: U256,
|
||||
/// What the value has been changed to.
|
||||
pub value: U256,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)]
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)]
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user