referrerpolicy=no-referrer-when-downgrade

pallet_revive/evm/tracing/
prestate_tracing.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17use crate::{
18	AccountInfo, Code, Config, ExecReturnValue, Key, Pallet, PristineCode, Weight,
19	evm::{Bytes, PrestateTrace, PrestateTraceInfo, PrestateTracerConfig},
20	tracing::Tracing,
21};
22use alloc::{
23	collections::{BTreeMap, BTreeSet},
24	vec::Vec,
25};
26use sp_core::{H160, U256};
27
28/// A tracer that traces the prestate.
29#[derive(frame_support::DefaultNoBound, Debug, Clone, PartialEq)]
30pub struct PrestateTracer<T> {
31	/// The tracer configuration.
32	config: PrestateTracerConfig,
33
34	/// Stack of calls.
35	calls: Vec<H160>,
36
37	/// The code used by create transaction
38	create_code: Option<Code>,
39
40	/// List of created contracts addresses.
41	created_addrs: BTreeSet<H160>,
42
43	/// List of destructed contracts addresses.
44	destructed_addrs: BTreeSet<H160>,
45
46	// pre / post state
47	trace: (BTreeMap<H160, PrestateTraceInfo>, BTreeMap<H160, PrestateTraceInfo>),
48
49	_phantom: core::marker::PhantomData<T>,
50}
51
52impl<T: Config> PrestateTracer<T>
53where
54	T::Nonce: Into<u32>,
55{
56	/// Create a new [`PrestateTracer`] instance.
57	pub fn new(config: PrestateTracerConfig) -> Self {
58		Self { config, ..Default::default() }
59	}
60
61	fn current_addr(&self) -> H160 {
62		self.calls.last().copied().unwrap_or_default()
63	}
64
65	/// Returns an empty trace.
66	pub fn empty_trace(&self) -> PrestateTrace {
67		if self.config.diff_mode {
68			PrestateTrace::DiffMode { pre: Default::default(), post: Default::default() }
69		} else {
70			PrestateTrace::Prestate(Default::default())
71		}
72	}
73
74	/// Collect the traces and return them.
75	pub fn collect_trace(self) -> PrestateTrace {
76		let (mut pre, mut post) = self.trace;
77		let include_code = !self.config.disable_code;
78
79		let is_empty = |info: &PrestateTraceInfo| {
80			!info.storage.values().any(|v| v.is_some()) &&
81				info.balance.is_none() &&
82				info.nonce.is_none() &&
83				info.code.is_none()
84		};
85
86		if self.config.diff_mode {
87			if include_code {
88				for addr in &self.created_addrs {
89					if let Some(info) = post.get_mut(addr) {
90						info.code = Self::bytecode(addr);
91					}
92				}
93			}
94
95			// collect destructed contracts info in post state
96			for addr in &self.destructed_addrs {
97				Self::update_prestate_info(post.entry(*addr).or_default(), addr, None);
98			}
99
100			// clean up the storage that are in pre but not in post these are just read
101			pre.iter_mut().for_each(|(addr, info)| {
102				if let Some(post_info) = post.get(addr) {
103					info.storage.retain(|k, _| post_info.storage.contains_key(k));
104				} else {
105					info.storage.clear();
106				}
107			});
108
109			// If the address was created and destructed we do not trace it
110			post.retain(|addr, _| {
111				if self.created_addrs.contains(addr) && self.destructed_addrs.contains(addr) {
112					return false;
113				}
114				true
115			});
116
117			pre.retain(|addr, pre_info| {
118				if is_empty(&pre_info) {
119					return false;
120				}
121
122				let post_info = post.entry(*addr).or_insert_with_key(|addr| {
123					Self::prestate_info(
124						addr,
125						Pallet::<T>::evm_balance(addr),
126						include_code.then(|| Self::bytecode(addr)).flatten(),
127					)
128				});
129
130				if post_info == pre_info {
131					post.remove(addr);
132					return false;
133				}
134
135				if post_info.code == pre_info.code {
136					post_info.code = None;
137				}
138
139				if post_info.balance == pre_info.balance {
140					post_info.balance = None;
141				}
142
143				if post_info.nonce == pre_info.nonce {
144					post_info.nonce = None;
145				}
146
147				if post_info == &Default::default() {
148					post.remove(addr);
149				}
150
151				true
152			});
153
154			post.retain(|_, info| !is_empty(&info));
155			PrestateTrace::DiffMode { pre, post }
156		} else {
157			pre.retain(|_, info| !is_empty(&info));
158			PrestateTrace::Prestate(pre)
159		}
160	}
161}
162
163/// Get the appropriate trace entry (pre or post) based on whether
164/// the address was created.
165/// Returns early if the address is created and diff_mode is false.
166macro_rules! get_entry {
167	($self:expr, $addr:expr) => {
168		if $self.created_addrs.contains(&$addr) {
169			if !$self.config.diff_mode {
170				return;
171			}
172			$self.trace.1.entry($addr)
173		} else {
174			$self.trace.0.entry($addr)
175		}
176	};
177}
178
179impl<T: Config> PrestateTracer<T>
180where
181	T::Nonce: Into<u32>,
182{
183	/// Get the code of the contract.
184	fn bytecode(address: &H160) -> Option<Bytes> {
185		// EIP-7702: report the delegation indicator, matching eth_getCode / EXTCODEHASH.
186		if let Some(target) = AccountInfo::<T>::get_delegation_target(address) {
187			return Some(AccountInfo::<T>::delegation_indicator(&target).to_vec().into());
188		}
189		let code_hash = AccountInfo::<T>::load_contract(address)?.code_hash;
190		let code: Vec<u8> = PristineCode::<T>::get(&code_hash)?.into();
191		return Some(code.into());
192	}
193
194	/// Update the prestate info for the given address.
195	fn update_prestate_info(entry: &mut PrestateTraceInfo, addr: &H160, code: Option<Bytes>) {
196		let info = Self::prestate_info(addr, Pallet::<T>::evm_balance(addr), code);
197		entry.balance = info.balance;
198		entry.nonce = info.nonce;
199		entry.code = info.code;
200	}
201
202	/// Set the PrestateTraceInfo for the given address.
203	fn prestate_info(addr: &H160, balance: U256, code: Option<Bytes>) -> PrestateTraceInfo {
204		let mut info = PrestateTraceInfo::default();
205		info.balance = Some(balance);
206		info.code = code;
207		let nonce = Pallet::<T>::evm_nonce(addr);
208		info.nonce = if nonce > 0 { Some(nonce) } else { None };
209		info
210	}
211
212	/// Record a read
213	fn read_account(&mut self, addr: H160) {
214		let include_code = !self.config.disable_code;
215		get_entry!(self, addr).or_insert_with_key(|addr| {
216			Self::prestate_info(
217				addr,
218				Pallet::<T>::evm_balance(addr),
219				include_code.then(|| Self::bytecode(addr)).flatten(),
220			)
221		});
222	}
223}
224
225impl<T: Config> Tracing for PrestateTracer<T>
226where
227	T::Nonce: Into<u32>,
228{
229	fn watch_address(&mut self, addr: &H160) {
230		let include_code = !self.config.disable_code;
231		self.trace.0.entry(*addr).or_insert_with_key(|addr| {
232			Self::prestate_info(
233				addr,
234				Pallet::<T>::evm_balance(addr),
235				include_code.then(|| Self::bytecode(addr)).flatten(),
236			)
237		});
238	}
239
240	fn instantiate_code(&mut self, code: &crate::Code, _salt: Option<&[u8; 32]>) {
241		self.create_code = Some(code.clone());
242	}
243
244	fn terminate(
245		&mut self,
246		contract_address: H160,
247		beneficiary_address: H160,
248		_gas_left: u64,
249		_value: U256,
250	) {
251		self.destructed_addrs.insert(contract_address);
252		self.trace.0.entry(beneficiary_address).or_insert_with_key(|addr| {
253			Self::prestate_info(addr, Pallet::<T>::evm_balance(addr), None)
254		});
255	}
256
257	fn enter_child_span(
258		&mut self,
259		from: H160,
260		to: H160,
261		code_address: Option<H160>,
262		is_delegate_call: bool,
263		_is_read_only: bool,
264		_value: U256,
265		_input: &[u8],
266		_gas_limit: u64,
267	) {
268		if is_delegate_call {
269			self.calls.push(self.current_addr());
270		} else {
271			self.calls.push(to);
272		}
273
274		if let Some(code_address) = code_address {
275			self.read_account(code_address);
276		}
277		self.read_account(from);
278
279		if self.create_code.take().is_some() {
280			self.created_addrs.insert(to);
281		} else {
282			self.read_account(to);
283		}
284	}
285
286	fn exit_child_span_with_error(
287		&mut self,
288		_error: crate::DispatchError,
289		_gas_used: u64,
290		_weight_consumed: Weight,
291	) {
292		self.calls.pop();
293	}
294
295	fn exit_child_span(
296		&mut self,
297		output: &ExecReturnValue,
298		_gas_used: u64,
299		_weight_consumed: Weight,
300	) {
301		let current_addr = self.calls.pop().unwrap_or_default();
302		if output.did_revert() {
303			return;
304		}
305
306		let code = if self.config.disable_code { None } else { Self::bytecode(&current_addr) };
307
308		Self::update_prestate_info(
309			self.trace.1.entry(current_addr).or_default(),
310			&current_addr,
311			code,
312		);
313	}
314
315	fn storage_write(&mut self, key: &Key, old_value: Option<Vec<u8>>, new_value: Option<&[u8]>) {
316		let current_addr = self.current_addr();
317		let key = Bytes::from(key.unhashed().to_vec());
318
319		let old_value = get_entry!(self, current_addr)
320			.or_default()
321			.storage
322			.entry(key.clone())
323			.or_insert_with(|| old_value.map(Into::into));
324
325		if !self.config.diff_mode {
326			return;
327		}
328
329		if old_value.as_ref().map(|v| v.0.as_ref()) != new_value {
330			self.trace
331				.1
332				.entry(current_addr)
333				.or_default()
334				.storage
335				.insert(key, new_value.map(|v| v.to_vec().into()));
336		} else {
337			self.trace.1.entry(self.current_addr()).or_default().storage.remove(&key);
338		}
339	}
340
341	fn storage_read(&mut self, key: &Key, value: Option<&[u8]>) {
342		let current_addr = self.current_addr();
343
344		get_entry!(self, current_addr)
345			.or_default()
346			.storage
347			.entry(key.unhashed().to_vec().into())
348			.or_insert_with(|| value.map(|v| v.to_vec().into()));
349	}
350
351	fn balance_read(&mut self, addr: &H160, value: U256) {
352		let include_code = !self.config.disable_code;
353		get_entry!(self, *addr).or_insert_with_key(|addr| {
354			Self::prestate_info(addr, value, include_code.then(|| Self::bytecode(addr)).flatten())
355		});
356	}
357}