pallet_revive/evm/tracing/
prestate_tracing.rs1use crate::{
18 evm::{Bytes, PrestateTrace, PrestateTraceInfo, PrestateTracerConfig},
19 tracing::Tracing,
20 AccountInfo, BalanceOf, Bounded, Code, Config, ExecReturnValue, Key, MomentOf, Pallet,
21 PristineCode, Weight,
22};
23use alloc::{collections::BTreeMap, vec::Vec};
24use sp_core::{H160, H256, U256};
25
26#[derive(frame_support::DefaultNoBound, Debug, Clone, PartialEq)]
28pub struct PrestateTracer<T> {
29 config: PrestateTracerConfig,
31
32 current_addr: H160,
34
35 is_create: Option<Code>,
37
38 trace: (BTreeMap<H160, PrestateTraceInfo>, BTreeMap<H160, PrestateTraceInfo>),
40
41 _phantom: core::marker::PhantomData<T>,
42}
43
44impl<T: Config> PrestateTracer<T>
45where
46 BalanceOf<T>: Into<U256> + TryFrom<U256> + Bounded,
47 MomentOf<T>: Into<U256>,
48 T::Hash: frame_support::traits::IsType<H256>,
49 T::Nonce: Into<u32>,
50{
51 pub fn new(config: PrestateTracerConfig) -> Self {
53 Self { config, ..Default::default() }
54 }
55
56 pub fn empty_trace(&self) -> PrestateTrace {
58 if self.config.diff_mode {
59 PrestateTrace::DiffMode { pre: Default::default(), post: Default::default() }
60 } else {
61 PrestateTrace::Prestate(Default::default())
62 }
63 }
64
65 pub fn collect_trace(&mut self) -> PrestateTrace {
67 let trace = core::mem::take(&mut self.trace);
68 let (mut pre, mut post) = trace;
69 let include_code = !self.config.disable_code;
70
71 let is_empty = |info: &PrestateTraceInfo| {
72 !info.storage.values().any(|v| v.is_some()) &&
73 info.balance.is_none() &&
74 info.nonce.is_none() &&
75 info.code.is_none()
76 };
77
78 if self.config.diff_mode {
79 pre.iter_mut().for_each(|(addr, info)| {
81 if let Some(post_info) = post.get(addr) {
82 info.storage.retain(|k, _| post_info.storage.contains_key(k));
83 } else {
84 info.storage.clear();
85 }
86 });
87
88 pre.retain(|addr, pre_info| {
89 if is_empty(&pre_info) {
90 return false
91 }
92
93 let post_info = post.entry(*addr).or_insert_with_key(|addr| {
94 Self::prestate_info(
95 addr,
96 Pallet::<T>::evm_balance(addr),
97 include_code.then(|| Self::bytecode(addr)).flatten(),
98 )
99 });
100
101 if post_info == pre_info {
102 post.remove(addr);
103 return false
104 }
105
106 if post_info.code == pre_info.code {
107 post_info.code = None;
108 }
109
110 if post_info.balance == pre_info.balance {
111 post_info.balance = None;
112 }
113
114 if post_info.nonce == pre_info.nonce {
115 post_info.nonce = None;
116 }
117
118 if post_info == &Default::default() {
119 post.remove(addr);
120 }
121
122 true
123 });
124
125 post.retain(|_, info| !is_empty(&info));
126 PrestateTrace::DiffMode { pre, post }
127 } else {
128 pre.retain(|_, info| !is_empty(&info));
129 PrestateTrace::Prestate(pre)
130 }
131 }
132}
133
134impl<T: Config> PrestateTracer<T>
135where
136 BalanceOf<T>: Into<U256> + TryFrom<U256> + Bounded,
137 MomentOf<T>: Into<U256>,
138 T::Hash: frame_support::traits::IsType<H256>,
139 T::Nonce: Into<u32>,
140{
141 fn bytecode(address: &H160) -> Option<Bytes> {
143 let code_hash = AccountInfo::<T>::load_contract(address)?.code_hash;
144 let code: Vec<u8> = PristineCode::<T>::get(&code_hash)?.into();
145 return Some(code.into())
146 }
147
148 fn update_prestate_info(entry: &mut PrestateTraceInfo, addr: &H160, code: Option<Bytes>) {
150 let info = Self::prestate_info(addr, Pallet::<T>::evm_balance(addr), code);
151 entry.balance = info.balance;
152 entry.nonce = info.nonce;
153 entry.code = info.code;
154 }
155
156 fn prestate_info(addr: &H160, balance: U256, code: Option<Bytes>) -> PrestateTraceInfo {
158 let mut info = PrestateTraceInfo::default();
159 info.balance = Some(balance);
160 info.code = code;
161 let nonce = Pallet::<T>::evm_nonce(addr);
162 info.nonce = if nonce > 0 { Some(nonce) } else { None };
163 info
164 }
165}
166
167impl<T: Config> Tracing for PrestateTracer<T>
168where
169 BalanceOf<T>: Into<U256> + TryFrom<U256> + Bounded,
170 MomentOf<T>: Into<U256>,
171 T::Hash: frame_support::traits::IsType<H256>,
172 T::Nonce: Into<u32>,
173{
174 fn watch_address(&mut self, addr: &H160) {
175 let include_code = !self.config.disable_code;
176 self.trace.0.entry(*addr).or_insert_with_key(|addr| {
177 Self::prestate_info(
178 addr,
179 Pallet::<T>::evm_balance(addr),
180 include_code.then(|| Self::bytecode(addr)).flatten(),
181 )
182 });
183 }
184
185 fn instantiate_code(&mut self, code: &crate::Code, _salt: Option<&[u8; 32]>) {
186 self.is_create = Some(code.clone());
187 }
188
189 fn enter_child_span(
190 &mut self,
191 from: H160,
192 to: H160,
193 is_delegate_call: bool,
194 _is_read_only: bool,
195 _value: U256,
196 _input: &[u8],
197 _gas: Weight,
198 ) {
199 let include_code = !self.config.disable_code;
200 self.trace.0.entry(from).or_insert_with_key(|addr| {
201 Self::prestate_info(
202 addr,
203 Pallet::<T>::evm_balance(addr),
204 include_code.then(|| Self::bytecode(addr)).flatten(),
205 )
206 });
207
208 if self.is_create.is_none() {
209 self.trace.0.entry(to).or_insert_with_key(|addr| {
210 Self::prestate_info(
211 addr,
212 Pallet::<T>::evm_balance(addr),
213 include_code.then(|| Self::bytecode(addr)).flatten(),
214 )
215 });
216 }
217
218 if !is_delegate_call {
219 self.current_addr = to;
220 }
221 }
222
223 fn exit_child_span_with_error(&mut self, _error: crate::DispatchError, _gas_used: Weight) {
224 self.is_create = None;
225 }
226
227 fn exit_child_span(&mut self, output: &ExecReturnValue, _gas_used: Weight) {
228 let create_code = self.is_create.take();
229 if output.did_revert() {
230 return
231 }
232
233 let code = if self.config.disable_code {
234 None
235 } else if let Some(code) = create_code {
236 match code {
237 Code::Upload(code) => Some(code.into()),
238 Code::Existing(code_hash) =>
239 PristineCode::<T>::get(&code_hash).map(|code| Bytes::from(code.to_vec())),
240 }
241 } else {
242 Self::bytecode(&self.current_addr)
243 };
244
245 Self::update_prestate_info(
246 self.trace.1.entry(self.current_addr).or_default(),
247 &self.current_addr,
248 code,
249 );
250 }
251
252 fn storage_write(&mut self, key: &Key, old_value: Option<Vec<u8>>, new_value: Option<&[u8]>) {
253 let key = Bytes::from(key.unhashed().to_vec());
254
255 let old_value = self
256 .trace
257 .0
258 .entry(self.current_addr)
259 .or_default()
260 .storage
261 .entry(key.clone())
262 .or_insert_with(|| old_value.map(Into::into));
263
264 if !self.config.diff_mode {
265 return
266 }
267
268 if old_value.as_ref().map(|v| v.0.as_ref()) != new_value {
269 self.trace
270 .1
271 .entry(self.current_addr)
272 .or_default()
273 .storage
274 .insert(key, new_value.map(|v| v.to_vec().into()));
275 } else {
276 self.trace.1.entry(self.current_addr).or_default().storage.remove(&key);
277 }
278 }
279
280 fn storage_read(&mut self, key: &Key, value: Option<&[u8]>) {
281 self.trace
282 .0
283 .entry(self.current_addr)
284 .or_default()
285 .storage
286 .entry(key.unhashed().to_vec().into())
287 .or_insert_with(|| value.map(|v| v.to_vec().into()));
288 }
289
290 fn balance_read(&mut self, addr: &H160, value: U256) {
291 let include_code = !self.config.disable_code;
292 self.trace.0.entry(*addr).or_insert_with_key(|addr| {
293 Self::prestate_info(addr, value, include_code.then(|| Self::bytecode(addr)).flatten())
294 });
295 }
296}