use crate::{
AccountInfo, Code, Config, ExecReturnValue, Key, Pallet, PristineCode, Weight,
evm::{Bytes, PrestateTrace, PrestateTraceInfo, PrestateTracerConfig},
tracing::Tracing,
};
use alloc::{
collections::{BTreeMap, BTreeSet},
vec::Vec,
};
use sp_core::{H160, U256};
#[derive(frame_support::DefaultNoBound, Debug, Clone, PartialEq)]
pub struct PrestateTracer<T> {
config: PrestateTracerConfig,
calls: Vec<H160>,
create_code: Option<Code>,
created_addrs: BTreeSet<H160>,
destructed_addrs: BTreeSet<H160>,
trace: (BTreeMap<H160, PrestateTraceInfo>, BTreeMap<H160, PrestateTraceInfo>),
_phantom: core::marker::PhantomData<T>,
}
impl<T: Config> PrestateTracer<T>
where
T::Nonce: Into<u32>,
{
pub fn new(config: PrestateTracerConfig) -> Self {
Self { config, ..Default::default() }
}
fn current_addr(&self) -> H160 {
self.calls.last().copied().unwrap_or_default()
}
pub fn empty_trace(&self) -> PrestateTrace {
if self.config.diff_mode {
PrestateTrace::DiffMode { pre: Default::default(), post: Default::default() }
} else {
PrestateTrace::Prestate(Default::default())
}
}
pub fn collect_trace(self) -> PrestateTrace {
let (mut pre, mut post) = self.trace;
let include_code = !self.config.disable_code;
let is_empty = |info: &PrestateTraceInfo| {
!info.storage.values().any(|v| v.is_some()) &&
info.balance.is_none() &&
info.nonce.is_none() &&
info.code.is_none()
};
if self.config.diff_mode {
if include_code {
for addr in &self.created_addrs {
if let Some(info) = post.get_mut(addr) {
info.code = Self::bytecode(addr);
}
}
}
for addr in &self.destructed_addrs {
Self::update_prestate_info(post.entry(*addr).or_default(), addr, None);
}
pre.iter_mut().for_each(|(addr, info)| {
if let Some(post_info) = post.get(addr) {
info.storage.retain(|k, _| post_info.storage.contains_key(k));
} else {
info.storage.clear();
}
});
post.retain(|addr, _| {
if self.created_addrs.contains(addr) && self.destructed_addrs.contains(addr) {
return false;
}
true
});
pre.retain(|addr, pre_info| {
if is_empty(&pre_info) {
return false;
}
let post_info = post.entry(*addr).or_insert_with_key(|addr| {
Self::prestate_info(
addr,
Pallet::<T>::evm_balance(addr),
include_code.then(|| Self::bytecode(addr)).flatten(),
)
});
if post_info == pre_info {
post.remove(addr);
return false;
}
if post_info.code == pre_info.code {
post_info.code = None;
}
if post_info.balance == pre_info.balance {
post_info.balance = None;
}
if post_info.nonce == pre_info.nonce {
post_info.nonce = None;
}
if post_info == &Default::default() {
post.remove(addr);
}
true
});
post.retain(|_, info| !is_empty(&info));
PrestateTrace::DiffMode { pre, post }
} else {
pre.retain(|_, info| !is_empty(&info));
PrestateTrace::Prestate(pre)
}
}
}
macro_rules! get_entry {
($self:expr, $addr:expr) => {
if $self.created_addrs.contains(&$addr) {
if !$self.config.diff_mode {
return;
}
$self.trace.1.entry($addr)
} else {
$self.trace.0.entry($addr)
}
};
}
impl<T: Config> PrestateTracer<T>
where
T::Nonce: Into<u32>,
{
fn bytecode(address: &H160) -> Option<Bytes> {
let code_hash = AccountInfo::<T>::load_contract(address)?.code_hash;
let code: Vec<u8> = PristineCode::<T>::get(&code_hash)?.into();
return Some(code.into());
}
fn update_prestate_info(entry: &mut PrestateTraceInfo, addr: &H160, code: Option<Bytes>) {
let info = Self::prestate_info(addr, Pallet::<T>::evm_balance(addr), code);
entry.balance = info.balance;
entry.nonce = info.nonce;
entry.code = info.code;
}
fn prestate_info(addr: &H160, balance: U256, code: Option<Bytes>) -> PrestateTraceInfo {
let mut info = PrestateTraceInfo::default();
info.balance = Some(balance);
info.code = code;
let nonce = Pallet::<T>::evm_nonce(addr);
info.nonce = if nonce > 0 { Some(nonce) } else { None };
info
}
fn read_account(&mut self, addr: H160) {
let include_code = !self.config.disable_code;
get_entry!(self, addr).or_insert_with_key(|addr| {
Self::prestate_info(
addr,
Pallet::<T>::evm_balance(addr),
include_code.then(|| Self::bytecode(addr)).flatten(),
)
});
}
}
impl<T: Config> Tracing for PrestateTracer<T>
where
T::Nonce: Into<u32>,
{
fn watch_address(&mut self, addr: &H160) {
let include_code = !self.config.disable_code;
self.trace.0.entry(*addr).or_insert_with_key(|addr| {
Self::prestate_info(
addr,
Pallet::<T>::evm_balance(addr),
include_code.then(|| Self::bytecode(addr)).flatten(),
)
});
}
fn instantiate_code(&mut self, code: &crate::Code, _salt: Option<&[u8; 32]>) {
self.create_code = Some(code.clone());
}
fn terminate(
&mut self,
contract_address: H160,
beneficiary_address: H160,
_gas_left: u64,
_value: U256,
) {
self.destructed_addrs.insert(contract_address);
self.trace.0.entry(beneficiary_address).or_insert_with_key(|addr| {
Self::prestate_info(addr, Pallet::<T>::evm_balance(addr), None)
});
}
fn enter_child_span(
&mut self,
from: H160,
to: H160,
delegate_call: Option<H160>,
_is_read_only: bool,
_value: U256,
_input: &[u8],
_gas_limit: u64,
) {
if let Some(delegate_call) = delegate_call {
self.calls.push(self.current_addr());
self.read_account(delegate_call);
} else {
self.calls.push(to);
self.read_account(from);
}
if self.create_code.take().is_some() {
self.created_addrs.insert(to);
} else {
self.read_account(to);
}
}
fn exit_child_span_with_error(
&mut self,
_error: crate::DispatchError,
_gas_used: u64,
_weight_consumed: Weight,
) {
self.calls.pop();
}
fn exit_child_span(
&mut self,
output: &ExecReturnValue,
_gas_used: u64,
_weight_consumed: Weight,
) {
let current_addr = self.calls.pop().unwrap_or_default();
if output.did_revert() {
return;
}
let code = if self.config.disable_code { None } else { Self::bytecode(¤t_addr) };
Self::update_prestate_info(
self.trace.1.entry(current_addr).or_default(),
¤t_addr,
code,
);
}
fn storage_write(&mut self, key: &Key, old_value: Option<Vec<u8>>, new_value: Option<&[u8]>) {
let current_addr = self.current_addr();
let key = Bytes::from(key.unhashed().to_vec());
let old_value = get_entry!(self, current_addr)
.or_default()
.storage
.entry(key.clone())
.or_insert_with(|| old_value.map(Into::into));
if !self.config.diff_mode {
return;
}
if old_value.as_ref().map(|v| v.0.as_ref()) != new_value {
self.trace
.1
.entry(current_addr)
.or_default()
.storage
.insert(key, new_value.map(|v| v.to_vec().into()));
} else {
self.trace.1.entry(self.current_addr()).or_default().storage.remove(&key);
}
}
fn storage_read(&mut self, key: &Key, value: Option<&[u8]>) {
let current_addr = self.current_addr();
get_entry!(self, current_addr)
.or_default()
.storage
.entry(key.unhashed().to_vec().into())
.or_insert_with(|| value.map(|v| v.to_vec().into()));
}
fn balance_read(&mut self, addr: &H160, value: U256) {
let include_code = !self.config.disable_code;
get_entry!(self, *addr).or_insert_with_key(|addr| {
Self::prestate_info(addr, value, include_code.then(|| Self::bytecode(addr)).flatten())
});
}
}