pallet_revive/evm/tracing/
call_tracing.rs1use crate::{
18 evm::{decode_revert_reason, CallLog, CallTrace, CallTracerConfig, CallType},
19 primitives::ExecReturnValue,
20 tracing::Tracing,
21 Code, DispatchError,
22};
23use alloc::{format, string::ToString, vec::Vec};
24use sp_core::{H160, H256, U256};
25
26#[derive(Default, Debug, Clone, PartialEq)]
28pub struct CallTracer {
29 traces: Vec<CallTrace<U256>>,
31 current_stack: Vec<usize>,
33 code_with_salt: Option<(Code, bool)>,
35 config: CallTracerConfig,
37}
38
39impl CallTracer {
40 pub fn new(config: CallTracerConfig) -> Self {
42 Self { traces: Vec::new(), code_with_salt: None, current_stack: Vec::new(), config }
43 }
44
45 pub fn collect_trace(mut self) -> Option<CallTrace> {
47 self.traces.pop()
48 }
49}
50
51impl Tracing for CallTracer {
52 fn instantiate_code(&mut self, code: &Code, salt: Option<&[u8; 32]>) {
53 self.code_with_salt = Some((code.clone(), salt.is_some()));
54 }
55
56 fn terminate(
57 &mut self,
58 contract_address: H160,
59 beneficiary_address: H160,
60 gas_left: U256,
61 value: U256,
62 ) {
63 self.traces.last_mut().unwrap().calls.push(CallTrace {
64 from: contract_address,
65 to: beneficiary_address,
66 call_type: CallType::Selfdestruct,
67 gas: gas_left,
68 value: Some(value),
69 ..Default::default()
70 });
71 }
72
73 fn enter_child_span(
74 &mut self,
75 from: H160,
76 to: H160,
77 delegate_call: Option<H160>,
78 is_read_only: bool,
79 value: U256,
80 input: &[u8],
81 gas_limit: U256,
82 ) {
83 if let Some(&index) = self.current_stack.last() {
85 if let Some(trace) = self.traces.get_mut(index) {
86 trace.child_call_count += 1;
87 }
88 }
89
90 if self.traces.is_empty() || !self.config.only_top_call {
91 let (call_type, input) = match self.code_with_salt.take() {
92 Some((Code::Upload(code), salt)) => (
93 if salt { CallType::Create2 } else { CallType::Create },
94 code.into_iter().chain(input.to_vec().into_iter()).collect::<Vec<_>>(),
95 ),
96 Some((Code::Existing(code_hash), salt)) => (
97 if salt { CallType::Create2 } else { CallType::Create },
98 code_hash
99 .to_fixed_bytes()
100 .into_iter()
101 .chain(input.to_vec().into_iter())
102 .collect::<Vec<_>>(),
103 ),
104 None => {
105 let call_type = if is_read_only {
106 CallType::StaticCall
107 } else if delegate_call.is_some() {
108 CallType::DelegateCall
109 } else {
110 CallType::Call
111 };
112 (call_type, input.to_vec())
113 },
114 };
115
116 self.traces.push(CallTrace {
117 from,
118 to,
119 value: if is_read_only { None } else { Some(value) },
120 call_type,
121 input: input.into(),
122 gas: gas_limit,
123 ..Default::default()
124 });
125
126 self.current_stack.push(self.traces.len() - 1);
128
129 } else {
131 self.current_stack.push(2);
132 }
133 }
134
135 fn log_event(&mut self, address: H160, topics: &[H256], data: &[u8]) {
136 if !self.config.with_logs {
137 return;
138 }
139
140 let current_index = self.current_stack.last().unwrap();
141
142 if let Some(trace) = self.traces.get_mut(*current_index) {
143 let log = CallLog {
144 address,
145 topics: topics.to_vec(),
146 data: data.to_vec().into(),
147 position: trace.child_call_count,
148 };
149
150 trace.logs.push(log);
151 }
152 }
153
154 fn exit_child_span(&mut self, output: &ExecReturnValue, gas_used: U256) {
155 self.code_with_salt = None;
156
157 let current_index = self.current_stack.pop().unwrap();
159
160 if let Some(trace) = self.traces.get_mut(current_index) {
161 trace.output = output.data.clone().into();
162 trace.gas_used = gas_used;
163
164 if output.did_revert() {
165 trace.revert_reason = decode_revert_reason(&output.data);
166 trace.error = Some("execution reverted".to_string());
167 }
168
169 if self.config.only_top_call {
170 return
171 }
172
173 if let Some(parent_index) = self.current_stack.last() {
175 let child_trace = self.traces.remove(current_index);
176 self.traces[*parent_index].calls.push(child_trace);
177 }
178 }
179 }
180 fn exit_child_span_with_error(&mut self, error: DispatchError, gas_used: U256) {
181 self.code_with_salt = None;
182
183 let current_index = self.current_stack.pop().unwrap();
185
186 if let Some(trace) = self.traces.get_mut(current_index) {
187 trace.gas_used = gas_used;
188
189 trace.error = match error {
190 DispatchError::Module(sp_runtime::ModuleError { message, .. }) =>
191 Some(message.unwrap_or_default().to_string()),
192 _ => Some(format!("{:?}", error)),
193 };
194
195 if self.config.only_top_call {
196 return
197 }
198
199 if let Some(parent_index) = self.current_stack.last() {
201 let child_trace = self.traces.remove(current_index);
202 self.traces[*parent_index].calls.push(child_trace);
203 }
204 }
205 }
206}