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

eth/tracers: use gencodec for native tracers #25637

Merged
merged 3 commits into from
Sep 26, 2022
Merged
Show file tree
Hide file tree
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
4 changes: 4 additions & 0 deletions eth/tracers/native/4byte.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,7 @@ func (t *fourByteTracer) Stop(err error) {
t.reason = err
atomic.StoreUint32(&t.interrupt, 1)
}

func bytesToHex(s []byte) string {
return "0x" + common.Bytes2Hex(s)
}
100 changes: 49 additions & 51 deletions eth/tracers/native/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,47 @@ import (
"encoding/json"
"errors"
"math/big"
"strconv"
"strings"
"sync/atomic"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
)

//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go

func init() {
register("callTracer", newCallTracer)
}

type callFrame struct {
Type string `json:"type"`
From string `json:"from"`
To string `json:"to,omitempty"`
Value string `json:"value,omitempty"`
Gas string `json:"gas"`
GasUsed string `json:"gasUsed"`
Input string `json:"input"`
Output string `json:"output,omitempty"`
Error string `json:"error,omitempty"`
Calls []callFrame `json:"calls,omitempty"`
Type vm.OpCode `json:"-"`
From common.Address `json:"from"`
Gas uint64 `json:"gas"`
Copy link
Contributor

Choose a reason for hiding this comment

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

This field must be declared optional for RLP, otherwise it will not work. The rule is: all fields after the first optional one must also be optional.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

GasUsed uint64 `json:"gasUsed"`
To common.Address `json:"to,omitempty" rlp:"optional"`
Input []byte `json:"input" rlp:"optional"`
Output []byte `json:"output,omitempty" rlp:"optional"`
Error string `json:"error,omitempty" rlp:"optional"`
Calls []callFrame `json:"calls,omitempty" rlp:"optional"`
// Placed at end on purpose. The RLP will be decoded to 0 instead of
// nil if there are non-empty elements after in the struct.
Value *big.Int `json:"value,omitempty" rlp:"optional"`
}

func (f callFrame) TypeString() string {
return f.Type.String()
}

type callFrameMarshaling struct {
TypeString string `json:"type"`
Gas hexutil.Uint64
GasUsed hexutil.Uint64
Value *hexutil.Big
Input hexutil.Bytes
Output hexutil.Bytes
}

type callTracer struct {
Expand Down Expand Up @@ -77,28 +93,29 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, e
func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
t.env = env
t.callstack[0] = callFrame{
Type: "CALL",
From: addrToHex(from),
To: addrToHex(to),
Input: bytesToHex(input),
Gas: uintToHex(gas),
Value: bigToHex(value),
Type: vm.CALL,
From: from,
To: to,
Input: common.CopyBytes(input),
Gas: gas,
Value: value,
}
if create {
t.callstack[0].Type = "CREATE"
t.callstack[0].Type = vm.CREATE
}
}

// CaptureEnd is called after the call finishes to finalize the tracing.
func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) {
t.callstack[0].GasUsed = uintToHex(gasUsed)
t.callstack[0].GasUsed = gasUsed
output = common.CopyBytes(output)
if err != nil {
t.callstack[0].Error = err.Error()
if err.Error() == "execution reverted" && len(output) > 0 {
t.callstack[0].Output = bytesToHex(output)
t.callstack[0].Output = output
}
} else {
t.callstack[0].Output = bytesToHex(output)
t.callstack[0].Output = output
}
}

Expand All @@ -122,12 +139,12 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.
}

call := callFrame{
Type: typ.String(),
From: addrToHex(from),
To: addrToHex(to),
Input: bytesToHex(input),
Gas: uintToHex(gas),
Value: bigToHex(value),
Type: typ,
From: from,
To: to,
Input: common.CopyBytes(input),
Gas: gas,
Value: value,
}
t.callstack = append(t.callstack, call)
}
Expand All @@ -147,13 +164,13 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
t.callstack = t.callstack[:size-1]
size -= 1

call.GasUsed = uintToHex(gasUsed)
call.GasUsed = gasUsed
if err == nil {
call.Output = bytesToHex(output)
call.Output = common.CopyBytes(output)
} else {
call.Error = err.Error()
if call.Type == "CREATE" || call.Type == "CREATE2" {
call.To = ""
if call.Type == vm.CREATE || call.Type == vm.CREATE2 {
call.To = common.Address{}
}
}
t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
Expand Down Expand Up @@ -181,22 +198,3 @@ func (t *callTracer) Stop(err error) {
t.reason = err
atomic.StoreUint32(&t.interrupt, 1)
}

func bytesToHex(s []byte) string {
return "0x" + common.Bytes2Hex(s)
}

func bigToHex(n *big.Int) string {
if n == nil {
return ""
}
return "0x" + n.Text(16)
}

func uintToHex(n uint64) string {
return "0x" + strconv.FormatUint(n, 16)
}

func addrToHex(a common.Address) string {
return strings.ToLower(a.Hex())
}
56 changes: 56 additions & 0 deletions eth/tracers/native/gen_account_json.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

95 changes: 95 additions & 0 deletions eth/tracers/native/gen_callframe_json.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 15 additions & 9 deletions eth/tracers/native/prestate.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,25 @@ import (
"github.com/ethereum/go-ethereum/eth/tracers"
)

//go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go

func init() {
register("prestateTracer", newPrestateTracer)
}

type prestate = map[common.Address]*account
type account struct {
Balance string `json:"balance"`
Balance *big.Int `json:"balance"`
Nonce uint64 `json:"nonce"`
Code string `json:"code"`
Code []byte `json:"code"`
Storage map[common.Hash]common.Hash `json:"storage"`
}

type accountMarshaling struct {
Balance *hexutil.Big
Code hexutil.Bytes
}

type prestateTracer struct {
env *vm.EVM
prestate prestate
Expand All @@ -67,17 +74,16 @@ func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to commo
t.lookupAccount(to)

// The recipient balance includes the value transferred.
toBal := hexutil.MustDecodeBig(t.prestate[to].Balance)
toBal = new(big.Int).Sub(toBal, value)
t.prestate[to].Balance = hexutil.EncodeBig(toBal)
toBal := new(big.Int).Sub(t.prestate[to].Balance, value)
t.prestate[to].Balance = toBal

// The sender balance is after reducing: value and gasLimit.
// We need to re-add them to get the pre-tx balance.
fromBal := hexutil.MustDecodeBig(t.prestate[from].Balance)
fromBal := t.prestate[from].Balance
gasPrice := env.TxContext.GasPrice
consumedGas := new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(t.gasLimit))
fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas))
t.prestate[from].Balance = hexutil.EncodeBig(fromBal)
t.prestate[from].Balance = fromBal
t.prestate[from].Nonce--
}

Expand Down Expand Up @@ -160,9 +166,9 @@ func (t *prestateTracer) lookupAccount(addr common.Address) {
return
}
t.prestate[addr] = &account{
Balance: bigToHex(t.env.StateDB.GetBalance(addr)),
Balance: t.env.StateDB.GetBalance(addr),
Nonce: t.env.StateDB.GetNonce(addr),
Code: bytesToHex(t.env.StateDB.GetCode(addr)),
Code: t.env.StateDB.GetCode(addr),
Storage: make(map[common.Hash]common.Hash),
}
}
Expand Down