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 delegate_call: Option<H160>,
78 is_read_only: bool,
79 value: U256,
80 input: &[u8],
81 gas_limit: u64,
82 ) {
83 if let Some(&index) = self.current_stack.last() &&
85 let Some(trace) = self.traces.get_mut(index)
86 {
87 trace.child_call_count += 1;
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], log_index: u32) {
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 index: log_index,
149 };
150
151 trace.logs.push(log);
152 }
153 }
154
155 fn exit_child_span(
156 &mut self,
157 output: &ExecReturnValue,
158 gas_used: u64,
159 _weight_consumed: Weight,
160 ) {
161 self.code_with_salt = None;
162
163 let current_index = self.current_stack.pop().unwrap();
165
166 if let Some(trace) = self.traces.get_mut(current_index) {
167 trace.output = output.data.clone().into();
168 trace.gas_used = gas_used;
169
170 if output.did_revert() {
171 trace.revert_reason = decode_revert_reason(&output.data);
172 trace.error = Some("execution reverted".to_string());
173 }
174
175 if self.config.only_top_call {
176 return;
177 }
178
179 if let Some(parent_index) = self.current_stack.last() {
181 let child_trace = self.traces.remove(current_index);
182 self.traces[*parent_index].calls.push(child_trace);
183 }
184 }
185 }
186
187 fn exit_child_span_with_error(
188 &mut self,
189 error: DispatchError,
190 gas_used: u64,
191 _weight_consumed: Weight,
192 ) {
193 self.code_with_salt = None;
194
195 let current_index = self.current_stack.pop().unwrap();
197
198 if let Some(trace) = self.traces.get_mut(current_index) {
199 trace.gas_used = gas_used;
200
201 trace.error = match error {
202 DispatchError::Module(sp_runtime::ModuleError { message, .. }) => {
203 Some(message.unwrap_or_default().to_string())
204 },
205 _ => Some(format!("{:?}", error)),
206 };
207
208 if self.config.only_top_call {
209 return;
210 }
211
212 if let Some(parent_index) = self.current_stack.last() {
214 let child_trace = self.traces.remove(current_index);
215 self.traces[*parent_index].calls.push(child_trace);
216 }
217 }
218 }
219}