Skip to content

Commit 13fe20f

Browse files
s1nablakehhuynh
authored andcommitted
eth/tracers: use gencodec for native tracers (ethereum#25637)
The call tracer and prestate tracer store data JSON-encoded in memory. In order to support alternative encodings (specifically RLP), it's better to keep data a native format during tracing. This PR does marshalling at the end, using gencodec. OBS! This PR changes the call tracer result slightly: - Order of type and value fields are changed (should not matter). - Output fields are completely omitted when they're empty (no more output: "0x"). Previously, this was only _sometimes_ omitted (e.g. when call ended in a non-revert error) and otherwise 0x when the output was actually empty.
1 parent 20dcc1a commit 13fe20f

5 files changed

Lines changed: 219 additions & 60 deletions

File tree

eth/tracers/native/4byte.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,7 @@ func (t *fourByteTracer) Stop(err error) {
151151
t.reason = err
152152
atomic.StoreUint32(&t.interrupt, 1)
153153
}
154+
155+
func bytesToHex(s []byte) string {
156+
return "0x" + common.Bytes2Hex(s)
157+
}

eth/tracers/native/call.go

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,47 @@ import (
2020
"encoding/json"
2121
"errors"
2222
"math/big"
23-
"strconv"
24-
"strings"
2523
"sync/atomic"
2624
"time"
2725

2826
"github.com/ethereum/go-ethereum/common"
27+
"github.com/ethereum/go-ethereum/common/hexutil"
2928
"github.com/ethereum/go-ethereum/core/vm"
3029
"github.com/ethereum/go-ethereum/eth/tracers"
3130
)
3231

32+
//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go
33+
3334
func init() {
3435
register("callTracer", newCallTracer)
3536
}
3637

3738
type callFrame struct {
38-
Type string `json:"type"`
39-
From string `json:"from"`
40-
To string `json:"to,omitempty"`
41-
Value string `json:"value,omitempty"`
42-
Gas string `json:"gas"`
43-
GasUsed string `json:"gasUsed"`
44-
Input string `json:"input"`
45-
Output string `json:"output,omitempty"`
46-
Error string `json:"error,omitempty"`
47-
Calls []callFrame `json:"calls,omitempty"`
39+
Type vm.OpCode `json:"-"`
40+
From common.Address `json:"from"`
41+
Gas uint64 `json:"gas"`
42+
GasUsed uint64 `json:"gasUsed"`
43+
To common.Address `json:"to,omitempty" rlp:"optional"`
44+
Input []byte `json:"input" rlp:"optional"`
45+
Output []byte `json:"output,omitempty" rlp:"optional"`
46+
Error string `json:"error,omitempty" rlp:"optional"`
47+
Calls []callFrame `json:"calls,omitempty" rlp:"optional"`
48+
// Placed at end on purpose. The RLP will be decoded to 0 instead of
49+
// nil if there are non-empty elements after in the struct.
50+
Value *big.Int `json:"value,omitempty" rlp:"optional"`
51+
}
52+
53+
func (f callFrame) TypeString() string {
54+
return f.Type.String()
55+
}
56+
57+
type callFrameMarshaling struct {
58+
TypeString string `json:"type"`
59+
Gas hexutil.Uint64
60+
GasUsed hexutil.Uint64
61+
Value *hexutil.Big
62+
Input hexutil.Bytes
63+
Output hexutil.Bytes
4864
}
4965

5066
type callTracer struct {
@@ -77,28 +93,29 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, e
7793
func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
7894
t.env = env
7995
t.callstack[0] = callFrame{
80-
Type: "CALL",
81-
From: addrToHex(from),
82-
To: addrToHex(to),
83-
Input: bytesToHex(input),
84-
Gas: uintToHex(gas),
85-
Value: bigToHex(value),
96+
Type: vm.CALL,
97+
From: from,
98+
To: to,
99+
Input: common.CopyBytes(input),
100+
Gas: gas,
101+
Value: value,
86102
}
87103
if create {
88-
t.callstack[0].Type = "CREATE"
104+
t.callstack[0].Type = vm.CREATE
89105
}
90106
}
91107

92108
// CaptureEnd is called after the call finishes to finalize the tracing.
93109
func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) {
94-
t.callstack[0].GasUsed = uintToHex(gasUsed)
110+
t.callstack[0].GasUsed = gasUsed
111+
output = common.CopyBytes(output)
95112
if err != nil {
96113
t.callstack[0].Error = err.Error()
97114
if err.Error() == "execution reverted" && len(output) > 0 {
98-
t.callstack[0].Output = bytesToHex(output)
115+
t.callstack[0].Output = output
99116
}
100117
} else {
101-
t.callstack[0].Output = bytesToHex(output)
118+
t.callstack[0].Output = output
102119
}
103120
}
104121

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

