Ethcore crate split part 1 (#6041)

* split out types into separate crate

* split out evm into its own crate
This commit is contained in:
Robert Habermeier
2017-07-12 13:09:17 +02:00
committed by Gav Wood
parent 24c8510932
commit d365281cce
108 changed files with 806 additions and 777 deletions

View File

@@ -410,7 +410,7 @@ mod tests {
use trace::{Filter, LocalizedTrace, AddressesFilter, TraceError};
use trace::trace::{Call, Action, Res};
use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces};
use types::executed::CallType;
use evm::CallType;
struct NoopExtras;

View File

@@ -17,7 +17,7 @@
//! Simple executive tracer.
use util::{Bytes, Address, U256};
use action_params::ActionParams;
use evm::action_params::ActionParams;
use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide};
use trace::{Tracer, VMTracer, FlatTrace, TraceError};

View File

@@ -22,21 +22,24 @@ mod db;
mod executive_tracer;
mod import;
mod noop_tracer;
mod types;
pub use types::trace_types::{filter, flat, localized, trace};
pub use types::trace_types::error::Error as TraceError;
pub use self::config::Config;
pub use self::db::TraceDB;
pub use types::trace_types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
pub use types::trace_types::flat::{FlatTrace, FlatTransactionTraces, FlatBlockTraces};
pub use self::noop_tracer::{NoopTracer, NoopVMTracer};
pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer};
pub use types::trace_types::filter::{Filter, AddressesFilter};
pub use self::import::ImportRequest;
pub use self::localized::LocalizedTrace;
pub use self::types::{filter, flat, localized, trace};
pub use self::types::error::Error as TraceError;
pub use self::types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
pub use self::types::flat::{FlatTrace, FlatTransactionTraces, FlatBlockTraces};
pub use self::types::filter::{Filter, AddressesFilter};
use util::{Bytes, Address, U256, H256, DBTransaction};
use self::trace::{Call, Create};
use action_params::ActionParams;
use evm::action_params::ActionParams;
use header::BlockNumber;
/// This trait is used by executive to build traces.

View File

@@ -17,7 +17,7 @@
//! Nonoperative tracer.
use util::{Bytes, Address, U256};
use action_params::ActionParams;
use evm::action_params::ActionParams;
use trace::{Tracer, VMTracer, FlatTrace, TraceError};
use trace::trace::{Call, Create, VMTrace};

View File

@@ -0,0 +1,142 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Trace errors.
use std::fmt;
use rlp::{Encodable, RlpStream, Decodable, DecoderError, UntrustedRlp};
use evm::Error as EvmError;
/// 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,
}
impl<'a> From<&'a EvmError> for Error {
fn from(e: &'a EvmError) -> Self {
match *e {
EvmError::OutOfGas => Error::OutOfGas,
EvmError::BadJumpDestination { .. } => Error::BadJumpDestination,
EvmError::BadInstruction { .. } => Error::BadInstruction,
EvmError::StackUnderflow { .. } => Error::StackUnderflow,
EvmError::OutOfStack { .. } => Error::OutOfStack,
EvmError::BuiltIn { .. } => Error::BuiltIn,
EvmError::Wasm { .. } => Error::Wasm,
EvmError::Internal(_) => Error::Internal,
EvmError::MutableCallInStaticContext => Error::MutableCallInStaticContext,
}
}
}
impl From<EvmError> for Error {
fn from(e: EvmError) -> 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",
};
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,
};
s.append_internal(&value);
}
}
impl Decodable for Error {
fn decode(rlp: &UntrustedRlp) -> 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),
_ => Err(DecoderError::Custom("Invalid error type")),
}
}
}
#[cfg(test)]
mod tests {
use rlp::*;
use super::Error;
#[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();
}
}

View File

