1use alloy_primitives::{map::HashSet, U256};
2use alloy_rpc_types_trace::{
3 geth::{
4 erc7562::Erc7562Config, CallConfig, FlatCallConfig, GethDefaultTracingOptions,
5 PreStateConfig,
6 },
7 parity::TraceType,
8};
9use revm::bytecode::opcode::OpCode;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13#[must_use]
14pub struct OpcodeFilter(U256);
15
16impl Default for OpcodeFilter {
17 fn default() -> Self {
18 Self::new()
19 }
20}
21
22impl OpcodeFilter {
23 #[inline]
25 pub const fn new() -> Self {
26 Self(U256::ZERO)
27 }
28
29 #[inline]
31 pub fn is_enabled(&self, op: OpCode) -> bool {
32 self.0.bit(op.get() as usize)
33 }
34
35 #[inline]
37 pub fn enable(&mut self, op: OpCode) -> &mut Self {
38 self.0.set_bit(op.get() as usize, true);
39 self
40 }
41
42 #[inline]
44 pub const fn enabled(mut self, op: OpCode) -> Self {
45 let index = op.get() as usize;
46 let mut limbs = self.0.into_limbs();
47 let (limb, bit) = (index / 64, index % 64);
48 limbs[limb] |= 1 << bit;
49 self.0 = U256::from_limbs(limbs);
50 self
51 }
52}
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
59pub struct TracingInspectorConfig {
60 pub record_steps: bool,
62 pub record_memory_snapshots: bool,
64 pub record_stack_snapshots: StackSnapshotType,
66 pub record_state_diff: bool,
68 pub record_returndata_snapshots: bool,
70 pub record_opcodes_filter: Option<OpcodeFilter>,
73 pub exclude_precompile_calls: bool,
75 pub record_logs: bool,
77 pub record_immediate_bytes: bool,
79}
80
81impl TracingInspectorConfig {
82 pub const fn all() -> Self {
84 Self {
85 record_steps: true,
86 record_memory_snapshots: true,
87 record_stack_snapshots: StackSnapshotType::All,
88 record_state_diff: true,
89 record_returndata_snapshots: true,
90 record_opcodes_filter: None,
91 exclude_precompile_calls: false,
92 record_logs: true,
93 record_immediate_bytes: true,
94 }
95 }
96
97 pub const fn none() -> Self {
99 Self {
100 record_steps: false,
101 record_memory_snapshots: false,
102 record_stack_snapshots: StackSnapshotType::None,
103 record_state_diff: false,
104 record_returndata_snapshots: false,
105 exclude_precompile_calls: false,
106 record_logs: false,
107 record_opcodes_filter: None,
108 record_immediate_bytes: false,
109 }
110 }
111
112 pub const fn default_parity() -> Self {
116 Self {
117 record_steps: false,
118 record_memory_snapshots: false,
119 record_stack_snapshots: StackSnapshotType::None,
120 record_state_diff: false,
121 record_returndata_snapshots: false,
122 exclude_precompile_calls: true,
123 record_logs: false,
124 record_opcodes_filter: None,
125 record_immediate_bytes: false,
126 }
127 }
128
129 pub const fn parity_statediff() -> Self {
136 Self::default_parity()
137 }
138
139 pub const fn parity_vm_trace() -> Self {
141 Self::default_parity()
142 .set_steps(true)
143 .set_stack_snapshots(StackSnapshotType::Pushes)
144 .set_memory_snapshots(true)
145 .set_state_diffs(true)
147 }
148
149 pub const fn default_geth() -> Self {
156 Self {
157 record_steps: true,
158 record_memory_snapshots: false,
159 record_stack_snapshots: StackSnapshotType::Full,
160 record_state_diff: true,
161 record_returndata_snapshots: false,
162 exclude_precompile_calls: false,
163 record_logs: false,
164 record_opcodes_filter: None,
165 record_immediate_bytes: false,
166 }
167 }
168
169 #[inline]
174 pub fn from_parity_config(trace_types: &HashSet<TraceType>) -> Self {
175 let needs_vm_trace = trace_types.contains(&TraceType::VmTrace);
176 let snap_type =
177 if needs_vm_trace { StackSnapshotType::Pushes } else { StackSnapshotType::None };
178 Self::default_parity()
179 .set_steps(needs_vm_trace)
180 .set_stack_snapshots(snap_type)
181 .set_memory_snapshots(needs_vm_trace)
182 }
183
184 #[inline]
189 pub fn from_geth_config(config: &GethDefaultTracingOptions) -> Self {
190 Self {
191 record_memory_snapshots: config.enable_memory.unwrap_or_default(),
192 record_stack_snapshots: if config.disable_stack.unwrap_or_default() {
193 StackSnapshotType::None
194 } else {
195 StackSnapshotType::Full
196 },
197 record_state_diff: !config.disable_storage.unwrap_or_default(),
198 record_returndata_snapshots: config.is_return_data_enabled(),
199 ..Self::default_geth()
200 }
201 }
202
203 #[inline]
208 pub fn from_geth_call_config(config: &CallConfig) -> Self {
209 Self::none()
210 .set_record_logs(config.with_log.unwrap_or_default())
212 }
213
214 #[inline]
217 pub fn from_geth_erc7562_config(config: &Erc7562Config) -> Self {
218 Self::none()
219 .set_record_logs(config.with_log.unwrap_or_default())
221 .set_memory_snapshots(true)
223 .set_stack_snapshots(StackSnapshotType::Full)
225 .set_state_diffs(true)
227 .steps()
228 }
229
230 #[inline]
237 pub fn from_flat_call_config(config: &FlatCallConfig) -> Self {
238 Self::default_parity()
239 .set_exclude_precompile_calls(!config.include_precompiles.unwrap_or_default())
241 }
242
243 #[inline]
249 pub const fn from_geth_prestate_config(_config: &PreStateConfig) -> Self {
250 Self::none()
251 }
252
253 #[inline]
255 pub fn merge(&mut self, other: Self) -> &mut Self {
256 self.record_steps |= other.record_steps;
257 self.record_memory_snapshots |= other.record_memory_snapshots;
258 self.record_stack_snapshots = other.record_stack_snapshots;
259 self.record_state_diff |= other.record_state_diff;
260 self.record_returndata_snapshots |= other.record_returndata_snapshots;
261 self.exclude_precompile_calls |= other.exclude_precompile_calls;
262 self.record_logs |= other.record_logs;
263 self.record_opcodes_filter = self.record_opcodes_filter.or(other.record_opcodes_filter);
264 self.record_immediate_bytes |= other.record_immediate_bytes;
265 self
266 }
267
268 pub const fn set_exclude_precompile_calls(mut self, exclude_precompile_calls: bool) -> Self {
272 self.exclude_precompile_calls = exclude_precompile_calls;
273 self
274 }
275
276 pub const fn disable_steps(self) -> Self {
278 self.set_steps(false)
279 }
280
281 pub const fn steps(self) -> Self {
283 self.set_steps(true)
284 }
285
286 pub const fn set_steps(mut self, record_steps: bool) -> Self {
288 self.record_steps = record_steps;
289 self
290 }
291
292 pub const fn disable_memory_snapshots(self) -> Self {
294 self.set_memory_snapshots(false)
295 }
296
297 pub const fn memory_snapshots(self) -> Self {
299 self.set_memory_snapshots(true)
300 }
301
302 pub const fn set_memory_snapshots(mut self, record_memory_snapshots: bool) -> Self {
304 self.record_memory_snapshots = record_memory_snapshots;
305 self
306 }
307
308 pub const fn disable_stack_snapshots(self) -> Self {
310 self.set_stack_snapshots(StackSnapshotType::None)
311 }
312
313 pub const fn stack_snapshots(self) -> Self {
315 self.set_stack_snapshots(StackSnapshotType::Full)
316 }
317
318 pub const fn set_stack_snapshots(mut self, record_stack_snapshots: StackSnapshotType) -> Self {
320 self.record_stack_snapshots = record_stack_snapshots;
321 self
322 }
323
324 pub const fn disable_state_diffs(self) -> Self {
326 self.set_state_diffs(false)
327 }
328
329 pub const fn set_state_diffs(mut self, record_state_diff: bool) -> Self {
331 self.record_state_diff = record_state_diff;
332 self
333 }
334
335 pub const fn with_state_diffs(self) -> Self {
339 self.set_steps_and_state_diffs(true)
340 }
341
342 pub const fn set_steps_and_state_diffs(mut self, steps_and_diffs: bool) -> Self {
347 self.record_steps = steps_and_diffs;
348 self.record_state_diff = steps_and_diffs;
349 self
350 }
351
352 pub const fn disable_record_logs(self) -> Self {
354 self.set_record_logs(false)
355 }
356
357 pub const fn record_logs(self) -> Self {
359 self.set_record_logs(true)
360 }
361
362 pub const fn set_record_logs(mut self, record_logs: bool) -> Self {
364 self.record_logs = record_logs;
365 self
366 }
367
368 pub const fn set_immediate_bytes(mut self, record_immediate_bytes: bool) -> Self {
370 self.record_immediate_bytes = record_immediate_bytes;
371 self
372 }
373
374 pub const fn record_immediate_bytes(self) -> Self {
376 self.set_immediate_bytes(true)
377 }
378
379 #[inline]
382 pub fn should_record_opcode(&self, op: OpCode) -> bool {
383 self.record_opcodes_filter.as_ref().is_none_or(|filter| filter.is_enabled(op))
384 }
385}
386
387#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
389pub enum StackSnapshotType {
390 #[default]
392 None,
393 All,
395 Pushes,
397 Full,
399}
400
401impl StackSnapshotType {
402 #[inline]
404 pub const fn is_all(self) -> bool {
405 matches!(self, Self::All)
406 }
407
408 #[inline]
410 pub const fn is_full(self) -> bool {
411 matches!(self, Self::Full)
412 }
413
414 #[inline]
416 pub const fn is_pushes(self) -> bool {
417 matches!(self, Self::Pushes)
418 }
419}
420
421#[derive(Clone, Copy, Debug, PartialEq, Eq)]
425pub(crate) enum TraceStyle {
426 Parity,
428 #[allow(dead_code)]
430 Geth,
431}
432
433impl TraceStyle {
434 pub(crate) const fn is_parity(self) -> bool {
436 matches!(self, Self::Parity)
437 }
438}
439
440#[cfg(test)]
441mod tests {
442 use super::*;
443
444 #[test]
445 fn test_parity_config() {
446 let mut s = HashSet::default();
447 s.insert(TraceType::StateDiff);
448 let config = TracingInspectorConfig::from_parity_config(&s);
449 assert!(!config.record_steps);
451 assert!(!config.record_state_diff);
452
453 let mut s = HashSet::default();
454 s.insert(TraceType::VmTrace);
455 let config = TracingInspectorConfig::from_parity_config(&s);
456 assert!(config.record_steps);
457 assert!(!config.record_state_diff);
458
459 let mut s = HashSet::default();
460 s.insert(TraceType::VmTrace);
461 s.insert(TraceType::StateDiff);
462 let config = TracingInspectorConfig::from_parity_config(&s);
463 assert!(config.record_steps);
464 assert!(!config.record_state_diff);
466 }
467
468 #[test]
469 fn test_flat_call_config() {
470 let config = FlatCallConfig { include_precompiles: Some(true), ..Default::default() };
471 let config = TracingInspectorConfig::from_flat_call_config(&config);
472 assert!(!config.exclude_precompile_calls);
473
474 let config = FlatCallConfig { include_precompiles: Some(false), ..Default::default() };
475 let config = TracingInspectorConfig::from_flat_call_config(&config);
476 assert!(config.exclude_precompile_calls);
477 }
478
479 #[test]
480 fn test_all_records_all_stack_snapshots() {
481 let config = TracingInspectorConfig::all();
484 assert_eq!(config.record_stack_snapshots, StackSnapshotType::All);
485 assert!(config.record_stack_snapshots.is_all());
486 }
487}