Skip to content

Commit

Permalink
fix(tracing/builder): ensure the selfdestruct trace is at the ending …
Browse files Browse the repository at this point in the history
…of the same depth (#192)

because the selfdestruct doesn't have separate trace, if will returned
just after the original call, this is inaccure, and we should keep them
staying at the ending of the same call depth.

---------

Signed-off-by: jsvisa <[email protected]>
  • Loading branch information
jsvisa committed Sep 9, 2024
1 parent 13519d0 commit 4aea842
Showing 1 changed file with 121 additions and 6 deletions.
127 changes: 121 additions & 6 deletions src/tracing/builder/parity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use revm::{
db::DatabaseRef,
primitives::{Account, ExecutionResult, ResultAndState, SpecId, KECCAK_EMPTY},
};
use std::collections::{HashSet, VecDeque};
use std::{
collections::{HashSet, VecDeque},
iter::Peekable,
};

/// A type for creating parity style traces
///
Expand Down Expand Up @@ -272,7 +275,8 @@ impl ParityTraceBuilder {
.into_iter()
.zip(trace_addresses)
.filter(|(node, _)| !node.is_precompile())
.map(|(node, trace_address)| (node.parity_transaction_trace(trace_address), node)),
.map(|(node, trace_address)| (node.parity_transaction_trace(trace_address), node))
.peekable(),
}
}

Expand Down Expand Up @@ -395,8 +399,8 @@ impl ParityTraceBuilder {
}

/// An iterator for [TransactionTrace]s
struct TransactionTraceIter<Iter> {
iter: Iter,
struct TransactionTraceIter<Iter: Iterator> {
iter: Peekable<Iter>,
next_selfdestruct: Option<TransactionTrace>,
}

Expand All @@ -407,9 +411,15 @@ where
type Item = TransactionTrace;

fn next(&mut self) -> Option<Self::Item> {
if let Some(selfdestruct) = self.next_selfdestruct.take() {
return Some(selfdestruct);
// ensure the selfdestruct trace is emitted just at the ending of the same depth
if let Some(selfdestruct) = &self.next_selfdestruct {
if self.iter.peek().map_or(true, |(next_trace, _)| {
selfdestruct.trace_address < next_trace.trace_address
}) {
return self.next_selfdestruct.take();
}
}

let (mut trace, node) = self.iter.next()?;
if node.is_selfdestruct() {
// since selfdestructs are emitted as additional trace, increase the trace count
Expand Down Expand Up @@ -559,3 +569,108 @@ where

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
use crate::tracing::types::{CallKind, CallTrace};

#[test]
fn test_parity_suicide_simple_call() {
let nodes = vec![CallTraceNode {
trace: CallTrace {
kind: CallKind::Call,
selfdestruct_refund_target: Some(Address::ZERO),
..Default::default()
},
..Default::default()
}];

let traces = ParityTraceBuilder::new(nodes, None, TracingInspectorConfig::default_parity())
.into_transaction_traces();

assert_eq!(traces.len(), 2);
assert_eq!(traces[0].trace_address.len(), 0);
assert!(traces[0].action.is_call());
assert_eq!(traces[1].trace_address, vec![0]);
assert!(traces[1].action.is_selfdestruct());
}

#[test]
fn test_parity_suicide_with_subsequent_calls() {
/*
contract Foo {
function foo() public {}
function close(Foo f) public {
f.foo();
selfdestruct(payable(msg.sender));
}
}
contract Bar {
Foo foo1;
Foo foo2;
constructor() {
foo1 = new Foo();
foo2 = new Foo();
}
function close() public {
foo1.close(foo2);
}
}
*/

let nodes = vec![
CallTraceNode {
parent: None,
children: vec![1],
idx: 0,
trace: CallTrace { depth: 0, ..Default::default() },
..Default::default()
},
CallTraceNode {
parent: Some(0),
idx: 1,
children: vec![2],
trace: CallTrace {
depth: 1,
kind: CallKind::Call,
selfdestruct_refund_target: Some(Address::ZERO),
..Default::default()
},
..Default::default()
},
CallTraceNode {
parent: Some(1),
idx: 2,
trace: CallTrace { depth: 2, ..Default::default() },
..Default::default()
},
];

let traces = ParityTraceBuilder::new(nodes, None, TracingInspectorConfig::default_parity())
.into_transaction_traces();

assert_eq!(traces.len(), 4);

// [] call
assert_eq!(traces[0].trace_address.len(), 0);
assert_eq!(traces[0].subtraces, 1);
assert!(traces[0].action.is_call());

// [0] call
assert_eq!(traces[1].trace_address, vec![0]);
assert_eq!(traces[1].subtraces, 2);
assert!(traces[1].action.is_call());

// [0, 0] call
assert_eq!(traces[2].trace_address, vec![0, 0]);
assert!(traces[2].action.is_call());

// [0, 1] suicide
assert_eq!(traces[3].trace_address, vec![0, 1]);
assert!(traces[3].action.is_selfdestruct());
}
}

0 comments on commit 4aea842

Please sign in to comment.