1use crate::{
2 config::TracingInspectorConfig,
3 fourbyte::FourByteInspector,
4 tracing_inspector::TracingInspector,
5 types::{LogCallOrder, TxExecContext},
6 utils::{to_alloy_address, to_alloy_h256, to_alloy_u256},
7};
8use alloy_primitives::{Address, Bytes, LogData};
9use alloy_rpc_types_trace::geth::{
10 CallConfig, GethDebugBuiltInTracerType, GethDebugBuiltInTracerType::*,
11 GethDebugTracerType, GethDebugTracingOptions, GethTrace, NoopFrame,
12 PreStateConfig,
13};
14use cfx_executor::{
15 machine::Machine,
16 observer::{
17 CallTracer, CheckpointTracer, DrainTrace, InternalTransferTracer,
18 OpcodeTracer, SetAuthTracer, StorageTracer,
19 },
20 stack::{FrameResult, FrameReturn},
21};
22use cfx_types::{Space, H160};
23use cfx_vm_types::{ActionParams, CallType, Error, InterpreterInfo};
24use revm::{database::InMemoryDB, state::EvmState as State};
25use revm_interpreter::{Gas, InstructionResult, InterpreterResult};
26
27use std::sync::Arc;
28
29pub struct GethTracer {
30 inner: TracingInspector,
31 fourbyte_inspector: FourByteInspector,
33 tx_gas_limit: u64, gas_left: u64, depth: usize,
39 opts: GethDebugTracingOptions,
41 pub gas_stack: Vec<u64>,
43}
44
45impl GethTracer {
46 pub fn new(
47 tx_exec_context: TxExecContext, machine: Arc<Machine>,
48 opts: GethDebugTracingOptions,
49 ) -> Self {
50 let TxExecContext { tx_gas_limit, .. } = tx_exec_context;
51 let config = match opts.tracer {
52 Some(GethDebugTracerType::BuiltInTracer(builtin_tracer)) => {
53 match builtin_tracer {
54 FourByteTracer | NoopTracer | MuxTracer => {
55 TracingInspectorConfig::none()
56 }
57 CallTracer => {
58 let c = opts
59 .tracer_config
60 .clone()
61 .into_call_config()
62 .expect("should success");
63 TracingInspectorConfig::from_geth_call_config(&c)
64 }
65 FlatCallTracer => TracingInspectorConfig::none(),
66 PreStateTracer => {
67 let c = opts
68 .tracer_config
69 .clone()
70 .into_pre_state_config()
71 .expect("should success");
72 TracingInspectorConfig::from_geth_prestate_config(&c)
73 }
74 Erc7562Tracer => TracingInspectorConfig::none(),
75 }
76 }
77 Some(GethDebugTracerType::JsTracer(_)) => {
78 TracingInspectorConfig::none()
79 }
80 None => TracingInspectorConfig::from_geth_config(&opts.config),
81 };
82
83 Self {
84 inner: TracingInspector::new(config, machine, tx_exec_context),
85 fourbyte_inspector: FourByteInspector::new(),
86 tx_gas_limit,
87 depth: 0,
88 gas_left: tx_gas_limit,
89 opts,
90 gas_stack: Vec::new(),
91 }
92 }
93
94 fn tracer_type(&self) -> Option<GethDebugBuiltInTracerType> {
95 match self.opts.tracer.clone() {
96 Some(t) => match t {
97 GethDebugTracerType::BuiltInTracer(builtin_tracer) => {
98 Some(builtin_tracer)
99 }
100 GethDebugTracerType::JsTracer(_) => {
101 Some(NoopTracer)
103 }
104 },
105 None => None,
106 }
107 }
108
109 fn call_config(&self) -> Option<CallConfig> {
110 self.opts.tracer_config.clone().into_call_config().ok()
111 }
112
113 fn prestate_config(&self) -> Option<PreStateConfig> {
114 self.opts.tracer_config.clone().into_pre_state_config().ok()
115 }
116
117 pub fn is_fourbyte_tracer(&self) -> bool {
118 self.tracer_type() == Some(FourByteTracer)
119 }
120
121 pub fn gas_used(&self) -> u64 { self.tx_gas_limit - self.gas_left }
122
123 pub fn drain(self) -> GethTrace {
124 let trace = match self.tracer_type() {
125 Some(t) => match t {
126 FourByteTracer => self.fourbyte_inspector.drain(),
127 CallTracer => {
128 let gas_used = self.gas_used();
129 let opts = self.call_config().expect("should have config");
130 let frame = self
131 .inner
132 .into_geth_builder()
133 .geth_call_traces(opts, gas_used);
134 GethTrace::CallTracer(frame)
135 }
136 PreStateTracer => {
137 let opts =
139 self.prestate_config().expect("should have config");
140 let state = State::default();
141 let db = InMemoryDB::default();
142 let frame = self
143 .inner
144 .into_geth_builder()
145 .geth_prestate_traces(state, opts, db)
146 .unwrap();
147 GethTrace::PreStateTracer(frame)
148 }
149 NoopTracer | MuxTracer | FlatCallTracer | Erc7562Tracer => {
150 GethTrace::NoopTracer(NoopFrame::default())
151 }
152 },
153 None => {
154 let gas_used = self.gas_used();
155 let return_value = self
156 .inner
157 .last_call_return_data
158 .clone()
159 .unwrap_or_default();
160 let opts = self.opts.config;
161 let frame = self.inner.into_geth_builder().geth_traces(
162 gas_used,
163 return_value,
164 opts,
165 );
166 GethTrace::Default(frame)
167 }
168 };
169
170 trace
171 }
172}
173
174impl DrainTrace for GethTracer {
175 fn drain_trace(self, map: &mut typemap::ShareDebugMap) {
176 map.insert::<GethTraceKey>(self.drain());
177 }
178}
179
180pub struct GethTraceKey;
181
182impl typemap::Key for GethTraceKey {
183 type Value = GethTrace;
184}
185
186impl CheckpointTracer for GethTracer {}
187
188impl InternalTransferTracer for GethTracer {}
189
190impl StorageTracer for GethTracer {}
191
192impl SetAuthTracer for GethTracer {}
193
194impl CallTracer for GethTracer {
195 fn record_call(&mut self, params: &ActionParams) {
196 if self.is_fourbyte_tracer() {
197 self.fourbyte_inspector.record_call(params);
198 return;
199 }
200
201 let gas_limit = params.gas.as_u64();
202 self.gas_stack.push(gas_limit);
203
204 let (from, to) = match params.call_type {
206 CallType::DelegateCall | CallType::CallCode => {
207 (params.address, params.code_address)
208 }
209 _ => (params.sender, params.address),
210 };
211
212 let value = if matches!(params.call_type, CallType::DelegateCall)
213 && self.inner.active_trace().is_some()
214 {
215 let parent = self.inner.active_trace().unwrap();
217 parent.trace.value
218 } else {
219 to_alloy_u256(params.value.value())
220 };
221
222 let maybe_precompile =
225 self.inner.config.exclude_precompile_calls.then(|| {
226 self.inner.is_precompile_call(&to, value, params.space)
227 });
228
229 let to = to_alloy_address(to);
230 let from = to_alloy_address(from);
231 self.inner.start_trace_on_call(
232 to,
233 params.data.clone().unwrap_or_default().into(),
234 value,
235 params.call_type.into(),
236 from,
237 params.gas.as_u64(),
238 maybe_precompile,
239 self.tx_gas_limit,
240 self.depth,
241 );
242
243 self.depth += 1;
244 }
245
246 fn record_call_result(&mut self, result: &FrameResult) {
247 if self.is_fourbyte_tracer() {
248 return;
249 }
250
251 self.depth -= 1;
252 let mut gas_spent = self.gas_stack.pop().expect("should have value");
253
254 if let Ok(r) = result {
255 gas_spent = gas_spent - r.gas_left.as_u64();
256 self.gas_left = r.gas_left.as_u64();
257 }
258
259 let instruction_result = to_instruction_result(result);
260
261 if instruction_result.is_error() {
262 self.inner.gas_inspector.set_gas_remainning(0);
263 }
264
265 let output = result
266 .as_ref()
267 .map(|f| Bytes::from(f.return_data.to_vec()))
268 .unwrap_or_default();
269
270 let outcome = InterpreterResult {
271 result: instruction_result,
272 output,
273 gas: Gas::default(),
274 };
275
276 self.inner.fill_trace_on_call_end(outcome, None, gas_spent);
277 }
278
279 fn record_create(&mut self, params: &ActionParams) {
280 if self.is_fourbyte_tracer() {
281 return;
282 }
283
284 let gas_limit = params.gas.as_u64();
285 self.gas_stack.push(gas_limit);
286
287 let value = if matches!(params.call_type, CallType::DelegateCall) {
288 if let Some(parent) = self.inner.active_trace() {
290 parent.trace.value
291 } else {
292 to_alloy_u256(params.value.value())
293 }
294 } else {
295 to_alloy_u256(params.value.value())
296 };
297
298 self.inner.start_trace_on_call(
299 Address::default(), params.data.clone().unwrap_or_default().into(),
301 value,
302 params.call_type.into(),
303 to_alloy_address(params.sender),
304 params.gas.as_u64(),
305 Some(false),
306 params.gas.as_u64(),
307 self.depth,
308 );
309
310 self.depth += 1;
311 }
312
313 fn record_create_result(&mut self, result: &FrameResult) {
314 if self.is_fourbyte_tracer() {
315 return;
316 }
317
318 self.depth -= 1;
319 let mut gas_spent = self.gas_stack.pop().expect("should have value");
320
321 if let Ok(r) = result {
322 gas_spent = gas_spent - r.gas_left.as_u64();
323 self.gas_left = r.gas_left.as_u64();
324 }
325
326 let instruction_result = to_instruction_result(result);
327
328 if instruction_result.is_error() {
329 self.inner.gas_inspector.set_gas_remainning(0);
330 }
331
332 let output = result
333 .as_ref()
334 .map(|f| Bytes::from(f.return_data.to_vec()))
335 .unwrap_or_default();
336
337 let outcome = InterpreterResult {
338 result: instruction_result,
339 output,
340 gas: Gas::default(),
341 };
342
343 let create_address =
344 if let Ok(FrameReturn { create_address, .. }) = result {
345 create_address.as_ref().map(|h| to_alloy_address(*h))
346 } else {
347 None
348 };
349
350 self.inner
351 .fill_trace_on_call_end(outcome, create_address, gas_spent);
352 }
353}
354
355impl OpcodeTracer for GethTracer {
356 fn do_trace_opcode(&self, enabled: &mut bool) {
357 if self.inner.config.record_steps {
358 *enabled |= true;
359 }
360 }
361
362 fn initialize_interp(&mut self, gas_limit: cfx_types::U256) {
363 self.inner
364 .gas_inspector
365 .set_gas_remainning(gas_limit.as_u64());
366 }
367
368 fn step(&mut self, interp: &dyn InterpreterInfo) {
369 self.inner
370 .gas_inspector
371 .set_gas_remainning(interp.gas_remainning().as_u64());
372
373 if self.inner.config.record_steps {
374 self.inner.start_step(interp, self.depth as u64);
375 }
376 }
377
378 fn step_end(&mut self, interp: &dyn InterpreterInfo) {
379 let remainning = interp.gas_remainning().as_u64();
380 let last_gas_cost = self
381 .inner
382 .gas_inspector
383 .gas_remaining()
384 .saturating_sub(remainning);
385 self.inner.gas_inspector.set_gas_remainning(remainning);
386 self.inner.gas_inspector.set_last_gas_cost(last_gas_cost);
387
388 if self.inner.config.record_steps {
390 self.inner.fill_step_on_step_end(interp);
391 }
392 }
393
394 fn log(
395 &mut self, _address: &cfx_types::Address,
396 topics: &Vec<cfx_types::H256>, data: &[u8],
397 ) {
398 if self.inner.config.record_logs {
399 let trace_idx = self.inner.last_trace_idx();
400 let trace = &mut self.inner.traces.arena[trace_idx];
401 trace.ordering.push(LogCallOrder::Log(trace.logs.len()));
402 trace.logs.push(LogData::new_unchecked(
403 topics.iter().map(|f| to_alloy_h256(*f)).collect(),
404 Bytes::from(data.to_vec()),
405 ));
406 }
407 }
408
409 fn selfdestruct(
410 &mut self, space: Space, _contract: &cfx_types::Address,
411 target: &cfx_types::Address, _value: cfx_types::U256,
412 ) {
413 if self.is_fourbyte_tracer() {
414 return;
415 }
416
417 let trace_idx = self.inner.last_trace_idx();
418 let trace = &mut self.inner.traces.arena[trace_idx].trace;
419 trace.selfdestruct_refund_target =
420 Some(to_alloy_address(*target as H160))
421 }
422}
423
424pub fn to_instruction_result(frame_result: &FrameResult) -> InstructionResult {
425 let result = match frame_result {
426 Ok(r) => match r.apply_state {
427 true => InstructionResult::Return,
428 false => InstructionResult::Revert,
429 },
430 Err(err) => match err {
431 Error::OutOfGas => InstructionResult::OutOfGas,
432 Error::BadJumpDestination { .. } => InstructionResult::InvalidJump,
433 Error::BadInstruction { .. } => InstructionResult::OpcodeNotFound,
434 Error::StackUnderflow { .. } => InstructionResult::StackUnderflow,
435 Error::OutOfStack { .. } => InstructionResult::StackOverflow,
436 Error::SubStackUnderflow { .. } => {
437 InstructionResult::StackUnderflow
438 }
439 Error::OutOfSubStack { .. } => InstructionResult::StackOverflow,
440 Error::InvalidSubEntry => InstructionResult::NotActivated, Error::NotEnoughBalanceForStorage { .. } => {
442 InstructionResult::OutOfFunds
443 }
444 Error::ExceedStorageLimit => InstructionResult::OutOfGas, Error::BuiltIn(_) => InstructionResult::PrecompileError,
446 Error::InternalContract(_) => InstructionResult::PrecompileError, Error::MutableCallInStaticContext => {
448 InstructionResult::StateChangeDuringStaticCall
449 }
450 Error::StateDbError(_) => InstructionResult::FatalExternalError,
451 Error::Wasm(_) => InstructionResult::NotActivated,
452 Error::OutOfBounds => InstructionResult::OutOfOffset,
453 Error::Reverted => InstructionResult::Revert,
454 Error::InvalidAddress(_) => InstructionResult::Revert, Error::ConflictAddress(_) => InstructionResult::CreateCollision,
457 Error::CreateContractStartingWithEF => {
458 InstructionResult::CreateContractStartingWithEF
459 }
460 Error::CreateInitCodeSizeLimit => {
461 InstructionResult::CreateInitCodeSizeLimit
462 }
463 Error::NonceOverflow(_) => InstructionResult::NonceOverflow,
464 },
465 };
466 result
467}