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	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/// A tracer that traces the prestate.
27#[derive(frame_support::DefaultNoBound, Debug, Clone, PartialEq)]
28pub struct PrestateTracer<T> {
29	/// The tracer configuration.
30	config: PrestateTracerConfig,
31
32	/// The current address of the contract's which storage is being accessed.
33	current_addr: H160,
34
35	/// Whether the current call is a contract creation.
36	is_create: Option<Code>,
37
38	// pre / post state
39	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	/// Create a new [`PrestateTracer`] instance.
52	pub fn new(config: PrestateTracerConfig) -> Self {
53		Self { config, ..Default::default() }
54	}
55
56	/// Returns an empty trace.
57	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	/// Collect the traces and return them.
66	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			// clean up the storage that are in pre but not in post these are just read
80			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	/// Get the code of the contract.
142	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	/// Update the prestate info for the given address.
149	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	/// Set the PrestateTraceInfo for the given address.
157	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}