Skip to content

Commit ad1f9ab

Browse files
w1tchermattssegrandizzy
authored
feat(anvil): add PreStateTracer support for debug_traceTransaction (#11709)
* feat(anvil): add PreStateTracer support for debug_traceTransaction * feat(anvil): add PreStateTracer support to debug_traceTransaction via transaction replay * Fix clippy --------- Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de> Co-authored-by: grandizzy <grandizzy.the.egg@gmail.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
1 parent 5dcf01b commit ad1f9ab

File tree

3 files changed

+334
-77
lines changed

3 files changed

+334
-77
lines changed

crates/anvil/src/eth/backend/mem/mod.rs

Lines changed: 160 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
eth::{
99
backend::{
1010
cheats::{CheatEcrecover, CheatsManager},
11-
db::{Db, MaybeFullDatabase, SerializableState},
11+
db::{Db, MaybeFullDatabase, SerializableState, StateDb},
1212
env::Env,
1313
executor::{ExecutedTransactions, TransactionExecutor},
1414
fork::ClientFork,
@@ -71,8 +71,8 @@ use alloy_rpc_types::{
7171
trace::{
7272
filter::TraceFilter,
7373
geth::{
74-
GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingCallOptions,
75-
GethDebugTracingOptions, GethTrace, NoopFrame,
74+
FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerType,
75+
GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, NoopFrame,
7676
},
7777
parity::LocalizedTransactionTrace,
7878
},
@@ -99,7 +99,10 @@ use foundry_evm::{
9999
constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE,
100100
decode::RevertDecoder,
101101
inspectors::AccessListInspector,
102-
traces::{CallTraceDecoder, TracingInspectorConfig},
102+
traces::{
103+
CallTraceDecoder, FourByteInspector, GethTraceBuilder, TracingInspector,
104+
TracingInspectorConfig,
105+
},
103106
utils::{get_blob_base_fee_update_fraction, get_blob_base_fee_update_fraction_by_spec_id},
104107
};
105108
use foundry_evm_core::{either_evm::EitherEvm, precompiles::EC_RECOVER};
@@ -1945,9 +1948,31 @@ impl Backend {
19451948
.geth_call_traces(call_config, result.gas_used())
19461949
.into())
19471950
}
1951+
GethDebugBuiltInTracerType::PreStateTracer => {
1952+
let pre_state_config = tracer_config
1953+
.into_pre_state_config()
1954+
.map_err(|e| RpcError::invalid_params(e.to_string()))?;
1955+
1956+
let mut inspector = TracingInspector::new(
1957+
TracingInspectorConfig::from_geth_prestate_config(
1958+
&pre_state_config,
1959+
),
1960+
);
1961+
1962+
let env = self.build_call_env(request, fee_details, block);
1963+
let mut evm =
1964+
self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector);
1965+
let result = evm.transact(env.tx)?;
1966+
1967+
drop(evm);
1968+
1969+
Ok(inspector
1970+
.into_geth_builder()
1971+
.geth_prestate_traces(&result, &pre_state_config, cache_db)?
1972+
.into())
1973+
}
19481974
GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()),
19491975
GethDebugBuiltInTracerType::FourByteTracer
1950-
| GethDebugBuiltInTracerType::PreStateTracer
19511976
| GethDebugBuiltInTracerType::MuxTracer
19521977
| GethDebugBuiltInTracerType::FlatCallTracer => {
19531978
Err(RpcError::invalid_params("unsupported tracer type").into())
@@ -2634,7 +2659,7 @@ impl Backend {
26342659
.await;
26352660
}
26362661

2637-
if let Some(trace) = self.mined_geth_trace_transaction(hash, opts.clone()) {
2662+
if let Some(trace) = self.mined_geth_trace_transaction(hash, opts.clone()).await {
26382663
return trace;
26392664
}
26402665

@@ -2645,16 +2670,19 @@ impl Backend {
26452670
Ok(GethTrace::Default(Default::default()))
26462671
}
26472672

2648-
/// Traces the transaction with the js tracer
2649-
#[cfg(feature = "js-tracer")]
2650-
pub async fn trace_tx_with_js_tracer(
2673+
fn replay_tx_with_inspector<I, F, T>(
26512674
&self,
26522675
hash: B256,
2653-
code: String,
2654-
opts: GethDebugTracingOptions,
2655-
) -> Result<GethTrace, BlockchainError> {
2656-
let GethDebugTracingOptions { tracer_config, .. } = opts;
2657-
2676+
mut inspector: I,
2677+
f: F,
2678+
) -> Result<T, BlockchainError>
2679+
where
2680+
for<'a> I: Inspector<EthEvmContext<WrapDatabaseRef<&'a CacheDB<Box<&'a StateDb>>>>>
2681+
+ Inspector<OpContext<WrapDatabaseRef<&'a CacheDB<Box<&'a StateDb>>>>>
2682+
+ 'a,
2683+
for<'a> F:
2684+
FnOnce(ResultAndState<OpHaltReason>, CacheDB<Box<&'a StateDb>>, I, TxEnv, Env) -> T,
2685+
{
26582686
let block = {
26592687
let storage = self.blockchain.storage.read();
26602688
let MinedTransaction { block_hash, .. } = storage
@@ -2730,23 +2758,41 @@ impl Backend {
27302758
let target_tx = PendingTransaction::from_maybe_impersonated(target_tx)?;
27312759
let tx_env = target_tx.to_revm_tx_env();
27322760

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

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

2742-
let trace = inspector
2743-
.json_result(
2744-
result,
2745-
&alloy_evm::IntoTxEnv::into_tx_env(tx_env),
2746-
&env.evm_env.block_env,
2747-
&cache_db,
2748-
)
2749-
.map_err(|e| BlockchainError::Message(e.to_string()))?;
2767+
Ok(f(result, cache_db, inspector, tx_env.base, env))
2768+
}
2769+
2770+
/// Traces the transaction with the js tracer
2771+
#[cfg(feature = "js-tracer")]
2772+
pub async fn trace_tx_with_js_tracer(
2773+
&self,
2774+
hash: B256,
2775+
code: String,
2776+
opts: GethDebugTracingOptions,
2777+
) -> Result<GethTrace, BlockchainError> {
2778+
let GethDebugTracingOptions { tracer_config, .. } = opts;
2779+
let config = tracer_config.into_json();
2780+
let inspector = revm_inspectors::tracing::js::JsInspector::new(code, config)
2781+
.map_err(|err| BlockchainError::Message(err.to_string()))?;
2782+
let trace = self.replay_tx_with_inspector(
2783+
hash,
2784+
inspector,
2785+
|result, cache_db, mut inspector, tx_env, env| {
2786+
inspector
2787+
.json_result(
2788+
result,
2789+
&alloy_evm::IntoTxEnv::into_tx_env(tx_env),
2790+
&env.evm_env.block_env,
2791+
&cache_db,
2792+
)
2793+
.map_err(|e| BlockchainError::Message(e.to_string()))
2794+
},
2795+
)??;
27502796
Ok(GethTrace::JS(trace))
27512797
}
27522798

@@ -2766,12 +2812,99 @@ impl Backend {
27662812
Ok(None)
27672813
}
27682814

2769-
fn mined_geth_trace_transaction(
2815+
fn geth_trace(
2816+
&self,
2817+
tx: &MinedTransaction,
2818+
opts: GethDebugTracingOptions,
2819+
) -> Result<GethTrace, BlockchainError> {
2820+
let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;
2821+
2822+
if let Some(tracer) = tracer {
2823+
match tracer {
2824+
GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
2825+
GethDebugBuiltInTracerType::FourByteTracer => {
2826+
let inspector = FourByteInspector::default();
2827+
let res = self.replay_tx_with_inspector(
2828+
tx.info.transaction_hash,
2829+
inspector,
2830+
|_, _, inspector, _, _| FourByteFrame::from(inspector).into(),
2831+
)?;
2832+
return Ok(res);
2833+
}
2834+
GethDebugBuiltInTracerType::CallTracer => {
2835+
return match tracer_config.into_call_config() {
2836+
Ok(call_config) => {
2837+
let inspector = TracingInspector::new(
2838+
TracingInspectorConfig::from_geth_call_config(&call_config),
2839+
);
2840+
let frame = self.replay_tx_with_inspector(
2841+
tx.info.transaction_hash,
2842+
inspector,
2843+
|_, _, inspector, _, _| {
2844+
inspector
2845+
.geth_builder()
2846+
.geth_call_traces(
2847+
call_config,
2848+
tx.receipt.cumulative_gas_used(),
2849+
)
2850+
.into()
2851+
},
2852+
)?;
2853+
Ok(frame)
2854+
}
2855+
Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
2856+
};
2857+
}
2858+
GethDebugBuiltInTracerType::PreStateTracer => {
2859+
return match tracer_config.into_pre_state_config() {
2860+
Ok(pre_state_config) => {
2861+
let inspector = TracingInspector::new(
2862+
TracingInspectorConfig::from_geth_prestate_config(
2863+
&pre_state_config,
2864+
),
2865+
);
2866+
let frame = self.replay_tx_with_inspector(
2867+
tx.info.transaction_hash,
2868+
inspector,
2869+
|state, db, inspector, _, _| {
2870+
inspector.geth_builder().geth_prestate_traces(
2871+
&state,
2872+
&pre_state_config,
2873+
db,
2874+
)
2875+
},
2876+
)??;
2877+
Ok(frame.into())
2878+
}
2879+
Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
2880+
};
2881+
}
2882+
GethDebugBuiltInTracerType::NoopTracer
2883+
| GethDebugBuiltInTracerType::MuxTracer
2884+
| GethDebugBuiltInTracerType::FlatCallTracer => {}
2885+
},
2886+
GethDebugTracerType::JsTracer(_code) => {}
2887+
}
2888+
2889+
return Ok(NoopFrame::default().into());
2890+
}
2891+
2892+
// default structlog tracer
2893+
Ok(GethTraceBuilder::new(tx.info.traces.clone())
2894+
.geth_traces(
2895+
tx.receipt.cumulative_gas_used(),
2896+
tx.info.out.clone().unwrap_or_default(),
2897+
config,
2898+
)
2899+
.into())
2900+
}
2901+
2902+
async fn mined_geth_trace_transaction(
27702903
&self,
27712904
hash: B256,
27722905
opts: GethDebugTracingOptions,
27732906
) -> Option<Result<GethTrace, BlockchainError>> {
2774-
self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.geth_trace(opts))
2907+
self.blockchain.storage.read().transactions.get(&hash).map(|tx| self.geth_trace(tx, opts))
27752908
}
27762909

27772910
/// Returns the traces for the given block

crates/anvil/src/eth/backend/mem/storage.rs

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use crate::eth::{
88
env::Env,
99
mem::cache::DiskStateCache,
1010
},
11-
error::BlockchainError,
1211
pool::transactions::PoolTransaction,
1312
};
1413
use alloy_consensus::constants::EMPTY_WITHDRAWALS;
@@ -20,10 +19,6 @@ use alloy_primitives::{
2019
use alloy_rpc_types::{
2120
BlockId, BlockNumberOrTag, TransactionInfo as RethTransactionInfo,
2221
trace::{
23-
geth::{
24-
FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerType,
25-
GethDebugTracingOptions, GethTrace, NoopFrame,
26-
},
2722
otterscan::{InternalOperation, OperationType},
2823
parity::LocalizedTransactionTrace,
2924
},
@@ -32,12 +27,9 @@ use anvil_core::eth::{
3227
block::{Block, PartialHeader},
3328
transaction::{MaybeImpersonatedTransaction, ReceiptResponse, TransactionInfo, TypedReceipt},
3429
};
35-
use anvil_rpc::error::RpcError;
3630
use foundry_evm::{
3731
backend::MemDb,
38-
traces::{
39-
CallKind, FourByteInspector, GethTraceBuilder, ParityTraceBuilder, TracingInspectorConfig,
40-
},
32+
traces::{CallKind, ParityTraceBuilder, TracingInspectorConfig},
4133
};
4234
use parking_lot::RwLock;
4335
use revm::{context::Block as RevmBlock, primitives::hardfork::SpecId};
@@ -558,45 +550,6 @@ impl MinedTransaction {
558550
})
559551
.collect()
560552
}
561-
562-
pub fn geth_trace(&self, opts: GethDebugTracingOptions) -> Result<GethTrace, BlockchainError> {
563-
let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;
564-
565-
if let Some(tracer) = tracer {
566-
match tracer {
567-
GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
568-
GethDebugBuiltInTracerType::FourByteTracer => {
569-
let inspector = FourByteInspector::default();
570-
return Ok(FourByteFrame::from(inspector).into());
571-
}
572-
GethDebugBuiltInTracerType::CallTracer => {
573-
return match tracer_config.into_call_config() {
574-
Ok(call_config) => Ok(GethTraceBuilder::new(self.info.traces.clone())
575-
.geth_call_traces(call_config, self.receipt.cumulative_gas_used())
576-
.into()),
577-
Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
578-
};
579-
}
580-
GethDebugBuiltInTracerType::PreStateTracer
581-
| GethDebugBuiltInTracerType::NoopTracer
582-
| GethDebugBuiltInTracerType::MuxTracer
583-
| GethDebugBuiltInTracerType::FlatCallTracer => {}
584-
},
585-
GethDebugTracerType::JsTracer(_code) => {}
586-
}
587-
588-
return Ok(NoopFrame::default().into());
589-
}
590-
591-
// default structlog tracer
592-
Ok(GethTraceBuilder::new(self.info.traces.clone())
593-
.geth_traces(
594-
self.receipt.cumulative_gas_used(),
595-
self.info.out.clone().unwrap_or_default(),
596-
config,
597-
)
598-
.into())
599-
}
600553
}
601554

602555
/// Intermediary Anvil representation of a receipt

0 commit comments

Comments
 (0)