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