124141
call := callFrame{
125-
Type: typ.String(),
126-
From: addrToHex(from),
127-
To: addrToHex(to),
128-
Input: bytesToHex(input),
129-
Gas: uintToHex(gas),
130-
Value: bigToHex(value),
142+
Type: typ,
143+
From: from,
144+
To: to,
145+
Input: common.CopyBytes(input),
146+
Gas: gas,
147+
Value: value,
131148
}
132149
t.callstack = append(t.callstack, call)
133150
}
@@ -147,13 +164,13 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
147164
t.callstack = t.callstack[:size-1]
148165
size -= 1
149166

150-
call.GasUsed = uintToHex(gasUsed)
167+
call.GasUsed = gasUsed
151168
if err == nil {
152-
call.Output = bytesToHex(output)
169+
call.Output = common.CopyBytes(output)
153170
} else {
154171
call.Error = err.Error()
155-
if call.Type == "CREATE" || call.Type == "CREATE2" {
156-
call.To = ""
172+
if call.Type == vm.CREATE || call.Type == vm.CREATE2 {
173+
call.To = common.Address{}
157174
}
158175
}
159176
t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
@@ -181,22 +198,3 @@ func (t *callTracer) Stop(err error) {
181198
t.reason = err
182199
atomic.StoreUint32(&t.interrupt, 1)
183200
}
184-
185-
func bytesToHex(s []byte) string {
186-
return "0x" + common.Bytes2Hex(s)
187-
}
188-
189-
func bigToHex(n *big.Int) string {
190-
if n == nil {
191-
return ""
192-
}
193-
return "0x" + n.Text(16)
194-
}
195-
196-
func uintToHex(n uint64) string {
197-
return "0x" + strconv.FormatUint(n, 16)
198-
}
199-
200-
func addrToHex(a common.Address) string {
201-
return strings.ToLower(a.Hex())
202-
}

eth/tracers/native/gen_account_json.go

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

eth/tracers/native/gen_callframe_json.go

Lines changed: 95 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

eth/tracers/native/prestate.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,25 @@ import (
2929
"github.com/ethereum/go-ethereum/eth/tracers"
3030
)
3131

32+
//go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go
33+
3234
func init() {
3335
register("prestateTracer", newPrestateTracer)
3436
}
3537

3638
type prestate = map[common.Address]*account
3739
type account struct {
38-
Balance string `json:"balance"`
40+
Balance *big.Int `json:"balance"`
3941
Nonce uint64 `json:"nonce"`
40-
Code string `json:"code"`
42+
Code []byte `json:"code"`
4143
Storage map[common.Hash]common.Hash `json:"storage"`
4244
}
4345

46+
type accountMarshaling struct {
47+
Balance *hexutil.Big
48+
Code hexutil.Bytes
49+
}
50+
4451
type prestateTracer struct {
4552
env *vm.EVM
4653
prestate prestate
@@ -67,17 +74,16 @@ func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to commo
6774
t.lookupAccount(to)
6875

6976
// The recipient balance includes the value transferred.
70-
toBal := hexutil.MustDecodeBig(t.prestate[to].Balance)
71-
toBal = new(big.Int).Sub(toBal, value)
72-
t.prestate[to].Balance = hexutil.EncodeBig(toBal)
77+
toBal := new(big.Int).Sub(t.prestate[to].Balance, value)
78+
t.prestate[to].Balance = toBal
7379

7480
// The sender balance is after reducing: value and gasLimit.
7581
// We need to re-add them to get the pre-tx balance.
76-
fromBal := hexutil.MustDecodeBig(t.prestate[from].Balance)
82+
fromBal := t.prestate[from].Balance
7783
gasPrice := env.TxContext.GasPrice
7884
consumedGas := new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(t.gasLimit))
7985
fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas))
80-
t.prestate[from].Balance = hexutil.EncodeBig(fromBal)
86+
t.prestate[from].Balance = fromBal
8187
t.prestate[from].Nonce--
8288
}
8389

@@ -160,9 +166,9 @@ func (t *prestateTracer) lookupAccount(addr common.Address) {
160166
return
161167
}
162168
t.prestate[addr] = &account{
163-
Balance: bigToHex(t.env.StateDB.GetBalance(addr)),
169+
Balance: t.env.StateDB.GetBalance(addr),
164170
Nonce: t.env.StateDB.GetNonce(addr),
165-
Code: bytesToHex(t.env.StateDB.GetCode(addr)),
171+
Code: t.env.StateDB.GetCode(addr),
166172
Storage: make(map[common.Hash]common.Hash),
167173
}
168174
}

0 commit comments

Comments
 (0)