Various improvements to tracing & diagnostics. (#1707)

* Various improvements to tracing & diagnostics.

- Manage possibility of `Account` not having code for `PodAccount`
- New RPC: `trace_sendRawTransaction`
- See raw transaction dump when inspecting over RPC

* Fix test

* Remove one of the dupe error messages

* Remove unneeded `&`s

* Reformat and extremely minor optimisation

* Minor optimisation

* Remove unneeded let

* Fix tests.

* Additional fix.

* Minor rename.

[ci:skip]

* Bowing to the pressure.
This commit is contained in:
Gav Wood
2016-07-26 16:48:50 +02:00
committed by GitHub
parent 53a975d1dc
commit 7cf807d1b4
9 changed files with 111 additions and 44 deletions

View File

@@ -18,14 +18,13 @@
use std::sync::{Weak, Arc};
use jsonrpc_core::*;
use std::collections::BTreeMap;
//use util::H256;
use util::rlp::{UntrustedRlp, View};
use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId};
use ethcore::miner::MinerService;
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
use v1::traits::Traces;
use v1::helpers::CallRequest as CRequest;
use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest, Bytes, StateDiff, VMTrace, H256};
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256};
/// Traces api implementation.
pub struct TracesClient<C, M> where C: BlockChainClient, M: MinerService {
@@ -126,22 +125,31 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M:
state_diffing: flags.contains(&("stateDiff".to_owned())),
};
let signed = try!(self.sign_call(request));
let r = take_weak!(self.client).call(&signed, analytics);
if let Ok(executed) = r {
// TODO maybe add other stuff to this?
let mut ret = map!["output".to_owned() => to_value(&Bytes(executed.output)).unwrap()];
if let Some(trace) = executed.trace {
ret.insert("trace".to_owned(), to_value(&Trace::from(trace)).unwrap());
}
if let Some(vm_trace) = executed.vm_trace {
ret.insert("vmTrace".to_owned(), to_value(&VMTrace::from(vm_trace)).unwrap());
}
if let Some(state_diff) = executed.state_diff {
ret.insert("stateDiff".to_owned(), to_value(&StateDiff::from(state_diff)).unwrap());
}
return Ok(Value::Object(ret))
match take_weak!(self.client).call(&signed, analytics) {
Ok(e) => to_value(&TraceResults::from(e)),
_ => Ok(Value::Null),
}
})
}
fn raw_transaction(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
trace!(target: "jsonrpc", "call: {:?}", params);
from_params::<(Bytes, Vec<String>)>(params)
.and_then(|(raw_transaction, flags)| {
let raw_transaction = raw_transaction.to_vec();
let analytics = CallAnalytics {
transaction_tracing: flags.contains(&("trace".to_owned())),
vm_tracing: flags.contains(&("vmTrace".to_owned())),
state_diffing: flags.contains(&("stateDiff".to_owned())),
};
match UntrustedRlp::new(&raw_transaction).as_val() {
Ok(signed) => match take_weak!(self.client).call(&signed, analytics) {
Ok(e) => to_value(&TraceResults::from(e)),
_ => Ok(Value::Null),
},
Err(_) => Err(Error::invalid_params()),
}
Ok(Value::Null)
})
}
}

View File

@@ -367,7 +367,7 @@ fn rpc_eth_pending_transaction_by_hash() {
tester.miner.pending_transactions.lock().insert(H256::zero(), tx);
}
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#;
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionByHash",

View File

@@ -35,6 +35,9 @@ pub trait Traces: Sized + Send + Sync + 'static {
/// Executes the given call and returns a number of possible traces for it.
fn call(&self, _: Params) -> Result<Value, Error>;
/// Executes the given raw transaction and returns a number of possible traces for it.
fn raw_transaction(&self, _: Params) -> Result<Value, Error>;
/// Should be used to convert object to io delegate.
fn to_delegate(self) -> IoDelegate<Self> {
let mut delegate = IoDelegate::new(Arc::new(self));
@@ -43,6 +46,7 @@ pub trait Traces: Sized + Send + Sync + 'static {
delegate.add_method("trace_transaction", Traces::transaction_traces);
delegate.add_method("trace_block", Traces::block_traces);
delegate.add_method("trace_call", Traces::call);
delegate.add_method("trace_rawTransaction", Traces::raw_transaction);
delegate
}

