From 9c4fd4e9c9c9e7a8acfc5bafa74f6a49af1fbf16 Mon Sep 17 00:00:00 2001
From: Felix Lange <fjl@twurst.com>
Date: Sun, 26 Feb 2017 23:52:10 +0100
Subject: [PATCH 1/4] Makefile: add devtools target

---
 Makefile | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Makefile b/Makefile
index 4bd5612be5..07bacba65a 100644
--- a/Makefile
+++ b/Makefile
@@ -40,6 +40,15 @@ test: all
 clean:
 	rm -fr build/_workspace/pkg/ $(GOBIN)/*
 
+# The devtools target installs tools required for 'go generate'.
+# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
+
+devtools:
+	go get -u golang.org/x/tools/cmd/stringer
+	go get -u github.com/jteeuwen/go-bindata/go-bindata
+	go get -u github.com/fjl/gencodec
+	go install ./cmd/abigen
+
 # Cross Compilation Targets (xgo)
 
 geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios

From 667cd518ceada7084698c8ea11d966db9f9e674a Mon Sep 17 00:00:00 2001
From: Felix Lange <fjl@twurst.com>
Date: Tue, 7 Mar 2017 01:48:52 +0100
Subject: [PATCH 2/4] internal/jsre/deps: ensure that go generate produces no
 changes

---
 internal/jsre/deps/bindata.go | 4 ++--
 internal/jsre/deps/deps.go    | 3 ++-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/internal/jsre/deps/bindata.go b/internal/jsre/deps/bindata.go
index 5f6a2b873a..782d4df808 100644
--- a/internal/jsre/deps/bindata.go
+++ b/internal/jsre/deps/bindata.go
@@ -84,7 +84,7 @@ func bignumberJs() (*asset, error) {
 		return nil, err
 	}
 
-	info := bindataFileInfo{name: "bignumber.js", size: 17314, mode: os.FileMode(420), modTime: time.Unix(1484232218, 0)}
+	info := bindataFileInfo{name: "bignumber.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 }
@@ -104,7 +104,7 @@ func web3Js() (*asset, error) {
 		return nil, err
 	}
 
-	info := bindataFileInfo{name: "web3.js", size: 491740, mode: os.FileMode(420), modTime: time.Unix(1484232456, 0)}
+	info := bindataFileInfo{name: "web3.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 }
diff --git a/internal/jsre/deps/deps.go b/internal/jsre/deps/deps.go
index 8d0e1a4000..fe2e6f2fad 100644
--- a/internal/jsre/deps/deps.go
+++ b/internal/jsre/deps/deps.go
@@ -17,4 +17,5 @@
 // Package deps contains the console JavaScript dependencies Go embedded.
 package deps
 
-//go:generate go-bindata -o bindata.go bignumber.js web3.js
+//go:generate go-bindata -nometadata -pkg deps -o bindata.go bignumber.js web3.js
+//go:generate gofmt -w -s bindata.go

From eee96a5bb7439ccee583d23e53be018fe7c35cfb Mon Sep 17 00:00:00 2001
From: Felix Lange <fjl@twurst.com>
Date: Tue, 7 Mar 2017 12:37:53 +0100
Subject: [PATCH 3/4] rlp: add support for "-" struct tag

---
 rlp/decode.go      | 16 ++++++++++------
 rlp/decode_test.go | 13 +++++++++++++
 rlp/encode_test.go |  1 +
 rlp/typecache.go   |  8 +++++++-
 4 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/rlp/decode.go b/rlp/decode.go
index c4e5869cc9..ee0b7dbcd3 100644
--- a/rlp/decode.go
+++ b/rlp/decode.go
@@ -63,12 +63,16 @@ type Decoder interface {
 // must contain an element for each decoded field. Decode returns an
 // error if there are too few or too many elements.
 //
-// The decoding of struct fields honours two struct tags, "tail" and
-// "nil". For an explanation of "tail", see the example.
-// The "nil" tag applies to pointer-typed fields and changes the
-// decoding rules for the field such that input values of size zero
-// decode as a nil pointer. This tag can be useful when decoding
-// recursive types.
+// The decoding of struct fields honours certain struct tags, "tail",
+// "nil" and "-".
+//
+// The "-" tag ignores fields.
+//
+// For an explanation of "tail", see the example.
+//
+// The "nil" tag applies to pointer-typed fields and changes the decoding
+// rules for the field such that input values of size zero decode as a nil
+// pointer. This tag can be useful when decoding recursive types.
 //
 //     type StructWithEmptyOK struct {
 //         Foo *[20]byte `rlp:"nil"`
diff --git a/rlp/decode_test.go b/rlp/decode_test.go
index 2d465b74dc..d762e195d0 100644
--- a/rlp/decode_test.go
+++ b/rlp/decode_test.go
@@ -339,6 +339,12 @@ var (
 	)
 )
 
+type hasIgnoredField struct {
+	A uint
+	B uint `rlp:"-"`
+	C uint
+}
+
 var decodeTests = []decodeTest{
 	// booleans
 	{input: "01", ptr: new(bool), value: true},
@@ -490,6 +496,13 @@ var decodeTests = []decodeTest{
 		value: tailRaw{A: 1, Tail: []RawValue{}},
 	},
 
+	// struct tag "-"
+	{
+		input: "C20102",
+		ptr:   new(hasIgnoredField),
+		value: hasIgnoredField{A: 1, C: 2},
+	},
+
 	// RawValue
 	{input: "01", ptr: new(RawValue), value: RawValue(unhex("01"))},
 	{input: "82FFFF", ptr: new(RawValue), value: RawValue(unhex("82FFFF"))},
diff --git a/rlp/encode_test.go b/rlp/encode_test.go
index 6f38294e46..827960f7c1 100644
--- a/rlp/encode_test.go
+++ b/rlp/encode_test.go
@@ -218,6 +218,7 @@ var encTests = []encTest{
 	{val: &tailRaw{A: 1, Tail: []RawValue{unhex("02")}}, output: "C20102"},
 	{val: &tailRaw{A: 1, Tail: []RawValue{}}, output: "C101"},
 	{val: &tailRaw{A: 1, Tail: nil}, output: "C101"},
+	{val: &hasIgnoredField{A: 1, B: 2, C: 3}, output: "C20103"},
 
 	// nil
 	{val: (*uint)(nil), output: "80"},
diff --git a/rlp/typecache.go b/rlp/typecache.go
index a2f217c66f..3df799e1ec 100644
--- a/rlp/typecache.go
+++ b/rlp/typecache.go
@@ -37,11 +37,12 @@ type typeinfo struct {
 type tags struct {
 	// rlp:"nil" controls whether empty input results in a nil pointer.
 	nilOK bool
-
 	// rlp:"tail" controls whether this field swallows additional list
 	// elements. It can only be set for the last field, which must be
 	// of slice type.
 	tail bool
+	// rlp:"-" ignores fields.
+	ignored bool
 }
 
 type typekey struct {
@@ -101,6 +102,9 @@ func structFields(typ reflect.Type) (fields []field, err error) {
 			if err != nil {
 				return nil, err
 			}
+			if tags.ignored {
+				continue
+			}
 			info, err := cachedTypeInfo1(f.Type, tags)
 			if err != nil {
 				return nil, err
@@ -117,6 +121,8 @@ func parseStructTag(typ reflect.Type, fi int) (tags, error) {
 	for _, t := range strings.Split(f.Tag.Get("rlp"), ",") {
 		switch t = strings.TrimSpace(t); t {
 		case "":
+		case "-":
+			ts.ignored = true
 		case "nil":
 			ts.nilOK = true
 		case "tail":

From 8cf08e4b25c4cd0e0955598342394f34feecca0c Mon Sep 17 00:00:00 2001
From: Felix Lange <fjl@twurst.com>
Date: Thu, 23 Feb 2017 17:58:15 +0100
Subject: [PATCH 4/4] core/types: use gencodec for JSON marshaling code

---
 core/types/block.go            | 123 ++++++-----------------------
 core/types/gen_header_json.go  | 136 +++++++++++++++++++++++++++++++++
 core/types/gen_log_json.go     |  90 ++++++++++++++++++++++
 core/types/gen_receipt_json.go |  79 +++++++++++++++++++
 core/types/gen_tx_json.go      |  99 ++++++++++++++++++++++++
 core/types/log.go              |  99 +++++++-----------------
 core/types/log_test.go         |   3 +-
 core/types/receipt.go          |  78 +++----------------
 core/types/transaction.go      | 107 +++++++++-----------------
 9 files changed, 506 insertions(+), 308 deletions(-)
 create mode 100644 core/types/gen_header_json.go
 create mode 100644 core/types/gen_log_json.go
 create mode 100644 core/types/gen_receipt_json.go
 create mode 100644 core/types/gen_tx_json.go

diff --git a/core/types/block.go b/core/types/block.go
index 1dae87962a..b699ba6862 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -19,8 +19,6 @@ package types
 
 import (
 	"encoding/binary"
-	"encoding/json"
-	"errors"
 	"fmt"
 	"io"
 	"math/big"
@@ -39,12 +37,6 @@ var (
 	EmptyUncleHash = CalcUncleHash(nil)
 )
 
-var (
-	errMissingHeaderMixDigest = errors.New("missing mixHash in JSON block header")
-	errMissingHeaderFields    = errors.New("missing required JSON block header fields")
-	errBadNonceSize           = errors.New("invalid block nonce size, want 8 bytes")
-)
-
 // A BlockNonce is a 64-bit hash which proves (combined with the
 // mix-hash) that a sufficient amount of computation has been carried
 // out on a block.
@@ -72,41 +64,35 @@ func (n *BlockNonce) UnmarshalText(input []byte) error {
 	return hexutil.UnmarshalFixedText("BlockNonce", input, n[:])
 }
 
+//go:generate gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
+
 // Header represents a block header in the Ethereum blockchain.
 type Header struct {
-	ParentHash  common.Hash    // Hash to the previous block
-	UncleHash   common.Hash    // Uncles of this block
-	Coinbase    common.Address // The coin base address
-	Root        common.Hash    // Block Trie state
-	TxHash      common.Hash    // Tx sha
-	ReceiptHash common.Hash    // Receipt sha
-	Bloom       Bloom          // Bloom
-	Difficulty  *big.Int       // Difficulty for the current block
-	Number      *big.Int       // The block number
-	GasLimit    *big.Int       // Gas limit
-	GasUsed     *big.Int       // Gas used
-	Time        *big.Int       // Creation time
-	Extra       []byte         // Extra data
-	MixDigest   common.Hash    // for quick difficulty verification
-	Nonce       BlockNonce
+	ParentHash  common.Hash    `json:"parentHash"`
+	UncleHash   common.Hash    `json:"sha3Uncles"`
+	Coinbase    common.Address `json:"miner"`
+	Root        common.Hash    `json:"stateRoot"`
+	TxHash      common.Hash    `json:"transactionsRoot"`
+	ReceiptHash common.Hash    `json:"receiptsRoot"`
+	Bloom       Bloom          `json:"logsBloom"`
+	Difficulty  *big.Int       `json:"difficulty"`
+	Number      *big.Int       `json:"number"`
+	GasLimit    *big.Int       `json:"gasLimit"`
+	GasUsed     *big.Int       `json:"gasUsed"`
+	Time        *big.Int       `json:"timestamp"`
+	Extra       []byte         `json:"extraData"`
+	MixDigest   common.Hash    `json:"mixHash"`
+	Nonce       BlockNonce     `json:"nonce"`
 }
 
-type jsonHeader struct {
-	ParentHash  *common.Hash    `json:"parentHash"`
-	UncleHash   *common.Hash    `json:"sha3Uncles"`
-	Coinbase    *common.Address `json:"miner"`
-	Root        *common.Hash    `json:"stateRoot"`
-	TxHash      *common.Hash    `json:"transactionsRoot"`
-	ReceiptHash *common.Hash    `json:"receiptsRoot"`
-	Bloom       *Bloom          `json:"logsBloom"`
-	Difficulty  *hexutil.Big    `json:"difficulty"`
-	Number      *hexutil.Big    `json:"number"`
-	GasLimit    *hexutil.Big    `json:"gasLimit"`
-	GasUsed     *hexutil.Big    `json:"gasUsed"`
-	Time        *hexutil.Big    `json:"timestamp"`
-	Extra       *hexutil.Bytes  `json:"extraData"`
-	MixDigest   *common.Hash    `json:"mixHash"`
-	Nonce       *BlockNonce     `json:"nonce"`
+// field type overrides for gencodec
+type headerMarshaling struct {
+	Difficulty *hexutil.Big
+	Number     *hexutil.Big
+	GasLimit   *hexutil.Big
+	GasUsed    *hexutil.Big
+	Time       *hexutil.Big
+	Extra      hexutil.Bytes
 }
 
 // Hash returns the block hash of the header, which is simply the keccak256 hash of its
@@ -134,65 +120,6 @@ func (h *Header) HashNoNonce() common.Hash {
 	})
 }
 
-// MarshalJSON encodes headers into the web3 RPC response block format.
-func (h *Header) MarshalJSON() ([]byte, error) {
-	return json.Marshal(&jsonHeader{
-		ParentHash:  &h.ParentHash,
-		UncleHash:   &h.UncleHash,
-		Coinbase:    &h.Coinbase,
-		Root:        &h.Root,
-		TxHash:      &h.TxHash,
-		ReceiptHash: &h.ReceiptHash,
-		Bloom:       &h.Bloom,
-		Difficulty:  (*hexutil.Big)(h.Difficulty),
-		Number:      (*hexutil.Big)(h.Number),
-		GasLimit:    (*hexutil.Big)(h.GasLimit),
-		GasUsed:     (*hexutil.Big)(h.GasUsed),
-		Time:        (*hexutil.Big)(h.Time),
-		Extra:       (*hexutil.Bytes)(&h.Extra),
-		MixDigest:   &h.MixDigest,
-		Nonce:       &h.Nonce,
-	})
-}
-
-// UnmarshalJSON decodes headers from the web3 RPC response block format.
-func (h *Header) UnmarshalJSON(input []byte) error {
-	var dec jsonHeader
-	if err := json.Unmarshal(input, &dec); err != nil {
-		return err
-	}
-	// Ensure that all fields are set. MixDigest is checked separately because
-	// it is a recent addition to the spec (as of August 2016) and older RPC server
-	// implementations might not provide it.
-	if dec.MixDigest == nil {
-		return errMissingHeaderMixDigest
-	}
-	if dec.ParentHash == nil || dec.UncleHash == nil || dec.Coinbase == nil ||
-		dec.Root == nil || dec.TxHash == nil || dec.ReceiptHash == nil ||
-		dec.Bloom == nil || dec.Difficulty == nil || dec.Number == nil ||
-		dec.GasLimit == nil || dec.GasUsed == nil || dec.Time == nil ||
-		dec.Extra == nil || dec.Nonce == nil {
-		return errMissingHeaderFields
-	}
-	// Assign all values.
-	h.ParentHash = *dec.ParentHash
-	h.UncleHash = *dec.UncleHash
-	h.Coinbase = *dec.Coinbase
-	h.Root = *dec.Root
-	h.TxHash = *dec.TxHash
-	h.ReceiptHash = *dec.ReceiptHash
-	h.Bloom = *dec.Bloom
-	h.Difficulty = (*big.Int)(dec.Difficulty)
-	h.Number = (*big.Int)(dec.Number)
-	h.GasLimit = (*big.Int)(dec.GasLimit)
-	h.GasUsed = (*big.Int)(dec.GasUsed)
-	h.Time = (*big.Int)(dec.Time)
-	h.Extra = *dec.Extra
-	h.MixDigest = *dec.MixDigest
-	h.Nonce = *dec.Nonce
-	return nil
-}
-
 func rlpHash(x interface{}) (h common.Hash) {
 	hw := sha3.NewKeccak256()
 	rlp.Encode(hw, x)
diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go
new file mode 100644
index 0000000000..860622e6eb
--- /dev/null
+++ b/core/types/gen_header_json.go
@@ -0,0 +1,136 @@
+// generated by github.com/fjl/gencodec, do not edit.
+
+package types
+
+import (
+	"encoding/json"
+	"errors"
+	"math/big"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+func (h *Header) MarshalJSON() ([]byte, error) {
+	type HeaderJSON struct {
+		ParentHash  *common.Hash    `json:"parentHash"`
+		UncleHash   *common.Hash    `json:"sha3Uncles"`
+		Coinbase    *common.Address `json:"miner"`
+		Root        *common.Hash    `json:"stateRoot"`
+		TxHash      *common.Hash    `json:"transactionsRoot"`
+		ReceiptHash *common.Hash    `json:"receiptsRoot"`
+		Bloom       *Bloom          `json:"logsBloom"`
+		Difficulty  *hexutil.Big    `json:"difficulty"`
+		Number      *hexutil.Big    `json:"number"`
+		GasLimit    *hexutil.Big    `json:"gasLimit"`
+		GasUsed     *hexutil.Big    `json:"gasUsed"`
+		Time        *hexutil.Big    `json:"timestamp"`
+		Extra       hexutil.Bytes   `json:"extraData"`
+		MixDigest   *common.Hash    `json:"mixHash"`
+		Nonce       *BlockNonce     `json:"nonce"`
+	}
+	var enc HeaderJSON
+	enc.ParentHash = &h.ParentHash
+	enc.UncleHash = &h.UncleHash
+	enc.Coinbase = &h.Coinbase
+	enc.Root = &h.Root
+	enc.TxHash = &h.TxHash
+	enc.ReceiptHash = &h.ReceiptHash
+	enc.Bloom = &h.Bloom
+	enc.Difficulty = (*hexutil.Big)(h.Difficulty)
+	enc.Number = (*hexutil.Big)(h.Number)
+	enc.GasLimit = (*hexutil.Big)(h.GasLimit)
+	enc.GasUsed = (*hexutil.Big)(h.GasUsed)
+	enc.Time = (*hexutil.Big)(h.Time)
+	enc.Extra = h.Extra
+	enc.MixDigest = &h.MixDigest
+	enc.Nonce = &h.Nonce
+	return json.Marshal(&enc)
+}
+
+func (h *Header) UnmarshalJSON(input []byte) error {
+	type HeaderJSON struct {
+		ParentHash  *common.Hash    `json:"parentHash"`
+		UncleHash   *common.Hash    `json:"sha3Uncles"`
+		Coinbase    *common.Address `json:"miner"`
+		Root        *common.Hash    `json:"stateRoot"`
+		TxHash      *common.Hash    `json:"transactionsRoot"`
+		ReceiptHash *common.Hash    `json:"receiptsRoot"`
+		Bloom       *Bloom          `json:"logsBloom"`
+		Difficulty  *hexutil.Big    `json:"difficulty"`
+		Number      *hexutil.Big    `json:"number"`
+		GasLimit    *hexutil.Big    `json:"gasLimit"`
+		GasUsed     *hexutil.Big    `json:"gasUsed"`
+		Time        *hexutil.Big    `json:"timestamp"`
+		Extra       hexutil.Bytes   `json:"extraData"`
+		MixDigest   *common.Hash    `json:"mixHash"`
+		Nonce       *BlockNonce     `json:"nonce"`
+	}
+	var dec HeaderJSON
+	if err := json.Unmarshal(input, &dec); err != nil {
+		return err
+	}
+	var x Header
+	if dec.ParentHash == nil {
+		return errors.New("missing required field 'parentHash' for Header")
+	}
+	x.ParentHash = *dec.ParentHash
+	if dec.UncleHash == nil {
+		return errors.New("missing required field 'sha3Uncles' for Header")
+	}
+	x.UncleHash = *dec.UncleHash
+	if dec.Coinbase == nil {
+		return errors.New("missing required field 'miner' for Header")
+	}
+	x.Coinbase = *dec.Coinbase
+	if dec.Root == nil {
+		return errors.New("missing required field 'stateRoot' for Header")
+	}
+	x.Root = *dec.Root
+	if dec.TxHash == nil {
+		return errors.New("missing required field 'transactionsRoot' for Header")
+	}
+	x.TxHash = *dec.TxHash
+	if dec.ReceiptHash == nil {
+		return errors.New("missing required field 'receiptsRoot' for Header")
+	}
+	x.ReceiptHash = *dec.ReceiptHash
+	if dec.Bloom == nil {
+		return errors.New("missing required field 'logsBloom' for Header")
+	}
+	x.Bloom = *dec.Bloom
+	if dec.Difficulty == nil {
+		return errors.New("missing required field 'difficulty' for Header")
+	}
+	x.Difficulty = (*big.Int)(dec.Difficulty)
+	if dec.Number == nil {
+		return errors.New("missing required field 'number' for Header")
+	}
+	x.Number = (*big.Int)(dec.Number)
+	if dec.GasLimit == nil {
+		return errors.New("missing required field 'gasLimit' for Header")
+	}
+	x.GasLimit = (*big.Int)(dec.GasLimit)
+	if dec.GasUsed == nil {
+		return errors.New("missing required field 'gasUsed' for Header")
+	}
+	x.GasUsed = (*big.Int)(dec.GasUsed)
+	if dec.Time == nil {
+		return errors.New("missing required field 'timestamp' for Header")
+	}
+	x.Time = (*big.Int)(dec.Time)
+	if dec.Extra == nil {
+		return errors.New("missing required field 'extraData' for Header")
+	}
+	x.Extra = dec.Extra
+	if dec.MixDigest == nil {
+		return errors.New("missing required field 'mixHash' for Header")
+	}
+	x.MixDigest = *dec.MixDigest
+	if dec.Nonce == nil {
+		return errors.New("missing required field 'nonce' for Header")
+	}
+	x.Nonce = *dec.Nonce
+	*h = x
+	return nil
+}
diff --git a/core/types/gen_log_json.go b/core/types/gen_log_json.go
new file mode 100644
index 0000000000..ef2cdfd890
--- /dev/null
+++ b/core/types/gen_log_json.go
@@ -0,0 +1,90 @@
+// generated by github.com/fjl/gencodec, do not edit.
+
+package types
+
+import (
+	"encoding/json"
+	"errors"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+func (l *Log) MarshalJSON() ([]byte, error) {
+	type LogJSON struct {
+		Address     *common.Address `json:"address"`
+		Topics      []common.Hash   `json:"topics"`
+		Data        hexutil.Bytes   `json:"data"`
+		BlockNumber *hexutil.Uint64 `json:"blockNumber" optional:"yes"`
+		TxHash      *common.Hash    `json:"transactionHash"`
+		TxIndex     *hexutil.Uint   `json:"transactionIndex"`
+		BlockHash   *common.Hash    `json:"blockHash" optional:"yes"`
+		Index       *hexutil.Uint   `json:"logIndex"`
+		Removed     *bool           `json:"removed" optional:"yes"`
+	}
+	var enc LogJSON
+	enc.Address = &l.Address
+	enc.Topics = l.Topics
+	enc.Data = l.Data
+	enc.BlockNumber = (*hexutil.Uint64)(&l.BlockNumber)
+	enc.TxHash = &l.TxHash
+	enc.TxIndex = (*hexutil.Uint)(&l.TxIndex)
+	enc.BlockHash = &l.BlockHash
+	enc.Index = (*hexutil.Uint)(&l.Index)
+	enc.Removed = &l.Removed
+	return json.Marshal(&enc)
+}
+
+func (l *Log) UnmarshalJSON(input []byte) error {
+	type LogJSON struct {
+		Address     *common.Address `json:"address"`
+		Topics      []common.Hash   `json:"topics"`
+		Data        hexutil.Bytes   `json:"data"`
+		BlockNumber *hexutil.Uint64 `json:"blockNumber" optional:"yes"`
+		TxHash      *common.Hash    `json:"transactionHash"`
+		TxIndex     *hexutil.Uint   `json:"transactionIndex"`
+		BlockHash   *common.Hash    `json:"blockHash" optional:"yes"`
+		Index       *hexutil.Uint   `json:"logIndex"`
+		Removed     *bool           `json:"removed" optional:"yes"`
+	}
+	var dec LogJSON
+	if err := json.Unmarshal(input, &dec); err != nil {
+		return err
+	}
+	var x Log
+	if dec.Address == nil {
+		return errors.New("missing required field 'address' for Log")
+	}
+	x.Address = *dec.Address
+	if dec.Topics == nil {
+		return errors.New("missing required field 'topics' for Log")
+	}
+	x.Topics = dec.Topics
+	if dec.Data == nil {
+		return errors.New("missing required field 'data' for Log")
+	}
+	x.Data = dec.Data
+	if dec.BlockNumber != nil {
+		x.BlockNumber = uint64(*dec.BlockNumber)
+	}
+	if dec.TxHash == nil {
+		return errors.New("missing required field 'transactionHash' for Log")
+	}
+	x.TxHash = *dec.TxHash
+	if dec.TxIndex == nil {
+		return errors.New("missing required field 'transactionIndex' for Log")
+	}
+	x.TxIndex = uint(*dec.TxIndex)
+	if dec.BlockHash != nil {
+		x.BlockHash = *dec.BlockHash
+	}
+	if dec.Index == nil {
+		return errors.New("missing required field 'logIndex' for Log")
+	}
+	x.Index = uint(*dec.Index)
+	if dec.Removed != nil {
+		x.Removed = *dec.Removed
+	}
+	*l = x
+	return nil
+}
diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go
new file mode 100644
index 0000000000..b9e9bee260
--- /dev/null
+++ b/core/types/gen_receipt_json.go
@@ -0,0 +1,79 @@
+// generated by github.com/fjl/gencodec, do not edit.
+
+package types
+
+import (
+	"encoding/json"
+	"errors"
+	"math/big"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+func (r *Receipt) MarshalJSON() ([]byte, error) {
+	type ReceiptJSON struct {
+		PostState         hexutil.Bytes   `json:"root"`
+		CumulativeGasUsed *hexutil.Big    `json:"cumulativeGasUsed"`
+		Bloom             *Bloom          `json:"logsBloom"`
+		Logs              []*Log          `json:"logs"`
+		TxHash            *common.Hash    `json:"transactionHash"`
+		ContractAddress   *common.Address `json:"contractAddress" optional:"true"`
+		GasUsed           *hexutil.Big    `json:"gasUsed"`
+	}
+	var enc ReceiptJSON
+	enc.PostState = r.PostState
+	enc.CumulativeGasUsed = (*hexutil.Big)(r.CumulativeGasUsed)
+	enc.Bloom = &r.Bloom
+	enc.Logs = r.Logs
+	enc.TxHash = &r.TxHash
+	enc.ContractAddress = &r.ContractAddress
+	enc.GasUsed = (*hexutil.Big)(r.GasUsed)
+	return json.Marshal(&enc)
+}
+
+func (r *Receipt) UnmarshalJSON(input []byte) error {
+	type ReceiptJSON struct {
+		PostState         hexutil.Bytes   `json:"root"`
+		CumulativeGasUsed *hexutil.Big    `json:"cumulativeGasUsed"`
+		Bloom             *Bloom          `json:"logsBloom"`
+		Logs              []*Log          `json:"logs"`
+		TxHash            *common.Hash    `json:"transactionHash"`
+		ContractAddress   *common.Address `json:"contractAddress" optional:"true"`
+		GasUsed           *hexutil.Big    `json:"gasUsed"`
+	}
+	var dec ReceiptJSON
+	if err := json.Unmarshal(input, &dec); err != nil {
+		return err
+	}
+	var x Receipt
+	if dec.PostState == nil {
+		return errors.New("missing required field 'root' for Receipt")
+	}
+	x.PostState = dec.PostState
+	if dec.CumulativeGasUsed == nil {
+		return errors.New("missing required field 'cumulativeGasUsed' for Receipt")
+	}
+	x.CumulativeGasUsed = (*big.Int)(dec.CumulativeGasUsed)
+	if dec.Bloom == nil {
+		return errors.New("missing required field 'logsBloom' for Receipt")
+	}
+	x.Bloom = *dec.Bloom
+	if dec.Logs == nil {
+		return errors.New("missing required field 'logs' for Receipt")
+	}
+	x.Logs = dec.Logs
+	if dec.TxHash == nil {
+		return errors.New("missing required field 'transactionHash' for Receipt")
+	}
+	x.TxHash = *dec.TxHash
+	if dec.ContractAddress != nil {
+		x.ContractAddress = *dec.ContractAddress
+	}
+	if dec.GasUsed == nil {
+		return errors.New("missing required field 'gasUsed' for Receipt")
+	}
+	x.GasUsed = (*big.Int)(dec.GasUsed)
+	*r = x
+	return nil
+}
diff --git a/core/types/gen_tx_json.go b/core/types/gen_tx_json.go
new file mode 100644
index 0000000000..8bbe629d70
--- /dev/null
+++ b/core/types/gen_tx_json.go
@@ -0,0 +1,99 @@
+// generated by github.com/fjl/gencodec, do not edit.
+
+package types
+
+import (
+	"encoding/json"
+	"errors"
+	"math/big"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+func (t *txdata) MarshalJSON() ([]byte, error) {
+	type txdataJSON struct {
+		AccountNonce *hexutil.Uint64 `json:"nonce"`
+		Price        *hexutil.Big    `json:"gasPrice"`
+		GasLimit     *hexutil.Big    `json:"gasLimit"`
+		Recipient    *common.Address `json:"to" optional:"yes" rlp:"nil"`
+		Amount       *hexutil.Big    `json:"value"`
+		Payload      hexutil.Bytes   `json:"input"`
+		V            *hexutil.Big    `json:"v"`
+		R            *hexutil.Big    `json:"r"`
+		S            *hexutil.Big    `json:"s"`
+		Hash         *common.Hash    `json:"hash" optional:"yes" rlp:"-"`
+	}
+	var enc txdataJSON
+	enc.AccountNonce = (*hexutil.Uint64)(&t.AccountNonce)
+	enc.Price = (*hexutil.Big)(t.Price)
+	enc.GasLimit = (*hexutil.Big)(t.GasLimit)
+	enc.Recipient = t.Recipient
+	enc.Amount = (*hexutil.Big)(t.Amount)
+	enc.Payload = t.Payload
+	enc.V = (*hexutil.Big)(t.V)
+	enc.R = (*hexutil.Big)(t.R)
+	enc.S = (*hexutil.Big)(t.S)
+	enc.Hash = t.Hash
+	return json.Marshal(&enc)
+}
+
+func (t *txdata) UnmarshalJSON(input []byte) error {
+	type txdataJSON struct {
+		AccountNonce *hexutil.Uint64 `json:"nonce"`
+		Price        *hexutil.Big    `json:"gasPrice"`
+		GasLimit     *hexutil.Big    `json:"gasLimit"`
+		Recipient    *common.Address `json:"to" optional:"yes" rlp:"nil"`
+		Amount       *hexutil.Big    `json:"value"`
+		Payload      hexutil.Bytes   `json:"input"`
+		V            *hexutil.Big    `json:"v"`
+		R            *hexutil.Big    `json:"r"`
+		S            *hexutil.Big    `json:"s"`
+		Hash         *common.Hash    `json:"hash" optional:"yes" rlp:"-"`
+	}
+	var dec txdataJSON
+	if err := json.Unmarshal(input, &dec); err != nil {
+		return err
+	}
+	var x txdata
+	if dec.AccountNonce == nil {
+		return errors.New("missing required field 'nonce' for txdata")
+	}
+	x.AccountNonce = uint64(*dec.AccountNonce)
+	if dec.Price == nil {
+		return errors.New("missing required field 'gasPrice' for txdata")
+	}
+	x.Price = (*big.Int)(dec.Price)
+	if dec.GasLimit == nil {
+		return errors.New("missing required field 'gasLimit' for txdata")
+	}
+	x.GasLimit = (*big.Int)(dec.GasLimit)
+	if dec.Recipient != nil {
+		x.Recipient = dec.Recipient
+	}
+	if dec.Amount == nil {
+		return errors.New("missing required field 'value' for txdata")
+	}
+	x.Amount = (*big.Int)(dec.Amount)
+	if dec.Payload == nil {
+		return errors.New("missing required field 'input' for txdata")
+	}
+	x.Payload = dec.Payload
+	if dec.V == nil {
+		return errors.New("missing required field 'v' for txdata")
+	}
+	x.V = (*big.Int)(dec.V)
+	if dec.R == nil {
+		return errors.New("missing required field 'r' for txdata")
+	}
+	x.R = (*big.Int)(dec.R)
+	if dec.S == nil {
+		return errors.New("missing required field 's' for txdata")
+	}
+	x.S = (*big.Int)(dec.S)
+	if dec.Hash != nil {
+		x.Hash = dec.Hash
+	}
+	*t = x
+	return nil
+}
diff --git a/core/types/log.go b/core/types/log.go
index 7efb06b5c1..57fc7b3631 100644
--- a/core/types/log.go
+++ b/core/types/log.go
@@ -17,8 +17,6 @@
 package types
 
 import (
-	"encoding/json"
-	"errors"
 	"fmt"
 	"io"
 
@@ -27,27 +25,42 @@ import (
 	"github.com/ethereum/go-ethereum/rlp"
 )
 
-var errMissingLogFields = errors.New("missing required JSON log fields")
+//go:generate gencodec -type Log -field-override logMarshaling -out gen_log_json.go
 
 // Log represents a contract log event. These events are generated by the LOG opcode and
 // stored/indexed by the node.
 type Log struct {
-	// Consensus fields.
-	Address common.Address // address of the contract that generated the event
-	Topics  []common.Hash  // list of topics provided by the contract.
-	Data    []byte         // supplied by the contract, usually ABI-encoded
+	// Consensus fields:
+	// address of the contract that generated the event
+	Address common.Address `json:"address"`
+	// list of topics provided by the contract.
+	Topics []common.Hash `json:"topics"`
+	// supplied by the contract, usually ABI-encoded
+	Data []byte `json:"data"`
 
 	// Derived fields. These fields are filled in by the node
 	// but not secured by consensus.
-	BlockNumber uint64      // block in which the transaction was included
-	TxHash      common.Hash // hash of the transaction
-	TxIndex     uint        // index of the transaction in the block
-	BlockHash   common.Hash // hash of the block in which the transaction was included
-	Index       uint        // index of the log in the receipt
+	// block in which the transaction was included
+	BlockNumber uint64 `json:"blockNumber" optional:"yes"`
+	// hash of the transaction
+	TxHash common.Hash `json:"transactionHash"`
+	// index of the transaction in the block
+	TxIndex uint `json:"transactionIndex"`
+	// hash of the block in which the transaction was included
+	BlockHash common.Hash `json:"blockHash" optional:"yes"`
+	// index of the log in the receipt
+	Index uint `json:"logIndex"`
 
 	// The Removed field is true if this log was reverted due to a chain reorganisation.
 	// You must pay attention to this field if you receive logs through a filter query.
-	Removed bool
+	Removed bool `json:"removed" optional:"yes"`
+}
+
+type logMarshaling struct {
+	Data        hexutil.Bytes
+	BlockNumber hexutil.Uint64
+	TxIndex     hexutil.Uint
+	Index       hexutil.Uint
 }
 
 type rlpLog struct {
@@ -67,18 +80,6 @@ type rlpStorageLog struct {
 	Index       uint
 }
 
-type jsonLog struct {
-	Address     *common.Address `json:"address"`
-	Topics      *[]common.Hash  `json:"topics"`
-	Data        *hexutil.Bytes  `json:"data"`
-	BlockNumber *hexutil.Uint64 `json:"blockNumber"`
-	TxIndex     *hexutil.Uint   `json:"transactionIndex"`
-	TxHash      *common.Hash    `json:"transactionHash"`
-	BlockHash   *common.Hash    `json:"blockHash"`
-	Index       *hexutil.Uint   `json:"logIndex"`
-	Removed     bool            `json:"removed"`
-}
-
 // EncodeRLP implements rlp.Encoder.
 func (l *Log) EncodeRLP(w io.Writer) error {
 	return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data})
@@ -98,54 +99,6 @@ func (l *Log) String() string {
 	return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index)
 }
 
-// MarshalJSON implements json.Marshaler.
-func (l *Log) MarshalJSON() ([]byte, error) {
-	jslog := &jsonLog{
-		Address: &l.Address,
-		Topics:  &l.Topics,
-		Data:    (*hexutil.Bytes)(&l.Data),
-		TxIndex: (*hexutil.Uint)(&l.TxIndex),
-		TxHash:  &l.TxHash,
-		Index:   (*hexutil.Uint)(&l.Index),
-		Removed: l.Removed,
-	}
-	// Set block information for mined logs.
-	if (l.BlockHash != common.Hash{}) {
-		jslog.BlockHash = &l.BlockHash
-		jslog.BlockNumber = (*hexutil.Uint64)(&l.BlockNumber)
-	}
-	return json.Marshal(jslog)
-}
-
-// UnmarshalJSON implements json.Umarshaler.
-func (l *Log) UnmarshalJSON(input []byte) error {
-	var dec jsonLog
-	if err := json.Unmarshal(input, &dec); err != nil {
-		return err
-	}
-	if dec.Address == nil || dec.Topics == nil || dec.Data == nil ||
-		dec.TxIndex == nil || dec.TxHash == nil || dec.Index == nil {
-		return errMissingLogFields
-	}
-	declog := Log{
-		Address: *dec.Address,
-		Topics:  *dec.Topics,
-		Data:    *dec.Data,
-		TxHash:  *dec.TxHash,
-		TxIndex: uint(*dec.TxIndex),
-		Index:   uint(*dec.Index),
-		Removed: dec.Removed,
-	}
-	// Block information may be missing if the log is received through
-	// the pending log filter, so it's handled specially here.
-	if dec.BlockHash != nil && dec.BlockNumber != nil {
-		declog.BlockHash = *dec.BlockHash
-		declog.BlockNumber = uint64(*dec.BlockNumber)
-	}
-	*l = declog
-	return nil
-}
-
 // LogForStorage is a wrapper around a Log that flattens and parses the entire content of
 // a log including non-consensus fields.
 type LogForStorage Log
diff --git a/core/types/log_test.go b/core/types/log_test.go
index bf742ccac2..0e56acfe4a 100644
--- a/core/types/log_test.go
+++ b/core/types/log_test.go
@@ -18,6 +18,7 @@ package types
 
 import (
 	"encoding/json"
+	"fmt"
 	"reflect"
 	"testing"
 
@@ -96,7 +97,7 @@ var unmarshalLogTests = map[string]struct {
 	},
 	"missing data": {
 		input:     `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
-		wantError: errMissingLogFields,
+		wantError: fmt.Errorf("missing required field 'data' for Log"),
 	},
 }
 
diff --git a/core/types/receipt.go b/core/types/receipt.go
index 0a6a35e334..5bfcb15fc7 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -17,8 +17,6 @@
 package types
 
 import (
-	"encoding/json"
-	"errors"
 	"fmt"
 	"io"
 	"math/big"
@@ -28,33 +26,26 @@ import (
 	"github.com/ethereum/go-ethereum/rlp"
 )
 
-var (
-	errMissingReceiptPostState = errors.New("missing post state root in JSON receipt")
-	errMissingReceiptFields    = errors.New("missing required JSON receipt fields")
-)
+//go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
 
 // Receipt represents the results of a transaction.
 type Receipt struct {
 	// Consensus fields
-	PostState         []byte
-	CumulativeGasUsed *big.Int
-	Bloom             Bloom
-	Logs              []*Log
+	PostState         []byte   `json:"root"`
+	CumulativeGasUsed *big.Int `json:"cumulativeGasUsed"`
+	Bloom             Bloom    `json:"logsBloom"`
+	Logs              []*Log   `json:"logs"`
 
 	// Implementation fields (don't reorder!)
-	TxHash          common.Hash
-	ContractAddress common.Address
-	GasUsed         *big.Int
+	TxHash          common.Hash    `json:"transactionHash"`
+	ContractAddress common.Address `json:"contractAddress" optional:"true"`
+	GasUsed         *big.Int       `json:"gasUsed"`
 }
 
-type jsonReceipt struct {
-	PostState         *common.Hash    `json:"root"`
-	CumulativeGasUsed *hexutil.Big    `json:"cumulativeGasUsed"`
-	Bloom             *Bloom          `json:"logsBloom"`
-	Logs              []*Log          `json:"logs"`
-	TxHash            *common.Hash    `json:"transactionHash"`
-	ContractAddress   *common.Address `json:"contractAddress"`
-	GasUsed           *hexutil.Big    `json:"gasUsed"`
+type receiptMarshaling struct {
+	PostState         hexutil.Bytes
+	CumulativeGasUsed *hexutil.Big
+	GasUsed           *hexutil.Big
 }
 
 // NewReceipt creates a barebone transaction receipt, copying the init fields.
@@ -84,51 +75,6 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
 	return nil
 }
 
-// MarshalJSON encodes receipts into the web3 RPC response block format.
-func (r *Receipt) MarshalJSON() ([]byte, error) {
-	root := common.BytesToHash(r.PostState)
-
-	return json.Marshal(&jsonReceipt{
-		PostState:         &root,
-		CumulativeGasUsed: (*hexutil.Big)(r.CumulativeGasUsed),
-		Bloom:             &r.Bloom,
-		Logs:              r.Logs,
-		TxHash:            &r.TxHash,
-		ContractAddress:   &r.ContractAddress,
-		GasUsed:           (*hexutil.Big)(r.GasUsed),
-	})
-}
-
-// UnmarshalJSON decodes the web3 RPC receipt format.
-func (r *Receipt) UnmarshalJSON(input []byte) error {
-	var dec jsonReceipt
-	if err := json.Unmarshal(input, &dec); err != nil {
-		return err
-	}
-	// Ensure that all fields are set. PostState is checked separately because it is a
-	// recent addition to the RPC spec (as of August 2016) and older implementations might
-	// not provide it. Note that ContractAddress is not checked because it can be null.
-	if dec.PostState == nil {
-		return errMissingReceiptPostState
-	}
-	if dec.CumulativeGasUsed == nil || dec.Bloom == nil ||
-		dec.Logs == nil || dec.TxHash == nil || dec.GasUsed == nil {
-		return errMissingReceiptFields
-	}
-	*r = Receipt{
-		PostState:         (*dec.PostState)[:],
-		CumulativeGasUsed: (*big.Int)(dec.CumulativeGasUsed),
-		Bloom:             *dec.Bloom,
-		Logs:              dec.Logs,
-		TxHash:            *dec.TxHash,
-		GasUsed:           (*big.Int)(dec.GasUsed),
-	}
-	if dec.ContractAddress != nil {
-		r.ContractAddress = *dec.ContractAddress
-	}
-	return nil
-}
-
 // String implements the Stringer interface.
 func (r *Receipt) String() string {
 	return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs)
diff --git a/core/types/transaction.go b/core/types/transaction.go
index ab0bba4dcd..a02f9ed009 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -18,7 +18,6 @@ package types
 
 import (
 	"container/heap"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
@@ -32,12 +31,11 @@ import (
 	"github.com/ethereum/go-ethereum/rlp"
 )
 
-var ErrInvalidSig = errors.New("invalid transaction v, r, s values")
+//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
 
 var (
-	errMissingTxSignatureFields = errors.New("missing required JSON transaction signature fields")
-	errMissingTxFields          = errors.New("missing required JSON transaction fields")
-	errNoSigner                 = errors.New("missing signing methods")
+	ErrInvalidSig = errors.New("invalid transaction v, r, s values")
+	errNoSigner   = errors.New("missing signing methods")
 )
 
 // deriveSigner makes a *best* guess about which signer to use.
@@ -58,26 +56,31 @@ type Transaction struct {
 }
 
 type txdata struct {
-	AccountNonce    uint64
-	Price, GasLimit *big.Int
-	Recipient       *common.Address `rlp:"nil"` // nil means contract creation
-	Amount          *big.Int
-	Payload         []byte
-	V               *big.Int // signature
-	R, S            *big.Int // signature
+	AccountNonce uint64          `json:"nonce"`
+	Price        *big.Int        `json:"gasPrice"`
+	GasLimit     *big.Int        `json:"gasLimit"`
+	Recipient    *common.Address `json:"to" optional:"yes" rlp:"nil"` // nil means contract creation
+	Amount       *big.Int        `json:"value"`
+	Payload      []byte          `json:"input"`
+
+	// Signature values
+	V *big.Int `json:"v"`
+	R *big.Int `json:"r"`
+	S *big.Int `json:"s"`
+
+	// This is only used when marshaling to JSON.
+	Hash *common.Hash `json:"hash" optional:"yes" rlp:"-"`
 }
 
-type jsonTransaction struct {
-	Hash         *common.Hash    `json:"hash"`
-	AccountNonce *hexutil.Uint64 `json:"nonce"`
-	Price        *hexutil.Big    `json:"gasPrice"`
-	GasLimit     *hexutil.Big    `json:"gas"`
-	Recipient    *common.Address `json:"to"`
-	Amount       *hexutil.Big    `json:"value"`
-	Payload      *hexutil.Bytes  `json:"input"`
-	V            *hexutil.Big    `json:"v"`
-	R            *hexutil.Big    `json:"r"`
-	S            *hexutil.Big    `json:"s"`
+type txdataMarshaling struct {
+	AccountNonce hexutil.Uint64
+	Price        *hexutil.Big
+	GasLimit     *hexutil.Big
+	Amount       *hexutil.Big
+	Payload      hexutil.Bytes
+	V            *hexutil.Big
+	R            *hexutil.Big
+	S            *hexutil.Big
 }
 
 func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
@@ -164,66 +167,30 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
 	return err
 }
 
-// MarshalJSON encodes transactions into the web3 RPC response block format.
 func (tx *Transaction) MarshalJSON() ([]byte, error) {
 	hash := tx.Hash()
-
-	return json.Marshal(&jsonTransaction{
-		Hash:         &hash,
-		AccountNonce: (*hexutil.Uint64)(&tx.data.AccountNonce),
-		Price:        (*hexutil.Big)(tx.data.Price),
-		GasLimit:     (*hexutil.Big)(tx.data.GasLimit),
-		Recipient:    tx.data.Recipient,
-		Amount:       (*hexutil.Big)(tx.data.Amount),
-		Payload:      (*hexutil.Bytes)(&tx.data.Payload),
-		V:            (*hexutil.Big)(tx.data.V),
-		R:            (*hexutil.Big)(tx.data.R),
-		S:            (*hexutil.Big)(tx.data.S),
-	})
+	data := tx.data
+	data.Hash = &hash
+	return data.MarshalJSON()
 }
 
 // UnmarshalJSON decodes the web3 RPC transaction format.
 func (tx *Transaction) UnmarshalJSON(input []byte) error {
-	var dec jsonTransaction
-	if err := json.Unmarshal(input, &dec); err != nil {
+	var dec txdata
+	if err := dec.UnmarshalJSON(input); err != nil {
 		return err
 	}
-	// Ensure that all fields are set. V, R, S are checked separately because they're a
-	// recent addition to the RPC spec (as of August 2016) and older implementations might
-	// not provide them. Note that Recipient is not checked because it can be missing for
-	// contract creations.
-	if dec.V == nil || dec.R == nil || dec.S == nil {
-		return errMissingTxSignatureFields
-	}
-
 	var V byte
-	if isProtectedV((*big.Int)(dec.V)) {
-		chainId := deriveChainId((*big.Int)(dec.V)).Uint64()
-		V = byte(dec.V.ToInt().Uint64() - 35 - 2*chainId)
+	if isProtectedV(dec.V) {
+		chainId := deriveChainId(dec.V).Uint64()
+		V = byte(dec.V.Uint64() - 35 - 2*chainId)
 	} else {
-		V = byte(((*big.Int)(dec.V)).Uint64() - 27)
+		V = byte(dec.V.Uint64() - 27)
 	}
-	if !crypto.ValidateSignatureValues(V, (*big.Int)(dec.R), (*big.Int)(dec.S), false) {
+	if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) {
 		return ErrInvalidSig
 	}
-
-	if dec.AccountNonce == nil || dec.Price == nil || dec.GasLimit == nil || dec.Amount == nil || dec.Payload == nil {
-		return errMissingTxFields
-	}
-	// Assign the fields. This is not atomic but reusing transactions
-	// for decoding isn't thread safe anyway.
-	*tx = Transaction{}
-	tx.data = txdata{
-		AccountNonce: uint64(*dec.AccountNonce),
-		Recipient:    dec.Recipient,
-		Amount:       (*big.Int)(dec.Amount),
-		GasLimit:     (*big.Int)(dec.GasLimit),
-		Price:        (*big.Int)(dec.Price),
-		Payload:      *dec.Payload,
-		V:            (*big.Int)(dec.V),
-		R:            (*big.Int)(dec.R),
-		S:            (*big.Int)(dec.S),
-	}
+	*tx = Transaction{data: dec}
 	return nil
 }