Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the optional output field on ots_traceTransaction #10014

Merged
merged 1 commit into from
Apr 22, 2024
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)
}
Loading