View File

@@ -103,7 +103,7 @@ mod tests {
fn test_serialize_block_transactions() {
let t = BlockTransactions::Full(vec![Transaction::default()]);
let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}]"#);
assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null,"raw":"0x"}]"#);
let t = BlockTransactions::Hashes(vec![H256::default().into()]);
let serialized = serde_json::to_string(&t).unwrap();

View File

@@ -42,6 +42,6 @@ pub use self::transaction::Transaction;
pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification};
pub use self::call_request::CallRequest;
pub use self::receipt::Receipt;
pub use self::trace::{Trace, LocalizedTrace, StateDiff, VMTrace};
pub use self::trace::{LocalizedTrace, TraceResults};
pub use self::trace_filter::TraceFilter;
pub use self::uint::U256;

View File

@@ -21,6 +21,7 @@ use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace};
use ethcore::trace as et;
use ethcore::state_diff;
use ethcore::account_diff;
use ethcore::client::Executed;
use util::Uint;
use v1::types::{Bytes, H160, H256, U256};
@@ -193,6 +194,7 @@ impl From<account_diff::AccountDiff> for AccountDiff {
}
}
#[derive(Debug)]
/// Serde-friendly `StateDiff` shadow.
pub struct StateDiff(BTreeMap<H160, AccountDiff>);
@@ -444,6 +446,32 @@ impl From<EthTrace> for Trace {
}
}
#[derive(Debug, Serialize)]
/// A diff of some chunk of memory.
pub struct TraceResults {
/// The output of the call/create
pub output: Vec<u8>,
/// The transaction trace.
pub trace: Option<Trace>,
/// The transaction trace.
#[serde(rename="vmTrace")]
pub vm_trace: Option<VMTrace>,
/// The transaction trace.
#[serde(rename="stateDiff")]
pub state_diff: Option<StateDiff>,
}
impl From<Executed> for TraceResults {
fn from(t: Executed) -> Self {
TraceResults {
output: t.output.into(),
trace: t.trace.map(Into::into),
vm_trace: t.vm_trace.map(Into::into),
state_diff: t.state_diff.map(Into::into),
}
}
}
#[cfg(test)]
mod tests {
use serde_json;
@@ -451,6 +479,18 @@ mod tests {
use v1::types::{Bytes, U256, H256, H160};
use super::*;
#[test]
fn should_serialize_trace_results() {
let r = TraceResults {
output: vec![0x60],
trace: None,
vm_trace: None,
state_diff: None,
};
let serialized = serde_json::to_string(&r).unwrap();
assert_eq!(serialized, r#"{"output":[96],"trace":null,"vmTrace":null,"stateDiff":null}"#);
}
#[test]
fn test_trace_serialize() {
let t = LocalizedTrace {

View File

@@ -14,6 +14,7 @@
// 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::rlp::encode;
use ethcore::contract_address;
use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction};
use v1::types::{Bytes, H160, H256, U256};
@@ -49,6 +50,8 @@ pub struct Transaction {
pub input: Bytes,
/// Creates contract
pub creates: Option<H160>,
/// Raw transaction data
pub raw: Bytes,
}
impl From<LocalizedTransaction> for Transaction {
@@ -72,6 +75,7 @@ impl From<LocalizedTransaction> for Transaction {
Action::Create => Some(contract_address(&t.sender().unwrap(), &t.nonce).into()),
Action::Call(_) => None,
},
raw: encode(&t.signed).to_vec().into(),
}
}
}
@@ -97,6 +101,7 @@ impl From<SignedTransaction> for Transaction {
Action::Create => Some(contract_address(&t.sender().unwrap(), &t.nonce).into()),
Action::Call(_) => None,
},
raw: encode(&t).to_vec().into(),
}
}
}
@@ -110,7 +115,7 @@ mod tests {
fn test_transaction_serialize() {
let t = Transaction::default();
let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}"#);
assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null,"raw":"0x"}"#);
}
}