arb_rpc/
stylus_debug.rs

1//! `debug_traceTransaction` override that adds the `stylusTracer`
2//! named tracer.
3//!
4//! When `opts.tracer == "stylusTracer"` the cached Stylus host-I/O
5//! records for `tx_hash` are returned as a JSON array; every other
6//! tracer name forwards to the standard handler unchanged.
7
8use alloy_primitives::B256;
9use alloy_rpc_types_trace::geth::{GethDebugTracerType, GethDebugTracingOptions, GethTrace};
10use jsonrpsee::{core::RpcResult, proc_macros::rpc};
11
12use crate::stylus_tracer::take_cached_trace;
13
14pub const STYLUS_TRACER_NAME: &str = "stylusTracer";
15
16/// `debug_*` namespace override exposing the `stylusTracer` option.
17#[rpc(server, namespace = "debug")]
18pub trait StylusDebug {
19    #[method(name = "traceTransaction")]
20    async fn trace_transaction(
21        &self,
22        tx_hash: B256,
23        opts: Option<GethDebugTracingOptions>,
24    ) -> RpcResult<GethTrace>;
25}
26
27/// Async forwarder for non-stylus tracer requests.
28pub type DebugForwarder = std::sync::Arc<
29    dyn Fn(
30            B256,
31            Option<GethDebugTracingOptions>,
32        )
33            -> std::pin::Pin<Box<dyn std::future::Future<Output = RpcResult<GethTrace>> + Send>>
34        + Send
35        + Sync,
36>;
37
38pub struct StylusDebugHandler {
39    forwarder: DebugForwarder,
40}
41
42impl std::fmt::Debug for StylusDebugHandler {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        f.debug_struct("StylusDebugHandler").finish()
45    }
46}
47
48impl StylusDebugHandler {
49    pub fn new(forwarder: DebugForwarder) -> Self {
50        Self { forwarder }
51    }
52}
53
54#[async_trait::async_trait]
55impl StylusDebugServer for StylusDebugHandler {
56    async fn trace_transaction(
57        &self,
58        tx_hash: B256,
59        opts: Option<GethDebugTracingOptions>,
60    ) -> RpcResult<GethTrace> {
61        if let Some(ref o) = opts {
62            if let Some(GethDebugTracerType::JsTracer(name)) = &o.tracer {
63                if name == STYLUS_TRACER_NAME {
64                    let records = take_cached_trace(tx_hash);
65                    let value = serde_json::to_value(&records).unwrap_or_default();
66                    return Ok(GethTrace::JS(value));
67                }
68            }
69        }
70        (self.forwarder)(tx_hash, opts).await
71    }
72}