pallet_revive/evm/tracing/
execution_tracing.rs1use crate::{
18 DispatchError, ExecReturnValue, Key, Weight,
19 evm::{
20 Bytes, ExecutionStep, ExecutionStepKind, ExecutionTrace, ExecutionTracerConfig,
21 tracing::Tracing,
22 },
23 tracing::{EVMFrameTraceInfo, FrameTraceInfo},
24 vm::pvm::env::lookup_trace_op_index,
25};
26use alloc::{
27 collections::BTreeMap,
28 format,
29 string::{String, ToString},
30 vec::Vec,
31};
32use sp_core::{H160, U256};
33
34#[derive(Default, Debug, Clone, PartialEq)]
37struct PendingStep {
38 step_index: usize,
40 child_gas: u64,
42 child_weight: Weight,
44}
45
46#[derive(Default, Debug, Clone, PartialEq)]
48pub struct ExecutionTracer {
49 config: ExecutionTracerConfig,
51
52 steps: Vec<ExecutionStep>,
54
55 pending: Vec<PendingStep>,
59
60 depth: u16,
62
63 step_count: u64,
65
66 total_gas_used: u64,
68
69 base_call_weight: Weight,
71
72 weight_consumed: Weight,
74
75 failed: bool,
77
78 return_value: Bytes,
80
81 storages_per_call: Vec<BTreeMap<Bytes, Bytes>>,
83}
84
85impl ExecutionTracer {
86 pub fn new(config: ExecutionTracerConfig) -> Self {
88 Self {
89 config,
90 steps: Vec::new(),
91 pending: Vec::new(),
92 depth: 0,
93 step_count: 0,
94 total_gas_used: 0,
95 base_call_weight: Default::default(),
96 weight_consumed: Default::default(),
97 failed: false,
98 return_value: Bytes::default(),
99 storages_per_call: alloc::vec![Default::default()],
100 }
101 }
102
103 pub fn collect_trace(self) -> ExecutionTrace {
105 let Self {
106 steps: struct_logs,
107 weight_consumed,
108 base_call_weight,
109 return_value,
110 total_gas_used: gas,
111 failed,
112 ..
113 } = self;
114 ExecutionTrace { gas, weight_consumed, base_call_weight, failed, return_value, struct_logs }
115 }
116
117 fn record_error(&mut self, error: String) {
119 if let Some(last_step) = self.steps.last_mut() {
120 last_step.error = Some(error);
121 }
122 }
123}
124
125impl Tracing for ExecutionTracer {
126 fn is_execution_tracer(&self) -> bool {
127 true
128 }
129
130 fn dispatch_result(&mut self, base_call_weight: Weight, weight_consumed: Weight) {
131 self.base_call_weight = base_call_weight;
132 self.weight_consumed = weight_consumed;
133 }
134
135 fn enter_opcode(&mut self, pc: u64, opcode: u8, trace_info: &dyn EVMFrameTraceInfo) {
136 if self.config.limit.map(|l| self.step_count >= l).unwrap_or(false) {
137 return;
138 }
139
140 let stack_data =
142 if !self.config.disable_stack { trace_info.stack_snapshot() } else { Vec::new() };
143
144 let memory_data = if self.config.enable_memory {
146 trace_info.memory_snapshot(self.config.memory_word_limit as usize)
147 } else {
148 Vec::new()
149 };
150
151 let return_data = if self.config.enable_return_data {
153 trace_info.last_frame_output()
154 } else {
155 crate::evm::Bytes::default()
156 };
157
158 let step = ExecutionStep {
159 gas: trace_info.gas_left(),
160 gas_cost: Default::default(),
161 weight_cost: trace_info.weight_consumed(), depth: self.depth,
164 return_data,
165 error: None,
166 kind: ExecutionStepKind::EVMOpcode {
167 pc: pc as u32,
168 op: opcode,
169 stack: stack_data,
170 memory: memory_data,
171 storage: None,
172 },
173 };
174
175 let step_index = self.steps.len();
176 self.steps.push(step);
177 self.pending
178 .push(PendingStep { step_index, child_gas: 0, child_weight: Weight::zero() });
179 self.step_count += 1;
180 }
181
182 fn enter_ecall(&mut self, ecall: &'static str, args: &[u64], trace_info: &dyn FrameTraceInfo) {
183 if self.config.limit.map(|l| self.step_count >= l).unwrap_or(false) {
184 return;
185 }
186
187 let return_data = if self.config.enable_return_data {
189 trace_info.last_frame_output()
190 } else {
191 crate::evm::Bytes::default()
192 };
193
194 let syscall_args =
196 if !self.config.disable_syscall_details { args.to_vec() } else { Vec::new() };
197
198 let step = ExecutionStep {
199 gas: trace_info.gas_left(),
200 gas_cost: Default::default(),
201 weight_cost: trace_info.weight_consumed(), depth: self.depth,
204 return_data,
205 error: None,
206 kind: ExecutionStepKind::PVMSyscall {
207 op: lookup_trace_op_index(ecall).unwrap_or_default(),
208 args: syscall_args,
209 returned: None,
210 },
211 };
212
213 let step_index = self.steps.len();
214 self.steps.push(step);
215 self.pending
216 .push(PendingStep { step_index, child_gas: 0, child_weight: Weight::zero() });
217 self.step_count += 1;
218 }
219
220 fn exit_step(&mut self, trace_info: &dyn FrameTraceInfo, returned: Option<u64>) {
221 let Some(pending) = self.pending.pop() else { return };
222 let Some(step) = self.steps.get_mut(pending.step_index) else { return };
223
224 let total_gas = step.gas.saturating_sub(trace_info.gas_left());
226 step.gas_cost = total_gas.saturating_sub(pending.child_gas);
227
228 let total_weight = trace_info.weight_consumed().saturating_sub(step.weight_cost);
230 step.weight_cost = total_weight.saturating_sub(pending.child_weight);
231
232 if !self.config.disable_syscall_details &&
233 let ExecutionStepKind::PVMSyscall { returned: ref mut ret, .. } = step.kind
234 {
235 *ret = returned;
236 }
237 }
238
239 fn enter_child_span(
240 &mut self,
241 _from: H160,
242 _to: H160,
243 _delegate_call: Option<H160>,
244 _is_read_only: bool,
245 _value: U256,
246 _input: &[u8],
247 _gas_limit: u64,
248 ) {
249 self.storages_per_call.push(Default::default());
251 self.depth += 1;
252 }
253
254 fn exit_child_span(
255 &mut self,
256 output: &ExecReturnValue,
257 gas_used: u64,
258 weight_consumed: Weight,
259 ) {
260 if let Some(parent) = self.pending.last_mut() {
262 parent.child_gas = parent.child_gas.saturating_add(gas_used);
263 parent.child_weight = parent.child_weight.saturating_add(weight_consumed);
264 }
265
266 if output.did_revert() {
267 self.record_error("execution reverted".to_string());
268 if self.depth == 0 {
269 self.failed = true;
270 }
271 } else {
272 self.return_value = Bytes(output.data.to_vec());
273 }
274
275 if self.depth == 1 {
276 self.total_gas_used = gas_used;
277 }
278
279 self.storages_per_call.pop();
280
281 if self.depth > 0 {
282 self.depth -= 1;
283 }
284 }
285
286 fn exit_child_span_with_error(
287 &mut self,
288 error: DispatchError,
289 gas_used: u64,
290 weight_consumed: Weight,
291 ) {
292 if let Some(parent) = self.pending.last_mut() {
294 parent.child_gas = parent.child_gas.saturating_add(gas_used);
295 parent.child_weight = parent.child_weight.saturating_add(weight_consumed);
296 }
297
298 self.record_error(format!("{:?}", error));
299
300 if self.depth == 1 {
302 self.failed = true;
303 self.total_gas_used = gas_used;
304 }
305
306 if self.depth > 0 {
307 self.depth -= 1;
308 }
309
310 self.storages_per_call.pop();
311 }
312
313 fn storage_write(&mut self, key: &Key, _old_value: Option<Vec<u8>>, new_value: Option<&[u8]>) {
314 if self.config.disable_storage {
316 return;
317 }
318
319 if let Some(storage) = self.storages_per_call.last_mut() {
320 let key_bytes = crate::evm::Bytes(key.unhashed().to_vec());
321 let value_bytes = crate::evm::Bytes(
322 new_value.map(|v| v.to_vec()).unwrap_or_else(|| alloc::vec![0u8; 32]),
323 );
324 storage.insert(key_bytes, value_bytes);
325
326 if let Some(step) = self.steps.last_mut() {
327 if let ExecutionStepKind::EVMOpcode { storage: ref mut step_storage, .. } =
328 step.kind
329 {
330 *step_storage = Some(storage.clone());
331 }
332 }
333 }
334 }
335
336 fn storage_read(&mut self, key: &Key, value: Option<&[u8]>) {
337 if self.config.disable_storage {
339 return;
340 }
341
342 if let Some(storage) = self.storages_per_call.last_mut() {
343 let key_bytes = crate::evm::Bytes(key.unhashed().to_vec());
344 storage.entry(key_bytes).or_insert_with(|| {
345 crate::evm::Bytes(value.map(|v| v.to_vec()).unwrap_or_else(|| alloc::vec![0u8; 32]))
346 });
347
348 if let Some(step) = self.steps.last_mut() {
349 if let ExecutionStepKind::EVMOpcode { storage: ref mut step_storage, .. } =
350 step.kind
351 {
352 *step_storage = Some(storage.clone());
353 }
354 }
355 }
356 }
357}