From 7c2adc41375d021e40c9c1e6456aeb6a8e7623a9 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Mon, 2 May 2016 12:17:30 +0200 Subject: [PATCH] Tracedb interface && cli (#997) * traces cli and jsonrpc api * missing if in docs * adding traces to modules --- ethcore/src/client/client.rs | 56 ++++++- ethcore/src/client/ids.rs | 8 + ethcore/src/client/mod.rs | 18 ++- ethcore/src/client/test_client.rs | 19 ++- parity/cli.rs | 8 +- parity/configuration.rs | 8 +- parity/rpc.rs | 6 +- rpc/src/v1/impls/mod.rs | 2 + rpc/src/v1/impls/traces.rs | 84 +++++++++++ rpc/src/v1/mod.rs | 2 +- rpc/src/v1/traits/mod.rs | 2 + rpc/src/v1/traits/traces.rs | 44 ++++++ rpc/src/v1/types/mod.rs.in | 5 +- rpc/src/v1/types/trace.rs | 238 ++++++++++++++++++++++++++++++ rpc/src/v1/types/trace_filter.rs | 82 ++++++++++ 15 files changed, 572 insertions(+), 10 deletions(-) create mode 100644 rpc/src/v1/impls/traces.rs create mode 100644 rpc/src/v1/traits/traces.rs create mode 100644 rpc/src/v1/types/trace.rs create mode 100644 rpc/src/v1/types/trace_filter.rs diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 964bb7bad..19802a93c 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -37,12 +37,14 @@ use filter::Filter; use log_entry::LocalizedLogEntry; use block_queue::{BlockQueue, BlockQueueInfo}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; -use client::{BlockId, TransactionId, UncleId, ClientConfig, BlockChainClient}; +use client::{BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient, TraceFilter}; 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}; +use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase, Filter as + TracedbFilter}; +use trace; /// General block status #[derive(Debug, Eq, PartialEq)] @@ -309,6 +311,8 @@ impl Client where V: Verifier { // Commit results let closed_block = closed_block.unwrap(); let receipts = closed_block.block().receipts().clone(); + let traces = From::from(closed_block.block().traces().clone().unwrap_or_else(Vec::new)); + closed_block.drain() .commit(header.number(), &header.hash(), ancient) .expect("State DB commit failed."); @@ -316,6 +320,14 @@ impl Client where V: Verifier { // And update the chain after commit to prevent race conditions // (when something is in chain but you are not able to fetch details) let route = self.chain.insert_block(&block.bytes, receipts); + self.tracedb.import(TraceImportRequest { + traces: traces, + block_hash: header.hash(), + block_number: header.number(), + enacted: route.enacted.clone(), + retracted: route.retracted.len() + }); + import_results.push(route); self.report.write().unwrap().accrue_block(&block); @@ -707,6 +719,46 @@ impl BlockChainClient for Client where V: Verifier { .collect() } + fn filter_traces(&self, filter: TraceFilter) -> Option> { + let start = self.block_number(filter.range.start); + let end = self.block_number(filter.range.end); + + if start.is_some() && end.is_some() { + let filter = trace::Filter { + range: start.unwrap() as usize..end.unwrap() as usize, + from_address: From::from(filter.from_address), + to_address: From::from(filter.to_address), + }; + + let traces = self.tracedb.filter(&filter); + Some(traces) + } else { + None + } + } + + fn trace(&self, trace: TraceId) -> Option { + let trace_address = trace.address; + self.transaction_address(trace.transaction) + .and_then(|tx_address| { + self.block_number(BlockId::Hash(tx_address.block_hash)) + .and_then(|number| self.tracedb.trace(number, tx_address.index, trace_address)) + }) + } + + fn transaction_traces(&self, transaction: TransactionId) -> Option> { + self.transaction_address(transaction) + .and_then(|tx_address| { + self.block_number(BlockId::Hash(tx_address.block_hash)) + .and_then(|number| self.tracedb.transaction_traces(number, tx_address.index)) + }) + } + + fn block_traces(&self, block: BlockId) -> Option> { + self.block_number(block) + .and_then(|number| self.tracedb.block_traces(number)) + } + fn last_hashes(&self) -> LastHashes { self.build_last_hashes(self.chain.best_block_hash()) } diff --git a/ethcore/src/client/ids.rs b/ethcore/src/client/ids.rs index 94ce58b8a..39a19c82d 100644 --- a/ethcore/src/client/ids.rs +++ b/ethcore/src/client/ids.rs @@ -44,6 +44,14 @@ pub enum TransactionId { Location(BlockId, usize) } +/// Uniquely identifies Trace. +pub struct TraceId { + /// Transaction + pub transaction: TransactionId, + /// Trace address within transaction. + pub address: Vec, +} + /// Uniquely identifies Uncle. pub struct UncleId ( /// Block id. diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 57be070e4..ebfb2a791 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -23,9 +23,10 @@ mod test_client; mod trace; pub use self::client::*; -pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig}; -pub use self::ids::{BlockId, TransactionId, UncleId}; +pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig, Switch}; +pub use self::ids::{BlockId, TransactionId, UncleId, TraceId}; pub use self::test_client::{TestBlockChainClient, EachBlockWith}; +pub use self::trace::Filter as TraceFilter; pub use executive::{Executed, Executive, TransactOptions}; pub use env_info::{LastHashes, EnvInfo}; @@ -43,6 +44,7 @@ use filter::Filter; use error::{ImportResult, Error}; use receipt::LocalizedReceipt; use engine::{Engine}; +use trace::LocalizedTrace; /// Blockchain database client. Owns and manages a blockchain and a block queue. pub trait BlockChainClient : Sync + Send { @@ -135,6 +137,18 @@ pub trait BlockChainClient : Sync + Send { /// Executes a function providing it with a reference to an engine. fn engine(&self) -> &Engine; + /// Returns traces matching given filter. + fn filter_traces(&self, filter: TraceFilter) -> Option>; + + /// Returns trace with given id. + fn trace(&self, trace: TraceId) -> Option; + + /// Returns traces created by transaction. + fn transaction_traces(&self, trace: TransactionId) -> Option>; + + /// Returns traces created by transaction from block. + fn block_traces(&self, trace: BlockId) -> Option>; + /// Get last hashes starting from best block. fn last_hashes(&self) -> LastHashes; } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 82bd4b048..c3df57b76 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -20,7 +20,7 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder}; use util::*; use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action}; use blockchain::TreeRoute; -use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockId, TransactionId, UncleId, LastHashes}; +use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockId, TransactionId, UncleId, TraceId, TraceFilter, LastHashes}; use header::{Header as BlockHeader, BlockNumber}; use filter::Filter; use log_entry::LocalizedLogEntry; @@ -33,6 +33,7 @@ use block::{SealedBlock, ClosedBlock, LockedBlock}; use executive::Executed; use error::Error; use engine::Engine; +use trace::LocalizedTrace; /// Test client. pub struct TestBlockChainClient { @@ -432,4 +433,20 @@ impl BlockChainClient for TestBlockChainClient { fn engine(&self) -> &Engine { unimplemented!(); } + + fn filter_traces(&self, _filter: TraceFilter) -> Option> { + unimplemented!(); + } + + fn trace(&self, _trace: TraceId) -> Option { + unimplemented!(); + } + + fn transaction_traces(&self, _trace: TransactionId) -> Option> { + unimplemented!(); + } + + fn block_traces(&self, _trace: BlockId) -> Option> { + unimplemented!(); + } } diff --git a/parity/cli.rs b/parity/cli.rs index 45c621665..e5a1d6e3d 100644 --- a/parity/cli.rs +++ b/parity/cli.rs @@ -69,7 +69,7 @@ API and Console Options: --jsonrpc-apis APIS Specify the APIs available through the JSONRPC interface. APIS is a comma-delimited list of API name. Possible name are web3, eth and net. - [default: web3,eth,net,personal,ethcore]. + [default: web3,eth,net,personal,ethcore,traces]. -w --webapp Enable the web applications server (e.g. status page). --webapp-port PORT Specify the port portion of the WebApps server @@ -103,6 +103,11 @@ Sealing/Mining Options: be included in next block) [default: 1024]. Footprint Options: + --tracing BOOL Indicates if full transaction tracing should be + enabled. Works only if client had been fully synced with + tracing enabled. BOOL may be one of auto, on, off. + auto uses last used value of this option (off if it does + not exist) [default: auto]. --pruning METHOD Configure pruning of the state/storage trie. METHOD may be one of auto, archive, basic, fast, light: archive - keep all state trie data. No pruning. @@ -164,6 +169,7 @@ pub struct Args { pub flag_bootnodes: Option, pub flag_network_id: Option, pub flag_pruning: String, + pub flag_tracing: String, pub flag_port: u16, pub flag_peers: usize, pub flag_no_discovery: bool, diff --git a/parity/configuration.rs b/parity/configuration.rs index 233a01e00..c7f3dc957 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -26,7 +26,7 @@ use die::*; use util::*; use util::keys::store::AccountService; use util::network_settings::NetworkSettings; -use ethcore::client::{append_path, get_db_path, ClientConfig}; +use ethcore::client::{append_path, get_db_path, ClientConfig, Switch}; use ethcore::ethereum; use ethcore::spec::Spec; use ethsync::SyncConfig; @@ -207,6 +207,12 @@ impl Configuration { client_config.blockchain.max_cache_size = self.args.flag_cache_max_size; } } + client_config.tracing.enabled = match self.args.flag_tracing.as_str() { + "auto" => Switch::Auto, + "on" => Switch::On, + "off" => Switch::Off, + _ => { die!("Invalid tracing method given!") } + }; client_config.pruning = match self.args.flag_pruning.as_str() { "archive" => journaldb::Algorithm::Archive, "light" => journaldb::Algorithm::EarlyMerge, diff --git a/parity/rpc.rs b/parity/rpc.rs index 6a71cf45a..ff853d478 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -99,7 +99,7 @@ pub fn setup_rpc_server( server.add_delegate(Web3Client::new().to_delegate()); }, "net" => { - modules.insert("web3".to_owned(), "1.0".to_owned()); + modules.insert("net".to_owned(), "1.0".to_owned()); server.add_delegate(NetClient::new(&deps.sync).to_delegate()); }, "eth" => { @@ -115,6 +115,10 @@ pub fn setup_rpc_server( // not adding to modules, since `ethcore` is not supported in geth server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()) }, + "traces" => { + modules.insert("traces".to_owned(), "1.0".to_owned()); + server.add_delegate(TracesClient::new(&deps.client).to_delegate()) + }, _ => { die!("{}: Invalid API name to be enabled.", api); }, diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index 8747811d0..582d61f03 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -30,6 +30,7 @@ mod eth; mod net; mod personal; mod ethcore; +mod traces; mod rpc; pub use self::web3::Web3Client; @@ -37,5 +38,6 @@ pub use self::eth::{EthClient, EthFilterClient}; pub use self::net::NetClient; pub use self::personal::PersonalClient; pub use self::ethcore::EthcoreClient; +pub use self::traces::TracesClient; pub use self::rpc::RpcClient; diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs new file mode 100644 index 000000000..b776a26eb --- /dev/null +++ b/rpc/src/v1/impls/traces.rs @@ -0,0 +1,84 @@ +// 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 . + +//! Traces api implementation. + +use std::sync::{Weak, Arc}; +use jsonrpc_core::*; +use util::H256; +use ethcore::client::{BlockChainClient, TransactionId, TraceId}; +use v1::traits::Traces; +use v1::types::{TraceFilter, Trace, BlockNumber, Index}; + +/// Traces api implementation. +pub struct TracesClient where C: BlockChainClient { + client: Weak, +} + +impl TracesClient where C: BlockChainClient { + /// Creates new Traces client. + pub fn new(client: &Arc) -> Self { + TracesClient { + client: Arc::downgrade(client), + } + } +} + +impl Traces for TracesClient where C: BlockChainClient + 'static { + fn filter(&self, params: Params) -> Result { + from_params::<(TraceFilter,)>(params) + .and_then(|(filter, )| { + let client = take_weak!(self.client); + let traces = client.filter_traces(filter.into()); + let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(Trace::from).collect()); + to_value(&traces) + }) + } + + fn block_traces(&self, params: Params) -> Result { + from_params::<(BlockNumber,)>(params) + .and_then(|(block_number,)| { + let client = take_weak!(self.client); + let traces = client.block_traces(block_number.into()); + let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(Trace::from).collect()); + to_value(&traces) + }) + } + + fn transaction_traces(&self, params: Params) -> Result { + from_params::<(H256,)>(params) + .and_then(|(transaction_hash,)| { + let client = take_weak!(self.client); + let traces = client.transaction_traces(TransactionId::Hash(transaction_hash)); + let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(Trace::from).collect()); + to_value(&traces) + }) + } + + fn trace(&self, params: Params) -> Result { + from_params::<(H256, Vec)>(params) + .and_then(|(transaction_hash, address)| { + let client = take_weak!(self.client); + let id = TraceId { + transaction: TransactionId::Hash(transaction_hash), + address: address.into_iter().map(|i| i.value()).collect() + }; + let trace = client.trace(id); + let trace = trace.map(Trace::from); + to_value(&trace) + }) + } +} diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index 367a26554..f3bb450f3 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -25,5 +25,5 @@ mod helpers; pub mod tests; -pub use self::traits::{Web3, Eth, EthFilter, Personal, Net, Ethcore, Rpc}; +pub use self::traits::{Web3, Eth, EthFilter, Personal, Net, Ethcore, Traces, Rpc}; pub use self::impls::*; diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs index bcddc04b5..bebf95bb7 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -25,6 +25,7 @@ pub mod eth; pub mod net; pub mod personal; pub mod ethcore; +pub mod traces; pub mod rpc; pub use self::web3::Web3; @@ -32,4 +33,5 @@ pub use self::eth::{Eth, EthFilter}; pub use self::net::Net; pub use self::personal::Personal; pub use self::ethcore::Ethcore; +pub use self::traces::Traces; pub use self::rpc::Rpc; diff --git a/rpc/src/v1/traits/traces.rs b/rpc/src/v1/traits/traces.rs new file mode 100644 index 000000000..b42feede5 --- /dev/null +++ b/rpc/src/v1/traits/traces.rs @@ -0,0 +1,44 @@ +// 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 . + +//! Traces specific rpc interface. +use std::sync::Arc; +use jsonrpc_core::*; + +/// Traces specific rpc interface. +pub trait Traces: Sized + Send + Sync + 'static { + /// Returns traces matching given filter. + fn filter(&self, _: Params) -> Result { rpc_unimplemented!() } + + /// Returns transaction trace at given index. + fn trace(&self, _: Params) -> Result { rpc_unimplemented!() } + + /// Returns all traces of given transaction. + fn transaction_traces(&self, _: Params) -> Result { rpc_unimplemented!() } + + /// Returns all traces produced at given block. + fn block_traces(&self, _: Params) -> Result { rpc_unimplemented!() } + + /// Should be used to convert object to io delegate. + fn to_delegate(self) -> IoDelegate { + let mut delegate = IoDelegate::new(Arc::new(self)); + delegate.add_method("trace_filter", Traces::filter); + delegate.add_method("trace_get", Traces::trace); + delegate.add_method("trace_transaction", Traces::transaction_traces); + delegate.add_method("trace_block", Traces::block_traces); + delegate + } +} diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 06b07b146..d896a64dc 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -26,6 +26,8 @@ mod transaction; mod transaction_request; mod call_request; mod receipt; +mod trace; +mod trace_filter; pub use self::block::{Block, BlockTransactions}; pub use self::block_number::BlockNumber; @@ -39,4 +41,5 @@ pub use self::transaction::Transaction; pub use self::transaction_request::TransactionRequest; pub use self::call_request::CallRequest; pub use self::receipt::Receipt; - +pub use self::trace::Trace; +pub use self::trace_filter::TraceFilter; diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs new file mode 100644 index 000000000..4cd1ac408 --- /dev/null +++ b/rpc/src/v1/types/trace.rs @@ -0,0 +1,238 @@ +// 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 . + +use util::{Address, U256, H256}; +use ethcore::trace::trace; +use ethcore::trace::LocalizedTrace; +use v1::types::Bytes; + +#[derive(Debug, Serialize)] +pub struct Create { + from: Address, + value: U256, + gas: U256, + init: Bytes, +} + +impl From for Create { + fn from(c: trace::Create) -> Self { + Create { + from: c.from, + value: c.value, + gas: c.gas, + init: Bytes::new(c.init), + } + } +} + +#[derive(Debug, Serialize)] +pub struct Call { + from: Address, + to: Address, + value: U256, + gas: U256, + input: Bytes, +} + +impl From for Call { + fn from(c: trace::Call) -> Self { + Call { + from: c.from, + to: c.to, + value: c.value, + gas: c.gas, + input: Bytes::new(c.input), + } + } +} + +#[derive(Debug, Serialize)] +pub enum Action { + #[serde(rename="call")] + Call(Call), + #[serde(rename="create")] + Create(Create), +} + +impl From for Action { + fn from(c: trace::Action) -> Self { + match c { + trace::Action::Call(call) => Action::Call(Call::from(call)), + trace::Action::Create(create) => Action::Create(Create::from(create)), + } + } +} + +#[derive(Debug, Serialize)] +pub struct CallResult { + #[serde(rename="gasUsed")] + gas_used: U256, + output: Bytes, +} + +impl From for CallResult { + fn from(c: trace::CallResult) -> Self { + CallResult { + gas_used: c.gas_used, + output: Bytes::new(c.output), + } + } +} + +#[derive(Debug, Serialize)] +pub struct CreateResult { + #[serde(rename="gasUsed")] + gas_used: U256, + code: Bytes, + address: Address, +} + +impl From for CreateResult { + fn from(c: trace::CreateResult) -> Self { + CreateResult { + gas_used: c.gas_used, + code: Bytes::new(c.code), + address: c.address, + } + } +} + +#[derive(Debug, Serialize)] +pub enum Res { + #[serde(rename="call")] + Call(CallResult), + #[serde(rename="create")] + Create(CreateResult), + #[serde(rename="failedCall")] + FailedCall, + #[serde(rename="failedCreate")] + FailedCreate, +} + +impl From for Res { + fn from(t: trace::Res) -> Self { + match t { + trace::Res::Call(call) => Res::Call(CallResult::from(call)), + trace::Res::Create(create) => Res::Create(CreateResult::from(create)), + trace::Res::FailedCall => Res::FailedCall, + trace::Res::FailedCreate => Res::FailedCreate, + } + } +} + +#[derive(Debug, Serialize)] +pub struct Trace { + action: Action, + result: Res, + #[serde(rename="traceAddress")] + trace_address: Vec, + subtraces: U256, + #[serde(rename="transactionPosition")] + transaction_position: U256, + #[serde(rename="transactionHash")] + transaction_hash: H256, + #[serde(rename="blockNumber")] + block_number: U256, + #[serde(rename="blockHash")] + block_hash: H256, +} + +impl From for Trace { + fn from(t: LocalizedTrace) -> Self { + Trace { + action: From::from(t.action), + result: From::from(t.result), + trace_address: t.trace_address.into_iter().map(From::from).collect(), + subtraces: From::from(t.subtraces), + transaction_position: From::from(t.transaction_number), + transaction_hash: t.transaction_hash, + block_number: From::from(t.block_number), + block_hash: t.block_hash, + } + } +} + +#[cfg(test)] +mod tests { + use serde_json; + use util::{U256, H256, Address}; + use v1::types::Bytes; + use super::*; + + #[test] + fn test_trace_serialize() { + let t = Trace { + action: Action::Call(Call { + from: Address::from(4), + to: Address::from(5), + value: U256::from(6), + gas: U256::from(7), + input: Bytes::new(vec![0x12, 0x34]), + }), + result: Res::Call(CallResult { + gas_used: U256::from(8), + output: Bytes::new(vec![0x56, 0x78]), + }), + trace_address: vec![U256::from(10)], + subtraces: U256::from(1), + transaction_position: U256::from(11), + transaction_hash: H256::from(12), + block_number: U256::from(13), + block_hash: H256::from(14), + }; + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","input":"0x1234"}},"result":{"call":{"gasUsed":"0x08","output":"0x5678"}},"traceAddress":["0x0a"],"subtraces":"0x01","transactionPosition":"0x0b","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0x0d","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); + } + + #[test] + fn test_action_serialize() { + let actions = vec![Action::Call(Call { + from: Address::from(1), + to: Address::from(2), + value: U256::from(3), + gas: U256::from(4), + input: Bytes::new(vec![0x12, 0x34]), + }), Action::Create(Create { + from: Address::from(5), + value: U256::from(6), + gas: U256::from(7), + init: Bytes::new(vec![0x56, 0x78]), + })]; + + let serialized = serde_json::to_string(&actions).unwrap(); + assert_eq!(serialized, r#"[{"call":{"from":"0x0000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000002","value":"0x03","gas":"0x04","input":"0x1234"}},{"create":{"from":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","init":"0x5678"}}]"#); + } + + #[test] + fn test_result_serialize() { + let results = vec![ + Res::Call(CallResult { + gas_used: U256::from(1), + output: Bytes::new(vec![0x12, 0x34]), + }), + Res::Create(CreateResult { + gas_used: U256::from(2), + code: Bytes::new(vec![0x45, 0x56]), + address: Address::from(3), + }), + Res::FailedCall, + Res::FailedCreate, + ]; + + let serialized = serde_json::to_string(&results).unwrap(); + assert_eq!(serialized, r#"[{"call":{"gasUsed":"0x01","output":"0x1234"}},{"create":{"gasUsed":"0x02","code":"0x4556","address":"0x0000000000000000000000000000000000000003"}},{"failedCall":[]},{"failedCreate":[]}]"#); + } +} diff --git a/rpc/src/v1/types/trace_filter.rs b/rpc/src/v1/types/trace_filter.rs new file mode 100644 index 000000000..a77a7422c --- /dev/null +++ b/rpc/src/v1/types/trace_filter.rs @@ -0,0 +1,82 @@ +// 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 . + +//! Trace filter deserialization. + +use util::Address; +use ethcore::client::BlockId; +use ethcore::client; +use super::BlockNumber; + +#[derive(Debug, PartialEq, Deserialize)] +pub struct TraceFilter { + #[serde(rename="fromBlock")] + pub from_block: Option, + #[serde(rename="toBlock")] + pub to_block: Option, + #[serde(rename="fromAddress")] + pub from_address: Option>, + #[serde(rename="toAddress")] + pub to_address: Option>, +} + +impl Into for TraceFilter { + fn into(self) -> client::TraceFilter { + let start = self.from_block.map_or(BlockId::Latest, Into::into); + let end = self.to_block.map_or(BlockId::Latest, Into::into); + client::TraceFilter { + range: start..end, + from_address: self.from_address.unwrap_or_else(Vec::new), + to_address: self.to_address.unwrap_or_else(Vec::new), + } + } +} + +#[cfg(test)] +mod tests { + use serde_json; + use util::Address; + use v1::types::{BlockNumber, TraceFilter}; + + #[test] + fn test_empty_trace_filter_deserialize() { + let s = r#"{}"#; + let deserialized: TraceFilter = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized, TraceFilter { + from_block: None, + to_block: None, + from_address: None, + to_address: None + }); + } + + #[test] + fn test_trace_filter_deserialize() { + let s = r#"{ + "fromBlock": "latest", + "toBlock": "latest", + "fromAddress": ["0x0000000000000000000000000000000000000003"], + "toAddress": ["0x0000000000000000000000000000000000000005"] + }"#; + let deserialized: TraceFilter = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized, TraceFilter { + from_block: Some(BlockNumber::Latest), + to_block: Some(BlockNumber::Latest), + from_address: Some(vec![Address::from(3)]), + to_address: Some(vec![Address::from(5)]), + }); + } +}