Core tracedb functionality. (#996)
* fixed encoding 0u8 * simplified if else stmt * tracedb core * more comprehensive tracedb tests * fixed minor review issues * addresses filter * fixed typos * replace malformed with corrupted * trace switch * db key is generic and can be made smaller * smaller tracedb keys * tracedb version * fixed ignored tests * rename Tracedb -> TraceDB * fixed typos * proves * trace only top level calls to builtins to avoid DDoS attacks * fixed tracedb config switches * fix comments fat replaced with trace * vector-addressing scheme for localized traces * removed comments * removed first, redundant 0 from trace address * updated db.trace method * additional tests for tracedb.trace()
This commit is contained in:
parent
e942f86bd7
commit
66477a9476
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -105,6 +105,11 @@ name = "blastfig"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bloomchain"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "0.5.1"
|
||||
@ -246,6 +251,7 @@ dependencies = [
|
||||
name = "ethcore"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.63 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -22,6 +22,7 @@ crossbeam = "0.1.5"
|
||||
lazy_static = "0.1"
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
ethjson = { path = "../json" }
|
||||
bloomchain = "0.1"
|
||||
|
||||
[features]
|
||||
jit = ["evmjit"]
|
||||
|
@ -22,6 +22,7 @@ use common::*;
|
||||
use engine::*;
|
||||
use state::*;
|
||||
use verification::PreverifiedBlock;
|
||||
use trace::Trace;
|
||||
|
||||
/// A block, encoded as it is on the block chain.
|
||||
#[derive(Default, Debug, Clone)]
|
||||
|
@ -55,9 +55,6 @@ impl Default for BlockChainConfig {
|
||||
|
||||
/// Interface for querying blocks by hash and by number.
|
||||
pub trait BlockProvider {
|
||||
/// True if we store full tracing information for transactions.
|
||||
fn have_tracing(&self) -> bool;
|
||||
|
||||
/// Returns true if the given block is known
|
||||
/// (though not necessarily a part of the canon chain).
|
||||
fn is_known(&self, hash: &H256) -> bool;
|
||||
@ -186,9 +183,6 @@ impl BlockProvider for BlockChain {
|
||||
self.extras_db.exists_with_cache(&self.block_details, hash)
|
||||
}
|
||||
|
||||
/// We do not store tracing information.
|
||||
fn have_tracing(&self) -> bool { false }
|
||||
|
||||
/// Get raw block data
|
||||
fn block(&self, hash: &H256) -> Option<Bytes> {
|
||||
{
|
||||
@ -734,9 +728,10 @@ impl BlockChain {
|
||||
self.query_extras(hash, &self.blocks_blooms)
|
||||
}
|
||||
|
||||
fn query_extras<K, T>(&self, hash: &K, cache: &RwLock<HashMap<K, T>>) -> Option<T> where
|
||||
fn query_extras<K, T, R>(&self, hash: &K, cache: &RwLock<HashMap<K, T>>) -> Option<T> where
|
||||
T: ExtrasIndexable + Clone + Decodable,
|
||||
K: Key<T> + Eq + Hash + Clone,
|
||||
K: Key<T, Target = R> + Eq + Hash + Clone,
|
||||
R: Deref<Target = [u8]>,
|
||||
H256: From<K> {
|
||||
self.note_used(CacheID::Extras(T::index(), H256::from(hash.clone())));
|
||||
self.extras_db.read_with_cache(cache, hash)
|
||||
|
@ -42,6 +42,7 @@ use env_info::EnvInfo;
|
||||
use executive::{Executive, Executed, TransactOptions, contract_address};
|
||||
use receipt::LocalizedReceipt;
|
||||
pub use blockchain::CacheSize as BlockChainCacheSize;
|
||||
use trace::{TraceDB, Database as TraceDatabase};
|
||||
|
||||
/// General block status
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
@ -103,6 +104,7 @@ impl ClientReport {
|
||||
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
|
||||
pub struct Client<V = CanonVerifier> where V: Verifier {
|
||||
chain: Arc<BlockChain>,
|
||||
tracedb: Arc<TraceDB<BlockChain>>,
|
||||
engine: Arc<Box<Engine>>,
|
||||
state_db: Mutex<Box<JournalDB>>,
|
||||
block_queue: BlockQueue,
|
||||
@ -150,6 +152,7 @@ impl<V> Client<V> where V: Verifier {
|
||||
let path = get_db_path(path, config.pruning, spec.genesis_header().hash());
|
||||
let gb = spec.genesis_block();
|
||||
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path));
|
||||
let tracedb = Arc::new(TraceDB::new(config.tracing, &path, chain.clone()));
|
||||
|
||||
let mut state_db = journaldb::new(&append_path(&path, "state"), config.pruning);
|
||||
|
||||
@ -165,6 +168,7 @@ impl<V> Client<V> where V: Verifier {
|
||||
|
||||
Arc::new(Client {
|
||||
chain: chain,
|
||||
tracedb: tracedb,
|
||||
engine: engine,
|
||||
state_db: Mutex::new(state_db),
|
||||
block_queue: block_queue,
|
||||
@ -225,7 +229,7 @@ impl<V> Client<V> where V: Verifier {
|
||||
let last_hashes = self.build_last_hashes(header.parent_hash.clone());
|
||||
let db = self.state_db.lock().unwrap().boxed_clone();
|
||||
|
||||
let enact_result = enact_verified(&block, engine, self.chain.have_tracing(), db, &parent, last_hashes);
|
||||
let enact_result = enact_verified(&block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes);
|
||||
if let Err(e) = enact_result {
|
||||
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||
return Err(());
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
pub use block_queue::BlockQueueConfig;
|
||||
pub use blockchain::BlockChainConfig;
|
||||
pub use trace::{Config as TraceConfig, Switch};
|
||||
use util::journaldb;
|
||||
|
||||
/// Client configuration. Includes configs for all sub-systems.
|
||||
@ -25,6 +26,8 @@ pub struct ClientConfig {
|
||||
pub queue: BlockQueueConfig,
|
||||
/// Blockchain configuration.
|
||||
pub blockchain: BlockChainConfig,
|
||||
/// Trace configuration.
|
||||
pub tracing: TraceConfig,
|
||||
/// The JournalDB ("pruning") algorithm to use.
|
||||
pub pruning: journaldb::Algorithm,
|
||||
/// The name of the client instance.
|
||||
|
@ -20,6 +20,7 @@ mod client;
|
||||
mod config;
|
||||
mod ids;
|
||||
mod test_client;
|
||||
mod trace;
|
||||
|
||||
pub use self::client::*;
|
||||
pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig};
|
||||
|
38
ethcore/src/client/trace.rs
Normal file
38
ethcore/src/client/trace.rs
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
//! Bridge between Tracedb and Blockchain.
|
||||
|
||||
use std::ops::Range;
|
||||
use util::{Address, H256};
|
||||
use header::BlockNumber;
|
||||
use trace::DatabaseExtras as TraceDatabaseExtras;
|
||||
use blockchain::{BlockChain, BlockProvider};
|
||||
use extras::TransactionAddress;
|
||||
use super::BlockId;
|
||||
|
||||
impl TraceDatabaseExtras for BlockChain {
|
||||
fn block_hash(&self, block_number: BlockNumber) -> Option<H256> {
|
||||
(self as &BlockProvider).block_hash(block_number)
|
||||
}
|
||||
|
||||
fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option<H256> {
|
||||
(self as &BlockProvider).block_hash(block_number)
|
||||
.and_then(|block_hash| {
|
||||
let tx_address = TransactionAddress {
|
||||
block_hash: block_hash,
|
||||
index: tx_position
|
||||
};
|
||||
self.transaction(&tx_address)
|
||||
})
|
||||
.map(|tx| tx.hash())
|
||||
}
|
||||
}
|
||||
|
||||
/// Easy to use trace filter.
|
||||
pub struct Filter {
|
||||
/// Range of filtering.
|
||||
pub range: Range<BlockId>,
|
||||
/// From address.
|
||||
pub from_address: Vec<Address>,
|
||||
/// To address.
|
||||
pub to_address: Vec<Address>,
|
||||
}
|
@ -26,4 +26,3 @@ pub use transaction::*;
|
||||
pub use log_entry::*;
|
||||
pub use receipt::*;
|
||||
pub use action_params::*;
|
||||
pub use trace::*;
|
@ -16,10 +16,11 @@
|
||||
|
||||
//! Extras db utils.
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::hash::Hash;
|
||||
use std::sync::RwLock;
|
||||
use std::collections::HashMap;
|
||||
use util::{H264, DBTransaction, Database};
|
||||
use util::{DBTransaction, Database};
|
||||
use util::rlp::{encode, Encodable, decode, Decodable};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@ -30,19 +31,22 @@ pub enum CacheUpdatePolicy {
|
||||
|
||||
/// Should be used to get database key associated with given value.
|
||||
pub trait Key<T> {
|
||||
type Target: Deref<Target = [u8]>;
|
||||
|
||||
/// Returns db key.
|
||||
fn key(&self) -> H264;
|
||||
fn key(&self) -> Self::Target;
|
||||
}
|
||||
|
||||
/// Should be used to write value into database.
|
||||
pub trait Writable {
|
||||
/// Writes the value into the database.
|
||||
fn write<T>(&self, key: &Key<T>, value: &T) where T: Encodable;
|
||||
fn write<T, R>(&self, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]>;
|
||||
|
||||
/// Writes the value into the database and updates the cache.
|
||||
fn write_with_cache<K, T>(&self, cache: &mut HashMap<K, T>, key: K, value: T, policy: CacheUpdatePolicy) where
|
||||
K: Key<T> + Hash + Eq,
|
||||
T: Encodable {
|
||||
fn write_with_cache<K, T, R>(&self, cache: &mut HashMap<K, T>, key: K, value: T, policy: CacheUpdatePolicy) where
|
||||
K: Key<T, Target = R> + Hash + Eq,
|
||||
T: Encodable,
|
||||
R: Deref<Target = [u8]> {
|
||||
self.write(&key, &value);
|
||||
match policy {
|
||||
CacheUpdatePolicy::Overwrite => {
|
||||
@ -55,8 +59,10 @@ pub trait Writable {
|
||||
}
|
||||
|
||||
/// Writes the values into the database and updates the cache.
|
||||
fn extend_with_cache<K, T>(&self, cache: &mut HashMap<K, T>, values: HashMap<K, T>, policy: CacheUpdatePolicy)
|
||||
where K: Key<T> + Hash + Eq, T: Encodable {
|
||||
fn extend_with_cache<K, T, R>(&self, cache: &mut HashMap<K, T>, values: HashMap<K, T>, policy: CacheUpdatePolicy) where
|
||||
K: Key<T, Target = R> + Hash + Eq,
|
||||
T: Encodable,
|
||||
R: Deref<Target = [u8]> {
|
||||
match policy {
|
||||
CacheUpdatePolicy::Overwrite => {
|
||||
for (key, value) in values.into_iter() {
|
||||
@ -77,7 +83,9 @@ pub trait Writable {
|
||||
/// Should be used to read values from database.
|
||||
pub trait Readable {
|
||||
/// Returns value for given key.
|
||||
fn read<T>(&self, key: &Key<T>) -> Option<T> where T: Decodable;
|
||||
fn read<T, R>(&self, key: &Key<T, Target = R>) -> Option<T> where
|
||||
T: Decodable,
|
||||
R: Deref<Target = [u8]>;
|
||||
|
||||
/// Returns value for given key either in cache or in database.
|
||||
fn read_with_cache<K, T>(&self, cache: &RwLock<HashMap<K, T>>, key: &K) -> Option<T> where
|
||||
@ -98,11 +106,12 @@ pub trait Readable {
|
||||
}
|
||||
|
||||
/// Returns true if given value exists.
|
||||
fn exists<T>(&self, key: &Key<T>) -> bool;
|
||||
fn exists<T, R>(&self, key: &Key<T, Target = R>) -> bool where R: Deref<Target= [u8]>;
|
||||
|
||||
/// Returns true if given value exists either in cache or in database.
|
||||
fn exists_with_cache<K, T>(&self, cache: &RwLock<HashMap<K, T>>, key: &K) -> bool where
|
||||
K: Eq + Hash + Key<T> {
|
||||
fn exists_with_cache<K, T, R>(&self, cache: &RwLock<HashMap<K, T>>, key: &K) -> bool where
|
||||
K: Eq + Hash + Key<T, Target = R>,
|
||||
R: Deref<Target = [u8]> {
|
||||
{
|
||||
let read = cache.read().unwrap();
|
||||
if read.get(key).is_some() {
|
||||
@ -110,38 +119,38 @@ pub trait Readable {
|
||||
}
|
||||
}
|
||||
|
||||
self.exists::<T>(key)
|
||||
self.exists::<T, R>(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl Writable for DBTransaction {
|
||||
fn write<T>(&self, key: &Key<T>, value: &T) where T: Encodable {
|
||||
fn write<T, R>(&self, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]> {
|
||||
let result = self.put(&key.key(), &encode(value));
|
||||
if let Err(err) = result {
|
||||
panic!("db put failed, key: {:?}, err: {:?}", key.key(), err);
|
||||
panic!("db put failed, key: {:?}, err: {:?}", &key.key() as &[u8], err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Readable for Database {
|
||||
fn read<T>(&self, key: &Key<T>) -> Option<T> where T: Decodable {
|
||||
fn read<T, R>(&self, key: &Key<T, Target = R>) -> Option<T> where T: Decodable, R: Deref<Target = [u8]> {
|
||||
let result = self.get(&key.key());
|
||||
|
||||
match result {
|
||||
Ok(option) => option.map(|v| decode(&v)),
|
||||
Err(err) => {
|
||||
panic!("db get failed, key: {:?}, err: {:?}", key.key(), err);
|
||||
panic!("db get failed, key: {:?}, err: {:?}", &key.key() as &[u8], err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn exists<T>(&self, key: &Key<T>) -> bool {
|
||||
fn exists<T, R>(&self, key: &Key<T, Target = R>) -> bool where R: Deref<Target = [u8]> {
|
||||
let result = self.get(&key.key());
|
||||
|
||||
match result {
|
||||
Ok(v) => v.is_some(),
|
||||
Err(err) => {
|
||||
panic!("db get failed, key: {:?}, err: {:?}", key.key(), err);
|
||||
panic!("db get failed, key: {:?}, err: {:?}", &key.key() as &[u8], err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ use engine::*;
|
||||
use evm::{self, Ext};
|
||||
use externalities::*;
|
||||
use substate::*;
|
||||
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer};
|
||||
use crossbeam;
|
||||
|
||||
/// Max depth to avoid stack overflow (when it's reached we start a new thread with VM)
|
||||
@ -246,22 +247,47 @@ impl<'a> Executive<'a> {
|
||||
}
|
||||
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
|
||||
|
||||
let delegate_call = params.code_address != params.address;
|
||||
|
||||
if self.engine.is_builtin(¶ms.code_address) {
|
||||
// if destination is builtin, try to execute it
|
||||
|
||||
let default = [];
|
||||
let data = if let Some(ref d) = params.data { d as &[u8] } else { &default as &[u8] };
|
||||
|
||||
let trace_info = tracer.prepare_trace_call(¶ms);
|
||||
|
||||
let cost = self.engine.cost_of_builtin(¶ms.code_address, data);
|
||||
match cost <= params.gas {
|
||||
true => {
|
||||
self.engine.execute_builtin(¶ms.code_address, data, &mut output);
|
||||
self.state.clear_snapshot();
|
||||
|
||||
// trace only top level calls to builtins to avoid DDoS attacks
|
||||
if self.depth == 0 {
|
||||
let mut trace_output = tracer.prepare_trace_output();
|
||||
if let Some(mut out) = trace_output.as_mut() {
|
||||
*out = output.to_owned();
|
||||
}
|
||||
|
||||
tracer.trace_call(
|
||||
trace_info,
|
||||
cost,
|
||||
trace_output,
|
||||
self.depth,
|
||||
vec![],
|
||||
delegate_call
|
||||
);
|
||||
}
|
||||
|
||||
Ok(params.gas - cost)
|
||||
},
|
||||
// just drain the whole gas
|
||||
false => {
|
||||
self.state.revert_snapshot();
|
||||
|
||||
tracer.trace_failed_call(trace_info, self.depth, vec![], delegate_call);
|
||||
|
||||
Err(evm::Error::OutOfGas)
|
||||
}
|
||||
}
|
||||
@ -269,7 +295,6 @@ impl<'a> Executive<'a> {
|
||||
let trace_info = tracer.prepare_trace_call(¶ms);
|
||||
let mut trace_output = tracer.prepare_trace_output();
|
||||
let mut subtracer = tracer.subtracer();
|
||||
let delegate_call = params.code_address != params.address;
|
||||
let gas = params.gas;
|
||||
|
||||
if params.code.is_some() {
|
||||
@ -442,6 +467,8 @@ mod tests {
|
||||
use evm::{Factory, VMType};
|
||||
use substate::*;
|
||||
use tests::helpers::*;
|
||||
use trace::trace;
|
||||
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer};
|
||||
|
||||
#[test]
|
||||
fn test_contract_address() {
|
||||
@ -591,26 +618,26 @@ mod tests {
|
||||
|
||||
let expected_trace = vec![ Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("cd1722f3947def4cf144679da39c4c32bdc35681"),
|
||||
to: x!("b010143a42d5980c7e5ef0e4a4416dc098a4fed3"),
|
||||
value: x!(100),
|
||||
gas: x!(100000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(55_248),
|
||||
output: vec![],
|
||||
}),
|
||||
subs: vec![Trace {
|
||||
depth: 1,
|
||||
action: TraceAction::Create(TraceCreate {
|
||||
action: trace::Action::Create(trace::Create {
|
||||
from: x!("b010143a42d5980c7e5ef0e4a4416dc098a4fed3"),
|
||||
value: x!(23),
|
||||
gas: x!(67979),
|
||||
init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85]
|
||||
}),
|
||||
result: TraceResult::Create(TraceCreateResult {
|
||||
result: trace::Res::Create(trace::CreateResult {
|
||||
gas_used: U256::from(3224),
|
||||
address: Address::from_str("c6d80f262ae5e0f164e5fde365044d7ada2bfa34").unwrap(),
|
||||
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
|
||||
@ -662,13 +689,13 @@ mod tests {
|
||||
|
||||
let expected_trace = vec![Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Create(TraceCreate {
|
||||
action: trace::Action::Create(trace::Create {
|
||||
from: params.sender,
|
||||
value: x!(100),
|
||||
gas: params.gas,
|
||||
init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85],
|
||||
}),
|
||||
result: TraceResult::Create(TraceCreateResult {
|
||||
result: trace::Res::Create(trace::CreateResult {
|
||||
gas_used: U256::from(3224),
|
||||
address: params.address,
|
||||
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
|
||||
|
@ -21,6 +21,7 @@ use engine::*;
|
||||
use executive::*;
|
||||
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult};
|
||||
use substate::*;
|
||||
use trace::Tracer;
|
||||
|
||||
/// Policy for handling output data on `RETURN` opcode.
|
||||
pub enum OutputPolicy<'a, 'b> {
|
||||
@ -293,6 +294,7 @@ mod tests {
|
||||
use substate::*;
|
||||
use tests::helpers::*;
|
||||
use super::*;
|
||||
use trace::{NoopTracer};
|
||||
|
||||
fn get_test_origin() -> OriginInfo {
|
||||
OriginInfo {
|
||||
|
@ -85,36 +85,48 @@ impl ExtrasIndexable for BlockReceipts {
|
||||
}
|
||||
|
||||
impl Key<H256> for BlockNumber {
|
||||
type Target = H264;
|
||||
|
||||
fn key(&self) -> H264 {
|
||||
with_index(&H256::from(*self), ExtrasIndex::BlockHash)
|
||||
}
|
||||
}
|
||||
|
||||
impl Key<BlockDetails> for H256 {
|
||||
type Target = H264;
|
||||
|
||||
fn key(&self) -> H264 {
|
||||
with_index(self, ExtrasIndex::BlockDetails)
|
||||
}
|
||||
}
|
||||
|
||||
impl Key<TransactionAddress> for H256 {
|
||||
type Target = H264;
|
||||
|
||||
fn key(&self) -> H264 {
|
||||
with_index(self, ExtrasIndex::TransactionAddress)
|
||||
}
|
||||
}
|
||||
|
||||
impl Key<BlockLogBlooms> for H256 {
|
||||
type Target = H264;
|
||||
|
||||
fn key(&self) -> H264 {
|
||||
with_index(self, ExtrasIndex::BlockLogBlooms)
|
||||
}
|
||||
}
|
||||
|
||||
impl Key<BlocksBlooms> for H256 {
|
||||
type Target = H264;
|
||||
|
||||
fn key(&self) -> H264 {
|
||||
with_index(self, ExtrasIndex::BlocksBlooms)
|
||||
}
|
||||
}
|
||||
|
||||
impl Key<BlockReceipts> for H256 {
|
||||
type Target = H264;
|
||||
|
||||
fn key(&self) -> H264 {
|
||||
with_index(self, ExtrasIndex::BlockReceipts)
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ use externalities::*;
|
||||
use substate::*;
|
||||
use tests::helpers::*;
|
||||
use ethjson;
|
||||
use trace::{Tracer, NoopTracer};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct CallCreate {
|
||||
|
@ -84,6 +84,7 @@ extern crate env_logger;
|
||||
extern crate num_cpus;
|
||||
extern crate crossbeam;
|
||||
extern crate ethjson;
|
||||
extern crate bloomchain;
|
||||
|
||||
#[cfg(test)] extern crate ethcore_devtools as devtools;
|
||||
#[cfg(feature = "jit" )] extern crate evmjit;
|
||||
|
@ -18,6 +18,7 @@ use common::*;
|
||||
use engine::Engine;
|
||||
use executive::{Executive, TransactOptions};
|
||||
use account_db::*;
|
||||
use trace::Trace;
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "json-tests")]
|
||||
use pod_account::*;
|
||||
@ -367,7 +368,8 @@ use env_info::*;
|
||||
use spec::*;
|
||||
use transaction::*;
|
||||
use util::log::init_log;
|
||||
use trace::*;
|
||||
use trace::trace;
|
||||
use trace::trace::{Trace};
|
||||
|
||||
#[test]
|
||||
fn should_apply_create_transaction() {
|
||||
@ -393,13 +395,13 @@ fn should_apply_create_transaction() {
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Create(TraceCreate {
|
||||
action: trace::Action::Create(trace::Create {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
value: x!(100),
|
||||
gas: x!(77412),
|
||||
init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85],
|
||||
}),
|
||||
result: TraceResult::Create(TraceCreateResult {
|
||||
result: trace::Res::Create(trace::CreateResult {
|
||||
gas_used: U256::from(3224),
|
||||
address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(),
|
||||
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
|
||||
@ -453,13 +455,13 @@ fn should_trace_failed_create_transaction() {
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Create(TraceCreate {
|
||||
action: trace::Action::Create(trace::Create {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
value: x!(100),
|
||||
gas: x!(78792),
|
||||
init: vec![91, 96, 0, 86],
|
||||
}),
|
||||
result: TraceResult::FailedCreate,
|
||||
result: trace::Res::FailedCreate,
|
||||
subs: vec![]
|
||||
});
|
||||
|
||||
@ -491,14 +493,14 @@ fn should_trace_call_transaction() {
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(3),
|
||||
output: vec![]
|
||||
}),
|
||||
@ -532,14 +534,14 @@ fn should_trace_basic_call_transaction() {
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(0),
|
||||
output: vec![]
|
||||
}),
|
||||
@ -550,7 +552,7 @@ fn should_trace_basic_call_transaction() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_trace_call_transaction_to_builtin() {
|
||||
fn should_trace_call_transaction_to_builtin() {
|
||||
init_log();
|
||||
|
||||
let temp = RandomTempPath::new();
|
||||
@ -571,7 +573,21 @@ fn should_not_trace_call_transaction_to_builtin() {
|
||||
|
||||
let result = state.apply(&info, engine.deref(), &t, true).unwrap();
|
||||
|
||||
assert_eq!(result.trace, None);
|
||||
assert_eq!(result.trace, Some(Trace {
|
||||
depth: 0,
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!("0000000000000000000000000000000000000001"),
|
||||
value: x!(0),
|
||||
gas: x!(79_000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(3000),
|
||||
output: vec![]
|
||||
}),
|
||||
subs: vec![]
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -599,14 +615,14 @@ fn should_not_trace_subcall_transaction_to_builtin() {
|
||||
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(0),
|
||||
gas: x!(79000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(28_061),
|
||||
output: vec![]
|
||||
}),
|
||||
@ -641,14 +657,14 @@ fn should_not_trace_callcode() {
|
||||
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(0),
|
||||
gas: x!(79000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(64),
|
||||
output: vec![]
|
||||
}),
|
||||
@ -686,14 +702,14 @@ fn should_not_trace_delegatecall() {
|
||||
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(0),
|
||||
gas: x!(79000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(61),
|
||||
output: vec![]
|
||||
}),
|
||||
@ -727,14 +743,14 @@ fn should_trace_failed_call_transaction() {
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::FailedCall,
|
||||
result: trace::Res::FailedCall,
|
||||
subs: vec![]
|
||||
});
|
||||
|
||||
@ -769,27 +785,27 @@ fn should_trace_call_with_subcall_transaction() {
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(69),
|
||||
output: vec![]
|
||||
}),
|
||||
subs: vec![Trace {
|
||||
depth: 1,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xa),
|
||||
to: x!(0xb),
|
||||
value: x!(0),
|
||||
gas: x!(78934),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(3),
|
||||
output: vec![]
|
||||
}),
|
||||
@ -825,27 +841,27 @@ fn should_trace_call_with_basic_subcall_transaction() {
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(31761),
|
||||
output: vec![]
|
||||
}),
|
||||
subs: vec![Trace {
|
||||
depth: 1,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xa),
|
||||
to: x!(0xb),
|
||||
value: x!(69),
|
||||
gas: x!(2300),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult::default()),
|
||||
result: trace::Res::Call(trace::CallResult::default()),
|
||||
subs: vec![]
|
||||
}]
|
||||
});
|
||||
@ -878,14 +894,14 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() {
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(31761),
|
||||
output: vec![]
|
||||
}),
|
||||
@ -921,27 +937,27 @@ fn should_trace_failed_subcall_transaction() {
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(79_000),
|
||||
output: vec![]
|
||||
}),
|
||||
subs: vec![Trace {
|
||||
depth: 1,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xa),
|
||||
to: x!(0xb),
|
||||
value: x!(0),
|
||||
gas: x!(78934),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::FailedCall,
|
||||
result: trace::Res::FailedCall,
|
||||
subs: vec![]
|
||||
}]
|
||||
});
|
||||
@ -976,40 +992,40 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(135),
|
||||
output: vec![]
|
||||
}),
|
||||
subs: vec![Trace {
|
||||
depth: 1,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xa),
|
||||
to: x!(0xb),
|
||||
value: x!(0),
|
||||
gas: x!(78934),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(69),
|
||||
output: vec![]
|
||||
}),
|
||||
subs: vec![Trace {
|
||||
depth: 2,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xb),
|
||||
to: x!(0xc),
|
||||
value: x!(0),
|
||||
gas: x!(78868),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(3),
|
||||
output: vec![]
|
||||
}),
|
||||
@ -1048,37 +1064,37 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = Some(Trace {
|
||||
depth: 0,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
|
||||
to: x!(0xa),
|
||||
value: x!(100),
|
||||
gas: x!(79000),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(79_000),
|
||||
output: vec![]
|
||||
}),
|
||||
subs: vec![Trace {
|
||||
depth: 1,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xa),
|
||||
to: x!(0xb),
|
||||
value: x!(0),
|
||||
gas: x!(78934),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::FailedCall,
|
||||
result: trace::Res::FailedCall,
|
||||
subs: vec![Trace {
|
||||
depth: 2,
|
||||
action: TraceAction::Call(TraceCall {
|
||||
action: trace::Action::Call(trace::Call {
|
||||
from: x!(0xb),
|
||||
to: x!(0xc),
|
||||
value: x!(0),
|
||||
gas: x!(78868),
|
||||
input: vec![],
|
||||
}),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
result: trace::Res::Call(trace::CallResult {
|
||||
gas_used: U256::from(3),
|
||||
output: vec![]
|
||||
}),
|
||||
|
42
ethcore/src/trace/block.rs
Normal file
42
ethcore/src/trace/block.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use util::rlp::*;
|
||||
use basic_types::LogBloom;
|
||||
use super::Trace;
|
||||
|
||||
/// Traces created by transactions from the same block.
|
||||
#[derive(Clone)]
|
||||
pub struct BlockTraces(Vec<Trace>);
|
||||
|
||||
impl From<Vec<Trace>> for BlockTraces {
|
||||
fn from(traces: Vec<Trace>) -> Self {
|
||||
BlockTraces(traces)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<Trace>> for BlockTraces {
|
||||
fn into(self) -> Vec<Trace> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for BlockTraces {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let traces = try!(Decodable::decode(decoder));
|
||||
let block_traces = BlockTraces(traces);
|
||||
Ok(block_traces)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for BlockTraces {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
Encodable::rlp_append(&self.0, s)
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockTraces {
|
||||
/// Returns bloom of all traces in given block.
|
||||
pub fn bloom(&self) -> LogBloom {
|
||||
self.0.iter()
|
||||
.fold(LogBloom::default(), |acc, trace| acc | trace.bloom())
|
||||
}
|
||||
}
|
||||
|
106
ethcore/src/trace/bloom.rs
Normal file
106
ethcore/src/trace/bloom.rs
Normal file
@ -0,0 +1,106 @@
|
||||
use bloomchain::Bloom;
|
||||
use bloomchain::group::{BloomGroup, GroupPosition};
|
||||
use util::rlp::*;
|
||||
use basic_types::LogBloom;
|
||||
|
||||
/// Helper structure representing bloom of the trace.
|
||||
#[derive(Clone)]
|
||||
pub struct BlockTracesBloom(LogBloom);
|
||||
|
||||
impl From<LogBloom> for BlockTracesBloom {
|
||||
fn from(bloom: LogBloom) -> BlockTracesBloom {
|
||||
BlockTracesBloom(bloom)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Bloom> for BlockTracesBloom {
|
||||
fn from(bloom: Bloom) -> BlockTracesBloom {
|
||||
let bytes: [u8; 256] = bloom.into();
|
||||
BlockTracesBloom(LogBloom::from(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Bloom> for BlockTracesBloom {
|
||||
fn into(self) -> Bloom {
|
||||
let log = self.0;
|
||||
Bloom::from(log.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents group of X consecutive blooms.
|
||||
#[derive(Clone)]
|
||||
pub struct BlockTracesBloomGroup {
|
||||
blooms: Vec<BlockTracesBloom>,
|
||||
}
|
||||
|
||||
impl From<BloomGroup> for BlockTracesBloomGroup {
|
||||
fn from(group: BloomGroup) -> Self {
|
||||
let blooms = group.blooms
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect();
|
||||
|
||||
BlockTracesBloomGroup {
|
||||
blooms: blooms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<BloomGroup> for BlockTracesBloomGroup {
|
||||
fn into(self) -> BloomGroup {
|
||||
let blooms = self.blooms
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
|
||||
BloomGroup {
|
||||
blooms: blooms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for BlockTracesBloom {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
Decodable::decode(decoder).map(BlockTracesBloom)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for BlockTracesBloom {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
Encodable::rlp_append(&self.0, s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for BlockTracesBloomGroup {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let blooms = try!(Decodable::decode(decoder));
|
||||
let group = BlockTracesBloomGroup {
|
||||
blooms: blooms
|
||||
};
|
||||
Ok(group)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for BlockTracesBloomGroup {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
Encodable::rlp_append(&self.blooms, s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents BloomGroup position in database.
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub struct TraceGroupPosition {
|
||||
/// Bloom level.
|
||||
pub level: u8,
|
||||
/// Group index.
|
||||
pub index: u32,
|
||||
}
|
||||
|
||||
impl From<GroupPosition> for TraceGroupPosition {
|
||||
fn from(p: GroupPosition) -> Self {
|
||||
TraceGroupPosition {
|
||||
level: p.level as u8,
|
||||
index: p.index as u32,
|
||||
}
|
||||
}
|
||||
}
|
62
ethcore/src/trace/config.rs
Normal file
62
ethcore/src/trace/config.rs
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
//! Traces config.
|
||||
use bloomchain::Config as BloomConfig;
|
||||
|
||||
/// 3-value enum.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Switch {
|
||||
/// True.
|
||||
On,
|
||||
/// False.
|
||||
Off,
|
||||
/// Auto.
|
||||
Auto,
|
||||
}
|
||||
|
||||
impl Switch {
|
||||
/// Tries to turn old switch to new value.
|
||||
pub fn turn_to(&self, to: Switch) -> Result<bool, &'static str> {
|
||||
match (*self, to) {
|
||||
(Switch::On, Switch::On) | (Switch::On, Switch::Auto) | (Switch::Auto, Switch::On) => Ok(true),
|
||||
(Switch::Off, Switch::On) => Err("Tracing can't be enabled"),
|
||||
_ => Ok(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Traces config.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
/// Indicates if tracing should be enabled or not.
|
||||
/// If it's None, it will be automatically configured.
|
||||
pub enabled: Switch,
|
||||
/// Traces blooms configuration.
|
||||
pub blooms: BloomConfig,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
enabled: Switch::Auto,
|
||||
blooms: BloomConfig {
|
||||
levels: 3,
|
||||
elements_per_index: 16,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
582
ethcore/src/trace/db.rs
Normal file
582
ethcore/src/trace/db.rs
Normal file
@ -0,0 +1,582 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 database.
|
||||
use std::ptr;
|
||||
use std::ops::Deref;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{RwLock, Arc};
|
||||
use std::path::Path;
|
||||
use bloomchain::{Number, Config as BloomConfig};
|
||||
use bloomchain::group::{BloomGroupDatabase, BloomGroupChain, GroupPosition, BloomGroup};
|
||||
use util::{FixedHash, H256, H264, Database, DBTransaction};
|
||||
use header::BlockNumber;
|
||||
use trace::{BlockTraces, LocalizedTrace, Config, Switch, Filter, Database as TraceDatabase, ImportRequest,
|
||||
DatabaseExtras};
|
||||
use db::{Key, Writable, Readable, CacheUpdatePolicy};
|
||||
use super::bloom::{TraceGroupPosition, BlockTracesBloom, BlockTracesBloomGroup};
|
||||
use super::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces};
|
||||
|
||||
const TRACE_DB_VER: &'static [u8] = b"1.0";
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum TraceDBIndex {
|
||||
/// Block traces index.
|
||||
BlockTraces = 0,
|
||||
/// Trace bloom group index.
|
||||
BlockTracesBloomGroups = 1,
|
||||
}
|
||||
|
||||
impl Key<BlockTraces> for H256 {
|
||||
type Target = H264;
|
||||
|
||||
fn key(&self) -> H264 {
|
||||
let mut result = H264::default();
|
||||
result[0] = TraceDBIndex::BlockTraces as u8;
|
||||
unsafe {
|
||||
ptr::copy(self.as_ptr(), result.as_mut_ptr().offset(1), 32);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper data structure created cause [u8; 6] does not implement Deref to &[u8].
|
||||
pub struct TraceGroupKey([u8; 6]);
|
||||
|
||||
impl Deref for TraceGroupKey {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Key<BlockTracesBloomGroup> for TraceGroupPosition {
|
||||
type Target = TraceGroupKey;
|
||||
|
||||
fn key(&self) -> Self::Target {
|
||||
let mut result = [0u8; 6];
|
||||
result[0] = TraceDBIndex::BlockTracesBloomGroups as u8;
|
||||
result[1] = self.level;
|
||||
unsafe {
|
||||
ptr::copy(&[self.index] as *const u32 as *const u8, result.as_mut_ptr().offset(2), 4);
|
||||
}
|
||||
TraceGroupKey(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trace database.
|
||||
pub struct TraceDB<T> where T: DatabaseExtras {
|
||||
// cache
|
||||
traces: RwLock<HashMap<H256, BlockTraces>>,
|
||||
blooms: RwLock<HashMap<TraceGroupPosition, BlockTracesBloomGroup>>,
|
||||
// db
|
||||
tracesdb: Database,
|
||||
// config,
|
||||
bloom_config: BloomConfig,
|
||||
// tracing enabled
|
||||
enabled: bool,
|
||||
// extras
|
||||
extras: Arc<T>,
|
||||
}
|
||||
|
||||
impl<T> BloomGroupDatabase for TraceDB<T> where T: DatabaseExtras {
|
||||
fn blooms_at(&self, position: &GroupPosition) -> Option<BloomGroup> {
|
||||
let position = TraceGroupPosition::from(position.clone());
|
||||
self.tracesdb.read_with_cache(&self.blooms, &position).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TraceDB<T> where T: DatabaseExtras {
|
||||
/// Creates new instance of `TraceDB`.
|
||||
pub fn new(config: Config, path: &Path, extras: Arc<T>) -> Self {
|
||||
let mut tracedb_path = path.to_path_buf();
|
||||
tracedb_path.push("tracedb");
|
||||
let tracesdb = Database::open_default(tracedb_path.to_str().unwrap()).unwrap();
|
||||
|
||||
// check if in previously tracing was enabled
|
||||
let old_tracing = match tracesdb.get(b"enabled").unwrap() {
|
||||
Some(ref value) if value as &[u8] == &[0x1] => Switch::On,
|
||||
Some(ref value) if value as &[u8] == &[0x0] => Switch::Off,
|
||||
Some(_) => { panic!("tracesdb is corrupted") },
|
||||
None => Switch::Auto,
|
||||
};
|
||||
|
||||
let enabled = old_tracing.turn_to(config.enabled).expect("Tracing can't be enabled. Resync required.");
|
||||
|
||||
let encoded_tracing = match enabled {
|
||||
true => [0x1],
|
||||
false => [0x0]
|
||||
};
|
||||
|
||||
tracesdb.put(b"enabled", &encoded_tracing).unwrap();
|
||||
tracesdb.put(b"version", TRACE_DB_VER).unwrap();
|
||||
|
||||
TraceDB {
|
||||
traces: RwLock::new(HashMap::new()),
|
||||
blooms: RwLock::new(HashMap::new()),
|
||||
tracesdb: tracesdb,
|
||||
bloom_config: config.blooms,
|
||||
enabled: enabled,
|
||||
extras: extras,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns traces for block with hash.
|
||||
fn traces(&self, block_hash: &H256) -> Option<BlockTraces> {
|
||||
self.tracesdb.read_with_cache(&self.traces, block_hash)
|
||||
}
|
||||
|
||||
/// Returns vector of transaction traces for given block.
|
||||
fn transactions_traces(&self, block_hash: &H256) -> Option<Vec<FlatTransactionTraces>> {
|
||||
self.traces(block_hash)
|
||||
.map(FlatBlockTraces::from)
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
fn matching_block_traces(
|
||||
&self,
|
||||
filter: &Filter,
|
||||
traces: FlatBlockTraces,
|
||||
block_hash: H256,
|
||||
block_number: BlockNumber
|
||||
) -> Vec<LocalizedTrace> {
|
||||
let tx_traces: Vec<FlatTransactionTraces> = traces.into();
|
||||
tx_traces.into_iter()
|
||||
.enumerate()
|
||||
.flat_map(|(tx_number, tx_trace)| {
|
||||
self.matching_transaction_traces(filter, tx_trace, block_hash.clone(), block_number, tx_number)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn matching_transaction_traces(
|
||||
&self,
|
||||
filter: &Filter,
|
||||
traces: FlatTransactionTraces,
|
||||
block_hash: H256,
|
||||
block_number: BlockNumber,
|
||||
tx_number: usize
|
||||
) -> Vec<LocalizedTrace> {
|
||||
let tx_hash = self.extras.transaction_hash(block_number, tx_number)
|
||||
.expect("Expected to find transaction hash. Database is probably corrupted");
|
||||
|
||||
let flat_traces: Vec<FlatTrace> = traces.into();
|
||||
flat_traces.into_iter()
|
||||
.filter_map(|trace| {
|
||||
match filter.matches(&trace) {
|
||||
true => Some(LocalizedTrace {
|
||||
action: trace.action,
|
||||
result: trace.result,
|
||||
subtraces: trace.subtraces,
|
||||
trace_address: trace.trace_address,
|
||||
transaction_number: tx_number,
|
||||
transaction_hash: tx_hash.clone(),
|
||||
block_number: block_number,
|
||||
block_hash: block_hash
|
||||
}),
|
||||
false => None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
||||
fn tracing_enabled(&self) -> bool {
|
||||
self.enabled
|
||||
}
|
||||
|
||||
/// Traces of import request's enacted blocks are expected to be already in database
|
||||
/// or to be the currently inserted trace.
|
||||
fn import(&self, request: ImportRequest) {
|
||||
// fast return if tracing is disabled
|
||||
if !self.tracing_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
let batch = DBTransaction::new();
|
||||
|
||||
// at first, let's insert new block traces
|
||||
{
|
||||
let mut traces = self.traces.write().unwrap();
|
||||
// it's important to use overwrite here,
|
||||
// cause this value might be queried by hash later
|
||||
batch.write_with_cache(&mut traces, request.block_hash, request.traces, CacheUpdatePolicy::Overwrite);
|
||||
}
|
||||
|
||||
// now let's rebuild the blooms
|
||||
{
|
||||
let range_start = request.block_number as Number + 1 - request.enacted.len();
|
||||
let range_end = range_start + request.retracted;
|
||||
let replaced_range = range_start..range_end;
|
||||
let enacted_blooms = request.enacted
|
||||
.iter()
|
||||
// all traces are expected to be found here. That's why `expect` has been used
|
||||
// instead of `filter_map`. If some traces haven't been found, it meens that
|
||||
// traces database is corrupted or incomplete.
|
||||
.map(|block_hash| self.traces(block_hash).expect("Traces database is incomplete."))
|
||||
.map(|block_traces| block_traces.bloom())
|
||||
.map(BlockTracesBloom::from)
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
|
||||
let chain = BloomGroupChain::new(self.bloom_config, self);
|
||||
let trace_blooms = chain.replace(&replaced_range, enacted_blooms);
|
||||
let blooms_to_insert = trace_blooms.into_iter()
|
||||
.map(|p| (From::from(p.0), From::from(p.1)))
|
||||
.collect::<HashMap<TraceGroupPosition, BlockTracesBloomGroup>>();
|
||||
|
||||
let mut blooms = self.blooms.write().unwrap();
|
||||
batch.extend_with_cache(&mut blooms, blooms_to_insert, CacheUpdatePolicy::Remove);
|
||||
}
|
||||
|
||||
self.tracesdb.write(batch).unwrap();
|
||||
}
|
||||
|
||||
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> {
|
||||
self.extras.block_hash(block_number)
|
||||
.and_then(|block_hash| self.transactions_traces(&block_hash)
|
||||
.and_then(|traces| traces.into_iter().nth(tx_position))
|
||||
.map(Into::<Vec<FlatTrace>>::into)
|
||||
// this may and should be optimized
|
||||
.and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position))
|
||||
.map(|trace| {
|
||||
let tx_hash = self.extras.transaction_hash(block_number, tx_position)
|
||||
.expect("Expected to find transaction hash. Database is probably corrupted");
|
||||
|
||||
LocalizedTrace {
|
||||
action: trace.action,
|
||||
result: trace.result,
|
||||
subtraces: trace.subtraces,
|
||||
trace_address: trace.trace_address,
|
||||
transaction_number: tx_position,
|
||||
transaction_hash: tx_hash,
|
||||
block_number: block_number,
|
||||
block_hash: block_hash,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fn transaction_traces(&self, block_number: BlockNumber, tx_position: usize) -> Option<Vec<LocalizedTrace>> {
|
||||
self.extras.block_hash(block_number)
|
||||
.and_then(|block_hash| self.transactions_traces(&block_hash)
|
||||
.and_then(|traces| traces.into_iter().nth(tx_position))
|
||||
.map(Into::<Vec<FlatTrace>>::into)
|
||||
.map(|traces| {
|
||||
let tx_hash = self.extras.transaction_hash(block_number, tx_position)
|
||||
.expect("Expected to find transaction hash. Database is probably corrupted");
|
||||
|
||||
traces.into_iter()
|
||||
.map(|trace| LocalizedTrace {
|
||||
action: trace.action,
|
||||
result: trace.result,
|
||||
subtraces: trace.subtraces,
|
||||
trace_address: trace.trace_address,
|
||||
transaction_number: tx_position,
|
||||
transaction_hash: tx_hash.clone(),
|
||||
block_number: block_number,
|
||||
block_hash: block_hash
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fn block_traces(&self, block_number: BlockNumber) -> Option<Vec<LocalizedTrace>> {
|
||||
self.extras.block_hash(block_number)
|
||||
.and_then(|block_hash| self.transactions_traces(&block_hash)
|
||||
.map(|traces| {
|
||||
traces.into_iter()
|
||||
.map(Into::<Vec<FlatTrace>>::into)
|
||||
.enumerate()
|
||||
.flat_map(|(tx_position, traces)| {
|
||||
let tx_hash = self.extras.transaction_hash(block_number, tx_position)
|
||||
.expect("Expected to find transaction hash. Database is probably corrupted");
|
||||
|
||||
traces.into_iter()
|
||||
.map(|trace| LocalizedTrace {
|
||||
action: trace.action,
|
||||
result: trace.result,
|
||||
subtraces: trace.subtraces,
|
||||
trace_address: trace.trace_address,
|
||||
transaction_number: tx_position,
|
||||
transaction_hash: tx_hash.clone(),
|
||||
block_number: block_number,
|
||||
block_hash: block_hash,
|
||||
})
|
||||
.collect::<Vec<LocalizedTrace>>()
|
||||
})
|
||||
.collect::<Vec<LocalizedTrace>>()
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fn filter(&self, filter: &Filter) -> Vec<LocalizedTrace> {
|
||||
let chain = BloomGroupChain::new(self.bloom_config, self);
|
||||
let numbers = chain.filter(filter);
|
||||
numbers.into_iter()
|
||||
.flat_map(|n| {
|
||||
let number = n as BlockNumber;
|
||||
let hash = self.extras.block_hash(number)
|
||||
.expect("Expected to find block hash. Extras db is probably corrupted");
|
||||
let traces = self.traces(&hash)
|
||||
.expect("Expected to find a trace. Db is probably corrupted.");
|
||||
let flat_block = FlatBlockTraces::from(traces);
|
||||
self.matching_block_traces(filter, flat_block, hash, number)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use util::{Address, U256, H256};
|
||||
use devtools::RandomTempPath;
|
||||
use header::BlockNumber;
|
||||
use trace::{Config, Switch, TraceDB, Database, DatabaseExtras, ImportRequest};
|
||||
use trace::{BlockTraces, Trace, Filter, LocalizedTrace, AddressesFilter};
|
||||
use trace::trace::{Call, Action, Res};
|
||||
|
||||
struct NoopExtras;
|
||||
|
||||
impl DatabaseExtras for NoopExtras {
|
||||
fn block_hash(&self, _block_number: BlockNumber) -> Option<H256> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn transaction_hash(&self, _block_number: BlockNumber, _tx_position: usize) -> Option<H256> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
struct Extras {
|
||||
block_hashes: HashMap<BlockNumber, H256>,
|
||||
transaction_hashes: HashMap<BlockNumber, Vec<H256>>,
|
||||
}
|
||||
|
||||
impl Default for Extras {
|
||||
fn default() -> Self {
|
||||
Extras {
|
||||
block_hashes: HashMap::new(),
|
||||
transaction_hashes: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseExtras for Extras {
|
||||
fn block_hash(&self, block_number: BlockNumber) -> Option<H256> {
|
||||
self.block_hashes.get(&block_number).cloned()
|
||||
}
|
||||
|
||||
fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option<H256> {
|
||||
self.transaction_hashes.get(&block_number)
|
||||
.and_then(|hashes| hashes.iter().cloned().nth(tx_position))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reopening_db_with_tracing_off() {
|
||||
let temp = RandomTempPath::new();
|
||||
let mut config = Config::default();
|
||||
|
||||
// set autotracing
|
||||
config.enabled = Switch::Auto;
|
||||
|
||||
{
|
||||
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
|
||||
assert_eq!(tracedb.tracing_enabled(), false);
|
||||
}
|
||||
|
||||
{
|
||||
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
|
||||
assert_eq!(tracedb.tracing_enabled(), false);
|
||||
}
|
||||
|
||||
config.enabled = Switch::Off;
|
||||
|
||||
{
|
||||
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
|
||||
assert_eq!(tracedb.tracing_enabled(), false);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reopening_db_with_tracing_on() {
|
||||
let temp = RandomTempPath::new();
|
||||
let mut config = Config::default();
|
||||
|
||||
// set tracing on
|
||||
config.enabled = Switch::On;
|
||||
|
||||
{
|
||||
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
|
||||
assert_eq!(tracedb.tracing_enabled(), true);
|
||||
}
|
||||
|
||||
{
|
||||
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
|
||||
assert_eq!(tracedb.tracing_enabled(), true);
|
||||
}
|
||||
|
||||
config.enabled = Switch::Auto;
|
||||
|
||||
{
|
||||
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
|
||||
assert_eq!(tracedb.tracing_enabled(), true);
|
||||
}
|
||||
|
||||
config.enabled = Switch::Off;
|
||||
|
||||
{
|
||||
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
|
||||
assert_eq!(tracedb.tracing_enabled(), false);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_invalid_reopening_db() {
|
||||
let temp = RandomTempPath::new();
|
||||
let mut config = Config::default();
|
||||
|
||||
// set tracing on
|
||||
config.enabled = Switch::Off;
|
||||
|
||||
{
|
||||
let tracedb = TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras));
|
||||
assert_eq!(tracedb.tracing_enabled(), true);
|
||||
}
|
||||
|
||||
config.enabled = Switch::On;
|
||||
TraceDB::new(config.clone(), temp.as_path(), Arc::new(NoopExtras)); // should panic!
|
||||
}
|
||||
|
||||
fn create_simple_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest {
|
||||
ImportRequest {
|
||||
traces: BlockTraces::from(vec![Trace {
|
||||
depth: 0,
|
||||
action: Action::Call(Call {
|
||||
from: Address::from(1),
|
||||
to: Address::from(2),
|
||||
value: U256::from(3),
|
||||
gas: U256::from(4),
|
||||
input: vec![],
|
||||
}),
|
||||
result: Res::FailedCall,
|
||||
subs: vec![],
|
||||
}]),
|
||||
block_hash: block_hash.clone(),
|
||||
block_number: block_number,
|
||||
enacted: vec![block_hash],
|
||||
retracted: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_simple_localized_trace(block_number: BlockNumber, block_hash: H256, tx_hash: H256) -> LocalizedTrace {
|
||||
LocalizedTrace {
|
||||
action: Action::Call(Call {
|
||||
from: Address::from(1),
|
||||
to: Address::from(2),
|
||||
value: U256::from(3),
|
||||
gas: U256::from(4),
|
||||
input: vec![],
|
||||
}),
|
||||
result: Res::FailedCall,
|
||||
trace_address: vec![],
|
||||
subtraces: 0,
|
||||
transaction_number: 0,
|
||||
transaction_hash: tx_hash,
|
||||
block_number: block_number,
|
||||
block_hash: block_hash,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_import() {
|
||||
let temp = RandomTempPath::new();
|
||||
let mut config = Config::default();
|
||||
config.enabled = Switch::On;
|
||||
let block_0 = H256::from(0xa1);
|
||||
let block_1 = H256::from(0xa2);
|
||||
let tx_0 = H256::from(0xff);
|
||||
let tx_1 = H256::from(0xaf);
|
||||
|
||||
let mut extras = Extras::default();
|
||||
extras.block_hashes.insert(0, block_0.clone());
|
||||
extras.block_hashes.insert(1, block_1.clone());
|
||||
extras.transaction_hashes.insert(0, vec![tx_0.clone()]);
|
||||
extras.transaction_hashes.insert(1, vec![tx_1.clone()]);
|
||||
|
||||
let tracedb = TraceDB::new(config, temp.as_path(), Arc::new(extras));
|
||||
|
||||
// import block 0
|
||||
let request = create_simple_import_request(0, block_0.clone());
|
||||
tracedb.import(request);
|
||||
|
||||
let filter = Filter {
|
||||
range: (0..0),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![]),
|
||||
};
|
||||
|
||||
let traces = tracedb.filter(&filter);
|
||||
assert_eq!(traces.len(), 1);
|
||||
assert_eq!(traces[0], create_simple_localized_trace(0, block_0.clone(), tx_0.clone()));
|
||||
|
||||
// import block 1
|
||||
let request = create_simple_import_request(1, block_1.clone());
|
||||
tracedb.import(request);
|
||||
|
||||
let filter = Filter {
|
||||
range: (0..1),
|
||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||
to_address: AddressesFilter::from(vec![]),
|
||||
};
|
||||
|
||||
let traces = tracedb.filter(&filter);
|
||||
assert_eq!(traces.len(), 2);
|
||||
assert_eq!(traces[0], create_simple_localized_trace(0, block_0.clone(), tx_0.clone()));
|
||||
assert_eq!(traces[1], create_simple_localized_trace(1, block_1.clone(), tx_1.clone()));
|
||||
|
||||
let traces = tracedb.block_traces(0).unwrap();
|
||||
assert_eq!(traces.len(), 1);
|
||||
assert_eq!(traces[0], create_simple_localized_trace(0, block_0.clone(), tx_0.clone()));
|
||||
|
||||
let traces = tracedb.block_traces(1).unwrap();
|
||||
assert_eq!(traces.len(), 1);
|
||||
assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone()));
|
||||
|
||||
assert_eq!(None, tracedb.block_traces(2));
|
||||
|
||||
let traces = tracedb.transaction_traces(0, 0).unwrap();
|
||||
assert_eq!(traces.len(), 1);
|
||||
assert_eq!(traces[0], create_simple_localized_trace(0, block_0.clone(), tx_0.clone()));
|
||||
|
||||
let traces = tracedb.transaction_traces(1, 0).unwrap();
|
||||
assert_eq!(traces.len(), 1);
|
||||
assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone()));
|
||||
|
||||
assert_eq!(None, tracedb.transaction_traces(1, 1));
|
||||
|
||||
assert_eq!(tracedb.trace(0, 0, vec![]).unwrap(), create_simple_localized_trace(0, block_0.clone(), tx_0.clone()));
|
||||
assert_eq!(tracedb.trace(1, 0, vec![]).unwrap(), create_simple_localized_trace(1, block_1.clone(), tx_1.clone()));
|
||||
}
|
||||
}
|
108
ethcore/src/trace/executive_tracer.rs
Normal file
108
ethcore/src/trace/executive_tracer.rs
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
//! Simple executive tracer.
|
||||
|
||||
use util::{Bytes, Address, U256};
|
||||
use action_params::ActionParams;
|
||||
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult};
|
||||
use trace::Tracer;
|
||||
|
||||
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
|
||||
#[derive(Default)]
|
||||
pub struct ExecutiveTracer {
|
||||
traces: Vec<Trace>
|
||||
}
|
||||
|
||||
impl Tracer for ExecutiveTracer {
|
||||
fn prepare_trace_call(&self, params: &ActionParams) -> Option<Call> {
|
||||
Some(Call::from(params.clone()))
|
||||
}
|
||||
|
||||
fn prepare_trace_create(&self, params: &ActionParams) -> Option<Create> {
|
||||
Some(Create::from(params.clone()))
|
||||
}
|
||||
|
||||
fn prepare_trace_output(&self) -> Option<Bytes> {
|
||||
Some(vec![])
|
||||
}
|
||||
|
||||
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: Option<Bytes>, depth: usize, subs:
|
||||
Vec<Trace>, delegate_call: bool) {
|
||||
// don't trace if it's DELEGATECALL or CALLCODE.
|
||||
if delegate_call {
|
||||
return;
|
||||
}
|
||||
|
||||
let trace = Trace {
|
||||
depth: depth,
|
||||
subs: subs,
|
||||
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
|
||||
result: Res::Call(CallResult {
|
||||
gas_used: gas_used,
|
||||
output: output.expect("self.prepare_trace_output().is_some(): so we must be tracing: qed")
|
||||
})
|
||||
};
|
||||
self.traces.push(trace);
|
||||
}
|
||||
|
||||
fn trace_create(&mut self, create: Option<Create>, gas_used: U256, code: Option<Bytes>, address: Address, depth: usize, subs: Vec<Trace>) {
|
||||
let trace = Trace {
|
||||
depth: depth,
|
||||
subs: subs,
|
||||
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
|
||||
result: Res::Create(CreateResult {
|
||||
gas_used: gas_used,
|
||||
code: code.expect("self.prepare_trace_output.is_some(): so we must be tracing: qed"),
|
||||
address: address
|
||||
})
|
||||
};
|
||||
self.traces.push(trace);
|
||||
}
|
||||
|
||||
fn trace_failed_call(&mut self, call: Option<Call>, depth: usize, subs: Vec<Trace>, delegate_call: bool) {
|
||||
// don't trace if it's DELEGATECALL or CALLCODE.
|
||||
if delegate_call {
|
||||
return;
|
||||
}
|
||||
|
||||
let trace = Trace {
|
||||
depth: depth,
|
||||
subs: subs,
|
||||
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
|
||||
result: Res::FailedCall,
|
||||
};
|
||||
self.traces.push(trace);
|
||||
}
|
||||
|
||||
fn trace_failed_create(&mut self, create: Option<Create>, depth: usize, subs: Vec<Trace>) {
|
||||
let trace = Trace {
|
||||
depth: depth,
|
||||
subs: subs,
|
||||
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
|
||||
result: Res::FailedCreate,
|
||||
};
|
||||
self.traces.push(trace);
|
||||
}
|
||||
|
||||
fn subtracer(&self) -> Self {
|
||||
ExecutiveTracer::default()
|
||||
}
|
||||
|
||||
fn traces(self) -> Vec<Trace> {
|
||||
self.traces
|
||||
}
|
||||
}
|
283
ethcore/src/trace/filter.rs
Normal file
283
ethcore/src/trace/filter.rs
Normal file
@ -0,0 +1,283 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
use std::ops::Range;
|
||||
use bloomchain::{Filter as BloomFilter, Bloom, Number};
|
||||
use util::{Address, FixedHash};
|
||||
use util::sha3::Hashable;
|
||||
use basic_types::LogBloom;
|
||||
use super::flat::FlatTrace;
|
||||
use super::trace::Action;
|
||||
|
||||
/// Addresses filter.
|
||||
///
|
||||
/// Used to create bloom possibilities and match filters.
|
||||
pub struct AddressesFilter(Vec<Address>);
|
||||
|
||||
impl From<Vec<Address>> for AddressesFilter {
|
||||
fn from(addresses: Vec<Address>) -> Self {
|
||||
AddressesFilter(addresses)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressesFilter {
|
||||
/// Returns true if address matches one of the searched addresses.
|
||||
pub fn matches(&self, address: &Address) -> bool {
|
||||
self.matches_all() || self.0.contains(address)
|
||||
}
|
||||
|
||||
/// Returns true if this address filter matches everything.
|
||||
pub fn matches_all(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
/// Returns blooms of this addresses filter.
|
||||
pub fn blooms(&self) -> Vec<LogBloom> {
|
||||
match self.0.is_empty() {
|
||||
true => vec![LogBloom::new()],
|
||||
false => self.0.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.0.is_empty() {
|
||||
true => blooms,
|
||||
false => blooms
|
||||
.into_iter()
|
||||
.flat_map(|bloom| self.0.iter()
|
||||
.map(|address| bloom.with_bloomed(&address.sha3()))
|
||||
.collect::<Vec<_>>())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 = self.to_address.matches_all();
|
||||
from_matches && to_matches
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::{FixedHash, Address, U256};
|
||||
use util::sha3::Hashable;
|
||||
use trace::trace::{Action, Call, Res};
|
||||
use trace::flat::FlatTrace;
|
||||
use trace::{Filter, AddressesFilter};
|
||||
use basic_types::LogBloom;
|
||||
|
||||
#[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![LogBloom::new()]);
|
||||
}
|
||||
|
||||
#[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: Address::from(1),
|
||||
to: Address::from(2),
|
||||
value: U256::from(3),
|
||||
gas: U256::from(4),
|
||||
input: vec![0x5],
|
||||
}),
|
||||
result: Res::FailedCall,
|
||||
trace_address: vec![0],
|
||||
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));
|
||||
}
|
||||
}
|
181
ethcore/src/trace/flat.rs
Normal file
181
ethcore/src/trace/flat.rs
Normal file
@ -0,0 +1,181 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
use trace::BlockTraces;
|
||||
use super::trace::{Trace, Action, Res};
|
||||
|
||||
/// Trace localized in vector of traces produced by a single transaction.
|
||||
///
|
||||
/// Parent and children indexes refer to positions in this vector.
|
||||
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>,
|
||||
}
|
||||
|
||||
/// Represents all traces produced by a single transaction.
|
||||
pub struct FlatTransactionTraces(Vec<FlatTrace>);
|
||||
|
||||
impl Into<Vec<FlatTrace>> for FlatTransactionTraces {
|
||||
fn into(self) -> Vec<FlatTrace> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents all traces produced by transactions in a single block.
|
||||
pub struct FlatBlockTraces(Vec<FlatTransactionTraces>);
|
||||
|
||||
impl From<BlockTraces> for FlatBlockTraces {
|
||||
fn from(block_traces: BlockTraces) -> Self {
|
||||
let traces: Vec<Trace> = block_traces.into();
|
||||
let ordered = traces.into_iter()
|
||||
.map(|trace| FlatBlockTraces::flatten(vec![], trace))
|
||||
.map(FlatTransactionTraces)
|
||||
.collect();
|
||||
FlatBlockTraces(ordered)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<FlatTransactionTraces>> for FlatBlockTraces {
|
||||
fn into(self) -> Vec<FlatTransactionTraces> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FlatBlockTraces {
|
||||
/// Helper function flattening nested tree structure to vector of ordered traces.
|
||||
fn flatten(address: Vec<usize>, trace: Trace) -> Vec<FlatTrace> {
|
||||
let subtraces = trace.subs.len();
|
||||
let all_subs = trace.subs
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.flat_map(|(index, subtrace)| {
|
||||
let mut subtrace_address = address.clone();
|
||||
subtrace_address.push(index);
|
||||
FlatBlockTraces::flatten(subtrace_address, subtrace)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let ordered = FlatTrace {
|
||||
action: trace.action,
|
||||
result: trace.result,
|
||||
subtraces: subtraces,
|
||||
trace_address: address,
|
||||
};
|
||||
|
||||
let mut result = vec![ordered];
|
||||
result.extend(all_subs);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{FlatBlockTraces, FlatTransactionTraces, FlatTrace};
|
||||
use util::{U256, Address};
|
||||
use trace::trace::{Action, Res, CallResult, Call, Create, Trace};
|
||||
use trace::BlockTraces;
|
||||
|
||||
#[test]
|
||||
fn test_block_from() {
|
||||
let trace = Trace {
|
||||
depth: 2,
|
||||
action: Action::Call(Call {
|
||||
from: Address::from(1),
|
||||
to: Address::from(2),
|
||||
value: U256::from(3),
|
||||
gas: U256::from(4),
|
||||
input: vec![0x5]
|
||||
}),
|
||||
subs: vec![
|
||||
Trace {
|
||||
depth: 3,
|
||||
action: Action::Create(Create {
|
||||
from: Address::from(6),
|
||||
value: U256::from(7),
|
||||
gas: U256::from(8),
|
||||
init: vec![0x9]
|
||||
}),
|
||||
subs: vec![
|
||||
Trace {
|
||||
depth: 3,
|
||||
action: Action::Create(Create {
|
||||
from: Address::from(6),
|
||||
value: U256::from(7),
|
||||
gas: U256::from(8),
|
||||
init: vec![0x9]
|
||||
}),
|
||||
subs: vec![
|
||||
],
|
||||
result: Res::FailedCreate
|
||||
},
|
||||
Trace {
|
||||
depth: 3,
|
||||
action: Action::Create(Create {
|
||||
from: Address::from(6),
|
||||
value: U256::from(7),
|
||||
gas: U256::from(8),
|
||||
init: vec![0x9]
|
||||
}),
|
||||
subs: vec![
|
||||
],
|
||||
result: Res::FailedCreate
|
||||
}
|
||||
],
|
||||
result: Res::FailedCreate
|
||||
},
|
||||
Trace {
|
||||
depth: 3,
|
||||
action: Action::Create(Create {
|
||||
from: Address::from(6),
|
||||
value: U256::from(7),
|
||||
gas: U256::from(8),
|
||||
init: vec![0x9]
|
||||
}),
|
||||
subs: vec![],
|
||||
result: Res::FailedCreate,
|
||||
}
|
||||
],
|
||||
result: Res::Call(CallResult {
|
||||
gas_used: U256::from(10),
|
||||
output: vec![0x11, 0x12]
|
||||
})
|
||||
};
|
||||
|
||||
let block_traces = FlatBlockTraces::from(BlockTraces::from(vec![trace]));
|
||||
let transaction_traces: Vec<FlatTransactionTraces> = block_traces.into();
|
||||
assert_eq!(transaction_traces.len(), 1);
|
||||
let ordered_traces: Vec<FlatTrace> = transaction_traces.into_iter().nth(0).unwrap().into();
|
||||
assert_eq!(ordered_traces.len(), 5);
|
||||
assert_eq!(ordered_traces[0].trace_address, vec![]);
|
||||
assert_eq!(ordered_traces[0].subtraces, 2);
|
||||
assert_eq!(ordered_traces[1].trace_address, vec![0]);
|
||||
assert_eq!(ordered_traces[1].subtraces, 2);
|
||||
assert_eq!(ordered_traces[2].trace_address, vec![0, 0]);
|
||||
assert_eq!(ordered_traces[2].subtraces, 0);
|
||||
assert_eq!(ordered_traces[3].trace_address, vec![0, 1]);
|
||||
assert_eq!(ordered_traces[3].subtraces, 0);
|
||||
assert_eq!(ordered_traces[4].trace_address, vec![1]);
|
||||
assert_eq!(ordered_traces[4].subtraces, 0);
|
||||
}
|
||||
}
|
36
ethcore/src/trace/import.rs
Normal file
36
ethcore/src/trace/import.rs
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
//! Traces import request.
|
||||
use util::H256;
|
||||
use header::BlockNumber;
|
||||
use trace::BlockTraces;
|
||||
|
||||
/// Traces import request.
|
||||
pub struct ImportRequest {
|
||||
/// Traces to import.
|
||||
pub traces: BlockTraces,
|
||||
/// 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,
|
||||
}
|
42
ethcore/src/trace/localized.rs
Normal file
42
ethcore/src/trace/localized.rs
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
use util::H256;
|
||||
use super::trace::{Action, Res};
|
||||
use header::BlockNumber;
|
||||
|
||||
/// Localized trace.
|
||||
#[derive(Debug, PartialEq)]
|
||||
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,
|
||||
}
|
@ -16,21 +16,39 @@
|
||||
|
||||
//! Tracing
|
||||
|
||||
mod trace;
|
||||
mod block;
|
||||
mod bloom;
|
||||
mod config;
|
||||
mod db;
|
||||
mod executive_tracer;
|
||||
mod filter;
|
||||
mod flat;
|
||||
mod import;
|
||||
mod localized;
|
||||
mod noop_tracer;
|
||||
pub mod trace;
|
||||
|
||||
pub use self::trace::*;
|
||||
use util::bytes::Bytes;
|
||||
use util::hash::Address;
|
||||
use util::numbers::U256;
|
||||
pub use self::block::BlockTraces;
|
||||
pub use self::config::{Config, Switch};
|
||||
pub use self::db::TraceDB;
|
||||
pub use self::trace::Trace;
|
||||
pub use self::noop_tracer::NoopTracer;
|
||||
pub use self::executive_tracer::ExecutiveTracer;
|
||||
pub use self::filter::{Filter, AddressesFilter};
|
||||
pub use self::import::ImportRequest;
|
||||
pub use self::localized::LocalizedTrace;
|
||||
use util::{Bytes, Address, U256, H256};
|
||||
use self::trace::{Call, Create};
|
||||
use action_params::ActionParams;
|
||||
use header::BlockNumber;
|
||||
|
||||
/// This trait is used by executive to build traces.
|
||||
pub trait Tracer: Send {
|
||||
/// Prepares call trace for given params. Noop tracer should return None.
|
||||
fn prepare_trace_call(&self, params: &ActionParams) -> Option<TraceCall>;
|
||||
fn prepare_trace_call(&self, params: &ActionParams) -> Option<Call>;
|
||||
|
||||
/// Prepares create trace for given params. Noop tracer should return None.
|
||||
fn prepare_trace_create(&self, params: &ActionParams) -> Option<TraceCreate>;
|
||||
fn prepare_trace_create(&self, params: &ActionParams) -> Option<Create>;
|
||||
|
||||
/// Prepare trace output. Noop tracer should return None.
|
||||
fn prepare_trace_output(&self) -> Option<Bytes>;
|
||||
@ -38,7 +56,7 @@ pub trait Tracer: Send {
|
||||
/// Stores trace call info.
|
||||
fn trace_call(
|
||||
&mut self,
|
||||
call: Option<TraceCall>,
|
||||
call: Option<Call>,
|
||||
gas_used: U256,
|
||||
output: Option<Bytes>,
|
||||
depth: usize,
|
||||
@ -49,7 +67,7 @@ pub trait Tracer: Send {
|
||||
/// Stores trace create info.
|
||||
fn trace_create(
|
||||
&mut self,
|
||||
create: Option<TraceCreate>,
|
||||
create: Option<Create>,
|
||||
gas_used: U256,
|
||||
code: Option<Bytes>,
|
||||
address: Address,
|
||||
@ -58,10 +76,10 @@ pub trait Tracer: Send {
|
||||
);
|
||||
|
||||
/// Stores failed call trace.
|
||||
fn trace_failed_call(&mut self, call: Option<TraceCall>, depth: usize, subs: Vec<Trace>, delegate_call: bool);
|
||||
fn trace_failed_call(&mut self, call: Option<Call>, depth: usize, subs: Vec<Trace>, delegate_call: bool);
|
||||
|
||||
/// Stores failed create trace.
|
||||
fn trace_failed_create(&mut self, create: Option<TraceCreate>, depth: usize, subs: Vec<Trace>);
|
||||
fn trace_failed_create(&mut self, create: Option<Create>, depth: usize, subs: Vec<Trace>);
|
||||
|
||||
/// Spawn subracer which will be used to trace deeper levels of execution.
|
||||
fn subtracer(&self) -> Self where Self: Sized;
|
||||
@ -70,132 +88,33 @@ pub trait Tracer: Send {
|
||||
fn traces(self) -> Vec<Trace>;
|
||||
}
|
||||
|
||||
/// Nonoperative tracer. Does not trace anything.
|
||||
pub struct NoopTracer;
|
||||
/// 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>;
|
||||
|
||||
impl Tracer for NoopTracer {
|
||||
fn prepare_trace_call(&self, _: &ActionParams) -> Option<TraceCall> {
|
||||
None
|
||||
}
|
||||
|
||||
fn prepare_trace_create(&self, _: &ActionParams) -> Option<TraceCreate> {
|
||||
None
|
||||
}
|
||||
|
||||
fn prepare_trace_output(&self) -> Option<Bytes> {
|
||||
None
|
||||
}
|
||||
|
||||
fn trace_call(&mut self, call: Option<TraceCall>, _: U256, output: Option<Bytes>, _: usize, _: Vec<Trace>,
|
||||
_: bool) {
|
||||
assert!(call.is_none());
|
||||
assert!(output.is_none());
|
||||
}
|
||||
|
||||
fn trace_create(&mut self, create: Option<TraceCreate>, _: U256, code: Option<Bytes>, _: Address, _: usize, _: Vec<Trace>) {
|
||||
assert!(create.is_none());
|
||||
assert!(code.is_none());
|
||||
}
|
||||
|
||||
fn trace_failed_call(&mut self, call: Option<TraceCall>, _: usize, _: Vec<Trace>, _: bool) {
|
||||
assert!(call.is_none());
|
||||
}
|
||||
|
||||
fn trace_failed_create(&mut self, create: Option<TraceCreate>, _: usize, _: Vec<Trace>) {
|
||||
assert!(create.is_none());
|
||||
}
|
||||
|
||||
fn subtracer(&self) -> Self {
|
||||
NoopTracer
|
||||
}
|
||||
|
||||
fn traces(self) -> Vec<Trace> {
|
||||
vec![]
|
||||
}
|
||||
/// Returns hash of transaction at given position.
|
||||
fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option<H256>;
|
||||
}
|
||||
|
||||
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
|
||||
#[derive(Default)]
|
||||
pub struct ExecutiveTracer {
|
||||
traces: Vec<Trace>
|
||||
}
|
||||
|
||||
impl Tracer for ExecutiveTracer {
|
||||
fn prepare_trace_call(&self, params: &ActionParams) -> Option<TraceCall> {
|
||||
Some(TraceCall::from(params.clone()))
|
||||
}
|
||||
|
||||
fn prepare_trace_create(&self, params: &ActionParams) -> Option<TraceCreate> {
|
||||
Some(TraceCreate::from(params.clone()))
|
||||
}
|
||||
|
||||
fn prepare_trace_output(&self) -> Option<Bytes> {
|
||||
Some(vec![])
|
||||
}
|
||||
|
||||
fn trace_call(&mut self, call: Option<TraceCall>, gas_used: U256, output: Option<Bytes>, depth: usize, subs:
|
||||
Vec<Trace>, delegate_call: bool) {
|
||||
// don't trace if it's DELEGATECALL or CALLCODE.
|
||||
if delegate_call {
|
||||
return;
|
||||
}
|
||||
|
||||
let trace = Trace {
|
||||
depth: depth,
|
||||
subs: subs,
|
||||
action: TraceAction::Call(call.expect("Trace call expected to be Some.")),
|
||||
result: TraceResult::Call(TraceCallResult {
|
||||
gas_used: gas_used,
|
||||
output: output.expect("Trace call output expected to be Some.")
|
||||
})
|
||||
};
|
||||
self.traces.push(trace);
|
||||
}
|
||||
|
||||
fn trace_create(&mut self, create: Option<TraceCreate>, gas_used: U256, code: Option<Bytes>, address: Address, depth: usize, subs: Vec<Trace>) {
|
||||
let trace = Trace {
|
||||
depth: depth,
|
||||
subs: subs,
|
||||
action: TraceAction::Create(create.expect("Trace create expected to be Some.")),
|
||||
result: TraceResult::Create(TraceCreateResult {
|
||||
gas_used: gas_used,
|
||||
code: code.expect("Trace create code expected to be Some."),
|
||||
address: address
|
||||
})
|
||||
};
|
||||
self.traces.push(trace);
|
||||
}
|
||||
|
||||
fn trace_failed_call(&mut self, call: Option<TraceCall>, depth: usize, subs: Vec<Trace>, delegate_call: bool) {
|
||||
// don't trace if it's DELEGATECALL or CALLCODE.
|
||||
if delegate_call {
|
||||
return;
|
||||
}
|
||||
|
||||
let trace = Trace {
|
||||
depth: depth,
|
||||
subs: subs,
|
||||
action: TraceAction::Call(call.expect("Trace call expected to be Some.")),
|
||||
result: TraceResult::FailedCall,
|
||||
};
|
||||
self.traces.push(trace);
|
||||
}
|
||||
|
||||
fn trace_failed_create(&mut self, create: Option<TraceCreate>, depth: usize, subs: Vec<Trace>) {
|
||||
let trace = Trace {
|
||||
depth: depth,
|
||||
subs: subs,
|
||||
action: TraceAction::Create(create.expect("Trace create expected to be Some.")),
|
||||
result: TraceResult::FailedCreate,
|
||||
};
|
||||
self.traces.push(trace);
|
||||
}
|
||||
|
||||
fn subtracer(&self) -> Self {
|
||||
ExecutiveTracer::default()
|
||||
}
|
||||
|
||||
fn traces(self) -> Vec<Trace> {
|
||||
self.traces
|
||||
}
|
||||
/// Db provides an interface to query tracesdb.
|
||||
pub trait Database {
|
||||
/// Returns true if tracing is enabled. Otherwise false.
|
||||
fn tracing_enabled(&self) -> bool;
|
||||
|
||||
/// Imports new block traces.
|
||||
fn import(&self, 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 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>>;
|
||||
|
||||
/// Filter traces matching given filter.
|
||||
fn filter(&self, filter: &Filter) -> Vec<LocalizedTrace>;
|
||||
}
|
||||
|
65
ethcore/src/trace/noop_tracer.rs
Normal file
65
ethcore/src/trace/noop_tracer.rs
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
//! Nonoperative tracer.
|
||||
|
||||
use util::{Bytes, Address, U256};
|
||||
use action_params::ActionParams;
|
||||
use trace::Tracer;
|
||||
use trace::trace::{Trace, Call, Create};
|
||||
|
||||
/// Nonoperative tracer. Does not trace anything.
|
||||
pub struct NoopTracer;
|
||||
|
||||
impl Tracer for NoopTracer {
|
||||
fn prepare_trace_call(&self, _: &ActionParams) -> Option<Call> {
|
||||
None
|
||||
}
|
||||
|
||||
fn prepare_trace_create(&self, _: &ActionParams) -> Option<Create> {
|
||||
None
|
||||
}
|
||||
|
||||
fn prepare_trace_output(&self) -> Option<Bytes> {
|
||||
None
|
||||
}
|
||||
|
||||
fn trace_call(&mut self, call: Option<Call>, _: U256, output: Option<Bytes>, _: usize, _: Vec<Trace>, _: bool) {
|
||||
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
|
||||
assert!(output.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed");
|
||||
}
|
||||
|
||||
fn trace_create(&mut self, create: Option<Create>, _: U256, code: Option<Bytes>, _: Address, _: usize, _: Vec<Trace>) {
|
||||
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
|
||||
assert!(code.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed");
|
||||
}
|
||||
|
||||
fn trace_failed_call(&mut self, call: Option<Call>, _: usize, _: Vec<Trace>, _: bool) {
|
||||
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
|
||||
}
|
||||
|
||||
fn trace_failed_create(&mut self, create: Option<Create>, _: usize, _: Vec<Trace>) {
|
||||
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
|
||||
}
|
||||
|
||||
fn subtracer(&self) -> Self {
|
||||
NoopTracer
|
||||
}
|
||||
|
||||
fn traces(self) -> Vec<Trace> {
|
||||
vec![]
|
||||
}
|
||||
}
|
@ -15,20 +15,44 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tracing datatypes.
|
||||
use common::*;
|
||||
use util::{U256, Bytes, Address, FixedHash};
|
||||
use util::rlp::*;
|
||||
use util::sha3::Hashable;
|
||||
use action_params::ActionParams;
|
||||
use basic_types::LogBloom;
|
||||
|
||||
/// `TraceCall` result.
|
||||
/// `Call` result.
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct TraceCallResult {
|
||||
pub struct CallResult {
|
||||
/// Gas used by call.
|
||||
pub gas_used: U256,
|
||||
/// Call Output.
|
||||
pub output: Bytes,
|
||||
}
|
||||
|
||||
/// `TraceCreate` result.
|
||||
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<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let res = CallResult {
|
||||
gas_used: try!(d.val_at(0)),
|
||||
output: try!(d.val_at(1)),
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
/// `Create` result.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TraceCreateResult {
|
||||
pub struct CreateResult {
|
||||
/// Gas used by create.
|
||||
pub gas_used: U256,
|
||||
/// Code of the newly created contract.
|
||||
@ -37,9 +61,31 @@ pub struct TraceCreateResult {
|
||||
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<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let res = CreateResult {
|
||||
gas_used: try!(d.val_at(0)),
|
||||
code: try!(d.val_at(1)),
|
||||
address: try!(d.val_at(2)),
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Description of a _call_ action, either a `CALL` operation or a message transction.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TraceCall {
|
||||
pub struct Call {
|
||||
/// The sending account.
|
||||
pub from: Address,
|
||||
/// The destination account.
|
||||
@ -52,9 +98,9 @@ pub struct TraceCall {
|
||||
pub input: Bytes,
|
||||
}
|
||||
|
||||
impl From<ActionParams> for TraceCall {
|
||||
impl From<ActionParams> for Call {
|
||||
fn from(p: ActionParams) -> Self {
|
||||
TraceCall {
|
||||
Call {
|
||||
from: p.sender,
|
||||
to: p.address,
|
||||
value: p.value.value(),
|
||||
@ -64,9 +110,44 @@ impl From<ActionParams> for TraceCall {
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Call {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(5);
|
||||
s.append(&self.from);
|
||||
s.append(&self.to);
|
||||
s.append(&self.value);
|
||||
s.append(&self.gas);
|
||||
s.append(&self.input);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Call {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let res = Call {
|
||||
from: try!(d.val_at(0)),
|
||||
to: try!(d.val_at(1)),
|
||||
value: try!(d.val_at(2)),
|
||||
gas: try!(d.val_at(3)),
|
||||
input: try!(d.val_at(4)),
|
||||
};
|
||||
|
||||
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)]
|
||||
pub struct TraceCreate {
|
||||
pub struct Create {
|
||||
/// The address of the creator.
|
||||
pub from: Address,
|
||||
/// The value with which the new account is endowed.
|
||||
@ -77,9 +158,9 @@ pub struct TraceCreate {
|
||||
pub init: Bytes,
|
||||
}
|
||||
|
||||
impl From<ActionParams> for TraceCreate {
|
||||
impl From<ActionParams> for Create {
|
||||
fn from(p: ActionParams) -> Self {
|
||||
TraceCreate {
|
||||
Create {
|
||||
from: p.sender,
|
||||
value: p.value.value(),
|
||||
gas: p.gas,
|
||||
@ -88,28 +169,137 @@ impl From<ActionParams> for TraceCreate {
|
||||
}
|
||||
}
|
||||
|
||||
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<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let res = Create {
|
||||
from: try!(d.val_at(0)),
|
||||
value: try!(d.val_at(1)),
|
||||
gas: try!(d.val_at(2)),
|
||||
init: try!(d.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())
|
||||
}
|
||||
}
|
||||
|
||||
/// Description of an action that we trace; will be either a call or a create.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TraceAction {
|
||||
pub enum Action {
|
||||
/// It's a call action.
|
||||
Call(TraceCall),
|
||||
Call(Call),
|
||||
/// It's a create action.
|
||||
Create(TraceCreate),
|
||||
Create(Create),
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Action {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let action_type: u8 = try!(d.val_at(0));
|
||||
match action_type {
|
||||
0 => d.val_at(1).map(Action::Call),
|
||||
1 => d.val_at(1).map(Action::Create),
|
||||
_ => 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of the performed action.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TraceResult {
|
||||
pub enum Res {
|
||||
/// Successful call action result.
|
||||
Call(TraceCallResult),
|
||||
Call(CallResult),
|
||||
/// Successful create action result.
|
||||
Create(TraceCreateResult),
|
||||
Create(CreateResult),
|
||||
/// Failed call.
|
||||
FailedCall,
|
||||
/// Failed create.
|
||||
FailedCreate,
|
||||
}
|
||||
|
||||
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 => {
|
||||
s.begin_list(1);
|
||||
s.append(&2u8);
|
||||
},
|
||||
Res::FailedCreate => {
|
||||
s.begin_list(1);
|
||||
s.append(&3u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Res {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let action_type: u8 = try!(d.val_at(0));
|
||||
match action_type {
|
||||
0 => d.val_at(1).map(Res::Call),
|
||||
1 => d.val_at(1).map(Res::Create),
|
||||
2 => Ok(Res::FailedCall),
|
||||
3 => Ok(Res::FailedCreate),
|
||||
_ => Err(DecoderError::Custom("Invalid result type.")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
/// A trace; includes a description of the action being traced and sub traces of each interior action.
|
||||
pub struct Trace {
|
||||
@ -117,9 +307,122 @@ pub struct Trace {
|
||||
/// the outer action of the transaction.
|
||||
pub depth: usize,
|
||||
/// The action being performed.
|
||||
pub action: TraceAction,
|
||||
pub action: Action,
|
||||
/// The sub traces for each interior action performed as part of this call.
|
||||
pub subs: Vec<Trace>,
|
||||
/// The result of the performed action.
|
||||
pub result: TraceResult,
|
||||
pub result: Res,
|
||||
}
|
||||
|
||||
impl Encodable for Trace {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(4);
|
||||
s.append(&self.depth);
|
||||
s.append(&self.action);
|
||||
s.append(&self.subs);
|
||||
s.append(&self.result);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Trace {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
let res = Trace {
|
||||
depth: try!(d.val_at(0)),
|
||||
action: try!(d.val_at(1)),
|
||||
subs: try!(d.val_at(2)),
|
||||
result: try!(d.val_at(3)),
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Trace {
|
||||
/// Returns trace bloom.
|
||||
pub fn bloom(&self) -> LogBloom {
|
||||
self.subs.iter().fold(self.action.bloom(), |b, s| b | s.bloom())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::{Address, U256, FixedHash};
|
||||
use util::rlp::{encode, decode};
|
||||
use util::sha3::Hashable;
|
||||
use trace::trace::{Call, CallResult, Create, Res, Action, Trace};
|
||||
|
||||
#[test]
|
||||
fn traces_rlp() {
|
||||
let trace = Trace {
|
||||
depth: 2,
|
||||
action: Action::Call(Call {
|
||||
from: Address::from(1),
|
||||
to: Address::from(2),
|
||||
value: U256::from(3),
|
||||
gas: U256::from(4),
|
||||
input: vec![0x5]
|
||||
}),
|
||||
subs: vec![
|
||||
Trace {
|
||||
depth: 3,
|
||||
action: Action::Create(Create {
|
||||
from: Address::from(6),
|
||||
value: U256::from(7),
|
||||
gas: U256::from(8),
|
||||
init: vec![0x9]
|
||||
}),
|
||||
subs: vec![],
|
||||
result: Res::FailedCreate
|
||||
}
|
||||
],
|
||||
result: Res::Call(CallResult {
|
||||
gas_used: U256::from(10),
|
||||
output: vec![0x11, 0x12]
|
||||
})
|
||||
};
|
||||
|
||||
let encoded = encode(&trace);
|
||||
let decoded: Trace = decode(&encoded);
|
||||
assert_eq!(trace, decoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn traces_bloom() {
|
||||
let trace = Trace {
|
||||
depth: 2,
|
||||
action: Action::Call(Call {
|
||||
from: Address::from(1),
|
||||
to: Address::from(2),
|
||||
value: U256::from(3),
|
||||
gas: U256::from(4),
|
||||
input: vec![0x5]
|
||||
}),
|
||||
subs: vec![
|
||||
Trace {
|
||||
depth: 3,
|
||||
action: Action::Create(Create {
|
||||
from: Address::from(6),
|
||||
value: U256::from(7),
|
||||
gas: U256::from(8),
|
||||
init: vec![0x9]
|
||||
}),
|
||||
subs: vec![],
|
||||
result: Res::FailedCreate
|
||||
}
|
||||
],
|
||||
result: Res::Call(CallResult {
|
||||
gas_used: U256::from(10),
|
||||
output: vec![0x11, 0x12]
|
||||
})
|
||||
};
|
||||
|
||||
let bloom = trace.bloom();
|
||||
|
||||
// right now only addresses are bloomed
|
||||
assert!(bloom.contains_bloomed(&Address::from(1).sha3()));
|
||||
assert!(bloom.contains_bloomed(&Address::from(2).sha3()));
|
||||
assert!(!bloom.contains_bloomed(&Address::from(20).sha3()));
|
||||
assert!(bloom.contains_bloomed(&Address::from(6).sha3()));
|
||||
}
|
||||
}
|
||||
|
@ -278,8 +278,6 @@ mod tests {
|
||||
}
|
||||
|
||||
impl BlockProvider for TestBlockChain {
|
||||
fn have_tracing(&self) -> bool { false }
|
||||
|
||||
fn is_known(&self, hash: &H256) -> bool {
|
||||
self.blocks.contains_key(hash)
|
||||
}
|
||||
|
@ -78,6 +78,12 @@ macro_rules! impl_hash {
|
||||
/// Unformatted binary data of fixed length.
|
||||
pub struct $from (pub [u8; $size]);
|
||||
|
||||
impl From<[u8; $size]> for $from {
|
||||
fn from(bytes: [u8; $size]) -> Self {
|
||||
$from(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for $from {
|
||||
type Target = [u8];
|
||||
|
||||
|
@ -40,7 +40,9 @@ pub enum DecoderError {
|
||||
/// Non-canonical (longer than necessary) representation used for data or list.
|
||||
RlpInvalidIndirection,
|
||||
/// Declared length is inconsistent with data specified after.
|
||||
RlpInconsistentLengthAndData
|
||||
RlpInconsistentLengthAndData,
|
||||
/// Custom rlp decoding error.
|
||||
Custom(&'static str),
|
||||
}
|
||||
|
||||
impl StdError for DecoderError {
|
||||
|
Loading…
Reference in New Issue
Block a user