1pub use crate::opcode_tracer::{LevmOpcodeTracer, OpcodeTracerConfig};
2use crate::{
3 errors::{ContextResult, InternalError, TxResult, VMError},
4 vm::VM,
5};
6use bytes::Bytes;
7use ethrex_common::{
8 Address, U256,
9 tracing::{CallLog, CallTraceFrame, CallType},
10 types::Log,
11};
12
13#[derive(Debug, Default)]
16pub struct LevmCallTracer {
17 pub callframes: Vec<CallTraceFrame>,
19 pub only_top_call: bool,
21 pub with_log: bool,
23 pub active: bool,
25}
26
27impl LevmCallTracer {
28 pub fn new(only_top_call: bool, with_log: bool) -> Self {
29 LevmCallTracer {
30 callframes: vec![],
31 only_top_call,
32 with_log,
33 active: true,
34 }
35 }
36
37 pub fn disabled() -> Self {
41 LevmCallTracer {
42 active: false,
43 ..Default::default()
44 }
45 }
46
47 pub fn enter(
49 &mut self,
50 call_type: CallType,
51 from: Address,
52 to: Address,
53 value: U256,
54 gas: u64,
55 input: &Bytes, ) {
57 if !self.active {
58 return;
59 }
60 if self.only_top_call && !self.callframes.is_empty() {
61 return;
63 }
64
65 let callframe = CallTraceFrame {
66 call_type,
67 from,
68 to,
69 value,
70 gas,
71 input: input.clone(),
72 ..Default::default()
73 };
74
75 self.callframes.push(callframe);
76 }
77
78 fn exit(
81 &mut self,
82 gas_used: u64,
83 output: Bytes,
84 error: Option<String>,
85 revert_reason: Option<String>,
86 ) -> Result<(), InternalError> {
87 let mut callframe = self.callframes.pop().ok_or(InternalError::CallFrame)?;
88
89 process_output(&mut callframe, gas_used, output, error, revert_reason);
90
91 if let Some(parent_callframe) = self.callframes.last_mut() {
93 parent_callframe.calls.push(callframe);
94 } else {
95 self.callframes.push(callframe);
96 };
97 Ok(())
98 }
99
100 pub fn exit_context(
102 &mut self,
103 ctx_result: &ContextResult,
104 is_top_call: bool,
105 ) -> Result<(), InternalError> {
106 if !self.active {
107 return Ok(());
108 }
109 if self.only_top_call && !is_top_call {
110 return Ok(());
112 }
113 if is_top_call {
114 clear_reverted_logs(self.current_callframe_mut()?);
116 }
117 let (gas_used, output) = (ctx_result.gas_used, ctx_result.output.clone());
118
119 let (error, revert_reason) = match ctx_result.result {
120 TxResult::Revert(ref err) => {
121 let reason = String::from_utf8(ctx_result.output.to_vec()).ok();
122 (Some(err.to_string()), reason)
123 }
124 _ => (None, None),
125 };
126
127 self.exit(gas_used, output, error, revert_reason)
128 }
129
130 pub fn exit_early(
132 &mut self,
133 gas_used: u64,
134 error: Option<String>,
135 ) -> Result<(), InternalError> {
136 if !self.active || self.only_top_call {
137 return Ok(());
138 }
139 self.exit(gas_used, Bytes::new(), error, None)
140 }
141
142 pub fn log(&mut self, log: &Log) -> Result<(), InternalError> {
145 if !self.active || !self.with_log {
146 return Ok(());
147 }
148 if self.only_top_call && self.callframes.len() > 1 {
149 return Ok(());
151 }
152 let callframe = self.current_callframe_mut()?;
153
154 let log = CallLog {
155 address: log.address,
156 topics: log.topics.clone(),
157 data: log.data.clone(),
158 position: match callframe.calls.len().try_into() {
159 Ok(pos) => pos,
160 Err(_) => return Err(InternalError::TypeConversion),
161 },
162 };
163
164 callframe.logs.push(log);
165 Ok(())
166 }
167
168 fn current_callframe_mut(&mut self) -> Result<&mut CallTraceFrame, InternalError> {
169 self.callframes.last_mut().ok_or(InternalError::CallFrame)
170 }
171}
172
173fn process_output(
174 callframe: &mut CallTraceFrame,
175 gas_used: u64,
176 output: Bytes,
177 error: Option<String>,
178 revert_reason: Option<String>,
179) {
180 callframe.gas_used = gas_used;
181 callframe.output = output;
182 callframe.error = error;
183 callframe.revert_reason = revert_reason;
184}
185
186fn clear_reverted_logs(callframe: &mut CallTraceFrame) {
188 if callframe.error.is_some() {
189 callframe.logs.clear();
190 }
191 for subcall in &mut callframe.calls {
192 clear_reverted_logs(subcall);
193 }
194}
195
196impl<'a> VM<'a> {
197 pub fn get_trace_result(&mut self) -> Result<CallTraceFrame, VMError> {
199 self.tracer
200 .callframes
201 .pop()
202 .ok_or(InternalError::CallFrame.into())
203 }
204}