Skip to content

Commit

Permalink
feat(rpc): implement debug_traceCall (kkrt-labs#1280)
Browse files Browse the repository at this point in the history
* wip draft

* trace call implementation

* add test

* cleanup

* fix lint

* update test and clean up

* fix fmt

* trace instrument fix

* fix comment

* fix conflicts

* fix comment

* fix

* Update src/tracing/mod.rs

Co-authored-by: greged93 <[email protected]>

---------

Co-authored-by: greged93 <[email protected]>
  • Loading branch information
tcoratger and greged93 authored Jul 14, 2024
1 parent 1c2b250 commit aa5db21
Show file tree
Hide file tree
Showing 8 changed files with 589 additions and 141 deletions.
343 changes: 235 additions & 108 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ reth-rpc-types = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.0.
"arbitrary",
] }
reth-rpc-types-compat = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.0.1", default-features = false }
reth-rpc-eth-types = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.0.1", default-features = false }
reth-rpc = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.0.1", default-features = false }
reth-revm = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.0.1", default-features = false }
reth-evm-ethereum = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.0.1", default-features = false }
reth-node-api = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.0.1", default-features = false }
Expand Down
6 changes: 5 additions & 1 deletion src/eth_provider/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ impl From<&EthApiError> for EthRpcErrorCode {
}
EthApiError::Signature(_)
| EthApiError::EthereumDataFormat(_)
| EthApiError::CalldataExceededLimit(_, _) => Self::InvalidParams,
| EthApiError::CalldataExceededLimit(_, _)
| EthApiError::RethEthApi(_) => Self::InvalidParams,
EthApiError::Transaction(err) => err.into(),
EthApiError::Unsupported(_) | EthApiError::Kakarot(_) => Self::InternalError,
EthApiError::Execution(_) => Self::ExecutionError,
Expand Down Expand Up @@ -65,6 +66,8 @@ pub enum EthApiError {
Kakarot(KakarotError),
/// Error related to transaction calldata being too large.
CalldataExceededLimit(usize, usize),
/// Reth Eth API error
RethEthApi(#[from] reth_rpc_eth_types::EthApiError),
}

impl std::fmt::Display for EthApiError {
Expand All @@ -75,6 +78,7 @@ impl std::fmt::Display for EthApiError {
Self::TransactionNotFound(tx) => write!(f, "transaction not found {tx}"),
Self::Transaction(err) => write!(f, "{err}"),
Self::Signature(err) => write!(f, "{err}"),
Self::RethEthApi(err) => write!(f, "{err}"),
Self::Unsupported(feature) => write!(f, "unsupported: {feature}"),
Self::EthereumDataFormat(err) => write!(f, "ethereum data format error: {err}"),
Self::Execution(err) => write!(f, "{err}"),
Expand Down
13 changes: 11 additions & 2 deletions src/eth_rpc/api/debug_api.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use jsonrpsee::{core::RpcResult as Result, proc_macros::rpc};
use reth_primitives::{Bytes, B256};
use reth_rpc_types::{
trace::geth::{GethDebugTracingOptions, GethTrace, TraceResult},
BlockId, BlockNumberOrTag,
trace::geth::{GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult},
BlockId, BlockNumberOrTag, TransactionRequest,
};

/// Debug API
Expand Down Expand Up @@ -56,4 +56,13 @@ pub trait DebugApi {
transaction_hash: B256,
opts: Option<GethDebugTracingOptions>,
) -> Result<GethTrace>;

/// Runs an `eth_call` within the context of a given block execution and returns the Geth debug trace.
#[method(name = "traceCall")]
async fn trace_call(
&self,
request: TransactionRequest,
block_number: Option<BlockId>,
opts: Option<GethDebugTracingCallOptions>,
) -> Result<GethTrace>;
}
40 changes: 30 additions & 10 deletions src/eth_rpc/servers/debug_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use alloy_rlp::Encodable;
use jsonrpsee::core::{async_trait, RpcResult as Result};
use reth_primitives::{Block, Bytes, Header, Log, Receipt, ReceiptWithBloom, TransactionSigned, B256};
use reth_rpc_types::{
trace::geth::{GethDebugTracingOptions, GethTrace, TraceResult},
BlockId, BlockNumberOrTag,
trace::geth::{GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult},
BlockId, BlockNumberOrTag, TransactionRequest,
};
use std::sync::Arc;

Expand All @@ -32,7 +32,7 @@ impl<P: EthereumProvider> DebugRpc<P> {
#[async_trait]
impl<P: EthereumProvider + Send + Sync + 'static> DebugApiServer for DebugRpc<P> {
/// Returns an RLP-encoded header.
#[tracing::instrument(skip(self), err, fields(block_id = ?block_id))]
#[tracing::instrument(skip(self), err)]
async fn raw_header(&self, block_id: BlockId) -> Result<Bytes> {
tracing::info!("Serving debug_getRawHeader");

Expand All @@ -52,7 +52,7 @@ impl<P: EthereumProvider + Send + Sync + 'static> DebugApiServer for DebugRpc<P>
}

/// Returns an RLP-encoded block.
#[tracing::instrument(skip(self), err, fields(block_id = ?block_id))]
#[tracing::instrument(skip(self), err)]
async fn raw_block(&self, block_id: BlockId) -> Result<Bytes> {
tracing::info!("Serving debug_getRawBlock");

Expand All @@ -72,7 +72,7 @@ impl<P: EthereumProvider + Send + Sync + 'static> DebugApiServer for DebugRpc<P>
/// Returns a EIP-2718 binary-encoded transaction.
///
/// If this is a pooled EIP-4844 transaction, the blob sidecar is included.
#[tracing::instrument(skip(self), err, fields(hash = ?hash))]
#[tracing::instrument(skip(self), err)]
async fn raw_transaction(&self, hash: B256) -> Result<Option<Bytes>> {
tracing::info!("Serving debug_getRawTransaction");

Expand All @@ -97,7 +97,7 @@ impl<P: EthereumProvider + Send + Sync + 'static> DebugApiServer for DebugRpc<P>
}

/// Returns an array of EIP-2718 binary-encoded transactions for the given [BlockId].
#[tracing::instrument(skip(self), err, fields(block_id = ?block_id))]
#[tracing::instrument(skip(self), err)]
async fn raw_transactions(&self, block_id: BlockId) -> Result<Vec<Bytes>> {
tracing::info!("Serving debug_getRawTransactions");

Expand All @@ -123,7 +123,7 @@ impl<P: EthereumProvider + Send + Sync + 'static> DebugApiServer for DebugRpc<P>
}

/// Returns an array of EIP-2718 binary-encoded receipts.
#[tracing::instrument(skip(self), err, fields(block_id = ?block_id))]
#[tracing::instrument(skip(self), err)]
async fn raw_receipts(&self, block_id: BlockId) -> Result<Vec<Bytes>> {
tracing::info!("Serving debug_getRawReceipts");

Expand Down Expand Up @@ -168,7 +168,7 @@ impl<P: EthereumProvider + Send + Sync + 'static> DebugApiServer for DebugRpc<P>
}

/// Returns the Geth debug trace for the given block number.
#[tracing::instrument(skip(self), err, fields(block_number = ?block_number, opts = ?opts))]
#[tracing::instrument(skip(self), err)]
async fn trace_block_by_number(
&self,
block_number: BlockNumberOrTag,
Expand All @@ -188,7 +188,7 @@ impl<P: EthereumProvider + Send + Sync + 'static> DebugApiServer for DebugRpc<P>
}

/// Returns the Geth debug trace for the given block hash.
#[tracing::instrument(skip(self), err, fields(block_hash = ?block_hash, opts = ?opts))]
#[tracing::instrument(skip(self), err)]
async fn trace_block_by_hash(
&self,
block_hash: B256,
Expand All @@ -207,7 +207,7 @@ impl<P: EthereumProvider + Send + Sync + 'static> DebugApiServer for DebugRpc<P>
}

/// Returns the Geth debug trace for the given transaction hash.
#[tracing::instrument(skip(self), err, fields(transaction_hash = ?transaction_hash, opts = ?opts))]
#[tracing::instrument(skip(self), err)]
async fn trace_transaction(
&self,
transaction_hash: B256,
Expand All @@ -224,4 +224,24 @@ impl<P: EthereumProvider + Send + Sync + 'static> DebugApiServer for DebugRpc<P>

Ok(tracer.debug_transaction(transaction_hash)?)
}

/// Runs an `eth_call` within the context of a given block execution and returns the Geth debug trace.
#[tracing::instrument(skip(self), err)]
async fn trace_call(
&self,
request: TransactionRequest,
block_number: Option<BlockId>,
opts: Option<GethDebugTracingCallOptions>,
) -> Result<GethTrace> {
tracing::info!("Serving debug_traceCall");

let tracer = TracerBuilder::new(Arc::new(&self.eth_provider))
.await?
.with_block_id(block_number.unwrap_or_default())
.await?
.with_tracing_options(opts.unwrap_or_default().into())
.build()?;

Ok(tracer.debug_transaction_request(&request)?)
}
}
45 changes: 43 additions & 2 deletions src/tracing/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use crate::eth_provider::{
use reth_primitives::{B256, U256};
use reth_revm::primitives::{BlockEnv, CfgEnv, Env, EnvWithHandlerCfg, HandlerCfg, SpecId};
use reth_rpc_types::{
trace::geth::GethDebugTracingOptions, Block, BlockHashOrNumber, BlockId, BlockTransactions, Header,
trace::geth::{GethDebugTracingCallOptions, GethDebugTracingOptions},
Block, BlockHashOrNumber, BlockId, BlockTransactions, Header,
};
use revm_inspectors::tracing::TracingInspectorConfig;

Expand All @@ -22,6 +23,40 @@ pub enum TracingOptions {
Geth(GethDebugTracingOptions),
/// Parity tracing options.
Parity(TracingInspectorConfig),
/// Geth debug call tracing options.
GethCall(GethDebugTracingCallOptions),
}

impl TracingOptions {
/// Returns `Some` with a reference to [`GethDebugTracingOptions`] if this is `Geth`,
/// otherwise returns `None`.
pub const fn as_geth(&self) -> Option<&GethDebugTracingOptions> {
if let Self::Geth(ref options) = self {
Some(options)
} else {
None
}
}

/// Returns `Some` with a reference to [`TracingInspectorConfig`] if this is `Parity`,
/// otherwise returns `None`.
pub const fn as_parity(&self) -> Option<&TracingInspectorConfig> {
if let Self::Parity(ref config) = self {
Some(config)
} else {
None
}
}

/// Returns `Some` with a reference to [`GethDebugTracingCallOptions`] if this is `GethCall`,
/// otherwise returns `None`.
pub const fn as_geth_call(&self) -> Option<&GethDebugTracingCallOptions> {
if let Self::GethCall(ref options) = self {
Some(options)
} else {
None
}
}
}

impl Default for TracingOptions {
Expand All @@ -42,6 +77,12 @@ impl From<TracingInspectorConfig> for TracingOptions {
}
}

impl From<GethDebugTracingCallOptions> for TracingOptions {
fn from(options: GethDebugTracingCallOptions) -> Self {
Self::GethCall(options)
}
}

#[derive(Debug)]
pub struct TracerBuilder<P: EthereumProvider + Send + Sync, Status = Floating> {
eth_provider: P,
Expand Down Expand Up @@ -113,7 +154,7 @@ impl<P: EthereumProvider + Send + Sync + Clone> TracerBuilder<P, Floating> {
.ok_or(match block_id {
BlockId::Hash(hash) => EthApiError::UnknownBlock(hash.block_hash.into()),
BlockId::Number(number) => {
EthApiError::UnknownBlock(BlockHashOrNumber::Number(number.as_number().unwrap()))
EthApiError::UnknownBlock(BlockHashOrNumber::Number(number.as_number().unwrap_or_default()))
}
})?;

Expand Down
Loading

0 comments on commit aa5db21

Please sign in to comment.