diff --git a/crates/revm/revm-inspectors/src/tracing/builder/geth.rs b/crates/revm/revm-inspectors/src/tracing/builder/geth.rs
index df8f1fca3e..36845840b3 100644
--- a/crates/revm/revm-inspectors/src/tracing/builder/geth.rs
+++ b/crates/revm/revm-inspectors/src/tracing/builder/geth.rs
@@ -1,10 +1,13 @@
//! Geth trace builder
-use crate::tracing::{types::CallTraceNode, TracingInspectorConfig};
+use crate::tracing::{
+ types::{CallTraceNode, CallTraceStepStackItem},
+ TracingInspectorConfig,
+};
use reth_primitives::{Address, JsonU256, H256, U256};
use reth_rpc_types::trace::geth::*;
-use revm::interpreter::opcode;
-use std::collections::{BTreeMap, HashMap};
+
+use std::collections::{BTreeMap, HashMap, VecDeque};
/// A type for creating geth style traces
#[derive(Clone, Debug)]
@@ -21,19 +24,29 @@ impl GethTraceBuilder {
Self { nodes, _config }
}
- /// Recursively fill in the geth trace by going through the traces
- ///
- /// TODO rewrite this iteratively
- fn add_to_geth_trace(
+ /// Fill in the geth trace with all steps of the trace and its children traces in the order they
+ /// appear in the transaction.
+ fn fill_geth_trace(
&self,
- storage: &mut HashMap
>,
- trace_node: &CallTraceNode,
- struct_logs: &mut Vec,
+ main_trace_node: &CallTraceNode,
opts: &GethDefaultTracingOptions,
+ storage: &mut HashMap>,
+ struct_logs: &mut Vec,
) {
- let mut child_id = 0;
+ // A stack with all the steps of the trace and all its children's steps.
+ // This is used to process the steps in the order they appear in the transactions.
+ // Steps are grouped by their Call Trace Node, in order to process them all in the order
+ // they appear in the transaction, we need to process steps of call nodes when they appear.
+ // When we find a call step, we push all the steps of the child trace on the stack, so they
+ // are processed next. The very next step is the last item on the stack
+ let mut step_stack = VecDeque::with_capacity(main_trace_node.trace.steps.len());
+
+ main_trace_node.push_steps_on_stack(&mut step_stack);
+
// Iterate over the steps inside the given trace
- for step in trace_node.trace.steps.iter() {
+ while let Some(CallTraceStepStackItem { trace_node, step, call_child_id }) =
+ step_stack.pop_back()
+ {
let mut log: StructLog = step.into();
// Fill in memory and storage depending on the options
@@ -59,23 +72,11 @@ impl GethTraceBuilder {
// Add step to geth trace
struct_logs.push(log);
- // If the opcode is a call, the descend into child trace
- match step.op.u8() {
- opcode::CREATE |
- opcode::CREATE2 |
- opcode::DELEGATECALL |
- opcode::CALL |
- opcode::STATICCALL |
- opcode::CALLCODE => {
- self.add_to_geth_trace(
- storage,
- &self.nodes[trace_node.children[child_id]],
- struct_logs,
- opts,
- );
- child_id += 1;
- }
- _ => {}
+ // If the step is a call, we first push all the steps of the child trace on the stack,
+ // so they are processed next
+ if let Some(call_child_id) = call_child_id {
+ let child_trace = &self.nodes[call_child_id];
+ child_trace.push_steps_on_stack(&mut step_stack);
}
}
}
@@ -96,7 +97,7 @@ impl GethTraceBuilder {
let mut struct_logs = Vec::new();
let mut storage = HashMap::new();
- self.add_to_geth_trace(&mut storage, main_trace_node, &mut struct_logs, &opts);
+ self.fill_geth_trace(main_trace_node, &opts, &mut storage, &mut struct_logs);
DefaultFrame {
// If the top-level trace succeeded, then it was a success
diff --git a/crates/revm/revm-inspectors/src/tracing/types.rs b/crates/revm/revm-inspectors/src/tracing/types.rs
index 906eed9072..91203e3502 100644
--- a/crates/revm/revm-inspectors/src/tracing/types.rs
+++ b/crates/revm/revm-inspectors/src/tracing/types.rs
@@ -11,10 +11,10 @@ use reth_rpc_types::trace::{
},
};
use revm::interpreter::{
- CallContext, CallScheme, CreateScheme, InstructionResult, Memory, OpCode, Stack,
+ opcode, CallContext, CallScheme, CreateScheme, InstructionResult, Memory, OpCode, Stack,
};
use serde::{Deserialize, Serialize};
-use std::collections::btree_map::Entry;
+use std::collections::{btree_map::Entry, VecDeque};
/// A unified representation of a call
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
@@ -196,6 +196,43 @@ pub(crate) struct CallTraceNode {
}
impl CallTraceNode {
+ /// Pushes all steps onto the stack in reverse order
+ /// so that the first step is on top of the stack
+ pub(crate) fn push_steps_on_stack<'a>(
+ &'a self,
+ stack: &mut VecDeque>,
+ ) {
+ stack.extend(self.call_step_stack().into_iter().rev());
+ }
+
+ /// 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.
+ pub(crate) fn call_step_stack(&self) -> Vec> {
+ let mut stack = Vec::with_capacity(self.trace.steps.len());
+ let mut child_id = 0;
+ for step in self.trace.steps.iter() {
+ let mut item = CallTraceStepStackItem { trace_node: self, step, call_child_id: None };
+
+ // If the opcode is a call, put the child trace on the stack
+ match step.op.u8() {
+ opcode::CREATE |
+ opcode::CREATE2 |
+ opcode::DELEGATECALL |
+ opcode::CALL |
+ opcode::STATICCALL |
+ opcode::CALLCODE => {
+ let call_id = self.children[child_id];
+ item.call_child_id = Some(call_id);
+ child_id += 1;
+ }
+ _ => {}
+ }
+ stack.push(item);
+ }
+ stack
+ }
+
/// Returns the kind of call the trace belongs to
pub(crate) fn kind(&self) -> CallKind {
self.trace.kind
@@ -356,6 +393,15 @@ impl CallTraceNode {
}
}
+pub(crate) struct CallTraceStepStackItem<'a> {
+ /// The trace node that contains this step
+ pub(crate) trace_node: &'a CallTraceNode,
+ /// The step that this stack item represents
+ pub(crate) step: &'a CallTraceStep,
+ /// The index of the child call in the CallArena if this step's opcode is a call
+ pub(crate) call_child_id: Option,
+}
+
/// Ordering enum for calls and logs
///
/// i.e. if Call 0 occurs before Log 0, it will be pushed into the `CallTraceNode`'s ordering before