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