pallet_revive/evm/tracing/
call_tracing.rs1use crate::{
18 Code, DispatchError, Weight,
19 evm::{CallLog, CallTrace, CallTracerConfig, CallType, decode_revert_reason},
20 primitives::ExecReturnValue,
21 tracing::Tracing,
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>,
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: u64,
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 _code_address: Option<H160>,
78 is_delegate_call: bool,
79 is_read_only: bool,
80 value: U256,
81 input: &[u8],
82 gas_limit: u64,
83 ) {
84 if let Some(&index) = self.current_stack.last() &&
86 let Some(trace) = self.traces.get_mut(index)
87 {
88 trace.child_call_count += 1;
89 }
90
91 if self.traces.is_empty() || !self.config.only_top_call {
92 let (call_type, input) = match self.code_with_salt.take() {
93 Some((Code::Upload(code), salt)) => (
94 if salt { CallType::Create2 } else { CallType::Create },
95 code.into_iter().chain(input.to_vec().into_iter()).collect::<Vec<_>>(),
96 ),
97 Some((Code::Existing(code_hash), salt)) => (
98 if salt { CallType::Create2 } else { CallType::Create },
99 code_hash
100 .to_fixed_bytes()
101 .into_iter()
102 .chain(input.to_vec().into_iter())
103 .collect::<Vec<_>>(),
104 ),
105 None => {
106 let call_type = if is_read_only {
107 CallType::StaticCall
108 } else if is_delegate_call {
109 CallType::DelegateCall
110 } else {
111 CallType::Call
112 };
113 (call_type, input.to_vec())
114 },
115 };
116
117 self.traces.push(CallTrace {
118 from,
119 to,
120 value: if is_read_only { None } else { Some(value) },
121 call_type,
122 input: input.into(),
123 gas: gas_limit,
124 ..Default::default()
125 });
126
127 self.current_stack.push(self.traces.len() - 1);
129
130 } else {
132 self.current_stack.push(2);
133 }
134 }
135
136 fn log_event(&mut self, address: H160, topics: &[H256], data: &[u8], log_index: u32) {
137 debug_assert!(
140 !self.current_stack.is_empty(),
141 "log_event called with no active call frame; use log_event_outside_frame for \
142 runtime-originated logs"
143 );
144 self.log_event_outside_frame(address, topics, data, log_index);
145 }
146
147 fn log_event_outside_frame(
148 &mut self,
149 address: H160,
150 topics: &[H256],
151 data: &[u8],
152 log_index: u32,
153 ) {
154 if !self.config.with_logs {
155 return;
156 }
157
158 let Some(¤t_index) = self.current_stack.last() else { return };
161
162 if let Some(trace) = self.traces.get_mut(current_index) {
163 let log = CallLog {
164 address,
165 topics: topics.to_vec(),
166 data: data.to_vec().into(),
167 position: trace.child_call_count,
168 index: log_index,
169 };
170
171 trace.logs.push(log);
172 }
173 }
174
175 fn exit_child_span(
176 &mut self,
177 output: &ExecReturnValue,
178 gas_used: u64,
179 _weight_consumed: Weight,
180 ) {
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.output = output.data.clone().into();
188 trace.gas_used = gas_used;
189
190 if output.did_revert() {
191 trace.revert_reason = decode_revert_reason(&output.data);
192 trace.error = Some("execution reverted".to_string());
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
207 fn exit_child_span_with_error(
208 &mut self,
209 error: DispatchError,
210 gas_used: u64,
211 _weight_consumed: Weight,
212 ) {
213 self.code_with_salt = None;
214
215 let current_index = self.current_stack.pop().unwrap();
217
218 if let Some(trace) = self.traces.get_mut(current_index) {
219 trace.gas_used = gas_used;
220
221 trace.error = match error {
222 DispatchError::Module(sp_runtime::ModuleError { message, .. }) => {
223 Some(message.unwrap_or_default().to_string())
224 },
225 _ => Some(format!("{:?}", error)),
226 };
227
228 if self.config.only_top_call {
229 return;
230 }
231
232 if let Some(parent_index) = self.current_stack.last() {
234 let child_trace = self.traces.remove(current_index);
235 self.traces[*parent_index].calls.push(child_trace);
236 }
237 }
238 }
239}