Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 54 additions & 37 deletions turbo/jsonrpc/otterscan_trace_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package jsonrpc

import (
"context"
"github.com/ledgerwatch/erigon-lib/common/hexutil"
"math/big"

"github.com/ledgerwatch/erigon-lib/common/hexutil"

"github.com/holiman/uint256"

"github.com/ledgerwatch/erigon-lib/common"
Expand All @@ -29,68 +30,68 @@ func (api *OtterscanAPIImpl) TraceTransaction(ctx context.Context, hash common.H
}

type TraceEntry struct {
Type string `json:"type"`
Depth int `json:"depth"`
From common.Address `json:"from"`
To common.Address `json:"to"`
Value *hexutil.Big `json:"value"`
Input hexutility.Bytes `json:"input"`
Type string `json:"type"`
Depth int `json:"depth"`
From common.Address `json:"from"`
To common.Address `json:"to"`
Value *hexutil.Big `json:"value"`
Input hexutility.Bytes `json:"input"`
Output hexutility.Bytes `json:"output"`
}

type TransactionTracer struct {
DefaultTracer
ctx context.Context
Results []*TraceEntry
depth int // computed from CaptureStart, CaptureEnter, and CaptureExit calls
stack []*TraceEntry
}

func NewTransactionTracer(ctx context.Context) *TransactionTracer {
return &TransactionTracer{
ctx: ctx,
Results: make([]*TraceEntry, 0),
stack: make([]*TraceEntry, 0),
}
}

func (t *TransactionTracer) captureStartOrEnter(typ vm.OpCode, from, to common.Address, precompile bool, input []byte, value *uint256.Int) {
if precompile {
return
}

inputCopy := make([]byte, len(input))
copy(inputCopy, input)
_value := new(big.Int)
if value != nil {
_value.Set(value.ToBig())
}

var entry *TraceEntry
if typ == vm.CALL {
t.Results = append(t.Results, &TraceEntry{"CALL", t.depth, from, to, (*hexutil.Big)(_value), inputCopy})
return
}
if typ == vm.STATICCALL {
t.Results = append(t.Results, &TraceEntry{"STATICCALL", t.depth, from, to, nil, inputCopy})
return
}
if typ == vm.DELEGATECALL {
t.Results = append(t.Results, &TraceEntry{"DELEGATECALL", t.depth, from, to, nil, inputCopy})
return
}
if typ == vm.CALLCODE {
t.Results = append(t.Results, &TraceEntry{"CALLCODE", t.depth, from, to, (*hexutil.Big)(_value), inputCopy})
return
}
if typ == vm.CREATE {
t.Results = append(t.Results, &TraceEntry{"CREATE", t.depth, from, to, (*hexutil.Big)(value.ToBig()), inputCopy})
return
}
if typ == vm.CREATE2 {
t.Results = append(t.Results, &TraceEntry{"CREATE2", t.depth, from, to, (*hexutil.Big)(value.ToBig()), inputCopy})
return
entry = &TraceEntry{"CALL", t.depth, from, to, (*hexutil.Big)(_value), inputCopy, nil}
} else if typ == vm.STATICCALL {
entry = &TraceEntry{"STATICCALL", t.depth, from, to, nil, inputCopy, nil}
} else if typ == vm.DELEGATECALL {
entry = &TraceEntry{"DELEGATECALL", t.depth, from, to, nil, inputCopy, nil}
} else if typ == vm.CALLCODE {
entry = &TraceEntry{"CALLCODE", t.depth, from, to, (*hexutil.Big)(_value), inputCopy, nil}
} else if typ == vm.CREATE {
entry = &TraceEntry{"CREATE", t.depth, from, to, (*hexutil.Big)(value.ToBig()), inputCopy, nil}
} else if typ == vm.CREATE2 {
entry = &TraceEntry{"CREATE2", t.depth, from, to, (*hexutil.Big)(value.ToBig()), inputCopy, nil}
} else if typ == vm.SELFDESTRUCT {
last := t.Results[len(t.Results)-1]
entry = &TraceEntry{"SELFDESTRUCT", last.Depth + 1, from, to, (*hexutil.Big)(value.ToBig()), nil, nil}
} else {
// safeguard in case new CALL-like opcodes are introduced but not handled,
// otherwise CaptureExit/stack will get out of sync
entry = &TraceEntry{"UNKNOWN", t.depth, from, to, (*hexutil.Big)(value.ToBig()), inputCopy, nil}
}

if typ == vm.SELFDESTRUCT {
last := t.Results[len(t.Results)-1]
t.Results = append(t.Results, &TraceEntry{"SELFDESTRUCT", last.Depth + 1, from, to, (*hexutil.Big)(value.ToBig()), nil})
// Ignore precompiles in the returned trace (maybe we shouldn't?)
if !precompile {
t.Results = append(t.Results, entry)
}

// stack precompiles in order to match captureEndOrExit
t.stack = append(t.stack, entry)
}

func (t *TransactionTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
Expand All @@ -103,6 +104,22 @@ func (t *TransactionTracer) CaptureEnter(typ vm.OpCode, from common.Address, to
t.captureStartOrEnter(typ, from, to, precompile, input, value)
}

func (t *TransactionTracer) CaptureExit(output []byte, usedGas uint64, err error) {
func (t *TransactionTracer) captureEndOrExit(output []byte, usedGas uint64, err error) {
t.depth--

lastIdx := len(t.stack) - 1
pop := t.stack[lastIdx]
t.stack = t.stack[:lastIdx]

outputCopy := make([]byte, len(output))
copy(outputCopy, output)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have common.Copy()

pop.Output = outputCopy
}

func (t *TransactionTracer) CaptureExit(output []byte, usedGas uint64, err error) {
t.captureEndOrExit(output, usedGas, err)
}

func (t *TransactionTracer) CaptureEnd(output []byte, usedGas uint64, err error) {
t.captureEndOrExit(output, usedGas, err)
}