1use crate::{config::TraceStyle, utils, utils::convert_memory};
32use alloy_primitives::{Address, Bytes, LogData, U256};
33use alloy_rpc_types_trace::geth::{
34 CallFrame, CallLogFrame, GethDefaultTracingOptions, GethTrace, StructLog,
35};
36use cfx_types::{Space, H256};
37use cfx_vm_types::CallType as CfxCallType;
38use primitives::{block::BlockHeight, BlockNumber};
39
40use revm_bytecode::{opcode, OpCode};
41use revm_interpreter::{CallScheme, CreateScheme, InstructionResult};
42use std::collections::VecDeque;
43
44#[derive(Clone, Debug, Default, PartialEq, Eq)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47pub struct CallTrace {
48 pub depth: usize,
50 pub success: bool,
52 pub caller: Address,
54 pub address: Address,
60 pub maybe_precompile: Option<bool>,
64 pub selfdestruct_refund_target: Option<Address>,
72 pub kind: CallKind,
74 pub value: U256,
76 pub data: Bytes,
78 pub output: Bytes,
81 pub gas_used: u64,
83 pub gas_limit: u64,
85 pub status: Option<InstructionResult>,
87 pub steps: Vec<CallTraceStep>,
89}
90
91impl CallTrace {
92 #[inline]
95 pub const fn is_error(&self) -> bool {
96 let Some(status) = self.status else {
97 return false;
98 };
99 !status.is_ok()
100 }
101
102 #[inline]
104 pub fn is_revert(&self) -> bool {
105 self.status
106 .is_some_and(|status| status == InstructionResult::Revert)
107 }
108
109 pub(crate) fn as_error_msg(&self, kind: TraceStyle) -> Option<String> {
111 self.status
112 .and_then(|status| utils::fmt_error_msg(status, kind))
113 }
114}
115
116#[derive(Clone, Debug, Default, PartialEq, Eq)]
118#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
119pub struct CallTraceNode {
120 pub parent: Option<usize>,
122 pub children: Vec<usize>,
124 pub idx: usize,
126 pub trace: CallTrace,
128 pub logs: Vec<LogData>,
130 pub ordering: Vec<LogCallOrder>,
132}
133
134impl CallTraceNode {
135 pub const fn execution_address(&self) -> Address {
140 if self.trace.kind.is_delegate() {
141 self.trace.caller
142 } else {
143 self.trace.address
144 }
145 }
146
147 pub(crate) fn push_steps_on_stack<'a>(
150 &'a self, stack: &mut VecDeque<CallTraceStepStackItem<'a>>,
151 ) {
152 stack.extend(self.call_step_stack().into_iter().rev());
153 }
154
155 pub(crate) fn call_step_stack(&self) -> Vec<CallTraceStepStackItem<'_>> {
160 let mut stack = Vec::with_capacity(self.trace.steps.len());
161 let mut child_id = 0;
162 for step in self.trace.steps.iter() {
163 let mut item = CallTraceStepStackItem {
164 trace_node: self,
165 step,
166 call_child_id: None,
167 };
168
169 if step.is_calllike_op() {
171 if let Some(call_id) = self.children.get(child_id).copied() {
174 item.call_child_id = Some(call_id);
175 child_id += 1;
176 }
177 }
178 stack.push(item);
179 }
180 stack
181 }
182
183 #[inline]
185 pub fn is_precompile(&self) -> bool {
186 self.trace.maybe_precompile.unwrap_or(false)
187 }
188
189 #[inline]
191 pub const fn kind(&self) -> CallKind { self.trace.kind }
192
193 #[inline]
195 pub const fn status(&self) -> Option<InstructionResult> {
196 self.trace.status
197 }
198
199 #[inline]
209 pub const fn is_selfdestruct(&self) -> bool {
210 self.trace.selfdestruct_refund_target.is_some()
211 }
212
213 pub fn geth_selfdestruct_call_trace(&self) -> Option<CallFrame> {
216 if self.is_selfdestruct() {
217 Some(CallFrame {
218 typ: "SELFDESTRUCT".to_string(),
219 from: self.trace.caller,
220 to: self.trace.selfdestruct_refund_target,
221 value: Some(self.trace.value),
222 ..Default::default()
223 })
224 } else {
225 None
226 }
227 }
228
229 pub fn geth_empty_call_frame(&self, include_logs: bool) -> CallFrame {
231 let mut call_frame = CallFrame {
232 typ: self.trace.kind.to_string(),
233 from: self.trace.caller,
234 to: Some(self.trace.address),
235 value: Some(self.trace.value),
236 gas: U256::from(self.trace.gas_limit),
237 gas_used: U256::from(self.trace.gas_used),
238 input: self.trace.data.clone(),
239 output: (!self.trace.output.is_empty())
240 .then(|| self.trace.output.clone()),
241 error: None,
242 revert_reason: None,
243 calls: Default::default(),
244 logs: Default::default(),
245 };
246
247 if self.trace.kind.is_static_call() {
248 call_frame.value = None;
250 }
251
252 if !self.trace.success {
254 call_frame.revert_reason =
255 utils::maybe_revert_reason(self.trace.output.as_ref());
256
257 call_frame.error = self.trace.as_error_msg(TraceStyle::Parity);
259 }
260
261 if include_logs && !self.logs.is_empty() {
262 call_frame.logs = self
263 .logs
264 .iter()
265 .map(|log| CallLogFrame {
266 address: Some(self.execution_address()),
267 topics: Some(log.topics().to_vec()),
268 data: Some(log.data.clone()),
269 index: None,
270 position: None,
271 })
272 .collect();
273 }
274
275 call_frame
276 }
277}
278
279#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
281#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
282#[cfg_attr(feature = "serde", serde(rename_all = "UPPERCASE"))]
283pub enum CallKind {
284 #[default]
286 Call,
287 StaticCall,
289 CallCode,
291 DelegateCall,
293 Create,
295 Create2,
297}
298
299impl CallKind {
300 #[inline]
302 pub const fn is_any_create(&self) -> bool {
303 matches!(self, Self::Create | Self::Create2)
304 }
305
306 #[inline]
308 pub const fn is_delegate(&self) -> bool {
309 matches!(self, Self::DelegateCall | Self::CallCode)
310 }
311
312 #[inline]
314 pub const fn is_static_call(&self) -> bool {
315 matches!(self, Self::StaticCall)
316 }
317}
318
319impl std::fmt::Display for CallKind {
320 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321 match self {
322 Self::Call => {
323 write!(f, "CALL")
324 }
325 Self::StaticCall => {
326 write!(f, "STATICCALL")
327 }
328 Self::CallCode => {
329 write!(f, "CALLCODE")
330 }
331 Self::DelegateCall => {
332 write!(f, "DELEGATECALL")
333 }
334 Self::Create => {
335 write!(f, "CREATE")
336 }
337 Self::Create2 => {
338 write!(f, "CREATE2")
339 }
340 }
341 }
342}
343
344impl From<CallScheme> for CallKind {
345 fn from(scheme: CallScheme) -> Self {
346 match scheme {
347 CallScheme::Call => Self::Call,
348 CallScheme::StaticCall => Self::StaticCall,
349 CallScheme::CallCode => Self::CallCode,
350 CallScheme::DelegateCall => Self::DelegateCall,
351 }
352 }
353}
354
355impl From<CreateScheme> for CallKind {
356 fn from(create: CreateScheme) -> Self {
357 match create {
358 CreateScheme::Create => Self::Create,
359 CreateScheme::Create2 { .. } => Self::Create2,
360 CreateScheme::Custom { .. } => unreachable!(), }
362 }
363}
364
365impl From<CfxCallType> for CallKind {
366 fn from(ct: CfxCallType) -> Self {
367 match ct {
368 CfxCallType::None => Self::Create,
369 CfxCallType::Call => Self::Call,
370 CfxCallType::CallCode => Self::CallCode,
371 CfxCallType::DelegateCall => Self::DelegateCall,
372 CfxCallType::StaticCall => Self::StaticCall,
373 }
374 }
375}
376
377pub(crate) struct CallTraceStepStackItem<'a> {
378 pub(crate) trace_node: &'a CallTraceNode,
380 pub(crate) step: &'a CallTraceStep,
382 pub(crate) call_child_id: Option<usize>,
385}
386
387#[derive(Clone, Copy, Debug, PartialEq, Eq)]
389#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
390pub enum LogCallOrder {
391 Log(usize),
393 Call(usize),
395}
396
397#[derive(Clone, Debug, PartialEq, Eq)]
399#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
400pub struct CallTraceStep {
401 pub depth: u64,
404 pub pc: usize,
406 #[cfg_attr(feature = "serde", serde(with = "opcode_serde"))]
408 pub op: OpCode,
409 pub contract: Address,
411 pub stack: Option<Vec<U256>>,
413 pub push_stack: Option<Vec<U256>>,
415 pub memory: RecordedMemory,
419 pub memory_size: usize,
421 pub gas_remaining: u64,
423 pub gas_refund_counter: u64,
425 pub gas_cost: u64,
428 pub storage_change: Option<StorageChange>,
431 pub status: Option<InstructionResult>,
435}
436
437impl CallTraceStep {
440 pub(crate) fn convert_to_geth_struct_log(
444 &self, opts: &GethDefaultTracingOptions,
445 ) -> StructLog {
446 let mut log = StructLog {
447 depth: self.depth,
448 error: self.as_error(),
449 gas: self.gas_remaining,
450 gas_cost: self.gas_cost,
451 op: self.op.as_str().into(),
452 pc: self.pc as u64,
453 refund_counter: (self.gas_refund_counter > 0)
454 .then_some(self.gas_refund_counter),
455 stack: None,
457 return_data: None,
460 storage: None,
462 memory: None,
464 memory_size: None,
466 };
467
468 if opts.is_stack_enabled() {
469 log.stack.clone_from(&self.stack);
470 }
471
472 if opts.is_memory_enabled() {
473 log.memory = Some(self.memory.memory_chunks());
474 }
475
476 log
477 }
478
479 #[inline]
481 pub(crate) const fn is_stop(&self) -> bool {
482 matches!(self.op.get(), opcode::STOP)
483 }
484
485 #[inline]
488 pub(crate) const fn is_calllike_op(&self) -> bool {
489 matches!(
490 self.op.get(),
491 opcode::CALL
492 | opcode::DELEGATECALL
493 | opcode::STATICCALL
494 | opcode::CREATE
495 | opcode::CALLCODE
496 | opcode::CREATE2
497 )
498 }
499
500 #[inline]
503 pub(crate) const fn is_error(&self) -> bool {
504 let Some(status) = self.status else {
505 return false;
506 };
507 status.is_error()
508 }
509
510 #[inline]
512 pub(crate) fn as_error(&self) -> Option<String> {
513 self.is_error().then(|| format!("{:?}", self.status))
514 }
515}
516
517#[allow(clippy::upper_case_acronyms)]
520#[derive(Clone, Copy, Debug, PartialEq, Eq)]
521#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
522pub enum StorageChangeReason {
523 SLOAD,
525 SSTORE,
527}
528
529#[derive(Clone, Copy, Debug, PartialEq, Eq)]
537#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
538pub struct StorageChange {
539 pub key: U256,
541 pub value: U256,
543 pub had_value: Option<U256>,
545 pub reason: StorageChangeReason,
547}
548
549#[derive(Clone, Debug, Default, PartialEq, Eq)]
554#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
555pub struct RecordedMemory(pub(crate) Vec<u8>);
556
557impl RecordedMemory {
558 #[inline]
559 pub(crate) fn new(mem: Vec<u8>) -> Self { Self(mem) }
560
561 #[inline]
563 pub fn as_bytes(&self) -> &[u8] { &self.0 }
564
565 #[inline]
566 pub(crate) fn resize(&mut self, size: usize) { self.0.resize(size, 0); }
567
568 #[inline]
570 pub fn len(&self) -> usize { self.0.len() }
571
572 #[inline]
574 pub fn is_empty(&self) -> bool { self.0.is_empty() }
575
576 #[inline]
578 pub fn memory_chunks(&self) -> Vec<String> {
579 convert_memory(self.as_bytes())
580 }
581}
582
583impl AsRef<[u8]> for RecordedMemory {
584 fn as_ref(&self) -> &[u8] { self.as_bytes() }
585}
586
587pub struct GethTraceWithHash {
588 pub trace: GethTrace,
589 pub tx_hash: H256,
590 pub space: Space,
591}
592
593#[derive(Clone)]
594pub struct TxExecContext {
595 pub tx_gas_limit: u64,
596 pub block_number: BlockNumber,
597 pub block_height: BlockHeight,
598}
599
600#[cfg(feature = "serde")]
601mod opcode_serde {
602 use super::OpCode;
603 use serde::{Deserialize, Deserializer, Serializer};
604
605 pub(super) fn serialize<S>(
606 op: &OpCode, serializer: S,
607 ) -> Result<S::Ok, S::Error>
608 where S: Serializer {
609 serializer.serialize_u8(op.get())
610 }
611
612 pub(super) fn deserialize<'de, D>(
613 deserializer: D,
614 ) -> Result<OpCode, D::Error>
615 where D: Deserializer<'de> {
616 let op = u8::deserialize(deserializer)?;
617 Ok(OpCode::new(op).unwrap_or_else(|| {
618 OpCode::new(revm_interpreter::opcode::INVALID).unwrap()
619 }))
620 }
621}