Tracedb interface && cli (#997)

* traces cli and jsonrpc api

* missing if in docs

* adding traces to modules
This commit is contained in:
Marek Kotewicz 2016-05-02 12:17:30 +02:00 committed by Gav Wood
parent e22e4b9b8b
commit 7c2adc4137
15 changed files with 572 additions and 10 deletions

View File

@ -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<V> Client<V> 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<V> Client<V> 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<V> BlockChainClient for Client<V> where V: Verifier {
.collect()
}
fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>> {
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<LocalizedTrace> {
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<Vec<LocalizedTrace>> {
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<Vec<LocalizedTrace>> {
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())
}

View File

@ -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<usize>,
}
/// Uniquely identifies Uncle.
pub struct UncleId (
/// Block id.

View File

@ -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<Vec<LocalizedTrace>>;
/// Returns trace with given id.
fn trace(&self, trace: TraceId) -> Option<LocalizedTrace>;
/// Returns traces created by transaction.
fn transaction_traces(&self, trace: TransactionId) -> Option<Vec<LocalizedTrace>>;
/// Returns traces created by transaction from block.
fn block_traces(&self, trace: BlockId) -> Option<Vec<LocalizedTrace>>;
/// Get last hashes starting from best block.
fn last_hashes(&self) -> LastHashes;
}

View File

@ -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<Vec<LocalizedTrace>> {
unimplemented!();
}
fn trace(&self, _trace: TraceId) -> Option<LocalizedTrace> {
unimplemented!();
}
fn transaction_traces(&self, _trace: TransactionId) -> Option<Vec<LocalizedTrace>> {
unimplemented!();
}
fn block_traces(&self, _trace: BlockId) -> Option<Vec<LocalizedTrace>> {
unimplemented!();
}
}

View File

@ -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<String>,
pub flag_network_id: Option<String>,
pub flag_pruning: String,
pub flag_tracing: String,
pub flag_port: u16,
pub flag_peers: usize,
pub flag_no_discovery: bool,

View File

@ -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,

View File

@ -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);
},

View File

@ -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;

View File

@ -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 <http://www.gnu.org/licenses/>.
//! 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<C> where C: BlockChainClient {
client: Weak<C>,
}
impl<C> TracesClient<C> where C: BlockChainClient {
/// Creates new Traces client.
pub fn new(client: &Arc<C>) -> Self {
TracesClient {
client: Arc::downgrade(client),
}
}
}
impl<C> Traces for TracesClient<C> where C: BlockChainClient + 'static {
fn filter(&self, params: Params) -> Result<Value, Error> {
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<Value, Error> {
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<Value, Error> {
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<Value, Error> {
from_params::<(H256, Vec<Index>)>(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)
})
}
}

View File

@ -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::*;

View File

@ -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;

View File

@ -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 <http://www.gnu.org/licenses/>.
//! 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<Value, Error> { rpc_unimplemented!() }
/// Returns transaction trace at given index.
fn trace(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
/// Returns all traces of given transaction.
fn transaction_traces(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
/// Returns all traces produced at given block.
fn block_traces(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
/// Should be used to convert object to io delegate.
fn to_delegate(self) -> IoDelegate<Self> {
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
}
}

View File

@ -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;

238
rpc/src/v1/types/trace.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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<trace::Create> 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<trace::Call> 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<trace::Action> 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<trace::CallResult> 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<trace::CreateResult> 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<trace::Res> 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<U256>,
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<LocalizedTrace> 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":[]}]"#);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
//! 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<BlockNumber>,
#[serde(rename="toBlock")]
pub to_block: Option<BlockNumber>,
#[serde(rename="fromAddress")]
pub from_address: Option<Vec<Address>>,
#[serde(rename="toAddress")]
pub to_address: Option<Vec<Address>>,
}
impl Into<client::TraceFilter> 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)]),
});
}
}