feat: add support for js tracer to geth trace, fix different return types (#2064)

* feat: add support for js tracer to geth trace, fix different return types

* chore: fix clippy

* chore: update geth structs
This commit is contained in:
Vid Kersic 2023-01-31 18:58:06 +01:00 committed by GitHub
parent 969c10a406
commit 7e7f9041b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 120 additions and 43 deletions

View File

@ -4,6 +4,7 @@
### Unreleased ### Unreleased
- Add support for custom JavaScript tracer to `debug_traceCall` and `debug_traceTransaction` [#2064](https://github.com/gakonst/ethers-rs/pull/2064)
- Add a `Send` bound to the `IntoFuture` implementation of `ContractCall` [#2083](https://github.com/gakonst/ethers-rs/pull/2083) - Add a `Send` bound to the `IntoFuture` implementation of `ContractCall` [#2083](https://github.com/gakonst/ethers-rs/pull/2083)
- Bump [`svm-rs`](https://github.com/roynalnaruto/svm-rs) dependency to fix conflicts with Rust Crytpo packages [#2051](https://github.com/gakonst/ethers-rs/pull/2051) - Bump [`svm-rs`](https://github.com/roynalnaruto/svm-rs) dependency to fix conflicts with Rust Crytpo packages [#2051](https://github.com/gakonst/ethers-rs/pull/2051)
- Avoid unnecessary allocations in `utils` [#2046](https://github.com/gakonst/ethers-rs/pull/2046) - Avoid unnecessary allocations in `utils` [#2046](https://github.com/gakonst/ethers-rs/pull/2046)

View File

@ -1,17 +1,18 @@
use crate::{ use crate::{
types::{Bytes, H256, U256}, types::{Address, Bytes, NameOrAddress, H256, U256},
utils::from_int_or_hex, utils::from_int_or_hex,
}; };
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::BTreeMap; use std::collections::BTreeMap;
// https://github.com/ethereum/go-ethereum/blob/a9ef135e2dd53682d106c6a2aede9187026cc1de/eth/tracers/logger/logger.go#L406-L411 // https://github.com/ethereum/go-ethereum/blob/a9ef135e2dd53682d106c6a2aede9187026cc1de/eth/tracers/logger/logger.go#L406-L411
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct GethTrace { pub struct DefaultFrame {
pub failed: bool, pub failed: bool,
#[serde(deserialize_with = "from_int_or_hex")] #[serde(deserialize_with = "from_int_or_hex")]
pub gas: U256, pub gas: U256,
#[serde(serialize_with = "serialize_bytes", rename = "returnValue")] #[serde(rename = "returnValue")]
pub return_value: Bytes, pub return_value: Bytes,
#[serde(rename = "structLogs")] #[serde(rename = "structLogs")]
pub struct_logs: Vec<StructLog>, pub struct_logs: Vec<StructLog>,
@ -21,34 +22,83 @@ pub struct GethTrace {
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct StructLog { pub struct StructLog {
pub depth: u64, pub depth: u64,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>, pub error: Option<String>,
pub gas: u64, pub gas: u64,
#[serde(rename = "gasCost")] #[serde(rename = "gasCost")]
pub gas_cost: u64, pub gas_cost: u64,
/// ref <https://github.com/ethereum/go-ethereum/blob/366d2169fbc0e0f803b68c042b77b6b480836dbc/eth/tracers/logger/logger.go#L450-L452> /// ref <https://github.com/ethereum/go-ethereum/blob/366d2169fbc0e0f803b68c042b77b6b480836dbc/eth/tracers/logger/logger.go#L450-L452>
#[serde(skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub memory: Option<Vec<String>>, pub memory: Option<Vec<String>>,
pub op: String, pub op: String,
pub pc: u64, pub pc: u64,
#[serde(rename = "refund", skip_serializing_if = "Option::is_none")] #[serde(default, rename = "refund", skip_serializing_if = "Option::is_none")]
pub refund_counter: Option<u64>, pub refund_counter: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub stack: Option<Vec<U256>>, pub stack: Option<Vec<U256>>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub storage: Option<BTreeMap<H256, H256>>, pub storage: Option<BTreeMap<H256, H256>>,
} }
// https://github.com/ethereum/go-ethereum/blob/a9ef135e2dd53682d106c6a2aede9187026cc1de/eth/tracers/native/call.go#L37
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct CallFrame {
#[serde(rename = "type")]
pub typ: String,
pub from: Address,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub to: Option<NameOrAddress>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value: Option<U256>,
#[serde(deserialize_with = "from_int_or_hex")]
pub gas: U256,
#[serde(deserialize_with = "from_int_or_hex", rename = "gasUsed")]
pub gas_used: U256,
pub input: Bytes,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output: Option<Bytes>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub calls: Option<Vec<CallFrame>>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum GethTraceFrame {
Default(DefaultFrame),
CallTracer(CallFrame),
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum GethTrace {
Known(GethTraceFrame),
Unknown(Value),
}
/// Available built-in tracers /// Available built-in tracers
/// ///
/// See <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers> /// See <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers>
pub enum GethDebugTracerType { #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
/// callTracer (native) pub enum GethDebugBuiltInTracerType {
#[serde(rename = "callTracer")] #[serde(rename = "callTracer")]
CallTracer, CallTracer,
} }
/// Available tracers
///
/// See <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers> and <https://geth.ethereum.org/docs/developers/evm-tracing/custom-tracer>
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum GethDebugTracerType {
/// built-in tracer
BuiltInTracer(GethDebugBuiltInTracerType),
/// custom JS tracer
JsTracer(String),
}
/// Bindings for additional `debug_traceTransaction` options /// Bindings for additional `debug_traceTransaction` options
/// ///
/// See <https://geth.ethereum.org/docs/rpc/ns-debug#debug_tracetransaction> /// See <https://geth.ethereum.org/docs/rpc/ns-debug#debug_tracetransaction>
@ -79,11 +129,3 @@ pub struct GethDebugTracingCallOptions {
pub tracing_options: GethDebugTracingOptions, pub tracing_options: GethDebugTracingOptions,
// TODO: Add stateoverrides and blockoverrides options // TODO: Add stateoverrides and blockoverrides options
} }
fn serialize_bytes<S, T>(x: T, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: AsRef<[u8]>,
{
s.serialize_str(&hex::encode(x.as_ref()))
}

View File

@ -1,22 +0,0 @@
use ethers::{
core::types::{GethDebugTracingOptions, H256},
providers::{Http, Middleware, Provider},
};
use eyre::Result;
use std::str::FromStr;
/// use `debug_traceTransaction` to fetch traces
/// requires, a valid endpoint in `RPC_URL` env var that supports `debug_traceTransaction`
#[tokio::main]
async fn main() -> Result<()> {
if let Ok(url) = std::env::var("RPC_URL") {
let client = Provider::<Http>::try_from(url)?;
let tx_hash = "0x97a02abf405d36939e5b232a5d4ef5206980c5a6661845436058f30600c52df7";
let h: H256 = H256::from_str(tx_hash)?;
let options: GethDebugTracingOptions = GethDebugTracingOptions::default();
let traces = client.debug_trace_transaction(h, options).await?;
println!("{traces:?}");
}
Ok(())
}

View File

@ -1,4 +1,11 @@
use ethers::prelude::*; use ethers::{
core::types::GethDebugTracingOptions,
providers::{Http, Middleware, Provider},
types::{
Address, BlockId, Bytes, GethDebugBuiltInTracerType, GethDebugTracerType,
GethDebugTracingCallOptions, TransactionRequest,
},
};
use eyre::Result; use eyre::Result;
use std::str::FromStr; use std::str::FromStr;
@ -14,7 +21,9 @@ async fn main() -> Result<()> {
tracing_options: GethDebugTracingOptions { tracing_options: GethDebugTracingOptions {
disable_storage: Some(true), disable_storage: Some(true),
enable_memory: Some(false), enable_memory: Some(false),
tracer: Some(GethDebugTracerType::CallTracer), tracer: Some(GethDebugTracerType::BuiltInTracer(
GethDebugBuiltInTracerType::CallTracer,
)),
..Default::default() ..Default::default()
}, },
}; };

View File

@ -0,0 +1,47 @@
use ethers::{
core::types::{GethDebugTracingOptions, H256},
providers::{Http, Middleware, Provider},
types::{GethDebugBuiltInTracerType, GethDebugTracerType},
};
use eyre::Result;
use std::str::FromStr;
/// use `debug_traceTransaction` to fetch traces
/// requires, a valid endpoint in `RPC_URL` env var that supports `debug_traceTransaction`
#[tokio::main]
async fn main() -> Result<()> {
if let Ok(url) = std::env::var("RPC_URL") {
let client = Provider::<Http>::try_from(url)?;
let tx_hash = "0x97a02abf405d36939e5b232a5d4ef5206980c5a6661845436058f30600c52df7";
let h: H256 = H256::from_str(tx_hash)?;
// default tracer
let options = GethDebugTracingOptions::default();
let traces = client.debug_trace_transaction(h, options).await?;
println!("{traces:?}");
// call tracer
let options = GethDebugTracingOptions {
disable_storage: Some(true),
enable_memory: Some(false),
tracer: Some(GethDebugTracerType::BuiltInTracer(
GethDebugBuiltInTracerType::CallTracer,
)),
..Default::default()
};
let traces = client.debug_trace_transaction(h, options).await?;
println!("{traces:?}");
// js tracer
let options = GethDebugTracingOptions {
disable_storage: Some(true),
enable_memory: Some(false),
tracer: Some(GethDebugTracerType::JsTracer(String::from("{data: [], fault: function(log) {}, step: function(log) { if(log.op.toString() == \"DELEGATECALL\") this.data.push(log.stack.peek(0)); }, result: function() { return this.data; }}"))),
..Default::default()
};
let traces = client.debug_trace_transaction(h, options).await?;
println!("{traces:?}");
}
Ok(())
}