Skip to content

Commit efe3fef

Browse files
s1nagzliudan
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 b5604d4 commit efe3fef

File tree

6 files changed

+224
-60
lines changed

6 files changed

+224
-60
lines changed

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,30 +20,47 @@ import (
2020
"encoding/json"
2121
"errors"
2222
"math/big"
23-
"strconv"
2423
"sync/atomic"
2524
"time"
2625

2726
"github.com/XinFinOrg/XDPoSChain/common"
27+
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
2828
"github.com/XinFinOrg/XDPoSChain/core/vm"
2929
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
3030
)
3131

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

3638
type callFrame struct {
37-
Type string `json:"type"`
38-
From string `json:"from"`
39-
To string `json:"to,omitempty"`
40-
Value string `json:"value,omitempty"`
41-
Gas string `json:"gas"`
42-
GasUsed string `json:"gasUsed"`
43-
Input string `json:"input"`
44-
Output string `json:"output,omitempty"`
45-
Error string `json:"error,omitempty"`
46-
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
4764
}
4865

4966
type callTracer struct {
@@ -76,28 +93,29 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, e
7693
func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
7794
t.env = env
7895
t.callstack[0] = callFrame{
79-
Type: "CALL",
80-
From: addrToHex(from),
81-
To: addrToHex(to),
82-
Input: bytesToHex(input),
83-
Gas: uintToHex(gas),
84-
Value: bigToHex(value),
96+
Type: vm.CALL,
97+
From: from,
98+
To: to,
99+
Input: common.CopyBytes(input),
100+
Gas: gas,
101+
Value: value,
85102
}
86103
if create {
87-
t.callstack[0].Type = "CREATE"
104+
t.callstack[0].Type = vm.CREATE
88105
}
89106
}
90107

91108
// CaptureEnd is called after the call finishes to finalize the tracing.
92109
func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) {
93-
t.callstack[0].GasUsed = uintToHex(gasUsed)
110+
t.callstack[0].GasUsed = gasUsed
111+
output = common.CopyBytes(output)
94112
if err != nil {
95113
t.callstack[0].Error = err.Error()
96114
if err.Error() == "execution reverted" && len(output) > 0 {
97-
t.callstack[0].Output = bytesToHex(output)
115+
t.callstack[0].Output = output
98116
}
99117
} else {
100-
t.callstack[0].Output = bytesToHex(output)
118+
t.callstack[0].Output = output
101119
}
102120
}
103121

@@ -121,12 +139,12 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.
121139
}
122140

123141
call := callFrame{
124-
Type: typ.String(),
125-
From: addrToHex(from),
126-
To: addrToHex(to),
127-
Input: bytesToHex(input),
128-
Gas: uintToHex(gas),
129-
Value: bigToHex(value),
142+
Type: typ,
143+
From: from,
144+
To: to,
145+
Input: common.CopyBytes(input),
146+
Gas: gas,
147+
Value: value,
130148
}
131149
t.callstack = append(t.callstack, call)
132150
}
@@ -146,13 +164,13 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
146164
t.callstack = t.callstack[:size-1]
147165
size -= 1
148166

149-
call.GasUsed = uintToHex(gasUsed)
167+
call.GasUsed = gasUsed
150168
if err == nil {
151-
call.Output = bytesToHex(output)
169+
call.Output = common.CopyBytes(output)
152170
} else {
153171
call.Error = err.Error()
154-
if call.Type == "CREATE" || call.Type == "CREATE2" {
155-
call.To = ""
172+
if call.Type == vm.CREATE || call.Type == vm.CREATE2 {
173+
call.To = common.Address{}
156174
}
157175
}
158176
t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
@@ -180,23 +198,3 @@ func (t *callTracer) Stop(err error) {
180198
t.reason = err
181199
atomic.StoreUint32(&t.interrupt, 1)
182200
}
183-
184-
func bytesToHex(s []byte) string {
185-
return "0x" + common.Bytes2Hex(s)
186-
}
187-
188-
func bigToHex(n *big.Int) string {
189-
if n == nil {
190-
return ""
191-
}
192-
return "0x" + n.Text(16)
193-
}
194-
195-
func uintToHex(n uint64) string {
196-
return "0x" + strconv.FormatUint(n, 16)
197-
}
198-
199-
func addrToHex(a common.Address) string {
200-
s, _ := a.MarshalText()
201-
return string(s)
202-
}

eth/tracers/native/contract.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"fmt"
66
"math/big"
7+
"strings"
78
"sync/atomic"
89
"time"
910

@@ -108,3 +109,7 @@ func (t *contractTracer) Stop(err error) {
108109
t.reason = err
109110
atomic.StoreUint32(&t.interrupt, 1)
110111
}
112+
113+
func addrToHex(a common.Address) string {
114+
return strings.ToLower(a.String0x())
115+
}

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/XinFinOrg/XDPoSChain/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)