1use crate::{Evm, IntoTxEnv};
4use core::{fmt::Debug, iter::Peekable};
5use revm::{
6 context::result::{ExecutionResult, ResultAndState},
7 state::EvmState,
8 DatabaseCommit,
9};
10
11#[derive(Debug, Clone)]
13pub struct TxTracer<E: Evm> {
14 evm: E,
15 fused_inspector: E::Inspector,
16}
17
18#[derive(Debug)]
20pub struct TracingCtx<'a, T, E: Evm> {
21 pub tx: T,
23 pub result: ExecutionResult<E::HaltReason>,
25 pub state: &'a EvmState,
27 pub inspector: &'a mut E::Inspector,
29 pub db: &'a mut E::DB,
31 fused_inspector: &'a E::Inspector,
33 was_fused: &'a mut bool,
35}
36
37impl<'a, T, E: Evm<Inspector: Clone>> TracingCtx<'a, T, E> {
38 pub fn take_inspector(&mut self) -> E::Inspector {
40 *self.was_fused = true;
41 core::mem::replace(self.inspector, self.fused_inspector.clone())
42 }
43}
44
45impl<E: Evm<Inspector: Clone, DB: DatabaseCommit>> TxTracer<E> {
46 pub fn new(mut evm: E) -> Self {
48 Self { fused_inspector: evm.inspector_mut().clone(), evm }
49 }
50
51 fn fuse_inspector(&mut self) -> E::Inspector {
52 core::mem::replace(self.evm.inspector_mut(), self.fused_inspector.clone())
53 }
54
55 pub fn trace(
57 &mut self,
58 tx: impl IntoTxEnv<E::Tx>,
59 ) -> Result<TraceOutput<E::HaltReason, E::Inspector>, E::Error> {
60 let result = self.evm.transact_commit(tx);
61 let inspector = self.fuse_inspector();
62 Ok(TraceOutput { result: result?, inspector })
63 }
64
65 #[expect(clippy::type_complexity)]
68 pub fn trace_many<Txs, T, F, O>(
69 &mut self,
70 txs: Txs,
71 mut f: F,
72 ) -> TracerIter<'_, E, Txs::IntoIter, impl FnMut(TracingCtx<'_, T, E>) -> Result<O, E::Error>>
73 where
74 T: IntoTxEnv<E::Tx> + Clone,
75 Txs: IntoIterator<Item = T>,
76 F: FnMut(TracingCtx<'_, Txs::Item, E>) -> O,
77 {
78 self.try_trace_many(txs, move |ctx| Ok(f(ctx)))
79 }
80
81 pub fn try_trace_many<Txs, T, F, O, Err>(
83 &mut self,
84 txs: Txs,
85 hook: F,
86 ) -> TracerIter<'_, E, Txs::IntoIter, F>
87 where
88 T: IntoTxEnv<E::Tx> + Clone,
89 Txs: IntoIterator<Item = T>,
90 F: FnMut(TracingCtx<'_, T, E>) -> Result<O, Err>,
91 Err: From<E::Error>,
92 {
93 TracerIter {
94 inner: self,
95 txs: txs.into_iter().peekable(),
96 hook,
97 skip_last_commit: true,
98 fuse: true,
99 }
100 }
101}
102
103#[derive(Debug, Clone)]
105pub struct TraceOutput<H, I> {
106 pub result: ExecutionResult<H>,
108 pub inspector: I,
110}
111
112#[derive(derive_more::Debug)]
114#[debug(bound(E::Inspector: Debug))]
115pub struct TracerIter<'a, E: Evm, Txs: Iterator, F> {
116 inner: &'a mut TxTracer<E>,
117 txs: Peekable<Txs>,
118 hook: F,
119 skip_last_commit: bool,
120 fuse: bool,
121}
122
123impl<E: Evm, Txs: Iterator, F> TracerIter<'_, E, Txs, F> {
124 pub const fn commit_last_tx(mut self) -> Self {
129 self.skip_last_commit = false;
130 self
131 }
132
133 pub const fn no_fuse(mut self) -> Self {
135 self.fuse = false;
136 self
137 }
138}
139
140impl<E, T, Txs, F, O, Err> Iterator for TracerIter<'_, E, Txs, F>
141where
142 E: Evm<DB: DatabaseCommit, Inspector: Clone>,
143 T: IntoTxEnv<E::Tx> + Clone,
144 Txs: Iterator<Item = T>,
145 Err: From<E::Error>,
146 F: FnMut(TracingCtx<'_, T, E>) -> Result<O, Err>,
147{
148 type Item = Result<O, Err>;
149
150 fn next(&mut self) -> Option<Self::Item> {
151 let tx = self.txs.next()?;
152 let result = self.inner.evm.transact(tx.clone());
153
154 let TxTracer { evm, fused_inspector } = self.inner;
155 let (db, inspector, _) = evm.components_mut();
156
157 let Ok(ResultAndState { result, state }) = result else {
158 return None;
159 };
160 let mut was_fused = false;
161 let output = (self.hook)(TracingCtx {
162 tx,
163 result,
164 state: &state,
165 inspector,
166 db,
167 fused_inspector: &*fused_inspector,
168 was_fused: &mut was_fused,
169 });
170
171 if !self.skip_last_commit || self.txs.peek().is_some() {
174 db.commit(state);
175 }
176
177 if self.fuse && !was_fused {
178 self.inner.fuse_inspector();
179 }
180
181 Some(output)
182 }
183}