mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(rpc): add parity trace conversion (#2576)
This commit is contained in:
@ -39,8 +39,8 @@ impl GethTraceBuilder {
|
||||
// Fill in memory and storage depending on the options
|
||||
if !opts.disable_storage.unwrap_or_default() {
|
||||
let contract_storage = storage.entry(step.contract).or_default();
|
||||
if let Some((key, value)) = step.state_diff {
|
||||
contract_storage.insert(key.into(), value.into());
|
||||
if let Some(change) = step.storage_change {
|
||||
contract_storage.insert(change.key.into(), change.value.into());
|
||||
log.storage = Some(contract_storage.clone());
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,26 +104,48 @@ impl ParityTraceBuilder {
|
||||
/// Returns the tracing types that are configured in the set
|
||||
pub fn into_trace_type_traces(
|
||||
self,
|
||||
_trace_types: &HashSet<TraceType>,
|
||||
trace_types: &HashSet<TraceType>,
|
||||
) -> (Option<Vec<TransactionTrace>>, Option<VmTrace>, Option<StateDiff>) {
|
||||
// TODO(mattsse): impl conversion
|
||||
(None, None, None)
|
||||
if trace_types.is_empty() || self.nodes.is_empty() {
|
||||
return (None, None, None)
|
||||
}
|
||||
|
||||
let with_traces = trace_types.contains(&TraceType::Trace);
|
||||
let with_diff = trace_types.contains(&TraceType::StateDiff);
|
||||
|
||||
let vm_trace = if trace_types.contains(&TraceType::VmTrace) {
|
||||
Some(vm_trace(&self.nodes))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let trace_addresses = self.trace_addresses();
|
||||
let mut traces = Vec::with_capacity(if with_traces { self.nodes.len() } else { 0 });
|
||||
let mut diff = StateDiff::default();
|
||||
|
||||
for (node, trace_address) in self.nodes.iter().zip(trace_addresses) {
|
||||
if with_traces {
|
||||
let trace = node.parity_transaction_trace(trace_address);
|
||||
traces.push(trace);
|
||||
}
|
||||
if with_diff {
|
||||
node.parity_update_state_diff(&mut diff);
|
||||
}
|
||||
}
|
||||
|
||||
let traces = with_traces.then_some(traces);
|
||||
let diff = with_diff.then_some(diff);
|
||||
|
||||
(traces, vm_trace, diff)
|
||||
}
|
||||
|
||||
/// Returns an iterator over all recorded traces for `trace_transaction`
|
||||
pub fn into_transaction_traces_iter(self) -> impl Iterator<Item = TransactionTrace> {
|
||||
let trace_addresses = self.trace_addresses();
|
||||
|
||||
self.nodes.into_iter().zip(trace_addresses).map(|(node, trace_address)| {
|
||||
let action = node.parity_action();
|
||||
let output = TraceResult::parity_success(node.parity_trace_output());
|
||||
TransactionTrace {
|
||||
action,
|
||||
result: Some(output),
|
||||
trace_address,
|
||||
subtraces: node.children.len(),
|
||||
}
|
||||
})
|
||||
self.nodes
|
||||
.into_iter()
|
||||
.zip(trace_addresses)
|
||||
.map(|(node, trace_address)| node.parity_transaction_trace(trace_address))
|
||||
}
|
||||
|
||||
/// Returns the raw traces of the transaction
|
||||
@ -131,3 +153,10 @@ impl ParityTraceBuilder {
|
||||
self.into_transaction_traces_iter().collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct the vmtrace for the entire callgraph
|
||||
fn vm_trace(nodes: &[CallTraceNode]) -> VmTrace {
|
||||
// TODO: populate vm trace
|
||||
|
||||
VmTrace { code: nodes[0].trace.data.clone().into(), ops: vec![] }
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ mod fourbyte;
|
||||
mod opcount;
|
||||
mod types;
|
||||
mod utils;
|
||||
use crate::tracing::types::StorageChange;
|
||||
pub use builder::{geth::GethTraceBuilder, parity::ParityTraceBuilder};
|
||||
pub use config::TracingInspectorConfig;
|
||||
pub use fourbyte::FourByteInspector;
|
||||
@ -190,7 +191,7 @@ impl TracingInspector {
|
||||
|
||||
// fields will be populated end of call
|
||||
gas_cost: 0,
|
||||
state_diff: None,
|
||||
storage_change: None,
|
||||
status: InstructionResult::Continue,
|
||||
});
|
||||
}
|
||||
@ -221,15 +222,16 @@ impl TracingInspector {
|
||||
.expect("exists; initialized with vec")
|
||||
.last();
|
||||
|
||||
step.state_diff = match (op, journal_entry) {
|
||||
step.storage_change = match (op, journal_entry) {
|
||||
(
|
||||
opcode::SLOAD | opcode::SSTORE,
|
||||
Some(JournalEntry::StorageChange { address, key, .. }),
|
||||
Some(JournalEntry::StorageChange { address, key, had_value }),
|
||||
) => {
|
||||
// SAFETY: (Address,key) exists if part if StorageChange
|
||||
let value =
|
||||
data.journaled_state.state[address].storage[key].present_value();
|
||||
Some((*key, value))
|
||||
let change = StorageChange { key: *key, value, had_value: *had_value };
|
||||
Some(change)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
@ -5,14 +5,16 @@ use reth_primitives::{bytes::Bytes, Address, H256, U256};
|
||||
use reth_rpc_types::trace::{
|
||||
geth::StructLog,
|
||||
parity::{
|
||||
Action, ActionType, CallAction, CallOutput, CallType, CreateAction, CreateOutput,
|
||||
SelfdestructAction, TraceOutput,
|
||||
Action, ActionType, CallAction, CallOutput, CallType, ChangedType, CreateAction,
|
||||
CreateOutput, Delta, SelfdestructAction, StateDiff, TraceOutput, TraceResult,
|
||||
TransactionTrace,
|
||||
},
|
||||
};
|
||||
use revm::interpreter::{
|
||||
CallContext, CallScheme, CreateScheme, InstructionResult, Memory, OpCode, Stack,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::btree_map::Entry;
|
||||
|
||||
/// A unified representation of a call
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
@ -28,6 +30,13 @@ pub enum CallKind {
|
||||
Create2,
|
||||
}
|
||||
|
||||
impl CallKind {
|
||||
/// Returns true if the call is a create
|
||||
pub fn is_any_create(&self) -> bool {
|
||||
matches!(self, CallKind::Create | CallKind::Create2)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CallScheme> for CallKind {
|
||||
fn from(scheme: CallScheme) -> Self {
|
||||
match scheme {
|
||||
@ -160,6 +169,68 @@ impl CallTraceNode {
|
||||
self.trace.status
|
||||
}
|
||||
|
||||
/// Updates the values of the state diff
|
||||
pub(crate) fn parity_update_state_diff(&self, diff: &mut StateDiff) {
|
||||
let addr = self.trace.address;
|
||||
let acc = diff.entry(addr).or_default();
|
||||
|
||||
if self.kind().is_any_create() {
|
||||
let code = self.trace.output.clone();
|
||||
if acc.code == Delta::Unchanged {
|
||||
acc.code = Delta::Added(code.into())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: track nonce and balance changes
|
||||
|
||||
// iterate over all storage diffs
|
||||
for change in self.trace.steps.iter().filter_map(|s| s.storage_change) {
|
||||
let StorageChange { key, value, had_value } = change;
|
||||
let value = H256::from(value);
|
||||
match acc.storage.entry(key.into()) {
|
||||
Entry::Vacant(entry) => {
|
||||
if let Some(had_value) = had_value {
|
||||
entry.insert(Delta::Changed(ChangedType {
|
||||
from: had_value.into(),
|
||||
to: value,
|
||||
}));
|
||||
} else {
|
||||
entry.insert(Delta::Added(value));
|
||||
}
|
||||
}
|
||||
Entry::Occupied(mut entry) => {
|
||||
let value = match entry.get() {
|
||||
Delta::Unchanged => Delta::Added(value),
|
||||
Delta::Added(added) => {
|
||||
if added == &value {
|
||||
Delta::Added(*added)
|
||||
} else {
|
||||
Delta::Changed(ChangedType { from: *added, to: value })
|
||||
}
|
||||
}
|
||||
Delta::Removed(_) => Delta::Added(value),
|
||||
Delta::Changed(c) => {
|
||||
Delta::Changed(ChangedType { from: c.from, to: value })
|
||||
}
|
||||
};
|
||||
entry.insert(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts this node into a parity `TransactionTrace`
|
||||
pub(crate) fn parity_transaction_trace(&self, trace_address: Vec<usize>) -> TransactionTrace {
|
||||
let action = self.parity_action();
|
||||
let output = TraceResult::parity_success(self.parity_trace_output());
|
||||
TransactionTrace {
|
||||
action,
|
||||
result: Some(output),
|
||||
trace_address,
|
||||
subtraces: self.children.len(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `Output` for a parity trace
|
||||
pub(crate) fn parity_trace_output(&self) -> TraceOutput {
|
||||
match self.kind() {
|
||||
@ -252,7 +323,7 @@ pub struct CallTraceStep {
|
||||
/// Gas cost of step execution
|
||||
pub gas_cost: u64,
|
||||
/// Change of the contract state after step execution (effect of the SLOAD/SSTORE instructions)
|
||||
pub state_diff: Option<(U256, U256)>,
|
||||
pub storage_change: Option<StorageChange>,
|
||||
/// Final status of the call
|
||||
pub status: InstructionResult,
|
||||
}
|
||||
@ -290,3 +361,11 @@ impl From<&CallTraceStep> for StructLog {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a storage change during execution
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct StorageChange {
|
||||
pub key: U256,
|
||||
pub value: U256,
|
||||
pub had_value: Option<U256>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user