From a7e05aa16a904f4922da44516127657cdcd44837 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi <itz.s1na@gmail.com> Date: Fri, 29 Jul 2022 11:45:07 +0200 Subject: [PATCH 1/3] eth/tracers: adopt gencodec in native tracers --- eth/tracers/native/4byte.go | 4 + eth/tracers/native/call.go | 100 +++++++++++------------ eth/tracers/native/gen_account_json.go | 56 +++++++++++++ eth/tracers/native/gen_callframe_json.go | 95 +++++++++++++++++++++ eth/tracers/native/prestate.go | 25 ++++-- 5 files changed, 220 insertions(+), 60 deletions(-) create mode 100644 eth/tracers/native/gen_account_json.go create mode 100644 eth/tracers/native/gen_callframe_json.go diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index 34e608bfd60d..4f341bf633e2 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -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) +} diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 7af0e658a8bf..dfeecc416449 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -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"` + GasUsed uint64 `json:"gasUsed"` + To common.Address `json:"to,omitempty" rlp:"optional"` + Input []byte `json:"input,omitempty" 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. It 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 { @@ -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 } } @@ -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) } @@ -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) @@ -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()) -} diff --git a/eth/tracers/native/gen_account_json.go b/eth/tracers/native/gen_account_json.go new file mode 100644 index 000000000000..7972d6dd464e --- /dev/null +++ b/eth/tracers/native/gen_account_json.go @@ -0,0 +1,56 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package native + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*accountMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (a account) MarshalJSON() ([]byte, error) { + type account struct { + Balance *hexutil.Big `json:"balance"` + Nonce hexutil.Uint64 `json:"nonce"` + Code hexutil.Bytes `json:"code"` + Storage map[common.Hash]common.Hash `json:"storage"` + } + var enc account + enc.Balance = (*hexutil.Big)(a.Balance) + enc.Nonce = hexutil.Uint64(a.Nonce) + enc.Code = a.Code + enc.Storage = a.Storage + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (a *account) UnmarshalJSON(input []byte) error { + type account struct { + Balance *hexutil.Big `json:"balance"` + Nonce *hexutil.Uint64 `json:"nonce"` + Code *hexutil.Bytes `json:"code"` + Storage map[common.Hash]common.Hash `json:"storage"` + } + var dec account + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Balance != nil { + a.Balance = (*big.Int)(dec.Balance) + } + if dec.Nonce != nil { + a.Nonce = uint64(*dec.Nonce) + } + if dec.Code != nil { + a.Code = *dec.Code + } + if dec.Storage != nil { + a.Storage = dec.Storage + } + return nil +} diff --git a/eth/tracers/native/gen_callframe_json.go b/eth/tracers/native/gen_callframe_json.go new file mode 100644 index 000000000000..01dc0d66f483 --- /dev/null +++ b/eth/tracers/native/gen_callframe_json.go @@ -0,0 +1,95 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package native + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/vm" +) + +var _ = (*callFrameMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (c callFrame) MarshalJSON() ([]byte, error) { + type callFrame0 struct { + Type vm.OpCode `json:"-"` + From common.Address `json:"from"` + Gas hexutil.Uint64 `json:"gas"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + To common.Address `json:"to,omitempty" rlp:"optional"` + Input hexutil.Bytes `json:"input,omitempty" rlp:"optional"` + Output hexutil.Bytes `json:"output,omitempty" rlp:"optional"` + Error string `json:"error,omitempty" rlp:"optional"` + Calls []callFrame `json:"calls,omitempty" rlp:"optional"` + Value *hexutil.Big `json:"value,omitempty" rlp:"optional"` + TypeString string `json:"type"` + } + var enc callFrame0 + enc.Type = c.Type + enc.From = c.From + enc.Gas = hexutil.Uint64(c.Gas) + enc.GasUsed = hexutil.Uint64(c.GasUsed) + enc.To = c.To + enc.Input = c.Input + enc.Output = c.Output + enc.Error = c.Error + enc.Calls = c.Calls + enc.Value = (*hexutil.Big)(c.Value) + enc.TypeString = c.TypeString() + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (c *callFrame) UnmarshalJSON(input []byte) error { + type callFrame0 struct { + Type *vm.OpCode `json:"-"` + From *common.Address `json:"from"` + Gas *hexutil.Uint64 `json:"gas"` + GasUsed *hexutil.Uint64 `json:"gasUsed"` + To *common.Address `json:"to,omitempty" rlp:"optional"` + Input *hexutil.Bytes `json:"input,omitempty" rlp:"optional"` + Output *hexutil.Bytes `json:"output,omitempty" rlp:"optional"` + Error *string `json:"error,omitempty" rlp:"optional"` + Calls []callFrame `json:"calls,omitempty" rlp:"optional"` + Value *hexutil.Big `json:"value,omitempty" rlp:"optional"` + } + var dec callFrame0 + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Type != nil { + c.Type = *dec.Type + } + if dec.From != nil { + c.From = *dec.From + } + if dec.Gas != nil { + c.Gas = uint64(*dec.Gas) + } + if dec.GasUsed != nil { + c.GasUsed = uint64(*dec.GasUsed) + } + if dec.To != nil { + c.To = *dec.To + } + if dec.Input != nil { + c.Input = *dec.Input + } + if dec.Output != nil { + c.Output = *dec.Output + } + if dec.Error != nil { + c.Error = *dec.Error + } + if dec.Calls != nil { + c.Calls = dec.Calls + } + if dec.Value != nil { + c.Value = (*big.Int)(dec.Value) + } + return nil +} diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index b513f383b9c2..4ccacc176687 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -29,18 +29,26 @@ 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 + Nonce hexutil.Uint64 + Code hexutil.Bytes +} + type prestateTracer struct { env *vm.EVM prestate prestate @@ -67,17 +75,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-- } @@ -160,9 +167,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), } } From 1e870989946feebe85c2209948d75c5cadad9f79 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi <itz.s1na@gmail.com> Date: Tue, 30 Aug 2022 16:44:47 +0200 Subject: [PATCH 2/3] minor fixes --- eth/tracers/native/call.go | 6 +++--- eth/tracers/native/gen_account_json.go | 8 ++++---- eth/tracers/native/gen_callframe_json.go | 16 ++++++++-------- eth/tracers/native/prestate.go | 1 - 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index dfeecc416449..2aecd4a4ff3d 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -38,14 +38,14 @@ func init() { type callFrame struct { Type vm.OpCode `json:"-"` From common.Address `json:"from"` + To common.Address `json:"to,omitempty" rlp:"optional"` Gas uint64 `json:"gas"` GasUsed uint64 `json:"gasUsed"` - To common.Address `json:"to,omitempty" rlp:"optional"` - Input []byte `json:"input,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. It will be decoded to 0 instead of + // 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"` } diff --git a/eth/tracers/native/gen_account_json.go b/eth/tracers/native/gen_account_json.go index 7972d6dd464e..25dc77dc7455 100644 --- a/eth/tracers/native/gen_account_json.go +++ b/eth/tracers/native/gen_account_json.go @@ -16,13 +16,13 @@ var _ = (*accountMarshaling)(nil) func (a account) MarshalJSON() ([]byte, error) { type account struct { Balance *hexutil.Big `json:"balance"` - Nonce hexutil.Uint64 `json:"nonce"` + Nonce uint64 `json:"nonce"` Code hexutil.Bytes `json:"code"` Storage map[common.Hash]common.Hash `json:"storage"` } var enc account enc.Balance = (*hexutil.Big)(a.Balance) - enc.Nonce = hexutil.Uint64(a.Nonce) + enc.Nonce = a.Nonce enc.Code = a.Code enc.Storage = a.Storage return json.Marshal(&enc) @@ -32,7 +32,7 @@ func (a account) MarshalJSON() ([]byte, error) { func (a *account) UnmarshalJSON(input []byte) error { type account struct { Balance *hexutil.Big `json:"balance"` - Nonce *hexutil.Uint64 `json:"nonce"` + Nonce *uint64 `json:"nonce"` Code *hexutil.Bytes `json:"code"` Storage map[common.Hash]common.Hash `json:"storage"` } @@ -44,7 +44,7 @@ func (a *account) UnmarshalJSON(input []byte) error { a.Balance = (*big.Int)(dec.Balance) } if dec.Nonce != nil { - a.Nonce = uint64(*dec.Nonce) + a.Nonce = *dec.Nonce } if dec.Code != nil { a.Code = *dec.Code diff --git a/eth/tracers/native/gen_callframe_json.go b/eth/tracers/native/gen_callframe_json.go index 01dc0d66f483..baf0e32e6c68 100644 --- a/eth/tracers/native/gen_callframe_json.go +++ b/eth/tracers/native/gen_callframe_json.go @@ -18,10 +18,10 @@ func (c callFrame) MarshalJSON() ([]byte, error) { type callFrame0 struct { Type vm.OpCode `json:"-"` From common.Address `json:"from"` + To common.Address `json:"to,omitempty" rlp:"optional"` Gas hexutil.Uint64 `json:"gas"` GasUsed hexutil.Uint64 `json:"gasUsed"` - To common.Address `json:"to,omitempty" rlp:"optional"` - Input hexutil.Bytes `json:"input,omitempty" rlp:"optional"` + Input hexutil.Bytes `json:"input" rlp:"optional"` Output hexutil.Bytes `json:"output,omitempty" rlp:"optional"` Error string `json:"error,omitempty" rlp:"optional"` Calls []callFrame `json:"calls,omitempty" rlp:"optional"` @@ -31,9 +31,9 @@ func (c callFrame) MarshalJSON() ([]byte, error) { var enc callFrame0 enc.Type = c.Type enc.From = c.From + enc.To = c.To enc.Gas = hexutil.Uint64(c.Gas) enc.GasUsed = hexutil.Uint64(c.GasUsed) - enc.To = c.To enc.Input = c.Input enc.Output = c.Output enc.Error = c.Error @@ -48,10 +48,10 @@ func (c *callFrame) UnmarshalJSON(input []byte) error { type callFrame0 struct { Type *vm.OpCode `json:"-"` From *common.Address `json:"from"` + To *common.Address `json:"to,omitempty" rlp:"optional"` Gas *hexutil.Uint64 `json:"gas"` GasUsed *hexutil.Uint64 `json:"gasUsed"` - To *common.Address `json:"to,omitempty" rlp:"optional"` - Input *hexutil.Bytes `json:"input,omitempty" rlp:"optional"` + Input *hexutil.Bytes `json:"input" rlp:"optional"` Output *hexutil.Bytes `json:"output,omitempty" rlp:"optional"` Error *string `json:"error,omitempty" rlp:"optional"` Calls []callFrame `json:"calls,omitempty" rlp:"optional"` @@ -67,15 +67,15 @@ func (c *callFrame) UnmarshalJSON(input []byte) error { if dec.From != nil { c.From = *dec.From } + if dec.To != nil { + c.To = *dec.To + } if dec.Gas != nil { c.Gas = uint64(*dec.Gas) } if dec.GasUsed != nil { c.GasUsed = uint64(*dec.GasUsed) } - if dec.To != nil { - c.To = *dec.To - } if dec.Input != nil { c.Input = *dec.Input } diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 4ccacc176687..918143a334e8 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -45,7 +45,6 @@ type account struct { type accountMarshaling struct { Balance *hexutil.Big - Nonce hexutil.Uint64 Code hexutil.Bytes } From 684f6d83dbb801969640ed8f54449c60622550d5 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi <itz.s1na@gmail.com> Date: Mon, 5 Sep 2022 15:45:33 +0200 Subject: [PATCH 3/3] re-arrange optional rlp field --- eth/tracers/native/call.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 2aecd4a4ff3d..54ae6dc8ab99 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -38,9 +38,9 @@ func init() { type callFrame struct { Type vm.OpCode `json:"-"` From common.Address `json:"from"` - To common.Address `json:"to,omitempty" rlp:"optional"` Gas uint64 `json:"gas"` 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"`