mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(rpc): impl traceCall (#2029)
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
use crate::tracing::{types::CallTraceNode, TracingInspectorConfig};
|
||||
use reth_rpc_types::{trace::parity::*, TransactionInfo};
|
||||
use revm::primitives::ExecutionResult;
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// A type for creating parity style traces
|
||||
@ -82,6 +83,24 @@ impl ParityTraceBuilder {
|
||||
self.into_localized_transaction_traces_iter(info).collect()
|
||||
}
|
||||
|
||||
/// Consumes the inspector and returns the trace results according to the configured trace
|
||||
/// types.
|
||||
pub fn into_trace_results(
|
||||
self,
|
||||
res: ExecutionResult,
|
||||
trace_types: &HashSet<TraceType>,
|
||||
) -> TraceResults {
|
||||
let output = match res {
|
||||
ExecutionResult::Success { output, .. } => output.into_data(),
|
||||
ExecutionResult::Revert { output, .. } => output,
|
||||
ExecutionResult::Halt { .. } => Default::default(),
|
||||
};
|
||||
|
||||
let (trace, vm_trace, state_diff) = self.into_trace_type_traces(trace_types);
|
||||
|
||||
TraceResults { output: output.into(), trace, vm_trace, state_diff }
|
||||
}
|
||||
|
||||
/// Returns the tracing types that are configured in the set
|
||||
pub fn into_trace_type_traces(
|
||||
self,
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
use crate::{
|
||||
stack::MaybeOwnedInspector,
|
||||
tracing::{
|
||||
types::{CallKind, LogCallOrder, RawLog},
|
||||
utils::{gas_used, get_create_address},
|
||||
},
|
||||
use crate::tracing::{
|
||||
types::{CallKind, LogCallOrder, RawLog},
|
||||
utils::{gas_used, get_create_address},
|
||||
};
|
||||
pub use arena::CallTraceArena;
|
||||
use reth_primitives::{bytes::Bytes, Address, H256, U256};
|
||||
@ -46,10 +43,7 @@ pub struct TracingInspector {
|
||||
/// Tracks the return value of the last call
|
||||
last_call_return_data: Option<Bytes>,
|
||||
/// The gas inspector used to track remaining gas.
|
||||
///
|
||||
/// This is either owned by this inspector directly or part of a stack of inspectors, in which
|
||||
/// case all delegated functions are no-ops.
|
||||
gas_inspector: MaybeOwnedInspector<GasInspector>,
|
||||
gas_inspector: GasInspector,
|
||||
}
|
||||
|
||||
// === impl TracingInspector ===
|
||||
@ -77,20 +71,6 @@ impl TracingInspector {
|
||||
GethTraceBuilder::new(self.traces.arena, self.config)
|
||||
}
|
||||
|
||||
/// Configures a [GasInspector]
|
||||
///
|
||||
/// If this [TracingInspector] is part of a stack [InspectorStack](crate::stack::InspectorStack)
|
||||
/// which already uses a [GasInspector], it can be reused as [MaybeOwnedInspector::Stacked] in
|
||||
/// which case the `gas_inspector`'s usage will be a no-op within the context of this
|
||||
/// [TracingInspector].
|
||||
pub fn with_stacked_gas_inspector(
|
||||
mut self,
|
||||
gas_inspector: MaybeOwnedInspector<GasInspector>,
|
||||
) -> Self {
|
||||
self.gas_inspector = gas_inspector;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the last trace [CallTrace] index from the stack.
|
||||
///
|
||||
/// # Panics
|
||||
@ -201,7 +181,7 @@ impl TracingInspector {
|
||||
stack,
|
||||
memory,
|
||||
memory_size: interp.memory.len(),
|
||||
gas: self.gas_inspector.as_ref().gas_remaining(),
|
||||
gas: self.gas_inspector.gas_remaining(),
|
||||
gas_refund_counter: interp.gas.refunded() as u64,
|
||||
|
||||
// fields will be populated end of call
|
||||
@ -251,7 +231,7 @@ impl TracingInspector {
|
||||
};
|
||||
}
|
||||
|
||||
step.gas_cost = step.gas - self.gas_inspector.as_ref().gas_remaining();
|
||||
step.gas_cost = step.gas - self.gas_inspector.gas_remaining();
|
||||
}
|
||||
|
||||
// set the status
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::{
|
||||
error::{EthApiError, EthResult, InvalidTransactionError, RevertError},
|
||||
revm_utils::{
|
||||
build_call_evm_env, cap_tx_gas_limit_with_caller_allowance, get_precompiles, inspect,
|
||||
prepare_call_env, transact,
|
||||
transact,
|
||||
},
|
||||
EthTransactions,
|
||||
},
|
||||
@ -18,11 +18,11 @@ use reth_revm::{
|
||||
access_list::AccessListInspector,
|
||||
database::{State, SubState},
|
||||
};
|
||||
use reth_rpc_types::{state::StateOverride, CallRequest};
|
||||
use reth_rpc_types::CallRequest;
|
||||
use reth_transaction_pool::TransactionPool;
|
||||
use revm::{
|
||||
db::{CacheDB, DatabaseRef},
|
||||
primitives::{BlockEnv, CfgEnv, Env, ExecutionResult, Halt, ResultAndState, TransactTo},
|
||||
primitives::{BlockEnv, CfgEnv, Env, ExecutionResult, Halt, TransactTo},
|
||||
};
|
||||
use tracing::trace;
|
||||
|
||||
@ -36,22 +36,6 @@ where
|
||||
Client: BlockProvider + StateProviderFactory + EvmEnvProvider + 'static,
|
||||
Network: Send + Sync + 'static,
|
||||
{
|
||||
/// Executes the call request at the given [BlockId]
|
||||
pub(crate) async fn transact_call_at(
|
||||
&self,
|
||||
request: CallRequest,
|
||||
at: BlockId,
|
||||
state_overrides: Option<StateOverride>,
|
||||
) -> EthResult<(ResultAndState, Env)> {
|
||||
let (cfg, block_env, at) = self.evm_env_at(at).await?;
|
||||
let state = self.state_at(at)?;
|
||||
let mut db = SubState::new(State::new(state));
|
||||
|
||||
let env = prepare_call_env(cfg, block_env, request, &mut db, state_overrides)?;
|
||||
trace!(target: "rpc::eth::call", ?env, "Executing call");
|
||||
transact(&mut db, env)
|
||||
}
|
||||
|
||||
/// Estimate gas needed for execution of the `request` at the [BlockId].
|
||||
pub(crate) async fn estimate_gas_at(
|
||||
&self,
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
use crate::{
|
||||
eth::{
|
||||
error::{EthApiError, EthResult, SignError},
|
||||
revm_utils::{inspect, prepare_call_env, transact},
|
||||
utils::recover_raw_transaction,
|
||||
},
|
||||
EthApi,
|
||||
@ -15,13 +16,18 @@ use reth_primitives::{
|
||||
TxLegacy, H256, U128, U256, U64,
|
||||
};
|
||||
use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderBox, StateProviderFactory};
|
||||
use reth_revm::database::{State, SubState};
|
||||
use reth_rpc_types::{
|
||||
Index, Log, Transaction, TransactionInfo, TransactionReceipt, TransactionRequest,
|
||||
TypedTransactionRequest,
|
||||
state::StateOverride, CallRequest, Index, Log, Transaction, TransactionInfo,
|
||||
TransactionReceipt, TransactionRequest, TypedTransactionRequest,
|
||||
};
|
||||
use reth_transaction_pool::{TransactionOrigin, TransactionPool};
|
||||
use revm::primitives::{BlockEnv, CfgEnv};
|
||||
use revm_primitives::utilities::create_address;
|
||||
use revm::{
|
||||
db::CacheDB,
|
||||
primitives::{BlockEnv, CfgEnv},
|
||||
Inspector,
|
||||
};
|
||||
use revm_primitives::{utilities::create_address, Env, ResultAndState};
|
||||
|
||||
/// Commonly used transaction related functions for the [EthApi] type in the `eth_` namespace
|
||||
#[async_trait::async_trait]
|
||||
@ -67,6 +73,37 @@ pub trait EthTransactions: Send + Sync {
|
||||
/// Signs transaction with a matching signer, if any and submits the transaction to the pool.
|
||||
/// Returns the hash of the signed transaction.
|
||||
async fn send_transaction(&self, request: TransactionRequest) -> EthResult<H256>;
|
||||
|
||||
/// Prepares the state and env for the given [CallRequest] at the given [BlockId] and executes
|
||||
/// the closure.
|
||||
async fn with_call_at<F, R>(
|
||||
&self,
|
||||
request: CallRequest,
|
||||
at: BlockId,
|
||||
state_overrides: Option<StateOverride>,
|
||||
f: F,
|
||||
) -> EthResult<R>
|
||||
where
|
||||
F: for<'r> FnOnce(CacheDB<State<StateProviderBox<'r>>>, Env) -> EthResult<R> + Send;
|
||||
|
||||
/// Executes the call request at the given [BlockId].
|
||||
async fn transact_call_at(
|
||||
&self,
|
||||
request: CallRequest,
|
||||
at: BlockId,
|
||||
state_overrides: Option<StateOverride>,
|
||||
) -> EthResult<(ResultAndState, Env)>;
|
||||
|
||||
/// Executes the call request at the given [BlockId]
|
||||
async fn inspect_call_at<I>(
|
||||
&self,
|
||||
request: CallRequest,
|
||||
at: BlockId,
|
||||
state_overrides: Option<StateOverride>,
|
||||
inspector: I,
|
||||
) -> EthResult<(ResultAndState, Env)>
|
||||
where
|
||||
I: for<'r> Inspector<CacheDB<State<StateProviderBox<'r>>>> + Send;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -212,6 +249,46 @@ where
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
async fn with_call_at<F, R>(
|
||||
&self,
|
||||
request: CallRequest,
|
||||
at: BlockId,
|
||||
state_overrides: Option<StateOverride>,
|
||||
f: F,
|
||||
) -> EthResult<R>
|
||||
where
|
||||
F: for<'r> FnOnce(CacheDB<State<StateProviderBox<'r>>>, Env) -> EthResult<R> + Send,
|
||||
{
|
||||
let (cfg, block_env, at) = self.evm_env_at(at).await?;
|
||||
let state = self.state_at(at)?;
|
||||
let mut db = SubState::new(State::new(state));
|
||||
|
||||
let env = prepare_call_env(cfg, block_env, request, &mut db, state_overrides)?;
|
||||
f(db, env)
|
||||
}
|
||||
|
||||
async fn transact_call_at(
|
||||
&self,
|
||||
request: CallRequest,
|
||||
at: BlockId,
|
||||
state_overrides: Option<StateOverride>,
|
||||
) -> EthResult<(ResultAndState, Env)> {
|
||||
self.with_call_at(request, at, state_overrides, |mut db, env| transact(&mut db, env)).await
|
||||
}
|
||||
|
||||
async fn inspect_call_at<I>(
|
||||
&self,
|
||||
request: CallRequest,
|
||||
at: BlockId,
|
||||
state_overrides: Option<StateOverride>,
|
||||
inspector: I,
|
||||
) -> EthResult<(ResultAndState, Env)>
|
||||
where
|
||||
I: for<'r> Inspector<CacheDB<State<StateProviderBox<'r>>>> + Send,
|
||||
{
|
||||
self.with_call_at(request, at, state_overrides, |db, env| inspect(db, env, inspector)).await
|
||||
}
|
||||
}
|
||||
|
||||
// === impl EthApi ===
|
||||
|
||||
@ -20,7 +20,7 @@ use reth_rpc_types::{
|
||||
trace::{filter::TraceFilter, parity::*},
|
||||
CallRequest, Index,
|
||||
};
|
||||
use revm::primitives::{Env, ExecutionResult, ResultAndState};
|
||||
use revm::primitives::{Env, ResultAndState};
|
||||
use std::collections::HashSet;
|
||||
use tokio::sync::{AcquireError, OwnedSemaphorePermit};
|
||||
|
||||
@ -92,11 +92,20 @@ where
|
||||
/// Executes the given call and returns a number of possible traces for it.
|
||||
pub async fn trace_call(
|
||||
&self,
|
||||
_call: CallRequest,
|
||||
_trace_types: HashSet<TraceType>,
|
||||
_block_id: Option<BlockId>,
|
||||
call: CallRequest,
|
||||
trace_types: HashSet<TraceType>,
|
||||
block_id: Option<BlockId>,
|
||||
) -> EthResult<TraceResults> {
|
||||
todo!()
|
||||
let _permit = self.acquire_trace_permit().await;
|
||||
let at = block_id.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest));
|
||||
let config = tracing_config(&trace_types);
|
||||
let mut inspector = TracingInspector::new(config);
|
||||
|
||||
let (res, _) = self.eth_api.inspect_call_at(call, at, None, &mut inspector).await?;
|
||||
|
||||
let trace_res =
|
||||
inspector.into_parity_builder().into_trace_results(res.result, &trace_types);
|
||||
Ok(trace_res)
|
||||
}
|
||||
|
||||
/// Traces a call to `eth_sendRawTransaction` without making the call, returning the traces.
|
||||
@ -119,18 +128,9 @@ where
|
||||
let config = tracing_config(&trace_types);
|
||||
|
||||
self.trace_at(env, config, at, |inspector, res| {
|
||||
let output = match res.result {
|
||||
ExecutionResult::Success { output, .. } => output.into_data(),
|
||||
ExecutionResult::Revert { output, .. } => output,
|
||||
ExecutionResult::Halt { .. } => Default::default(),
|
||||
};
|
||||
|
||||
let (trace, vm_trace, state_diff) =
|
||||
inspector.into_parity_builder().into_trace_type_traces(&trace_types);
|
||||
|
||||
let res = TraceResults { output: output.into(), trace, vm_trace, state_diff };
|
||||
|
||||
Ok(res)
|
||||
let trace_res =
|
||||
inspector.into_parity_builder().into_trace_results(res.result, &trace_types);
|
||||
Ok(trace_res)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user