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]) {
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(
155 &mut self,
156 output: &ExecReturnValue,
157 gas_used: u64,
158 _weight_consumed: Weight,
159 ) {
160 self.code_with_salt = None;
161
162 let current_index = self.current_stack.pop().unwrap();
164
165 if let Some(trace) = self.traces.get_mut(current_index) {
166 trace.output = output.data.clone().into();
167 trace.gas_used = gas_used;
168
169 if output.did_revert() {
170 trace.revert_reason = decode_revert_reason(&output.data);
171 trace.error = Some("execution reverted".to_string());
172 }
173
174 if self.config.only_top_call {
175 return;
176 }
177
178 if let Some(parent_index) = self.current_stack.last() {
180 let child_trace = self.traces.remove(current_index);
181 self.traces[*parent_index].calls.push(child_trace);
182 }
183 }
184 }
185
186 fn exit_child_span_with_error(
187 &mut self,
188 error: DispatchError,
189 gas_used: u64,
190 _weight_consumed: Weight,
191 ) {
192 self.code_with_salt = None;
193
194 let current_index = self.current_stack.pop().unwrap();
196
197 if let Some(trace) = self.traces.get_mut(current_index) {
198 trace.gas_used = gas_used;
199
200 trace.error = match error {
201 DispatchError::Module(sp_runtime::ModuleError { message, .. }) => {
202 Some(message.unwrap_or_default().to_string())
203 },
204 _ => Some(format!("{:?}", error)),
205 };
206
207 if self.config.only_top_call {
208 return;
209 }
210
211 if let Some(parent_index) = self.current_stack.last() {
213 let child_trace = self.traces.remove(current_index);
214 self.traces[*parent_index].calls.push(child_trace);
215 }
216 }
217 }
218}