Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
feat: add geth debug_traceBlock methods (#2366)
Browse files Browse the repository at this point in the history
* feat: add geth debug_traceBlock methods

* fix: clippy, fmt, and debug_trace_block test

* chore: make geth trace result private
  • Loading branch information
bsh98 authored Apr 27, 2023
1 parent f6ddb9a commit 97d363b
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 1 deletion.
21 changes: 21 additions & 0 deletions ethers-core/src/types/trace/geth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,32 @@ impl From<NoopFrame> for GethTraceFrame {

#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
enum GethTraceResult {
ResultKnown { result: GethTraceFrame },
ResultUnknown { result: Value },
DefaultKnown(GethTraceFrame),
DefaultUnknown(Value),
}

#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(from = "GethTraceResult")]
#[serde(untagged)]
pub enum GethTrace {
Known(GethTraceFrame),
Unknown(Value),
}

impl From<GethTraceResult> for GethTrace {
fn from(value: GethTraceResult) -> Self {
match value {
GethTraceResult::DefaultKnown(t) => GethTrace::Known(t),
GethTraceResult::DefaultUnknown(v) => GethTrace::Unknown(v),
GethTraceResult::ResultKnown { result } => GethTrace::Known(result),
GethTraceResult::ResultUnknown { result } => GethTrace::Unknown(result),
}
}
}

impl From<GethTraceFrame> for GethTrace {
fn from(value: GethTraceFrame) -> Self {
GethTrace::Known(value)
Expand Down
30 changes: 30 additions & 0 deletions ethers-providers/src/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,36 @@ pub trait Middleware: Sync + Send + Debug {
.map_err(MiddlewareError::from_err)
}

/// Replays all transactions in a given block (specified by block number) and returns the traces
/// configured with passed options
/// Ref:
/// [Here](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug#debugtraceblockbynumber)
async fn debug_trace_block_by_number(
&self,
block: Option<BlockNumber>,
trace_options: GethDebugTracingOptions,
) -> Result<Vec<GethTrace>, Self::Error> {
self.inner()
.debug_trace_block_by_number(block, trace_options)
.await
.map_err(MiddlewareError::from_err)
}

/// Replays all transactions in a given block (specified by block hash) and returns the traces
/// configured with passed options
/// Ref:
/// [Here](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug#debugtraceblockbyhash)
async fn debug_trace_block_by_hash(
&self,
block: H256,
trace_options: GethDebugTracingOptions,
) -> Result<Vec<GethTrace>, Self::Error> {
self.inner()
.debug_trace_block_by_hash(block, trace_options)
.await
.map_err(MiddlewareError::from_err)
}

// Parity `trace` support

/// Executes the given call and returns a number of possible traces for it
Expand Down
69 changes: 68 additions & 1 deletion ethers-providers/src/rpc/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,26 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
self.request("debug_traceCall", [req, block, trace_options]).await
}

async fn debug_trace_block_by_number(
&self,
block: Option<BlockNumber>,
trace_options: GethDebugTracingOptions,
) -> Result<Vec<GethTrace>, ProviderError> {
let block = utils::serialize(&block.unwrap_or(BlockNumber::Latest));
let trace_options = utils::serialize(&trace_options);
self.request("debug_traceBlockByNumber", [block, trace_options]).await
}

async fn debug_trace_block_by_hash(
&self,
block: H256,
trace_options: GethDebugTracingOptions,
) -> Result<Vec<GethTrace>, ProviderError> {
let block = utils::serialize(&block);
let trace_options = utils::serialize(&trace_options);
self.request("debug_traceBlockByHash", [block, trace_options]).await
}

async fn trace_call<T: Into<TypedTransaction> + Send + Sync>(
&self,
req: T,
Expand Down Expand Up @@ -1477,7 +1497,9 @@ mod tests {
use crate::Http;
use ethers_core::{
types::{
transaction::eip2930::AccessList, Eip1559TransactionRequest, TransactionRequest, H256,
transaction::eip2930::AccessList, Eip1559TransactionRequest,
GethDebugBuiltInTracerConfig, GethDebugBuiltInTracerType, GethDebugTracerConfig,
GethDebugTracerType, PreStateConfig, TransactionRequest, H256,
},
utils::{Anvil, Genesis, Geth, GethInstance},
};
Expand Down Expand Up @@ -1655,6 +1677,51 @@ mod tests {
assert!(!receipts.is_empty());
}

#[tokio::test]
#[cfg_attr(feature = "celo", ignore)]
async fn debug_trace_block() {
let provider = Provider::<Http>::try_from("https://eth.llamarpc.com").unwrap();

let opts = GethDebugTracingOptions {
disable_storage: Some(false),
tracer: Some(GethDebugTracerType::BuiltInTracer(
GethDebugBuiltInTracerType::PreStateTracer,
)),
tracer_config: Some(GethDebugTracerConfig::BuiltInTracer(
GethDebugBuiltInTracerConfig::PreStateTracer(PreStateConfig {
diff_mode: Some(true),
}),
)),
..Default::default()
};

let latest_block = provider
.get_block(BlockNumber::Latest)
.await
.expect("Failed to fetch latest block.")
.expect("Latest block is none.");

// debug_traceBlockByNumber
let latest_block_num = BlockNumber::Number(latest_block.number.unwrap());
let traces_by_num = provider
.debug_trace_block_by_number(Some(latest_block_num), opts.clone())
.await
.unwrap();
for trace in &traces_by_num {
assert!(matches!(trace, GethTrace::Known(..)));
}

// debug_traceBlockByHash
let latest_block_hash = latest_block.hash.unwrap();
let traces_by_hash =
provider.debug_trace_block_by_hash(latest_block_hash, opts).await.unwrap();
for trace in &traces_by_hash {
assert!(matches!(trace, GethTrace::Known(..)));
}

assert_eq!(traces_by_num, traces_by_hash);
}

#[tokio::test]
#[cfg_attr(feature = "celo", ignore)]
async fn fee_history() {
Expand Down

0 comments on commit 97d363b

Please sign in to comment.