mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
fix: geth prestate tracing (#5040)
This commit is contained in:
@ -6,7 +6,7 @@ use crate::tracing::{
|
||||
};
|
||||
use reth_primitives::{Address, Bytes, B256, U256};
|
||||
use reth_rpc_types::trace::geth::{
|
||||
AccountChangeKind, AccountState, CallConfig, CallFrame, DefaultFrame, DiffMode, DiffStateKind,
|
||||
AccountChangeKind, AccountState, CallConfig, CallFrame, DefaultFrame, DiffMode,
|
||||
GethDefaultTracingOptions, PreStateConfig, PreStateFrame, PreStateMode, StructLog,
|
||||
};
|
||||
use revm::{
|
||||
@ -192,23 +192,25 @@ impl GethTraceBuilder {
|
||||
let is_diff = prestate_config.is_diff_mode();
|
||||
if !is_diff {
|
||||
let mut prestate = PreStateMode::default();
|
||||
for (addr, _) in account_diffs {
|
||||
for (addr, changed_acc) in account_diffs {
|
||||
let db_acc = db.basic(addr)?.unwrap_or_default();
|
||||
|
||||
prestate.0.insert(
|
||||
addr,
|
||||
AccountState::from_account_info(
|
||||
let mut pre_state = AccountState::from_account_info(
|
||||
db_acc.nonce,
|
||||
db_acc.balance,
|
||||
db_acc.code.as_ref().map(|code| code.original_bytes()),
|
||||
),
|
||||
);
|
||||
|
||||
// handle _touched_ storage slots
|
||||
for (key, slot) in changed_acc.storage.iter() {
|
||||
pre_state.storage.insert((*key).into(), slot.previous_or_original_value.into());
|
||||
}
|
||||
|
||||
prestate.0.insert(addr, pre_state);
|
||||
}
|
||||
self.update_storage_from_trace_prestate_mode(&mut prestate.0, DiffStateKind::Pre);
|
||||
Ok(PreStateFrame::Default(prestate))
|
||||
} else {
|
||||
let mut state_diff = DiffMode::default();
|
||||
let mut change_types = HashMap::with_capacity(account_diffs.len());
|
||||
let mut account_change_kinds = HashMap::with_capacity(account_diffs.len());
|
||||
for (addr, changed_acc) in account_diffs {
|
||||
let db_acc = db.basic(addr)?.unwrap_or_default();
|
||||
let db_code = db_acc.code.as_ref();
|
||||
@ -228,14 +230,22 @@ impl GethTraceBuilder {
|
||||
})
|
||||
.map(Into::into);
|
||||
|
||||
let pre_state =
|
||||
let mut pre_state =
|
||||
AccountState::from_account_info(db_acc.nonce, db_acc.balance, pre_code);
|
||||
|
||||
let post_state = AccountState::from_account_info(
|
||||
let mut post_state = AccountState::from_account_info(
|
||||
changed_acc.info.nonce,
|
||||
changed_acc.info.balance,
|
||||
changed_acc.info.code.as_ref().map(|code| code.original_bytes()),
|
||||
);
|
||||
|
||||
// handle storage changes
|
||||
for (key, slot) in changed_acc.storage.iter().filter(|(_, slot)| slot.is_changed())
|
||||
{
|
||||
pre_state.storage.insert((*key).into(), slot.previous_or_original_value.into());
|
||||
post_state.storage.insert((*key).into(), slot.present_value.into());
|
||||
}
|
||||
|
||||
state_diff.pre.insert(addr, pre_state);
|
||||
state_diff.post.insert(addr, post_state);
|
||||
|
||||
@ -251,44 +261,17 @@ impl GethTraceBuilder {
|
||||
AccountChangeKind::Modify
|
||||
};
|
||||
|
||||
change_types.insert(addr, (pre_change, post_change));
|
||||
account_change_kinds.insert(addr, (pre_change, post_change));
|
||||
}
|
||||
|
||||
self.update_storage_from_trace_diff_mode(&mut state_diff.pre, DiffStateKind::Pre);
|
||||
self.update_storage_from_trace_diff_mode(&mut state_diff.post, DiffStateKind::Post);
|
||||
|
||||
// ensure we're only keeping changed entries
|
||||
state_diff.retain_changed().remove_zero_storage_values();
|
||||
|
||||
self.diff_traces(&mut state_diff.pre, &mut state_diff.post, change_types);
|
||||
self.diff_traces(&mut state_diff.pre, &mut state_diff.post, account_change_kinds);
|
||||
Ok(PreStateFrame::Diff(state_diff))
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the account storage for all nodes in the trace for pre-state mode.
|
||||
#[inline]
|
||||
fn update_storage_from_trace_prestate_mode(
|
||||
&self,
|
||||
account_states: &mut BTreeMap<Address, AccountState>,
|
||||
kind: DiffStateKind,
|
||||
) {
|
||||
for node in self.nodes.iter() {
|
||||
node.geth_update_account_storage(account_states, kind);
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the account storage for all nodes in the trace for diff mode.
|
||||
#[inline]
|
||||
fn update_storage_from_trace_diff_mode(
|
||||
&self,
|
||||
account_states: &mut BTreeMap<Address, AccountState>,
|
||||
kind: DiffStateKind,
|
||||
) {
|
||||
for node in self.nodes.iter() {
|
||||
node.geth_update_account_storage_diff_mode(account_states, kind);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the difference between the pre and post state of the transaction depending on the
|
||||
/// kind of changes of that account (pre,post)
|
||||
fn diff_traces(
|
||||
@ -297,10 +280,9 @@ impl GethTraceBuilder {
|
||||
post: &mut BTreeMap<Address, AccountState>,
|
||||
change_type: HashMap<Address, (AccountChangeKind, AccountChangeKind)>,
|
||||
) {
|
||||
// Don't keep destroyed accounts in the post state
|
||||
post.retain(|addr, post_state| {
|
||||
// only keep accounts that are not created
|
||||
if change_type.get(addr).map(|ty| !ty.1.is_selfdestruct()).unwrap_or(false) {
|
||||
// Don't keep destroyed accounts in the post state
|
||||
if change_type.get(addr).map(|ty| ty.1.is_selfdestruct()).unwrap_or(false) {
|
||||
return false
|
||||
}
|
||||
if let Some(pre_state) = pre.get(addr) {
|
||||
|
||||
@ -121,6 +121,11 @@ impl TracingInspectorConfig {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets state diff recording to true.
|
||||
pub fn with_state_diffs(self) -> Self {
|
||||
self.set_steps_and_state_diffs(true)
|
||||
}
|
||||
|
||||
/// Configure whether the tracer should record state diffs
|
||||
pub fn set_state_diffs(mut self, record_state_diff: bool) -> Self {
|
||||
self.record_state_diff = record_state_diff;
|
||||
|
||||
@ -4,9 +4,7 @@ use crate::tracing::{config::TraceStyle, utils::convert_memory};
|
||||
use alloy_sol_types::decode_revert_reason;
|
||||
use reth_primitives::{Address, Bytes, B256, U256, U64};
|
||||
use reth_rpc_types::trace::{
|
||||
geth::{
|
||||
AccountState, CallFrame, CallLogFrame, DiffStateKind, GethDefaultTracingOptions, StructLog,
|
||||
},
|
||||
geth::{CallFrame, CallLogFrame, GethDefaultTracingOptions, StructLog},
|
||||
parity::{
|
||||
Action, ActionType, CallAction, CallOutput, CallType, CreateAction, CreateOutput,
|
||||
SelfdestructAction, TraceOutput, TransactionTrace,
|
||||
@ -16,7 +14,7 @@ use revm::interpreter::{
|
||||
opcode, CallContext, CallScheme, CreateScheme, InstructionResult, Memory, OpCode, Stack,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
/// A unified representation of a call
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
@ -253,16 +251,6 @@ impl CallTraceNode {
|
||||
stack.extend(self.call_step_stack().into_iter().rev());
|
||||
}
|
||||
|
||||
/// Returns all changed slots and the recorded changes
|
||||
fn changed_storage_slots(&self) -> BTreeMap<U256, Vec<StorageChange>> {
|
||||
let mut changed_slots: BTreeMap<U256, Vec<StorageChange>> = BTreeMap::new();
|
||||
for change in self.trace.steps.iter().filter_map(|s| s.storage_change) {
|
||||
changed_slots.entry(change.key).or_default().push(change);
|
||||
}
|
||||
|
||||
changed_slots
|
||||
}
|
||||
|
||||
/// Returns a list of all steps in this trace in the order they were executed
|
||||
///
|
||||
/// If the step is a call, the id of the child trace is set.
|
||||
@ -446,84 +434,6 @@ impl CallTraceNode {
|
||||
|
||||
call_frame
|
||||
}
|
||||
|
||||
/// Adds storage in-place to account state for all accounts that were touched in the trace
|
||||
/// [CallTrace] execution.
|
||||
///
|
||||
/// * `account_states` - the account map updated in place.
|
||||
/// * `kind` - if [DiffStateKind::Post], it adds storage values after trace transaction
|
||||
/// execution, if [DiffStateKind::Pre], returns the storage values before trace execution.
|
||||
pub(crate) fn geth_update_account_storage(
|
||||
&self,
|
||||
account_states: &mut BTreeMap<Address, AccountState>,
|
||||
kind: DiffStateKind,
|
||||
) {
|
||||
let addr = self.trace.address;
|
||||
let acc_state = account_states.entry(addr).or_default();
|
||||
for change in self.trace.steps.iter().filter_map(|s| s.storage_change) {
|
||||
let StorageChange { key, value, had_value, .. } = change;
|
||||
let value_to_insert = if kind.is_post() {
|
||||
B256::from(value)
|
||||
} else {
|
||||
match had_value {
|
||||
Some(had_value) => B256::from(had_value),
|
||||
None => continue,
|
||||
}
|
||||
};
|
||||
acc_state.storage.insert(key.into(), value_to_insert);
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the account storage for all accounts that were touched in the trace.
|
||||
///
|
||||
/// Depending on the [DiffStateKind] this will either insert the initial value
|
||||
/// [DiffStateKind::Pre] or the final value [DiffStateKind::Post] of the storage slot.
|
||||
pub(crate) fn geth_update_account_storage_diff_mode(
|
||||
&self,
|
||||
account_states: &mut BTreeMap<Address, AccountState>,
|
||||
kind: DiffStateKind,
|
||||
) {
|
||||
let addr = self.execution_address();
|
||||
let changed_slots = self.changed_storage_slots();
|
||||
|
||||
// loop over all changed slots and track the storage changes of that slot
|
||||
for (slot, changes) in changed_slots {
|
||||
let account = account_states.entry(addr).or_default();
|
||||
|
||||
let mut initial_value = account.storage.get(&B256::from(slot)).copied().map(Into::into);
|
||||
let mut final_value = None;
|
||||
|
||||
for change in changes {
|
||||
if initial_value.is_none() {
|
||||
// set the initial value for the first storage change depending on the change
|
||||
// reason
|
||||
initial_value = match change.reason {
|
||||
StorageChangeReason::SSTORE => Some(change.had_value.unwrap_or_default()),
|
||||
StorageChangeReason::SLOAD => Some(change.value),
|
||||
};
|
||||
}
|
||||
|
||||
if change.reason == StorageChangeReason::SSTORE {
|
||||
// keep track of the actual state value that's updated on sstore
|
||||
final_value = Some(change.value);
|
||||
}
|
||||
}
|
||||
|
||||
if final_value.is_none() || initial_value.is_none() {
|
||||
continue
|
||||
}
|
||||
|
||||
if initial_value == final_value {
|
||||
// unchanged
|
||||
continue
|
||||
}
|
||||
|
||||
let value_to_write =
|
||||
if kind.is_post() { final_value } else { initial_value }.expect("exists; qed");
|
||||
|
||||
account.storage.insert(B256::from(slot), B256::from(value_to_write));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct CallTraceStepStackItem<'a> {
|
||||
|
||||
Reference in New Issue
Block a user