Skip to content

Commit

Permalink
fix: geth trace inconsistence with selfdestruct (#173)
Browse files Browse the repository at this point in the history
## Problem

The inconsistent problem in getting the Geth Debug trace result of a
transaction
which includes a `suicide` / `selfdestruct` call type.

### Reproducible Example

With the latest version reth v1.0.3, using `debug_traceTransaction()`
and `callTracer` to trace
the transaction
`0x47e9962fec372e5c34e650cbd094e396fe6278cd7b0ee5d34bccfe2739809e9c`

```
curl http://localhost:8545 \
-X POST \
-H "Content-Type: application/json" \
--data '{"method":"debug_traceTransaction","params":["0x47e9962fec372e5c34e650cbd094e396fe6278cd7b0ee5d34bccfe2739809e9c", {"tracer": "callTracer"}], "id":1,"jsonrpc":"2.0"}'
```

**From Reth**

which is a two-layer nested structure, and  
the `from` field of `SELFDESTRUCT` call is
`0x322237f0f8d0cf75c413d76fbbbb658cd2edf7c1`

```
{'calls': [{'from': '0x322237f0f8d0cf75c413d76fbbbb658cd2edf7c1',
            'gas': '0x4df8',
            'gasUsed': '0x1e85',
            'input': '0x608060405260405160593803806059833981016040819052601e91602a565b806001600160a01b0316ff5b600060208284031215603b57600080fd5b81516001600160a01b0381168114605157600080fd5b939250505056fe000000000000000000000000487294db79b9b0b66666627e21d1b56277b627c4',
            'to': '0x480641f98eaeb20a12e7c7fa29e4dc7649245cfb',
            'type': 'CREATE',
            'value': '0x4b1c80ab7791a50'},
           {'from': '0x322237f0f8d0cf75c413d76fbbbb658cd2edf7c1',
            'gas': '0x0',
            'gasUsed': '0x0',
            'input': '0x',
            'to': '0x487294db79b9b0b66666627e21d1b56277b627c4',
            'type': 'SELFDESTRUCT',
            'value': '0x4b1c80ab7791a50'}],
 'from': '0x98707bb9d75a98e1ee05446e7acaf864f5a09fdf',
 'gas': '0x12134',
 'gasUsed': '0xf0b3',
 'input': '0x687528fa000000000000000000000000487294db79b9b0b66666627e21d1b56277b627c4',
 'to': '0x322237f0f8d0cf75c413d76fbbbb658cd2edf7c1',
 'type': 'CALL',
 'value': '0x4b1c80ab7791a50'}
```

**From
[Etherscan](https://etherscan.io/tx/0x47e9962fec372e5c34e650cbd094e396fe6278cd7b0ee5d34bccfe2739809e9c#internal)
and QuickNode**

which is a three-layer nested structure, and  
the `from` field of `SELFDESTRUCT` call is
`0x480641f98eaeb20a12e7c7fa29e4dc7649245cfb`

```
{'calls': [{'calls': [{'from': '0x480641f98eaeb20a12e7c7fa29e4dc7649245cfb',
                       'gas': '0x0',
                       'gasUsed': '0x0',
                       'input': '0x',
                       'to': '0x487294db79b9b0b66666627e21d1b56277b627c4',
                       'type': 'SELFDESTRUCT',
                       'value': '0x4b1c80ab7791a50'}],
            'from': '0x322237f0f8d0cf75c413d76fbbbb658cd2edf7c1',
            'gas': '0x4df8',
            'gasUsed': '0x1e85',
            'input': '0x608060405260405160593803806059833981016040819052601e91602a565b806001600160a01b0316ff5b600060208284031215603b57600080fd5b81516001600160a01b0381168114605157600080fd5b939250505056fe000000000000000000000000487294db79b9b0b66666627e21d1b56277b627c4',
            'to': '0x480641f98eaeb20a12e7c7fa29e4dc7649245cfb',
            'type': 'CREATE',
            'value': '0x4b1c80ab7791a50'}],
 'from': '0x98707bb9d75a98e1ee05446e7acaf864f5a09fdf',
 'gas': '0x12134',
 'gasUsed': '0xf0b3',
 'input': '0x687528fa000000000000000000000000487294db79b9b0b66666627e21d1b56277b627c4',
 'to': '0x322237f0f8d0cf75c413d76fbbbb658cd2edf7c1',
 'type': 'CALL',
 'value': '0x4b1c80ab7791a50'}
```

**More Examples**

- `0xc71cea6fa00d11e98f6733ee8740f239cb37b11dec29e7cf85d7a4077977fa65`
- `0x9610e6727b31f207d169013c470c3a7b2964ff67b3f0f169c272e99a6c0694ce`
- `0x1cc935268fe1a77d130574a44abe2f44d427c3f9bb3e035dd6438dd8a898b67a`

## Solution

1. The `selfdestruct` call trace should not be added as an additional
`CallFrame` object to the parent of the call trace where it is derived
from, such that it is in the same level of the call trace where it is
derived from.

It should be added as the first child of the call trace where it is
derived from.

2. The `from` field in its `CallFrame` object should be
`self.trace.address` rather than `self.trace.caller`
  • Loading branch information
ZzPoLariszZ authored Aug 1, 2024
1 parent 2ef6dc1 commit 318c121
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 6 deletions.
11 changes: 6 additions & 5 deletions src/tracing/builder/geth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,16 @@ impl GethTraceBuilder {
call_frames.push((0, root_call_frame));

for (idx, trace) in self.nodes.iter().enumerate().skip(1) {
// include logs only if call and all its parents were successful
let include_logs = include_logs && !self.call_or_parent_failed(trace);
call_frames.push((idx, trace.geth_empty_call_frame(include_logs)));

// selfdestructs are not recorded as individual call traces but are derived from
// the call trace and are added as additional `CallFrame` objects to the parent call
// the call trace and are added as additional `CallFrame` objects
// becoming the first child of the derived call
if let Some(selfdestruct) = trace.geth_selfdestruct_call_trace() {
call_frames.last_mut().expect("not empty").1.calls.push(selfdestruct);
}

// include logs only if call and all its parents were successful
let include_logs = include_logs && !self.call_or_parent_failed(trace);
call_frames.push((idx, trace.geth_empty_call_frame(include_logs)));
}

// pop the _children_ calls frame and move it to the parent
Expand Down
2 changes: 1 addition & 1 deletion src/tracing/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ impl CallTraceNode {
if self.is_selfdestruct() {
Some(CallFrame {
typ: "SELFDESTRUCT".to_string(),
from: self.trace.caller,
from: self.trace.address,
to: self.trace.selfdestruct_refund_target,
value: self.trace.selfdestruct_transferred_value,
..Default::default()
Expand Down

0 comments on commit 318c121

Please sign in to comment.