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(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			// clean up the storage that are in pre but not in post these are just read
79			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	/// Get the code of the contract.
141	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	/// Update the prestate info for the given address.
148	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	/// Set the PrestateTraceInfo for the given address.
156	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}