From c30f45fc72ca4751c09b08967f12f9b031da62b7 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Sat, 31 Oct 2020 12:44:08 +0200 Subject: [PATCH] feat: port over tracing from rust-web3 (#93) --- ethers-core/src/types/mod.rs | 3 + .../src/types/trace/example-trace-str.rs | 1097 +++++++++++++++++ .../src/types/trace/example-traces-str.rs | 68 + ethers-core/src/types/trace/filter.rs | 360 ++++++ ethers-core/src/types/trace/mod.rs | 197 +++ ethers-core/src/types/trace/parity.rs | 1 + ethers-providers/src/lib.rs | 86 ++ ethers-providers/src/provider.rs | 107 +- 8 files changed, 1916 insertions(+), 3 deletions(-) create mode 100644 ethers-core/src/types/trace/example-trace-str.rs create mode 100644 ethers-core/src/types/trace/example-traces-str.rs create mode 100644 ethers-core/src/types/trace/filter.rs create mode 100644 ethers-core/src/types/trace/mod.rs create mode 100644 ethers-core/src/types/trace/parity.rs diff --git a/ethers-core/src/types/mod.rs b/ethers-core/src/types/mod.rs index 1bac8e0a..98b1258f 100644 --- a/ethers-core/src/types/mod.rs +++ b/ethers-core/src/types/mod.rs @@ -30,3 +30,6 @@ pub use signature::*; mod txpool; pub use txpool::*; + +mod trace; +pub use trace::*; diff --git a/ethers-core/src/types/trace/example-trace-str.rs b/ethers-core/src/types/trace/example-trace-str.rs new file mode 100644 index 00000000..ab76f69d --- /dev/null +++ b/ethers-core/src/types/trace/example-trace-str.rs @@ -0,0 +1,1097 @@ +r#"{ + "output": "0x", + "stateDiff": { + "0x01f0eb5c4b0a9d8285b67195f5f10ce22971a102": { + "balance": { + "*": { + "from": "0x7361af5818297800", + "to": "0x734a36bb22448000" + } + }, + "code": "=", + "nonce": { + "*": { + "from": "0x1d6", + "to": "0x1d7" + } + }, + "storage": {} + }, + "0xb2930b35844a230f00e51431acae96fe543a0347": { + "balance": { + "*": { + "from": "0x11b39d46046d14d44e5", + "to": "0x11b39d687ebea8b3ce5" + } + }, + "code": "=", + "nonce": "=", + "storage": {} + }, + "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3": { + "balance": { + "*": { + "from": "0x109397d7f6f000", + "to": "0x25e48fb49df000" + } + }, + "code": "=", + "nonce": "=", + "storage": {} + } + }, + "trace": [ + { + "action": { + "callType": "call", + "from": "0x01f0eb5c4b0a9d8285b67195f5f10ce22971a102", + "gas": "0xa5f8", + "input": "0x1a695230000000000000000000000000c227a75b32ed37d3f9d6341b9904d003dad3b1b3", + "to": "0x0b95993a39a363d99280ac950f5e4536ab5c5566", + "value": "0x1550f7dca70000" + }, + "result": { + "gasUsed": "0x1ddf", + "output": "0x" + }, + "subtraces": 1, + "traceAddress": [], + "type": "call" + }, + { + "action": { + "callType": "call", + "from": "0x0b95993a39a363d99280ac950f5e4536ab5c5566", + "gas": "0x8fc", + "input": "0x", + "to": "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3", + "value": "0x1550f7dca70000" + }, + "result": { + "gasUsed": "0x0", + "output": "0x" + }, + "subtraces": 0, + "traceAddress": [ + 0 + ], + "type": "call" + } + ], + "vmTrace": { + "code": "0x60606040523615610055576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631a6952301461005e5780637362377b1461008c5780638da5cb5b146100a1575b61005c5b5b565b005b61008a600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100f6565b005b341561009757600080fd5b61009f61013a565b005b34156100ac57600080fd5b6100b4610210565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b8073ffffffffffffffffffffffffffffffffffffffff166108fc349081150290604051600060405180830381858888f19350505050151561013657600080fd5b5b50565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561019557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051600060405180830381858888f19350505050151561020d57600080fd5b5b565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a7230582029eabe8a624d811f3ea09c310d65be79ddefa23e3b702541dc1687b475f091690029", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x60" + ], + "store": null, + "used": 42485 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x40" + ], + "store": null, + "used": 42482 + }, + "pc": 2, + "sub": null + }, + { + "cost": 12, + "ex": { + "mem": { + "data": "0x0000000000000000000000000000000000000000000000000000000000000060", + "off": 64 + }, + "push": [], + "store": null, + "used": 42470 + }, + "pc": 4, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0x24" + ], + "store": null, + "used": 42468 + }, + "pc": 5, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 42465 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x55" + ], + "store": null, + "used": 42462 + }, + "pc": 7, + "sub": null + }, + { + "cost": 10, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 42452 + }, + "pc": 10, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 42449 + }, + "pc": 11, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1a695230000000000000000000000000c227a75b32ed37d3f9d6341b9904d003" + ], + "store": null, + "used": 42446 + }, + "pc": 13, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x100000000000000000000000000000000000000000000000000000000" + ], + "store": null, + "used": 42443 + }, + "pc": 14, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x100000000000000000000000000000000000000000000000000000000", + "0x1a695230000000000000000000000000c227a75b32ed37d3f9d6341b9904d003" + ], + "store": null, + "used": 42440 + }, + "pc": 44, + "sub": null + }, + { + "cost": 5, + "ex": { + "mem": null, + "push": [ + "0x1a695230" + ], + "store": null, + "used": 42435 + }, + "pc": 45, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0xffffffff" + ], + "store": null, + "used": 42432 + }, + "pc": 46, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1a695230" + ], + "store": null, + "used": 42429 + }, + "pc": 51, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1a695230", + "0x1a695230" + ], + "store": null, + "used": 42426 + }, + "pc": 52, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1a695230" + ], + "store": null, + "used": 42423 + }, + "pc": 53, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 42420 + }, + "pc": 58, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x5e" + ], + "store": null, + "used": 42417 + }, + "pc": 59, + "sub": null + }, + { + "cost": 10, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 42407 + }, + "pc": 62, + "sub": null + }, + { + "cost": 1, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 42406 + }, + "pc": 94, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x8a" + ], + "store": null, + "used": 42403 + }, + "pc": 95, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x4" + ], + "store": null, + "used": 42400 + }, + "pc": 98, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x4", + "0x4" + ], + "store": null, + "used": 42397 + }, + "pc": 100, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x4", + "0x4" + ], + "store": null, + "used": 42394 + }, + "pc": 101, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3" + ], + "store": null, + "used": 42391 + }, + "pc": 102, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0xffffffffffffffffffffffffffffffffffffffff" + ], + "store": null, + "used": 42388 + }, + "pc": 103, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3" + ], + "store": null, + "used": 42385 + }, + "pc": 124, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3", + "0x4" + ], + "store": null, + "used": 42382 + }, + "pc": 125, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 42379 + }, + "pc": 126, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x24" + ], + "store": null, + "used": 42376 + }, + "pc": 128, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x24", + "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3" + ], + "store": null, + "used": 42373 + }, + "pc": 129, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3", + "0x24", + "0x4" + ], + "store": null, + "used": 42370 + }, + "pc": 130, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x4", + "0x24" + ], + "store": null, + "used": 42367 + }, + "pc": 131, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 42365 + }, + "pc": 132, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 42363 + }, + "pc": 133, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0xf6" + ], + "store": null, + "used": 42360 + }, + "pc": 134, + "sub": null + }, + { + "cost": 8, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 42352 + }, + "pc": 137, + "sub": null + }, + { + "cost": 1, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 42351 + }, + "pc": 246, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3", + "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3" + ], + "store": null, + "used": 42348 + }, + "pc": 247, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0xffffffffffffffffffffffffffffffffffffffff" + ], + "store": null, + "used": 42345 + }, + "pc": 248, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3" + ], + "store": null, + "used": 42342 + }, + "pc": 269, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x8fc" + ], + "store": null, + "used": 42339 + }, + "pc": 270, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0x1550f7dca70000" + ], + "store": null, + "used": 42337 + }, + "pc": 273, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1550f7dca70000", + "0x8fc" + ], + "store": null, + "used": 42334 + }, + "pc": 274, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1550f7dca70000", + "0x8fc", + "0x1550f7dca70000" + ], + "store": null, + "used": 42331 + }, + "pc": 275, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 42328 + }, + "pc": 276, + "sub": null + }, + { + "cost": 5, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 42323 + }, + "pc": 277, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0", + "0x1550f7dca70000" + ], + "store": null, + "used": 42320 + }, + "pc": 278, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x40" + ], + "store": null, + "used": 42317 + }, + "pc": 279, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": { + "data": "0x0000000000000000000000000000000000000000000000000000000000000060", + "off": 64 + }, + "push": [ + "0x60" + ], + "store": null, + "used": 42314 + }, + "pc": 281, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 42311 + }, + "pc": 282, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x40" + ], + "store": null, + "used": 42308 + }, + "pc": 284, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": { + "data": "0x0000000000000000000000000000000000000000000000000000000000000060", + "off": 64 + }, + "push": [ + "0x60" + ], + "store": null, + "used": 42305 + }, + "pc": 286, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x60", + "0x60" + ], + "store": null, + "used": 42302 + }, + "pc": 287, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x60", + "0x0", + "0x60", + "0x60", + "0x60" + ], + "store": null, + "used": 42299 + }, + "pc": 288, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 42296 + }, + "pc": 289, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x60", + "0x0", + "0x60" + ], + "store": null, + "used": 42293 + }, + "pc": 290, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1550f7dca70000", + "0x60", + "0x0", + "0x60", + "0x0", + "0x60", + "0x1550f7dca70000" + ], + "store": null, + "used": 42290 + }, + "pc": 291, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3", + "0x0", + "0x1550f7dca70000", + "0x60", + "0x0", + "0x60", + "0x0", + "0x60", + "0x1550f7dca70000", + "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3" + ], + "store": null, + "used": 42287 + }, + "pc": 292, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0", + "0x1550f7dca70000", + "0x60", + "0x0", + "0x60", + "0x0", + "0x60", + "0x1550f7dca70000", + "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3", + "0x0" + ], + "store": null, + "used": 42284 + }, + "pc": 293, + "sub": null + }, + { + "cost": 9700, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 34884 + }, + "pc": 294, + "sub": { + "code": "0x", + "ops": [] + } + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1", + "0x0", + "0x1550f7dca70000", + "0x60", + "0xc227a75b32ed37d3f9d6341b9904d003dad3b1b3" + ], + "store": null, + "used": 34881 + }, + "pc": 295, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 34879 + }, + "pc": 296, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 34877 + }, + "pc": 297, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 34875 + }, + "pc": 298, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 34873 + }, + "pc": 299, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 34870 + }, + "pc": 300, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 34867 + }, + "pc": 301, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x136" + ], + "store": null, + "used": 34864 + }, + "pc": 302, + "sub": null + }, + { + "cost": 10, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 34854 + }, + "pc": 305, + "sub": null + }, + { + "cost": 1, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 34853 + }, + "pc": 310, + "sub": null + }, + { + "cost": 1, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 34852 + }, + "pc": 311, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 34850 + }, + "pc": 312, + "sub": null + }, + { + "cost": 8, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 34842 + }, + "pc": 313, + "sub": null + }, + { + "cost": 1, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 34841 + }, + "pc": 138, + "sub": null + }, + { + "cost": 0, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 34841 + }, + "pc": 139, + "sub": null + } + ] + } +}"# diff --git a/ethers-core/src/types/trace/example-traces-str.rs b/ethers-core/src/types/trace/example-traces-str.rs new file mode 100644 index 00000000..fa82880f --- /dev/null +++ b/ethers-core/src/types/trace/example-traces-str.rs @@ -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": [] + } +}]"# diff --git a/ethers-core/src/types/trace/filter.rs b/ethers-core/src/types/trace/filter.rs new file mode 100644 index 00000000..5128dbc9 --- /dev/null +++ b/ethers-core/src/types/trace/filter.rs @@ -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, + /// To block + #[serde(rename = "toBlock", skip_serializing_if = "Option::is_none")] + to_block: Option, + /// From address + #[serde(rename = "fromAddress", skip_serializing_if = "Option::is_none")] + from_address: Option>, + /// To address + #[serde(rename = "toAddress", skip_serializing_if = "Option::is_none")] + to_address: Option>, + /// Output offset + #[serde(skip_serializing_if = "Option::is_none")] + after: Option, + /// Output amount + #[serde(skip_serializing_if = "Option::is_none")] + count: Option, +} + +impl TraceFilter { + /// Sets From block + #[allow(clippy::wrong_self_convention)] + pub fn from_block>(mut self, block: T) -> Self { + self.from_block = Some(block.into()); + self + } + + /// Sets to block + #[allow(clippy::wrong_self_convention)] + pub fn to_block>(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) -> Self { + self.to_address = Some(address); + self + } + + /// Sets from address + #[allow(clippy::wrong_self_convention)] + pub fn from_address(mut self, address: Vec) -> 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, + /// Trace address + #[serde(rename = "traceAddress")] + pub trace_address: Vec, + /// Subtraces + pub subtraces: usize, + /// Transaction position + #[serde(rename = "transactionPosition")] + pub transaction_position: Option, + /// Transaction hash + #[serde(rename = "transactionHash")] + pub transaction_hash: Option, + /// 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, +} + +/// 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(); + } +} diff --git a/ethers-core/src/types/trace/mod.rs b/ethers-core/src/types/trace/mod.rs new file mode 100644 index 00000000..1a70ca20 --- /dev/null +++ b/ethers-core/src/types/trace/mod.rs @@ -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>, + /// Virtual Machine Execution Trace + #[serde(rename = "vmTrace")] + pub vm_trace: Option, + /// State Difference + #[serde(rename = "stateDiff")] + pub state_diff: Option, + /// Transaction Hash + #[serde(rename = "transactionHash")] + pub transaction_hash: Option, +} + +//---------------- State Diff ---------------- +/// Aux type for Diff::Changed. +#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] +pub struct ChangedType { + /// Previous value. + pub from: T, + /// Current value. + pub to: T, +} + +/// Serde-friendly `Diff` shadow. +#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] +pub enum Diff { + /// 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), +} + +/// Serde-friendly `AccountDiff` shadow. +#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] +pub struct AccountDiff { + /// Account balance. + pub balance: Diff, + /// Account nonce. + pub nonce: Diff, + /// Account code. + pub code: Diff, + /// Account storage. + pub storage: BTreeMap>, +} + +/// Serde-friendly `StateDiff` shadow. +#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] +pub struct StateDiff(pub BTreeMap); + +// ------------------ Trace ------------- +/// Trace +#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] +pub struct TransactionTrace { + /// Trace address + #[serde(rename = "traceAddress")] + pub trace_address: Vec, + /// Subtraces + pub subtraces: usize, + /// Action + pub action: Action, + /// Action Type + #[serde(rename = "type")] + pub action_type: ActionType, + /// Result + pub result: Option, + /// Error + pub error: Option, +} + +// ---------------- 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, +} + +#[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, + /// Subordinate trace of the CALL/CREATE if applicable. + // #[serde(bound="VMTrace: Deserialize")] + pub sub: Option, +} + +#[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, + /// If altered, the memory delta. + #[serde(rename = "mem")] + pub mem: Option, + /// The altered storage value, if any. + #[serde(rename = "store")] + pub store: Option, +} + +#[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 = serde_json::from_str(EXAMPLE_TRACES).unwrap(); + } +} diff --git a/ethers-core/src/types/trace/parity.rs b/ethers-core/src/types/trace/parity.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/ethers-core/src/types/trace/parity.rs @@ -0,0 +1 @@ + diff --git a/ethers-providers/src/lib.rs b/ethers-providers/src/lib.rs index ff0a0937..fbaad912 100644 --- a/ethers-providers/src/lib.rs +++ b/ethers-providers/src/lib.rs @@ -358,6 +358,8 @@ pub trait Middleware: Sync + Send + Debug { self.inner().pending_transaction(tx_hash) } + // Mempool inspection for Geth's API + async fn txpool_content(&self) -> Result { 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 { 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, + block: Option, + ) -> Result { + 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, + ) -> Result { + 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, + ) -> Result { + 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, + ) -> Result, 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, 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, ProviderError> { + self.inner() + .trace_filter(filter) + .await + .map_err(FromErr::from) + } + + /// Returns trace at the given position + async fn trace_get + Send + Sync>( + &self, + hash: H256, + index: Vec, + ) -> Result { + 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, ProviderError> { + self.inner() + .trace_transaction(hash) + .await + .map_err(FromErr::from) + } } diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs index 3f06b0cb..b995db50 100644 --- a/ethers-providers/src/provider.rs +++ b/ethers-providers/src/provider.rs @@ -7,9 +7,9 @@ use crate::{ use ethers_core::{ abi::{self, Detokenize, ParamType}, types::{ - Address, Block, BlockId, BlockNumber, Bytes, Filter, Log, NameOrAddress, Selector, - Signature, Transaction, TransactionReceipt, TransactionRequest, TxHash, TxpoolContent, - TxpoolInspect, TxpoolStatus, H256, U256, U64, + Address, Block, BlockId, BlockNumber, BlockTrace, Bytes, Filter, Log, NameOrAddress, + Selector, Signature, Trace, TraceFilter, TraceType, Transaction, TransactionReceipt, + TransactionRequest, TxHash, TxpoolContent, TxpoolInspect, TxpoolStatus, H256, U256, U64, }, utils, }; @@ -541,6 +541,107 @@ impl Middleware for Provider

{ .await .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, + block: Option, + ) -> Result { + 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, + ) -> Result { + 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, + ) -> Result { + 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, + ) -> Result, 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, 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, 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 + Send + Sync>( + &self, + hash: H256, + index: Vec, + ) -> Result { + let hash = utils::serialize(&hash); + let index: Vec = 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, ProviderError> { + let hash = utils::serialize(&hash); + self.0 + .request("trace_transaction", vec![hash]) + .await + .map_err(Into::into) + } } impl Provider

{