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"`