feat(rpc): add parity trace conversion (#2576)

This commit is contained in:
Matthias Seitz
2023-05-05 19:48:31 +02:00
committed by GitHub
parent a5b9737a4f
commit e6107a1bce
5 changed files with 167 additions and 25 deletions

View File

@ -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());
}
}

View File

@ -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![] }
}

View File

@ -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,
};

View File

@ -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>,
}