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

feat: add geth debug_traceBlock methods #2366

Merged
merged 3 commits into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -908,6 +908,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 @@ -1457,7 +1477,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 @@ -1635,6 +1657,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