feat(rpc): add missing geth trace calls (#1700)

This commit is contained in:
Matthias Seitz
2023-03-10 14:27:33 +01:00
committed by GitHub
parent ad5f9aa78c
commit 3ed9ef49d0
6 changed files with 220 additions and 65 deletions

View File

@ -1,6 +1,9 @@
use jsonrpsee::{core::RpcResult as Result, proc_macros::rpc};
use reth_primitives::{BlockId, Bytes, H256};
use reth_rpc_types::RichBlock;
use reth_primitives::{BlockId, BlockNumberOrTag, Bytes, H256};
use reth_rpc_types::{
trace::geth::{BlockTraceResult, GethDebugTracingOptions, GethTraceFrame, TraceResult},
CallRequest, RichBlock,
};
/// Debug rpc interface.
#[cfg_attr(not(feature = "client"), rpc(server))]
@ -25,4 +28,75 @@ pub trait DebugApi {
/// Returns an array of recent bad blocks that the client has seen on the network.
#[method(name = "debug_getBadBlocks")]
async fn bad_blocks(&self) -> Result<Vec<RichBlock>>;
/// Returns the structured logs created during the execution of EVM between two blocks
/// (excluding start) as a JSON object.
#[method(name = "debug_traceChain")]
async fn debug_trace_chain(
&self,
start_exclusive: BlockNumberOrTag,
end_inclusive: BlockNumberOrTag,
) -> Result<Vec<BlockTraceResult>>;
/// The `debug_traceBlock` method will return a full stack trace of all invoked opcodes of all
/// transaction that were included in this block.
///
/// This expects an rlp encoded block
///
/// Note, the parent of this block must be present, or it will fail. For the second parameter
/// see [GethDebugTracingOptions] reference.
#[method(name = "debug_traceBlock")]
async fn debug_trace_block(
&self,
rlp_block: Bytes,
opts: GethDebugTracingOptions,
) -> Result<Vec<TraceResult>>;
/// Similar to `debug_traceBlock`, `debug_traceBlockByHash` accepts a block hash and will replay
/// the block that is already present in the database. For the second parameter see
/// [GethDebugTracingOptions].
#[method(name = "debug_traceBlockByHash")]
async fn debug_trace_block_by_hash(
&self,
block: H256,
opts: GethDebugTracingOptions,
) -> Result<Vec<TraceResult>>;
/// Similar to `debug_traceBlockByNumber`, `debug_traceBlockByHash` accepts a block number
/// [BlockNumberOrTag] and will replay the block that is already present in the database.
/// For the second parameter see [GethDebugTracingOptions].
#[method(name = "debug_traceBlockByNumber")]
async fn debug_trace_block_by_number(
&self,
block: BlockNumberOrTag,
opts: GethDebugTracingOptions,
) -> Result<Vec<TraceResult>>;
/// The `debug_traceTransaction` debugging method will attempt to run the transaction in the
/// exact same manner as it was executed on the network. It will replay any transaction that
/// may have been executed prior to this one before it will finally attempt to execute the
/// transaction that corresponds to the given hash.
#[method(name = "debug_traceTransaction")]
async fn debug_trace_transaction(
&self,
tx_hash: H256,
opts: GethDebugTracingOptions,
) -> Result<GethTraceFrame>;
/// The debug_traceCall method lets you run an `eth_call` within the context of the given block
/// execution using the final state of parent block as the base.
///
/// The first argument (just as in`eth_call`) is a transaction request.
/// The block can be specified either by hash or by number as
/// the second argument.
/// The trace can be configured similar to `debug_traceTransaction`,
/// see [GethDebugTracingOptions]. The method returns the same output as
/// `debug_traceTransaction`.
#[method(name = "debug_traceCall")]
async fn debug_trace_call(
&self,
request: CallRequest,
block_number: Option<BlockId>,
opts: GethDebugTracingOptions,
) -> Result<GethTraceFrame>;
}

View File

@ -0,0 +1,14 @@
//! Types used by tracing backends
use serde::{Deserialize, Serialize};
/// The result of a single transaction trace.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
#[allow(missing_docs)]
pub enum TraceResult<Ok, Err> {
/// Untagged success variant
Success { result: Ok },
/// Untagged error variant
Error { error: Err },
}

View File

@ -0,0 +1,59 @@
#![allow(missing_docs)]
/// Geth tracing types
use reth_primitives::{Bytes, JsonU256, H256, U256};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
// re-exported for geth tracing types
pub use ethers_core::types::{GethDebugTracingOptions, GethTraceFrame};
/// Result type for geth style transaction trace
pub type TraceResult = crate::trace::common::TraceResult<serde_json::Value, String>;
/// blockTraceResult represents the results of tracing a single block when an entire chain is being
/// traced. ref <https://github.com/ethereum/go-ethereum/blob/ee530c0d5aa70d2c00ab5691a89ab431b73f8165/eth/tracers/api.go#L218-L222>
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct BlockTraceResult {
/// Block number corresponding to the trace task
pub block: U256,
/// Block hash corresponding to the trace task
pub hash: H256,
/// Trace results produced by the trace task
pub traces: Vec<TraceResult>,
}
/// Geth Default trace frame
///
/// <https://github.com/ethereum/go-ethereum/blob/a9ef135e2dd53682d106c6a2aede9187026cc1de/eth/tracers/logger/logger.go#L406-L411>
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DefaultFrame {
pub failed: bool,
pub gas: JsonU256,
pub return_value: Bytes,
pub struct_logs: Vec<StructLog>,
}
/// Represents a struct log entry in a trace
///
/// <https://github.com/ethereum/go-ethereum/blob/366d2169fbc0e0f803b68c042b77b6b480836dbc/eth/tracers/logger/logger.go#L413-L426>
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct StructLog {
pub depth: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
pub gas: u64,
#[serde(rename = "gasCost")]
pub gas_cost: u64,
/// ref <https://github.com/ethereum/go-ethereum/blob/366d2169fbc0e0f803b68c042b77b6b480836dbc/eth/tracers/logger/logger.go#L450-L452>
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory: Option<Vec<String>>,
pub op: String,
pub pc: u64,
#[serde(default, rename = "refund", skip_serializing_if = "Option::is_none")]
pub refund_counter: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stack: Option<Vec<U256>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub storage: Option<BTreeMap<H256, H256>>,
}

View File

@ -1,52 +1,6 @@
//! Types for tracing
pub mod common;
pub mod filter;
pub mod geth;
pub mod parity;
/// Geth tracing types
pub mod geth {
#![allow(missing_docs)]
use reth_primitives::{Bytes, JsonU256, H256, U256};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
// re-exported for geth tracing types
pub use ethers_core::types::GethDebugTracingOptions;
/// Geth Default trace frame
///
/// <https://github.com/ethereum/go-ethereum/blob/a9ef135e2dd53682d106c6a2aede9187026cc1de/eth/tracers/logger/logger.go#L406-L411>
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DefaultFrame {
pub failed: bool,
pub gas: JsonU256,
pub return_value: Bytes,
pub struct_logs: Vec<StructLog>,
}
/// Represents a struct log entry in a trace
///
/// <https://github.com/ethereum/go-ethereum/blob/366d2169fbc0e0f803b68c042b77b6b480836dbc/eth/tracers/logger/logger.go#L413-L426>
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct StructLog {
pub depth: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
pub gas: u64,
#[serde(rename = "gasCost")]
pub gas_cost: u64,
/// ref <https://github.com/ethereum/go-ethereum/blob/366d2169fbc0e0f803b68c042b77b6b480836dbc/eth/tracers/logger/logger.go#L450-L452>
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory: Option<Vec<String>>,
pub op: String,
pub pc: u64,
#[serde(default, rename = "refund", skip_serializing_if = "Option::is_none")]
pub refund_counter: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stack: Option<Vec<U256>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub storage: Option<BTreeMap<H256, H256>>,
}
}

View File

@ -7,6 +7,9 @@ use reth_primitives::{Address, Bytes, H256, U256, U64};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
/// Result type for parity style transaction trace
pub type TraceResult = crate::trace::common::TraceResult<TraceOutput, String>;
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum TraceType {
@ -171,13 +174,6 @@ pub enum TraceOutput {
Create(CreateOutput),
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum TraceResult {
Success { result: TraceOutput },
Error { error: String },
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionTrace {

View File

@ -1,9 +1,12 @@
use crate::{result::internal_rpc_err, EthApiSpec};
use async_trait::async_trait;
use jsonrpsee::core::RpcResult as Result;
use reth_primitives::{BlockId, Bytes, H256};
use jsonrpsee::core::RpcResult;
use reth_primitives::{BlockId, BlockNumberOrTag, Bytes, H256};
use reth_rpc_api::DebugApiServer;
use reth_rpc_types::RichBlock;
use reth_rpc_types::{
trace::geth::{BlockTraceResult, GethDebugTracingOptions, GethTraceFrame, TraceResult},
CallRequest, RichBlock,
};
/// `debug` API implementation.
///
@ -28,24 +31,79 @@ impl<Eth> DebugApiServer for DebugApi<Eth>
where
Eth: EthApiSpec + 'static,
{
async fn raw_header(&self, _block_id: BlockId) -> Result<Bytes> {
async fn raw_header(&self, _block_id: BlockId) -> RpcResult<Bytes> {
Err(internal_rpc_err("unimplemented"))
}
async fn raw_block(&self, _block_id: BlockId) -> Result<Bytes> {
async fn raw_block(&self, _block_id: BlockId) -> RpcResult<Bytes> {
Err(internal_rpc_err("unimplemented"))
}
/// Returns the bytes of the transaction for the given hash.
async fn raw_transaction(&self, _hash: H256) -> Result<Bytes> {
async fn raw_transaction(&self, _hash: H256) -> RpcResult<Bytes> {
Err(internal_rpc_err("unimplemented"))
}
async fn raw_receipts(&self, _block_id: BlockId) -> Result<Vec<Bytes>> {
async fn raw_receipts(&self, _block_id: BlockId) -> RpcResult<Vec<Bytes>> {
Err(internal_rpc_err("unimplemented"))
}
async fn bad_blocks(&self) -> Result<Vec<RichBlock>> {
async fn bad_blocks(&self) -> RpcResult<Vec<RichBlock>> {
Err(internal_rpc_err("unimplemented"))
}
/// Handler for `debug_traceChain`
async fn debug_trace_chain(
&self,
_start_exclusive: BlockNumberOrTag,
_end_inclusive: BlockNumberOrTag,
) -> RpcResult<Vec<BlockTraceResult>> {
Err(internal_rpc_err("unimplemented"))
}
/// Handler for `debug_traceBlock`
async fn debug_trace_block(
&self,
_rlp_block: Bytes,
_opts: GethDebugTracingOptions,
) -> RpcResult<Vec<TraceResult>> {
Err(internal_rpc_err("unimplemented"))
}
/// Handler for `debug_traceBlockByHash`
async fn debug_trace_block_by_hash(
&self,
_block: H256,
_opts: GethDebugTracingOptions,
) -> RpcResult<Vec<TraceResult>> {
Err(internal_rpc_err("unimplemented"))
}
/// Handler for `debug_traceBlockByNumber`
async fn debug_trace_block_by_number(
&self,
_block: BlockNumberOrTag,
_opts: GethDebugTracingOptions,
) -> RpcResult<Vec<TraceResult>> {
Err(internal_rpc_err("unimplemented"))
}
/// Handler for `debug_traceTransaction`
async fn debug_trace_transaction(
&self,
_tx_hash: H256,
_opts: GethDebugTracingOptions,
) -> RpcResult<GethTraceFrame> {
Err(internal_rpc_err("unimplemented"))
}
/// Handler for `debug_traceCall`
async fn debug_trace_call(
&self,
_request: CallRequest,
_block_number: Option<BlockId>,
_opts: GethDebugTracingOptions,
) -> RpcResult<GethTraceFrame> {
Err(internal_rpc_err("unimplemented"))
}
}