Skip to content
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
187 changes: 160 additions & 27 deletions crates/anvil/src/eth/backend/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
eth::{
backend::{
cheats::{CheatEcrecover, CheatsManager},
db::{Db, MaybeFullDatabase, SerializableState},
db::{Db, MaybeFullDatabase, SerializableState, StateDb},
env::Env,
executor::{ExecutedTransactions, TransactionExecutor},
fork::ClientFork,
Expand Down Expand Up @@ -71,8 +71,8 @@ use alloy_rpc_types::{
trace::{
filter::TraceFilter,
geth::{
GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingCallOptions,
GethDebugTracingOptions, GethTrace, NoopFrame,
FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerType,
GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, NoopFrame,
},
parity::LocalizedTransactionTrace,
},
Expand All @@ -99,7 +99,10 @@ use foundry_evm::{
constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE,
decode::RevertDecoder,
inspectors::AccessListInspector,
traces::{CallTraceDecoder, TracingInspectorConfig},
traces::{
CallTraceDecoder, FourByteInspector, GethTraceBuilder, TracingInspector,
TracingInspectorConfig,
},
utils::{get_blob_base_fee_update_fraction, get_blob_base_fee_update_fraction_by_spec_id},
};
use foundry_evm_core::{either_evm::EitherEvm, precompiles::EC_RECOVER};
Expand Down Expand Up @@ -1945,9 +1948,31 @@ impl Backend {
.geth_call_traces(call_config, result.gas_used())
.into())
}
GethDebugBuiltInTracerType::PreStateTracer => {
let pre_state_config = tracer_config
.into_pre_state_config()
.map_err(|e| RpcError::invalid_params(e.to_string()))?;

let mut inspector = TracingInspector::new(
TracingInspectorConfig::from_geth_prestate_config(
&pre_state_config,
),
);

let env = self.build_call_env(request, fee_details, block);
let mut evm =
self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector);
let result = evm.transact(env.tx)?;

drop(evm);

Ok(inspector
.into_geth_builder()
.geth_prestate_traces(&result, &pre_state_config, cache_db)?
.into())
}
GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()),
GethDebugBuiltInTracerType::FourByteTracer
| GethDebugBuiltInTracerType::PreStateTracer
| GethDebugBuiltInTracerType::MuxTracer
| GethDebugBuiltInTracerType::FlatCallTracer => {
Err(RpcError::invalid_params("unsupported tracer type").into())
Expand Down Expand Up @@ -2634,7 +2659,7 @@ impl Backend {
.await;
}

if let Some(trace) = self.mined_geth_trace_transaction(hash, opts.clone()) {
if let Some(trace) = self.mined_geth_trace_transaction(hash, opts.clone()).await {
return trace;
}

Expand All @@ -2645,16 +2670,19 @@ impl Backend {
Ok(GethTrace::Default(Default::default()))
}

/// Traces the transaction with the js tracer
#[cfg(feature = "js-tracer")]
pub async fn trace_tx_with_js_tracer(
fn replay_tx_with_inspector<I, F, T>(
&self,
hash: B256,
code: String,
opts: GethDebugTracingOptions,
) -> Result<GethTrace, BlockchainError> {
let GethDebugTracingOptions { tracer_config, .. } = opts;

mut inspector: I,
f: F,
) -> Result<T, BlockchainError>
where
for<'a> I: Inspector<EthEvmContext<WrapDatabaseRef<&'a CacheDB<Box<&'a StateDb>>>>>
+ Inspector<OpContext<WrapDatabaseRef<&'a CacheDB<Box<&'a StateDb>>>>>
+ 'a,
for<'a> F:
FnOnce(ResultAndState<OpHaltReason>, CacheDB<Box<&'a StateDb>>, I, TxEnv, Env) -> T,
Comment on lines +2679 to +2684
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are a bit horrific, but not much we can do here :D

{
let block = {
let storage = self.blockchain.storage.read();
let MinedTransaction { block_hash, .. } = storage
Expand Down Expand Up @@ -2730,23 +2758,41 @@ impl Backend {
let target_tx = PendingTransaction::from_maybe_impersonated(target_tx)?;
let tx_env = target_tx.to_revm_tx_env();

let config = tracer_config.into_json();
let mut inspector = revm_inspectors::tracing::js::JsInspector::new(code, config)
.map_err(|err| BlockchainError::Message(err.to_string()))?;
let mut evm = self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector);

let result = evm
.transact(tx_env.clone())
.map_err(|err| BlockchainError::Message(err.to_string()))?;

let trace = inspector
.json_result(
result,
&alloy_evm::IntoTxEnv::into_tx_env(tx_env),
&env.evm_env.block_env,
&cache_db,
)
.map_err(|e| BlockchainError::Message(e.to_string()))?;
Ok(f(result, cache_db, inspector, tx_env.base, env))
}

/// Traces the transaction with the js tracer
#[cfg(feature = "js-tracer")]
pub async fn trace_tx_with_js_tracer(
&self,
hash: B256,
code: String,
opts: GethDebugTracingOptions,
) -> Result<GethTrace, BlockchainError> {
let GethDebugTracingOptions { tracer_config, .. } = opts;
let config = tracer_config.into_json();
let inspector = revm_inspectors::tracing::js::JsInspector::new(code, config)
.map_err(|err| BlockchainError::Message(err.to_string()))?;
let trace = self.replay_tx_with_inspector(
hash,
inspector,
|result, cache_db, mut inspector, tx_env, env| {
inspector
.json_result(
result,
&alloy_evm::IntoTxEnv::into_tx_env(tx_env),
&env.evm_env.block_env,
&cache_db,
)
.map_err(|e| BlockchainError::Message(e.to_string()))
},
)??;
Ok(GethTrace::JS(trace))
}

Expand All @@ -2766,12 +2812,99 @@ impl Backend {
Ok(None)
}

fn mined_geth_trace_transaction(
fn geth_trace(
&self,
tx: &MinedTransaction,
opts: GethDebugTracingOptions,
) -> Result<GethTrace, BlockchainError> {
let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;

if let Some(tracer) = tracer {
match tracer {
GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
GethDebugBuiltInTracerType::FourByteTracer => {
let inspector = FourByteInspector::default();
let res = self.replay_tx_with_inspector(
tx.info.transaction_hash,
inspector,
|_, _, inspector, _, _| FourByteFrame::from(inspector).into(),
)?;
return Ok(res);
}
GethDebugBuiltInTracerType::CallTracer => {
return match tracer_config.into_call_config() {
Ok(call_config) => {
let inspector = TracingInspector::new(
TracingInspectorConfig::from_geth_call_config(&call_config),
);
let frame = self.replay_tx_with_inspector(
tx.info.transaction_hash,
inspector,
|_, _, inspector, _, _| {
inspector
.geth_builder()
.geth_call_traces(
call_config,
tx.receipt.cumulative_gas_used(),
)
.into()
},
)?;
Ok(frame)
}
Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
};
}
GethDebugBuiltInTracerType::PreStateTracer => {
return match tracer_config.into_pre_state_config() {
Ok(pre_state_config) => {
let inspector = TracingInspector::new(
TracingInspectorConfig::from_geth_prestate_config(
&pre_state_config,
),
);
let frame = self.replay_tx_with_inspector(
tx.info.transaction_hash,
inspector,
|state, db, inspector, _, _| {
inspector.geth_builder().geth_prestate_traces(
&state,
&pre_state_config,
db,
)
},
)??;
Ok(frame.into())
}
Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
};
}
GethDebugBuiltInTracerType::NoopTracer
| GethDebugBuiltInTracerType::MuxTracer
| GethDebugBuiltInTracerType::FlatCallTracer => {}
},
GethDebugTracerType::JsTracer(_code) => {}
}

return Ok(NoopFrame::default().into());
}

// default structlog tracer
Ok(GethTraceBuilder::new(tx.info.traces.clone())
.geth_traces(
tx.receipt.cumulative_gas_used(),
tx.info.out.clone().unwrap_or_default(),
config,
)
.into())
}

async fn mined_geth_trace_transaction(
&self,
hash: B256,
opts: GethDebugTracingOptions,
) -> Option<Result<GethTrace, BlockchainError>> {
self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.geth_trace(opts))
self.blockchain.storage.read().transactions.get(&hash).map(|tx| self.geth_trace(tx, opts))
}

/// Returns the traces for the given block
Expand Down
49 changes: 1 addition & 48 deletions crates/anvil/src/eth/backend/mem/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use crate::eth::{
env::Env,
mem::cache::DiskStateCache,
},
error::BlockchainError,
pool::transactions::PoolTransaction,
};
use alloy_consensus::constants::EMPTY_WITHDRAWALS;
Expand All @@ -20,10 +19,6 @@ use alloy_primitives::{
use alloy_rpc_types::{
BlockId, BlockNumberOrTag, TransactionInfo as RethTransactionInfo,
trace::{
geth::{
FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerType,
GethDebugTracingOptions, GethTrace, NoopFrame,
},
otterscan::{InternalOperation, OperationType},
parity::LocalizedTransactionTrace,
},
Expand All @@ -32,12 +27,9 @@ use anvil_core::eth::{
block::{Block, PartialHeader},
transaction::{MaybeImpersonatedTransaction, ReceiptResponse, TransactionInfo, TypedReceipt},
};
use anvil_rpc::error::RpcError;
use foundry_evm::{
backend::MemDb,
traces::{
CallKind, FourByteInspector, GethTraceBuilder, ParityTraceBuilder, TracingInspectorConfig,
},
traces::{CallKind, ParityTraceBuilder, TracingInspectorConfig},
};
use parking_lot::RwLock;
use revm::{context::Block as RevmBlock, primitives::hardfork::SpecId};
Expand Down Expand Up @@ -558,45 +550,6 @@ impl MinedTransaction {
})
.collect()
}

pub fn geth_trace(&self, opts: GethDebugTracingOptions) -> Result<GethTrace, BlockchainError> {
let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;

if let Some(tracer) = tracer {
match tracer {
GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
GethDebugBuiltInTracerType::FourByteTracer => {
let inspector = FourByteInspector::default();
return Ok(FourByteFrame::from(inspector).into());
}
GethDebugBuiltInTracerType::CallTracer => {
return match tracer_config.into_call_config() {
Ok(call_config) => Ok(GethTraceBuilder::new(self.info.traces.clone())
.geth_call_traces(call_config, self.receipt.cumulative_gas_used())
.into()),
Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
};
}
GethDebugBuiltInTracerType::PreStateTracer
| GethDebugBuiltInTracerType::NoopTracer
| GethDebugBuiltInTracerType::MuxTracer
| GethDebugBuiltInTracerType::FlatCallTracer => {}
},
GethDebugTracerType::JsTracer(_code) => {}
}

return Ok(NoopFrame::default().into());
}

// default structlog tracer
Ok(GethTraceBuilder::new(self.info.traces.clone())
.geth_traces(
self.receipt.cumulative_gas_used(),
self.info.out.clone().unwrap_or_default(),
config,
)
.into())
}
}

/// Intermediary Anvil representation of a receipt
Expand Down
Loading
Loading