pallet_revive/evm/tracing/
call_tracing.rs1use crate::{
18 evm::{decode_revert_reason, CallLog, CallTrace, CallTracerConfig, CallType},
19 primitives::ExecReturnValue,
20 tracing::Tracing,
21 Code, DispatchError, Weight,
22};
23use alloc::{format, string::ToString, vec::Vec};
24use sp_core::{H160, H256, U256};
25
26#[derive(Default, Debug, Clone, PartialEq)]
28pub struct CallTracer<Gas, GasMapper> {
29 gas_mapper: GasMapper,
31 traces: Vec<CallTrace<Gas>>,
33 current_stack: Vec<usize>,
35 code_with_salt: Option<(Code, bool)>,
37 config: CallTracerConfig,
39}
40
41impl<Gas, GasMapper> CallTracer<Gas, GasMapper> {
42 pub fn new(config: CallTracerConfig, gas_mapper: GasMapper) -> Self {
44 Self {
45 gas_mapper,
46 traces: Vec::new(),
47 code_with_salt: None,
48 current_stack: Vec::new(),
49 config,
50 }
51 }
52
53 pub fn collect_trace(mut self) -> Option<CallTrace<Gas>> {
55 self.traces.pop()
56 }
57}
58
59impl<Gas: Default, GasMapper: Fn(Weight) -> Gas> Tracing for CallTracer<Gas, GasMapper> {
60 fn instantiate_code(&mut self, code: &Code, salt: Option<&[u8; 32]>) {
61 self.code_with_salt = Some((code.clone(), salt.is_some()));
62 }
63
64 fn enter_child_span(
65 &mut self,
66 from: H160,
67 to: H160,
68 is_delegate_call: bool,
69 is_read_only: bool,
70 value: U256,
71 input: &[u8],
72 gas_left: Weight,
73 ) {
74 if let Some(&index) = self.current_stack.last() {
76 if let Some(trace) = self.traces.get_mut(index) {
77 trace.child_call_count += 1;
78 }
79 }
80
81 if self.traces.is_empty() || !self.config.only_top_call {
82 let (call_type, input) = match self.code_with_salt.take() {
83 Some((Code::Upload(v), salt)) => (
84 if salt { CallType::Create2 } else { CallType::Create },
85 v.into_iter().chain(input.to_vec().into_iter()).collect::<Vec<_>>(),
86 ),
87 Some((Code::Existing(v), salt)) => (
88 if salt { CallType::Create2 } else { CallType::Create },
89 v.to_fixed_bytes()
90 .into_iter()
91 .chain(input.to_vec().into_iter())
92 .collect::<Vec<_>>(),
93 ),
94 None => {
95 let call_type = if is_read_only {
96 CallType::StaticCall
97 } else if is_delegate_call {
98 CallType::DelegateCall
99 } else {
100 CallType::Call
101 };
102 (call_type, input.to_vec())
103 },
104 };
105
106 self.traces.push(CallTrace {
107 from,
108 to,
109 value: if is_read_only { None } else { Some(value) },
110 call_type,
111 input: input.into(),
112 gas: (self.gas_mapper)(gas_left),
113 ..Default::default()
114 });
115
116 self.current_stack.push(self.traces.len() - 1);
118
119 } else {
121 self.current_stack.push(2);
122 }
123 }
124
125 fn log_event(&mut self, address: H160, topics: &[H256], data: &[u8]) {
126 if !self.config.with_logs {
127 return;
128 }
129
130 let current_index = self.current_stack.last().unwrap();
131
132 if let Some(trace) = self.traces.get_mut(*current_index) {
133 let log = CallLog {
134 address,
135 topics: topics.to_vec(),
136 data: data.to_vec().into(),
137 position: trace.child_call_count,
138 };
139
140 trace.logs.push(log);
141 }
142 }
143
144 fn exit_child_span(&mut self, output: &ExecReturnValue, gas_used: Weight) {
145 self.code_with_salt = None;
146
147 let current_index = self.current_stack.pop().unwrap();
149
150 if let Some(trace) = self.traces.get_mut(current_index) {
151 trace.output = output.data.clone().into();
152 trace.gas_used = (self.gas_mapper)(gas_used);
153
154 if output.did_revert() {
155 trace.revert_reason = decode_revert_reason(&output.data);
156 trace.error = Some("execution reverted".to_string());
157 }
158
159 if self.config.only_top_call {
160 return
161 }
162
163 if let Some(parent_index) = self.current_stack.last() {
165 let child_trace = self.traces.remove(current_index);
166 self.traces[*parent_index].calls.push(child_trace);
167 }
168 }
169 }
170 fn exit_child_span_with_error(&mut self, error: DispatchError, gas_used: Weight) {
171 self.code_with_salt = None;
172
173 let current_index = self.current_stack.pop().unwrap();
175
176 if let Some(trace) = self.traces.get_mut(current_index) {
177 trace.gas_used = (self.gas_mapper)(gas_used);
178
179 trace.error = match error {
180 DispatchError::Module(sp_runtime::ModuleError { message, .. }) =>
181 Some(message.unwrap_or_default().to_string()),
182 _ => Some(format!("{:?}", error)),
183 };
184
185 if self.config.only_top_call {
186 return
187 }
188
189 if let Some(parent_index) = self.current_stack.last() {
191 let child_trace = self.traces.remove(current_index);
192 self.traces[*parent_index].calls.push(child_trace);
193 }
194 }
195 }
196}