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

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