@@ -0,0 +1,345 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Trace filters type definitions
use std::ops::Range;
use bloomchain::{Filter as BloomFilter, Bloom, Number};
use util::Address;
use util::sha3::Hashable;
use util::bloom::Bloomable;
use basic_types::LogBloom;
use trace::flat::FlatTrace;
use super::trace::{Action, Res};
/// Addresses filter.
///
/// Used to create bloom possibilities and match filters.
#[derive(Debug)]
pub struct AddressesFilter {
list: Vec<Address>
}
impl From<Vec<Address>> for AddressesFilter {
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 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<LogBloom> {
match self.list.is_empty() {
true => vec![LogBloom::default()],
false => self.list.iter()
.map(|address| LogBloom::from_bloomed(&address.sha3()))
.collect(),
}
}
/// Returns vector of blooms zipped with blooms of this addresses filter.
pub fn with_blooms(&self, blooms: Vec<LogBloom>) -> Vec<LogBloom> {
match self.list.is_empty() {
true => blooms,
false => blooms
.into_iter()
.flat_map(|bloom| self.list.iter()
.map(|address| bloom.with_bloomed(&address.sha3()))
.collect::<Vec<_>>())
.collect(),
}
}
}
#[derive(Debug)]
/// Traces filter.
pub struct Filter {
/// Block range.
pub range: Range<usize>,
/// From address filter.
pub from_address: AddressesFilter,
/// To address filter.
pub to_address: AddressesFilter,
}
impl BloomFilter for Filter {
fn bloom_possibilities(&self) -> Vec<Bloom> {
self.bloom_possibilities()
.into_iter()
.map(|b| Bloom::from(b.0))
.collect()
}
fn range(&self) -> Range<Number> {
self.range.clone()
}
}
impl Filter {
/// Returns combinations of each address.
fn bloom_possibilities(&self) -> Vec<LogBloom> {
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);
let to_matches = match trace.result {
Res::Create(ref create_result) => self.to_address.matches(&create_result.address),
_ => false
};
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
}
}
}
}
#[cfg(test)]
mod tests {
use util::Address;
use util::sha3::Hashable;
use util::bloom::Bloomable;
use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide};
use trace::flat::FlatTrace;
use trace::{Filter, AddressesFilter, TraceError};
use evm::CallType;
#[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![Default::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)]),
};
let blooms = filter.bloom_possibilities();
assert_eq!(blooms.len(), 1);
assert!(blooms[0].contains_bloomed(&Address::from(1).sha3()));
assert!(blooms[0].contains_bloomed(&Address::from(2).sha3()));
assert!(!blooms[0].contains_bloomed(&Address::from(3).sha3()));
}
#[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);
assert!(blooms[0].contains_bloomed(&Address::from(1).sha3()));
assert!(!blooms[0].contains_bloomed(&Address::from(2).sha3()));
}
#[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);
assert!(blooms[0].contains_bloomed(&Address::from(1).sha3()));
assert!(!blooms[0].contains_bloomed(&Address::from(2).sha3()));
}
#[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);
assert!(blooms[0].contains_bloomed(&Address::from(1).sha3()));
assert!(blooms[0].contains_bloomed(&Address::from(2).sha3()));
assert!(!blooms[0].contains_bloomed(&Address::from(3).sha3()));
assert!(!blooms[0].contains_bloomed(&Address::from(4).sha3()));
assert!(blooms[1].contains_bloomed(&Address::from(1).sha3()));
assert!(blooms[1].contains_bloomed(&Address::from(4).sha3()));
assert!(!blooms[1].contains_bloomed(&Address::from(2).sha3()));
assert!(!blooms[1].contains_bloomed(&Address::from(3).sha3()));
assert!(blooms[2].contains_bloomed(&Address::from(2).sha3()));
assert!(blooms[2].contains_bloomed(&Address::from(3).sha3()));
assert!(!blooms[2].contains_bloomed(&Address::from(1).sha3()));
assert!(!blooms[2].contains_bloomed(&Address::from(4).sha3()));
assert!(blooms[3].contains_bloomed(&Address::from(3).sha3()));
assert!(blooms[3].contains_bloomed(&Address::from(4).sha3()));
assert!(!blooms[3].contains_bloomed(&Address::from(1).sha3()));
assert!(!blooms[3].contains_bloomed(&Address::from(2).sha3()));
}
#[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 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 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 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,
};
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,
};
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
};
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));
}
}

