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 if !self.config.with_logs {
138 return;
139 }
140
141 let current_index = self.current_stack.last().unwrap();
142
143 if let Some(trace) = self.traces.get_mut(*current_index) {
144 let log = CallLog {
145 address,
146 topics: topics.to_vec(),
147 data: data.to_vec().into(),
148 position: trace.child_call_count,
149 index: log_index,
150 };
151
152 trace.logs.push(log);
153 }
154 }
155
156 fn exit_child_span(
157 &mut self,
158 output: &ExecReturnValue,
159 gas_used: u64,
160 _weight_consumed: Weight,
161 ) {
162 self.code_with_salt = None;
163
164 let current_index = self.current_stack.pop().unwrap();
166
167 if let Some(trace) = self.traces.get_mut(current_index) {
168 trace.output = output.data.clone().into();
169 trace.gas_used = gas_used;
170
171 if output.did_revert() {
172 trace.revert_reason = decode_revert_reason(&output.data);
173 trace.error = Some("execution reverted".to_string());
174 }
175
176 if self.config.only_top_call {
177 return;
178 }
179
180 if let Some(parent_index) = self.current_stack.last() {
182 let child_trace = self.traces.remove(current_index);
183 self.traces[*parent_index].calls.push(child_trace);
184 }
185 }
186 }
187
188 fn exit_child_span_with_error(
189 &mut self,
190 error: DispatchError,
191 gas_used: u64,
192 _weight_consumed: Weight,
193 ) {
194 self.code_with_salt = None;
195
196 let current_index = self.current_stack.pop().unwrap();
198
199 if let Some(trace) = self.traces.get_mut(current_index) {
200 trace.gas_used = gas_used;
201
202 trace.error = match error {
203 DispatchError::Module(sp_runtime::ModuleError { message, .. }) => {
204 Some(message.unwrap_or_default().to_string())
205 },
206 _ => Some(format!("{:?}", error)),
207 };
208
209 if self.config.only_top_call {
210 return;
211 }
212
213 if let Some(parent_index) = self.current_stack.last() {
215 let child_trace = self.traces.remove(current_index);
216 self.traces[*parent_index].calls.push(child_trace);
217 }
218 }
219 }
220}