mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
fix: process geth steps iteratively (#2980)
Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This commit is contained in:
@ -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<Address, BTreeMap<H256, H256>>,
|
||||
trace_node: &CallTraceNode,
|
||||
struct_logs: &mut Vec<StructLog>,
|
||||
main_trace_node: &CallTraceNode,
|
||||
opts: &GethDefaultTracingOptions,
|
||||
storage: &mut HashMap<Address, BTreeMap<H256, H256>>,
|
||||
struct_logs: &mut Vec<StructLog>,
|
||||
) {
|
||||
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
|
||||
|
||||
@ -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<CallTraceStepStackItem<'a>>,
|
||||
) {
|
||||
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<CallTraceStepStackItem<'_>> {
|
||||
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<usize>,
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
||||
Reference in New Issue
Block a user