feat: port over tracing from rust-web3 (#93)
This commit is contained in:
parent
98c63f9a3d
commit
c30f45fc72
|
@ -30,3 +30,6 @@ pub use signature::*;
|
||||||
|
|
||||||
mod txpool;
|
mod txpool;
|
||||||
pub use txpool::*;
|
pub use txpool::*;
|
||||||
|
|
||||||
|
mod trace;
|
||||||
|
pub use trace::*;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,68 @@
|
||||||
|
r#"[{
|
||||||
|
"output": "0x",
|
||||||
|
"stateDiff": {
|
||||||
|
"0x5df9b87991262f6ba471f09758cde1c0fc1de734": {
|
||||||
|
"balance": {
|
||||||
|
"+": "0x7a69"
|
||||||
|
},
|
||||||
|
"code": {
|
||||||
|
"+": "0x"
|
||||||
|
},
|
||||||
|
"nonce": {
|
||||||
|
"+": "0x0"
|
||||||
|
},
|
||||||
|
"storage": {}
|
||||||
|
},
|
||||||
|
"0xa1e4380a3b1f749673e270229993ee55f35663b4": {
|
||||||
|
"balance": {
|
||||||
|
"*": {
|
||||||
|
"from": "0x6c6b935b8bbd400000",
|
||||||
|
"to": "0x6c5d01021be7168597"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"code": "=",
|
||||||
|
"nonce": {
|
||||||
|
"*": {
|
||||||
|
"from": "0x0",
|
||||||
|
"to": "0x1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"storage": {}
|
||||||
|
},
|
||||||
|
"0xe6a7a1d47ff21b6321162aea7c6cb457d5476bca": {
|
||||||
|
"balance": {
|
||||||
|
"*": {
|
||||||
|
"from": "0xf3426785a8ab466000",
|
||||||
|
"to": "0xf350f9df18816f6000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"code": "=",
|
||||||
|
"nonce": "=",
|
||||||
|
"storage": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"trace": [
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"callType": "call",
|
||||||
|
"from": "0xa1e4380a3b1f749673e270229993ee55f35663b4",
|
||||||
|
"gas": "0x0",
|
||||||
|
"input": "0x",
|
||||||
|
"to": "0x5df9b87991262f6ba471f09758cde1c0fc1de734",
|
||||||
|
"value": "0x7a69"
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"gasUsed": "0x0",
|
||||||
|
"output": "0x"
|
||||||
|
},
|
||||||
|
"subtraces": 0,
|
||||||
|
"traceAddress": [],
|
||||||
|
"type": "call"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transactionHash": "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060",
|
||||||
|
"vmTrace": {
|
||||||
|
"code": "0x",
|
||||||
|
"ops": []
|
||||||
|
}
|
||||||
|
}]"#
|
|
@ -0,0 +1,360 @@
|
||||||
|
//! Types for the Parity Transaction-Trace Filtering API
|
||||||
|
use crate::types::{Address, BlockNumber, Bytes, H160, H256, U256};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Trace filter
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct TraceFilter {
|
||||||
|
/// From block
|
||||||
|
#[serde(rename = "fromBlock", skip_serializing_if = "Option::is_none")]
|
||||||
|
from_block: Option<BlockNumber>,
|
||||||
|
/// To block
|
||||||
|
#[serde(rename = "toBlock", skip_serializing_if = "Option::is_none")]
|
||||||
|
to_block: Option<BlockNumber>,
|
||||||
|
/// From address
|
||||||
|
#[serde(rename = "fromAddress", skip_serializing_if = "Option::is_none")]
|
||||||
|
from_address: Option<Vec<Address>>,
|
||||||
|
/// To address
|
||||||
|
#[serde(rename = "toAddress", skip_serializing_if = "Option::is_none")]
|
||||||
|
to_address: Option<Vec<Address>>,
|
||||||
|
/// Output offset
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
after: Option<usize>,
|
||||||
|
/// Output amount
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
count: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TraceFilter {
|
||||||
|
/// Sets From block
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
pub fn from_block<T: Into<BlockNumber>>(mut self, block: T) -> Self {
|
||||||
|
self.from_block = Some(block.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets to block
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
pub fn to_block<T: Into<BlockNumber>>(mut self, block: T) -> Self {
|
||||||
|
self.to_block = Some(block.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets to address
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
pub fn to_address(mut self, address: Vec<H160>) -> Self {
|
||||||
|
self.to_address = Some(address);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets from address
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
pub fn from_address(mut self, address: Vec<H160>) -> Self {
|
||||||
|
self.from_address = Some(address);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets after offset
|
||||||
|
pub fn after(mut self, after: usize) -> Self {
|
||||||
|
self.after = Some(after);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets amount of traces to display
|
||||||
|
pub fn count(mut self, count: usize) -> Self {
|
||||||
|
self.count = Some(count);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `LocalizedTrace` in Parity
|
||||||
|
/// Trace-Filtering API trace type
|
||||||
|
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct Trace {
|
||||||
|
/// Action
|
||||||
|
pub action: Action,
|
||||||
|
/// Result
|
||||||
|
pub result: Option<Res>,
|
||||||
|
/// Trace address
|
||||||
|
#[serde(rename = "traceAddress")]
|
||||||
|
pub trace_address: Vec<usize>,
|
||||||
|
/// Subtraces
|
||||||
|
pub subtraces: usize,
|
||||||
|
/// Transaction position
|
||||||
|
#[serde(rename = "transactionPosition")]
|
||||||
|
pub transaction_position: Option<usize>,
|
||||||
|
/// Transaction hash
|
||||||
|
#[serde(rename = "transactionHash")]
|
||||||
|
pub transaction_hash: Option<H256>,
|
||||||
|
/// Block Number
|
||||||
|
#[serde(rename = "blockNumber")]
|
||||||
|
pub block_number: u64,
|
||||||
|
/// Block Hash
|
||||||
|
#[serde(rename = "blockHash")]
|
||||||
|
pub block_hash: H256,
|
||||||
|
/// Action Type
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub action_type: ActionType,
|
||||||
|
/// Error
|
||||||
|
pub error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Response
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum Res {
|
||||||
|
/// Call
|
||||||
|
Call(CallResult),
|
||||||
|
/// Create
|
||||||
|
Create(CreateResult),
|
||||||
|
/// None
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Res {
|
||||||
|
fn default() -> Res {
|
||||||
|
Res::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Action
|
||||||
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged, rename_all = "lowercase")]
|
||||||
|
pub enum Action {
|
||||||
|
/// Call
|
||||||
|
Call(Call),
|
||||||
|
/// Create
|
||||||
|
Create(Create),
|
||||||
|
/// Suicide
|
||||||
|
Suicide(Suicide),
|
||||||
|
/// Reward
|
||||||
|
Reward(Reward),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An external action type.
|
||||||
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum ActionType {
|
||||||
|
/// Contract call.
|
||||||
|
Call,
|
||||||
|
/// Contract creation.
|
||||||
|
Create,
|
||||||
|
/// Contract suicide.
|
||||||
|
Suicide,
|
||||||
|
/// A block reward.
|
||||||
|
Reward,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call Result
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
|
||||||
|
pub struct CallResult {
|
||||||
|
/// Gas used
|
||||||
|
#[serde(rename = "gasUsed")]
|
||||||
|
pub gas_used: U256,
|
||||||
|
/// Output bytes
|
||||||
|
pub output: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Craete Result
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
|
||||||
|
pub struct CreateResult {
|
||||||
|
/// Gas used
|
||||||
|
#[serde(rename = "gasUsed")]
|
||||||
|
pub gas_used: U256,
|
||||||
|
/// Code
|
||||||
|
pub code: Bytes,
|
||||||
|
/// Assigned address
|
||||||
|
pub address: Address,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call response
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
|
||||||
|
pub struct Call {
|
||||||
|
/// Sender
|
||||||
|
pub from: Address,
|
||||||
|
/// Recipient
|
||||||
|
pub to: Address,
|
||||||
|
/// Transfered Value
|
||||||
|
pub value: U256,
|
||||||
|
/// Gas
|
||||||
|
pub gas: U256,
|
||||||
|
/// Input data
|
||||||
|
pub input: Bytes,
|
||||||
|
/// The type of the call.
|
||||||
|
#[serde(rename = "callType")]
|
||||||
|
pub call_type: CallType,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call type.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub enum CallType {
|
||||||
|
/// None
|
||||||
|
#[serde(rename = "none")]
|
||||||
|
None,
|
||||||
|
/// Call
|
||||||
|
#[serde(rename = "call")]
|
||||||
|
Call,
|
||||||
|
/// Call code
|
||||||
|
#[serde(rename = "callcode")]
|
||||||
|
CallCode,
|
||||||
|
/// Delegate call
|
||||||
|
#[serde(rename = "delegatecall")]
|
||||||
|
DelegateCall,
|
||||||
|
/// Static call
|
||||||
|
#[serde(rename = "staticcall")]
|
||||||
|
StaticCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CallType {
|
||||||
|
fn default() -> CallType {
|
||||||
|
CallType::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create response
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
|
||||||
|
pub struct Create {
|
||||||
|
/// Sender
|
||||||
|
pub from: Address,
|
||||||
|
/// Value
|
||||||
|
pub value: U256,
|
||||||
|
/// Gas
|
||||||
|
pub gas: U256,
|
||||||
|
/// Initialization code
|
||||||
|
pub init: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Suicide
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
|
||||||
|
pub struct Suicide {
|
||||||
|
/// Address.
|
||||||
|
pub address: Address,
|
||||||
|
/// Refund address.
|
||||||
|
#[serde(rename = "refundAddress")]
|
||||||
|
pub refund_address: Address,
|
||||||
|
/// Balance.
|
||||||
|
pub balance: U256,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reward action
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct Reward {
|
||||||
|
/// Author's address.
|
||||||
|
pub author: Address,
|
||||||
|
/// Reward amount.
|
||||||
|
pub value: U256,
|
||||||
|
/// Reward type.
|
||||||
|
#[serde(rename = "rewardType")]
|
||||||
|
pub reward_type: RewardType,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reward type.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub enum RewardType {
|
||||||
|
/// Block
|
||||||
|
#[serde(rename = "block")]
|
||||||
|
Block,
|
||||||
|
/// Uncle
|
||||||
|
#[serde(rename = "uncle")]
|
||||||
|
Uncle,
|
||||||
|
/// EmptyStep (AuthorityRound)
|
||||||
|
#[serde(rename = "emptyStep")]
|
||||||
|
EmptyStep,
|
||||||
|
/// External (attributed as part of an external protocol)
|
||||||
|
#[serde(rename = "external")]
|
||||||
|
External,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const EXAMPLE_TRACE_CALL: &str = r#"{
|
||||||
|
"action": {
|
||||||
|
"callType": "call",
|
||||||
|
"from": "0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb",
|
||||||
|
"gas": "0x63ab9",
|
||||||
|
"input": "0xb9f256cd000000000000000000000000fb6916095ca1df60bb79ce92ce3ea74c37c5d3590000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000e85468697320697320746865206f6666696369616c20457468657265756d20466f756e646174696f6e20546970204a61722e20466f722065766572792061626f76652061206365727461696e2076616c756520646f6e6174696f6e207765276c6c2063726561746520616e642073656e6420746f20796f752061206272616e64206e657720556e69636f726e20546f6b656e2028f09fa684292e20436865636b2074686520756e69636f726e2070726963652062656c6f77202831206574686572203d20313030302066696e6e6579292e205468616e6b7320666f722074686520737570706f72742100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"to": "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359",
|
||||||
|
"value": "0x0"
|
||||||
|
},
|
||||||
|
"blockHash": "0x6474a53a9ebf72d306a1406ec12ded12e210b6c3141b4373bfb3a3cea987dfb8",
|
||||||
|
"blockNumber": 988775,
|
||||||
|
"result": {
|
||||||
|
"gasUsed": "0x4b419",
|
||||||
|
"output": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
},
|
||||||
|
"subtraces": 1,
|
||||||
|
"traceAddress": [],
|
||||||
|
"transactionHash": "0x342c284238149db221f9d87db87f90ffad7ac0aac57c0c480142f4c21b63f652",
|
||||||
|
"transactionPosition": 1,
|
||||||
|
"type": "call"
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
const EXAMPLE_TRACE_CREATE: &str = r#"{
|
||||||
|
"action": {
|
||||||
|
"from": "0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb",
|
||||||
|
"gas": "0x63ab9",
|
||||||
|
"init": "0xb9f256cd000000000000000000000000fb6916095ca1df60bb79ce92ce3ea74c37c5d3590000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000e85468697320697320746865206f6666696369616c20457468657265756d20466f756e646174696f6e20546970204a61722e20466f722065766572792061626f76652061206365727461696e2076616c756520646f6e6174696f6e207765276c6c2063726561746520616e642073656e6420746f20796f752061206272616e64206e657720556e69636f726e20546f6b656e2028f09fa684292e20436865636b2074686520756e69636f726e2070726963652062656c6f77202831206574686572203d20313030302066696e6e6579292e205468616e6b7320666f722074686520737570706f72742100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"value": "0x0"
|
||||||
|
},
|
||||||
|
"blockHash": "0x6474a53a9ebf72d306a1406ec12ded12e210b6c3141b4373bfb3a3cea987dfb8",
|
||||||
|
"blockNumber": 988775,
|
||||||
|
"result": {
|
||||||
|
"gasUsed": "0x4b419",
|
||||||
|
"output": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
},
|
||||||
|
"subtraces": 1,
|
||||||
|
"traceAddress": [],
|
||||||
|
"transactionHash": "0x342c284238149db221f9d87db87f90ffad7ac0aac57c0c480142f4c21b63f652",
|
||||||
|
"transactionPosition": 1,
|
||||||
|
"type": "create"
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
const EXAMPLE_TRACE_SUICIDE: &str = r#"{
|
||||||
|
"action": {
|
||||||
|
"address": "0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb",
|
||||||
|
"refundAddress": "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359",
|
||||||
|
"balance": "0x0"
|
||||||
|
},
|
||||||
|
"blockHash": "0x6474a53a9ebf72d306a1406ec12ded12e210b6c3141b4373bfb3a3cea987dfb8",
|
||||||
|
"blockNumber": 988775,
|
||||||
|
"result": {
|
||||||
|
"gasUsed": "0x4b419",
|
||||||
|
"output": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
},
|
||||||
|
"subtraces": 1,
|
||||||
|
"traceAddress": [],
|
||||||
|
"transactionHash": "0x342c284238149db221f9d87db87f90ffad7ac0aac57c0c480142f4c21b63f652",
|
||||||
|
"transactionPosition": 1,
|
||||||
|
"type": "suicide"
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
const EXAMPLE_TRACE_REWARD: &str = r#"{
|
||||||
|
"action": {
|
||||||
|
"author": "0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb",
|
||||||
|
"value": "0x0",
|
||||||
|
"rewardType": "block"
|
||||||
|
},
|
||||||
|
"blockHash": "0x6474a53a9ebf72d306a1406ec12ded12e210b6c3141b4373bfb3a3cea987dfb8",
|
||||||
|
"blockNumber": 988775,
|
||||||
|
"result": {
|
||||||
|
"gasUsed": "0x4b419",
|
||||||
|
"output": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
},
|
||||||
|
"subtraces": 1,
|
||||||
|
"traceAddress": [],
|
||||||
|
"transactionHash": "0x342c284238149db221f9d87db87f90ffad7ac0aac57c0c480142f4c21b63f652",
|
||||||
|
"transactionPosition": 1,
|
||||||
|
"type": "reward"
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialize_trace() {
|
||||||
|
let _trace: Trace = serde_json::from_str(EXAMPLE_TRACE_CALL).unwrap();
|
||||||
|
let _trace: Trace = serde_json::from_str(EXAMPLE_TRACE_CREATE).unwrap();
|
||||||
|
let _trace: Trace = serde_json::from_str(EXAMPLE_TRACE_SUICIDE).unwrap();
|
||||||
|
let _trace: Trace = serde_json::from_str(EXAMPLE_TRACE_REWARD).unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
//! Types for the Parity Ad-Hoc Trace API
|
||||||
|
//!
|
||||||
|
//! https://openethereum.github.io/wiki/JSONRPC-trace-module
|
||||||
|
use crate::types::{Bytes, H160, H256, U256};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
mod filter;
|
||||||
|
pub use filter::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
/// Description of the type of trace to make
|
||||||
|
pub enum TraceType {
|
||||||
|
/// Transaction Trace
|
||||||
|
#[serde(rename = "trace")]
|
||||||
|
Trace,
|
||||||
|
/// Virtual Machine Execution Trace
|
||||||
|
#[serde(rename = "vmTrace")]
|
||||||
|
VmTrace,
|
||||||
|
/// State Difference
|
||||||
|
#[serde(rename = "stateDiff")]
|
||||||
|
StateDiff,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||||
|
/// Ad-Hoc trace API type
|
||||||
|
pub struct BlockTrace {
|
||||||
|
/// Output Bytes
|
||||||
|
pub output: Bytes,
|
||||||
|
/// Transaction Trace
|
||||||
|
pub trace: Option<Vec<TransactionTrace>>,
|
||||||
|
/// Virtual Machine Execution Trace
|
||||||
|
#[serde(rename = "vmTrace")]
|
||||||
|
pub vm_trace: Option<VMTrace>,
|
||||||
|
/// State Difference
|
||||||
|
#[serde(rename = "stateDiff")]
|
||||||
|
pub state_diff: Option<StateDiff>,
|
||||||
|
/// Transaction Hash
|
||||||
|
#[serde(rename = "transactionHash")]
|
||||||
|
pub transaction_hash: Option<H256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------- State Diff ----------------
|
||||||
|
/// Aux type for Diff::Changed.
|
||||||
|
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct ChangedType<T> {
|
||||||
|
/// Previous value.
|
||||||
|
pub from: T,
|
||||||
|
/// Current value.
|
||||||
|
pub to: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serde-friendly `Diff` shadow.
|
||||||
|
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||||
|
pub enum Diff<T> {
|
||||||
|
/// No change.
|
||||||
|
#[serde(rename = "=")]
|
||||||
|
Same,
|
||||||
|
/// A new value has been set.
|
||||||
|
#[serde(rename = "+")]
|
||||||
|
Born(T),
|
||||||
|
/// A value has been removed.
|
||||||
|
#[serde(rename = "-")]
|
||||||
|
Died(T),
|
||||||
|
/// Value changed.
|
||||||
|
#[serde(rename = "*")]
|
||||||
|
Changed(ChangedType<T>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serde-friendly `AccountDiff` shadow.
|
||||||
|
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct AccountDiff {
|
||||||
|
/// Account balance.
|
||||||
|
pub balance: Diff<U256>,
|
||||||
|
/// Account nonce.
|
||||||
|
pub nonce: Diff<U256>,
|
||||||
|
/// Account code.
|
||||||
|
pub code: Diff<Bytes>,
|
||||||
|
/// Account storage.
|
||||||
|
pub storage: BTreeMap<H256, Diff<H256>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serde-friendly `StateDiff` shadow.
|
||||||
|
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct StateDiff(pub BTreeMap<H160, AccountDiff>);
|
||||||
|
|
||||||
|
// ------------------ Trace -------------
|
||||||
|
/// Trace
|
||||||
|
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct TransactionTrace {
|
||||||
|
/// Trace address
|
||||||
|
#[serde(rename = "traceAddress")]
|
||||||
|
pub trace_address: Vec<usize>,
|
||||||
|
/// Subtraces
|
||||||
|
pub subtraces: usize,
|
||||||
|
/// Action
|
||||||
|
pub action: Action,
|
||||||
|
/// Action Type
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub action_type: ActionType,
|
||||||
|
/// Result
|
||||||
|
pub result: Option<Res>,
|
||||||
|
/// Error
|
||||||
|
pub error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------- VmTrace ------------------------------
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
|
||||||
|
/// A record of a full VM trace for a CALL/CREATE.
|
||||||
|
pub struct VMTrace {
|
||||||
|
/// The code to be executed.
|
||||||
|
pub code: Bytes,
|
||||||
|
/// The operations executed.
|
||||||
|
pub ops: Vec<VMOperation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
|
||||||
|
/// A record of the execution of a single VM operation.
|
||||||
|
pub struct VMOperation {
|
||||||
|
/// The program counter.
|
||||||
|
pub pc: usize,
|
||||||
|
/// The gas cost for this instruction.
|
||||||
|
pub cost: u64,
|
||||||
|
/// Information concerning the execution of the operation.
|
||||||
|
pub ex: Option<VMExecutedOperation>,
|
||||||
|
/// Subordinate trace of the CALL/CREATE if applicable.
|
||||||
|
// #[serde(bound="VMTrace: Deserialize")]
|
||||||
|
pub sub: Option<VMTrace>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
|
||||||
|
/// A record of an executed VM operation.
|
||||||
|
pub struct VMExecutedOperation {
|
||||||
|
/// The total gas used.
|
||||||
|
#[serde(rename = "used")]
|
||||||
|
pub used: u64,
|
||||||
|
/// The stack item placed, if any.
|
||||||
|
pub push: Vec<U256>,
|
||||||
|
/// If altered, the memory delta.
|
||||||
|
#[serde(rename = "mem")]
|
||||||
|
pub mem: Option<MemoryDiff>,
|
||||||
|
/// The altered storage value, if any.
|
||||||
|
#[serde(rename = "store")]
|
||||||
|
pub store: Option<StorageDiff>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
|
||||||
|
/// A diff of some chunk of memory.
|
||||||
|
pub struct MemoryDiff {
|
||||||
|
/// Offset into memory the change begins.
|
||||||
|
pub off: usize,
|
||||||
|
/// The changed data.
|
||||||
|
pub data: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Deserialize, Serialize)]
|
||||||
|
/// A diff of some storage value.
|
||||||
|
pub struct StorageDiff {
|
||||||
|
/// Which key in storage is changed.
|
||||||
|
pub key: U256,
|
||||||
|
/// What the value has been changed to.
|
||||||
|
pub val: U256,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// tx: https://etherscan.io/tx/0x4a91b11dbd2b11c308cfe7775eac2036f20c501691e3f8005d83b2dcce62d6b5
|
||||||
|
// using the 'trace_replayTransaction' API function
|
||||||
|
// with 'trace', 'vmTrace', 'stateDiff'
|
||||||
|
const EXAMPLE_TRACE: &str = include!("./example-trace-str.rs");
|
||||||
|
|
||||||
|
// block: https://etherscan.io/block/46147
|
||||||
|
// using the 'trace_replayBlockTransactions' API function
|
||||||
|
// with 'trace', 'vmTrace', 'stateDiff'
|
||||||
|
const EXAMPLE_TRACES: &str = include!("./example-traces-str.rs");
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serialize_trace_type() {
|
||||||
|
let trace_type_str = r#"["trace","vmTrace","stateDiff"]"#;
|
||||||
|
let trace_type = vec![TraceType::Trace, TraceType::VmTrace, TraceType::StateDiff];
|
||||||
|
|
||||||
|
let se_trace_str: String = serde_json::to_string(&trace_type).unwrap();
|
||||||
|
assert_eq!(trace_type_str, se_trace_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialize_blocktrace() {
|
||||||
|
let _trace: BlockTrace = serde_json::from_str(EXAMPLE_TRACE).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialize_blocktraces() {
|
||||||
|
let _traces: Vec<BlockTrace> = serde_json::from_str(EXAMPLE_TRACES).unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -358,6 +358,8 @@ pub trait Middleware: Sync + Send + Debug {
|
||||||
self.inner().pending_transaction(tx_hash)
|
self.inner().pending_transaction(tx_hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mempool inspection for Geth's API
|
||||||
|
|
||||||
async fn txpool_content(&self) -> Result<TxpoolContent, Self::Error> {
|
async fn txpool_content(&self) -> Result<TxpoolContent, Self::Error> {
|
||||||
self.inner().txpool_content().await.map_err(FromErr::from)
|
self.inner().txpool_content().await.map_err(FromErr::from)
|
||||||
}
|
}
|
||||||
|
@ -369,4 +371,88 @@ pub trait Middleware: Sync + Send + Debug {
|
||||||
async fn txpool_status(&self) -> Result<TxpoolStatus, Self::Error> {
|
async fn txpool_status(&self) -> Result<TxpoolStatus, Self::Error> {
|
||||||
self.inner().txpool_status().await.map_err(FromErr::from)
|
self.inner().txpool_status().await.map_err(FromErr::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parity `trace` support
|
||||||
|
|
||||||
|
/// Executes the given call and returns a number of possible traces for it
|
||||||
|
async fn trace_call(
|
||||||
|
&self,
|
||||||
|
req: TransactionRequest,
|
||||||
|
trace_type: Vec<TraceType>,
|
||||||
|
block: Option<BlockNumber>,
|
||||||
|
) -> Result<BlockTrace, ProviderError> {
|
||||||
|
self.inner()
|
||||||
|
.trace_call(req, trace_type, block)
|
||||||
|
.await
|
||||||
|
.map_err(FromErr::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Traces a call to `eth_sendRawTransaction` without making the call, returning the traces
|
||||||
|
async fn trace_raw_transaction(
|
||||||
|
&self,
|
||||||
|
data: Bytes,
|
||||||
|
trace_type: Vec<TraceType>,
|
||||||
|
) -> Result<BlockTrace, ProviderError> {
|
||||||
|
self.inner()
|
||||||
|
.trace_raw_transaction(data, trace_type)
|
||||||
|
.await
|
||||||
|
.map_err(FromErr::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replays a transaction, returning the traces
|
||||||
|
async fn trace_replay_transaction(
|
||||||
|
&self,
|
||||||
|
hash: H256,
|
||||||
|
trace_type: Vec<TraceType>,
|
||||||
|
) -> Result<BlockTrace, ProviderError> {
|
||||||
|
self.inner()
|
||||||
|
.trace_replay_transaction(hash, trace_type)
|
||||||
|
.await
|
||||||
|
.map_err(FromErr::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replays all transactions in a block returning the requested traces for each transaction
|
||||||
|
async fn trace_replay_block_transactions(
|
||||||
|
&self,
|
||||||
|
block: BlockNumber,
|
||||||
|
trace_type: Vec<TraceType>,
|
||||||
|
) -> Result<Vec<BlockTrace>, ProviderError> {
|
||||||
|
self.inner()
|
||||||
|
.trace_replay_block_transactions(block, trace_type)
|
||||||
|
.await
|
||||||
|
.map_err(FromErr::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns traces created at given block
|
||||||
|
async fn trace_block(&self, block: BlockNumber) -> Result<Vec<Trace>, ProviderError> {
|
||||||
|
self.inner().trace_block(block).await.map_err(FromErr::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return traces matching the given filter
|
||||||
|
async fn trace_filter(&self, filter: TraceFilter) -> Result<Vec<Trace>, ProviderError> {
|
||||||
|
self.inner()
|
||||||
|
.trace_filter(filter)
|
||||||
|
.await
|
||||||
|
.map_err(FromErr::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns trace at the given position
|
||||||
|
async fn trace_get<T: Into<U64> + Send + Sync>(
|
||||||
|
&self,
|
||||||
|
hash: H256,
|
||||||
|
index: Vec<T>,
|
||||||
|
) -> Result<Trace, ProviderError> {
|
||||||
|
self.inner()
|
||||||
|
.trace_get(hash, index)
|
||||||
|
.await
|
||||||
|
.map_err(FromErr::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all traces of a given transaction
|
||||||
|
async fn trace_transaction(&self, hash: H256) -> Result<Vec<Trace>, ProviderError> {
|
||||||
|
self.inner()
|
||||||
|
.trace_transaction(hash)
|
||||||
|
.await
|
||||||
|
.map_err(FromErr::from)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,9 @@ use crate::{
|
||||||
use ethers_core::{
|
use ethers_core::{
|
||||||
abi::{self, Detokenize, ParamType},
|
abi::{self, Detokenize, ParamType},
|
||||||
types::{
|
types::{
|
||||||
Address, Block, BlockId, BlockNumber, Bytes, Filter, Log, NameOrAddress, Selector,
|
Address, Block, BlockId, BlockNumber, BlockTrace, Bytes, Filter, Log, NameOrAddress,
|
||||||
Signature, Transaction, TransactionReceipt, TransactionRequest, TxHash, TxpoolContent,
|
Selector, Signature, Trace, TraceFilter, TraceType, Transaction, TransactionReceipt,
|
||||||
TxpoolInspect, TxpoolStatus, H256, U256, U64,
|
TransactionRequest, TxHash, TxpoolContent, TxpoolInspect, TxpoolStatus, H256, U256, U64,
|
||||||
},
|
},
|
||||||
utils,
|
utils,
|
||||||
};
|
};
|
||||||
|
@ -541,6 +541,107 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
|
||||||
.await
|
.await
|
||||||
.map_err(Into::into)?)
|
.map_err(Into::into)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes the given call and returns a number of possible traces for it
|
||||||
|
async fn trace_call(
|
||||||
|
&self,
|
||||||
|
req: TransactionRequest,
|
||||||
|
trace_type: Vec<TraceType>,
|
||||||
|
block: Option<BlockNumber>,
|
||||||
|
) -> Result<BlockTrace, ProviderError> {
|
||||||
|
let req = utils::serialize(&req);
|
||||||
|
let block = utils::serialize(&block.unwrap_or(BlockNumber::Latest));
|
||||||
|
let trace_type = utils::serialize(&trace_type);
|
||||||
|
self.0
|
||||||
|
.request("trace_call", [req, trace_type, block])
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Traces a call to `eth_sendRawTransaction` without making the call, returning the traces
|
||||||
|
async fn trace_raw_transaction(
|
||||||
|
&self,
|
||||||
|
data: Bytes,
|
||||||
|
trace_type: Vec<TraceType>,
|
||||||
|
) -> Result<BlockTrace, ProviderError> {
|
||||||
|
let data = utils::serialize(&data);
|
||||||
|
let trace_type = utils::serialize(&trace_type);
|
||||||
|
self.0
|
||||||
|
.request("trace_rawTransaction", [data, trace_type])
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replays a transaction, returning the traces
|
||||||
|
async fn trace_replay_transaction(
|
||||||
|
&self,
|
||||||
|
hash: H256,
|
||||||
|
trace_type: Vec<TraceType>,
|
||||||
|
) -> Result<BlockTrace, ProviderError> {
|
||||||
|
let hash = utils::serialize(&hash);
|
||||||
|
let trace_type = utils::serialize(&trace_type);
|
||||||
|
Ok(self
|
||||||
|
.0
|
||||||
|
.request("trace_replayTransaction", [hash, trace_type])
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replays all transactions in a block returning the requested traces for each transaction
|
||||||
|
async fn trace_replay_block_transactions(
|
||||||
|
&self,
|
||||||
|
block: BlockNumber,
|
||||||
|
trace_type: Vec<TraceType>,
|
||||||
|
) -> Result<Vec<BlockTrace>, ProviderError> {
|
||||||
|
let block = utils::serialize(&block);
|
||||||
|
let trace_type = utils::serialize(&trace_type);
|
||||||
|
self.0
|
||||||
|
.request("trace_replayBlockTransactions", [block, trace_type])
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns traces created at given block
|
||||||
|
async fn trace_block(&self, block: BlockNumber) -> Result<Vec<Trace>, ProviderError> {
|
||||||
|
let block = utils::serialize(&block);
|
||||||
|
self.0
|
||||||
|
.request("trace_block", [block])
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return traces matching the given filter
|
||||||
|
async fn trace_filter(&self, filter: TraceFilter) -> Result<Vec<Trace>, ProviderError> {
|
||||||
|
let filter = utils::serialize(&filter);
|
||||||
|
self.0
|
||||||
|
.request("trace_filter", vec![filter])
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns trace at the given position
|
||||||
|
async fn trace_get<T: Into<U64> + Send + Sync>(
|
||||||
|
&self,
|
||||||
|
hash: H256,
|
||||||
|
index: Vec<T>,
|
||||||
|
) -> Result<Trace, ProviderError> {
|
||||||
|
let hash = utils::serialize(&hash);
|
||||||
|
let index: Vec<U64> = index.into_iter().map(|i| i.into()).collect();
|
||||||
|
let index = utils::serialize(&index);
|
||||||
|
self.0
|
||||||
|
.request("trace_get", vec![hash, index])
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all traces of a given transaction
|
||||||
|
async fn trace_transaction(&self, hash: H256) -> Result<Vec<Trace>, ProviderError> {
|
||||||
|
let hash = utils::serialize(&hash);
|
||||||
|
self.0
|
||||||
|
.request("trace_transaction", vec![hash])
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: JsonRpcClient> Provider<P> {
|
impl<P: JsonRpcClient> Provider<P> {
|
||||||
|
|
Loading…
Reference in New Issue