View File

@@ -0,0 +1,250 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Flat trace module
use std::collections::VecDeque;
use rlp::*;
use util::HeapSizeOf;
use basic_types::LogBloom;
use super::trace::{Action, Res};
/// 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: VecDeque<usize>,
}
impl FlatTrace {
/// Returns bloom of the trace.
pub fn bloom(&self) -> LogBloom {
self.action.bloom() | self.result.bloom()
}
}
impl HeapSizeOf for FlatTrace {
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<_>>());
}
}
impl Decodable for FlatTrace {
fn decode(d: &UntrustedRlp) -> 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)
}
}
/// Represents all traces produced by a single transaction.
#[derive(Debug, PartialEq, Clone)]
pub struct FlatTransactionTraces(Vec<FlatTrace>);
impl From<Vec<FlatTrace>> for FlatTransactionTraces {
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()
}
}
impl FlatTransactionTraces {
/// Returns bloom of all traces in the collection.
pub fn bloom(&self) -> LogBloom {
self.0.iter().fold(Default::default(), | bloom, trace | bloom | trace.bloom())
}
}
impl Encodable for FlatTransactionTraces {
fn rlp_append(&self, s: &mut RlpStream) {
s.append_list(&self.0);
}
}
impl Decodable for FlatTransactionTraces {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(FlatTransactionTraces(rlp.as_list()?))
}
}
impl Into<Vec<FlatTrace>> for FlatTransactionTraces {
fn into(self) -> Vec<FlatTrace> {
self.0
}
}
/// Represents all traces produced by transactions in a single block.
#[derive(Debug, PartialEq, Clone, Default)]
pub struct FlatBlockTraces(Vec<FlatTransactionTraces>);
impl HeapSizeOf for FlatBlockTraces {
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)
}
}
impl FlatBlockTraces {
/// Returns bloom of all traces in the block.
pub fn bloom(&self) -> LogBloom {
self.0.iter().fold(Default::default(), | bloom, tx_traces | bloom | tx_traces.bloom())
}
}
impl Encodable for FlatBlockTraces {
fn rlp_append(&self, s: &mut RlpStream) {
s.append_list(&self.0);
}
}
impl Decodable for FlatBlockTraces {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(FlatBlockTraces(rlp.as_list()?))
}
}
impl Into<Vec<FlatTransactionTraces>> for FlatBlockTraces {
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};
use evm::CallType;
#[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();
}
#[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();
}
#[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_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 block_traces = FlatBlockTraces(vec![
FlatTransactionTraces(vec![flat_trace]),
FlatTransactionTraces(vec![flat_trace1, flat_trace2])
]);
let encoded = ::rlp::encode(&block_traces);
let decoded = ::rlp::decode(&encoded);
assert_eq!(block_traces, decoded);
}
}

View File

@@ -0,0 +1,44 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Localized traces type definitions
use util::H256;
use super::trace::{Action, Res};
use header::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: usize,
/// Signed transaction hash.
pub transaction_hash: H256,
/// Block number.
pub block_number: BlockNumber,
/// Block hash.
pub block_hash: H256,
}

View File

@@ -0,0 +1,23 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Types used in the public api
pub mod error;
pub mod filter;
pub mod flat;
pub mod trace;
pub mod localized;

View File

@@ -0,0 +1,559 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Tracing datatypes.
use util::{U256, Bytes, Address};
use util::sha3::Hashable;
use util::bloom::Bloomable;
use rlp::*;
use evm::action_params::ActionParams;
use basic_types::LogBloom;
use evm::CallType;
use super::error::Error;
/// `Call` result.
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "ipc", binary)]
pub struct CallResult {
/// Gas used by call.
pub gas_used: U256,
/// Call Output.
pub output: Bytes,
}
impl Encodable for CallResult {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(2);
s.append(&self.gas_used);
s.append(&self.output);
}
}
impl Decodable for CallResult {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = CallResult {
gas_used: rlp.val_at(0)?,
output: rlp.val_at(1)?,
};
Ok(res)
}
}
/// `Create` result.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "ipc", binary)]
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,
}
impl Encodable for CreateResult {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(3);
s.append(&self.gas_used);
s.append(&self.code);
s.append(&self.address);
}
}
impl Decodable for CreateResult {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = CreateResult {
gas_used: rlp.val_at(0)?,
code: rlp.val_at(1)?,
address: rlp.val_at(2)?,
};
Ok(res)
}
}
impl CreateResult {
/// Returns bloom.
pub fn bloom(&self) -> LogBloom {
LogBloom::from_bloomed(&self.address.sha3())
}
}
/// Description of a _call_ action, either a `CALL` operation or a message transction.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "ipc", binary)]
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,
}
impl From<ActionParams> for Call {
fn from(p: ActionParams) -> Self {
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 Encodable for Call {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(6);
s.append(&self.from);
s.append(&self.to);
s.append(&self.value);
s.append(&self.gas);
s.append(&self.input);
s.append(&self.call_type);
}
}
impl Decodable for Call {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = Call {
from: rlp.val_at(0)?,
to: rlp.val_at(1)?,
value: rlp.val_at(2)?,
gas: rlp.val_at(3)?,
input: rlp.val_at(4)?,
call_type: rlp.val_at(5)?,
};
Ok(res)
}
}
impl Call {
/// Returns call action bloom.
/// The bloom contains from and to addresses.
pub fn bloom(&self) -> LogBloom {
LogBloom::from_bloomed(&self.from.sha3())
.with_bloomed(&self.to.sha3())
}
}
/// Description of a _create_ action, either a `CREATE` operation or a create transction.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "ipc", binary)]
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,
}
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()),
}
}
}
impl Encodable for Create {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.from);
s.append(&self.value);
s.append(&self.gas);
s.append(&self.init);
}
}
impl Decodable for Create {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = Create {
from: rlp.val_at(0)?,
value: rlp.val_at(1)?,
gas: rlp.val_at(2)?,
init: rlp.val_at(3)?,
};
Ok(res)
}
}
impl Create {
/// Returns bloom create action bloom.
/// The bloom contains only from address.
pub fn bloom(&self) -> LogBloom {
LogBloom::from_bloomed(&self.from.sha3())
}
}
/// Suicide action.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "ipc", binary)]
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,
}
impl Suicide {
/// Return suicide action bloom.
pub fn bloom(&self) -> LogBloom {
LogBloom::from_bloomed(&self.address.sha3())
.with_bloomed(&self.refund_address.sha3())
}
}
impl Encodable for Suicide {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(3);
s.append(&self.address);
s.append(&self.refund_address);
s.append(&self.balance);
}
}
impl Decodable for Suicide {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = Suicide {
address: rlp.val_at(0)?,
refund_address: rlp.val_at(1)?,
balance: rlp.val_at(2)?,
};
Ok(res)
}
}
/// Description of an action that we trace; will be either a call or a create.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "ipc", binary)]
pub enum Action {
/// It's a call action.
Call(Call),
/// It's a create action.
Create(Create),
/// Suicide.
Suicide(Suicide),
}
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);
}
}
}
}
impl Decodable for Action {
fn decode(rlp: &UntrustedRlp) -> 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),
_ => Err(DecoderError::Custom("Invalid action type.")),
}
}
}
impl Action {
/// Returns action bloom.
pub fn bloom(&self) -> LogBloom {
match *self {
Action::Call(ref call) => call.bloom(),
Action::Create(ref create) => create.bloom(),
Action::Suicide(ref suicide) => suicide.bloom(),
}
}
}
/// The result of the performed action.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "ipc", binary)]
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,
}
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);
}
}
}
}
impl Decodable for Res {
fn decode(rlp: &UntrustedRlp) -> 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) -> LogBloom {
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,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "ipc", binary)]
/// A diff of some chunk of memory.
pub struct MemoryDiff {
/// Offset into memory the change begins.
pub offset: usize,
/// The changed data.
pub data: Bytes,
}
impl Encodable for MemoryDiff {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(2);
s.append(&self.offset);
s.append(&self.data);
}
}
impl Decodable for MemoryDiff {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(MemoryDiff {
offset: rlp.val_at(0)?,
data: rlp.val_at(1)?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "ipc", binary)]
/// A diff of some storage value.
pub struct StorageDiff {
/// Which key in storage is changed.
pub location: U256,
/// What the value has been changed to.
pub value: U256,
}
impl Encodable for StorageDiff {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(2);
s.append(&self.location);
s.append(&self.value);
}
}
impl Decodable for StorageDiff {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(StorageDiff {
location: rlp.val_at(0)?,
value: rlp.val_at(1)?,
})
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "ipc", binary)]
/// A record of an executed VM operation.
pub struct VMExecutedOperation {
/// The total gas used.
pub gas_used: U256,
/// The stack item placed, if any.
pub stack_push: Vec<U256>,
/// If altered, the memory delta.
pub mem_diff: Option<MemoryDiff>,
/// The altered storage value, if any.
pub store_diff: Option<StorageDiff>,
}
impl Encodable for VMExecutedOperation {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.gas_used);
s.append_list(&self.stack_push);
s.append(&self.mem_diff);
s.append(&self.store_diff);
}
}
impl Decodable for VMExecutedOperation {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(VMExecutedOperation {
gas_used: rlp.val_at(0)?,
stack_push: rlp.list_at(1)?,
mem_diff: rlp.val_at(2)?,
store_diff: rlp.val_at(3)?,
})
}
}
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "ipc", binary)]
/// A record of the execution of a single VM operation.
pub struct VMOperation {
/// The program counter.
pub pc: usize,
/// The instruction executed.
pub instruction: u8,
/// The gas cost for this instruction.
pub gas_cost: U256,
/// Information concerning the execution of the operation.
pub executed: Option<VMExecutedOperation>,
}
impl Encodable for VMOperation {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.pc);
s.append(&self.instruction);
s.append(&self.gas_cost);
s.append(&self.executed);
}
}
impl Decodable for VMOperation {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = VMOperation {
pc: rlp.val_at(0)?,
instruction: rlp.val_at(1)?,
gas_cost: rlp.val_at(2)?,
executed: rlp.val_at(3)?,
};
Ok(res)
}
}
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "ipc", binary)]
/// A record of a full VM trace for a CALL/CREATE.
pub struct VMTrace {
/// The step (i.e. index into operations) at which this trace corresponds.
pub parent_step: usize,
/// The code to be executed.
pub code: Bytes,
/// The operations executed.
pub operations: Vec<VMOperation>,
/// The sub traces for each interior action performed as part of this call/create.
/// Thre is a 1:1 correspondance between these and a CALL/CREATE/CALLCODE/DELEGATECALL instruction.
pub subs: Vec<VMTrace>,
}
impl Encodable for VMTrace {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.parent_step);
s.append(&self.code);
s.append_list(&self.operations);
s.append_list(&self.subs);
}
}
impl Decodable for VMTrace {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = VMTrace {
parent_step: rlp.val_at(0)?,
code: rlp.val_at(1)?,
operations: rlp.list_at(2)?,
subs: rlp.list_at(3)?,
};
Ok(res)
}
}