Эх сурвалжийг харах

Merge pull request #3355 from fjl/hexutil-2

Improve hex encoding/decoding
Péter Szilágyi 9 жил өмнө
parent
commit
8d0108fc5d

+ 5 - 4
accounts/addrcache.go

@@ -225,7 +225,7 @@ func (ac *addrCache) scan() ([]Account, error) {
 		buf     = new(bufio.Reader)
 		addrs   []Account
 		keyJSON struct {
-			Address common.Address `json:"address"`
+			Address string `json:"address"`
 		}
 	)
 	for _, fi := range files {
@@ -241,15 +241,16 @@ func (ac *addrCache) scan() ([]Account, error) {
 		}
 		buf.Reset(fd)
 		// Parse the address.
-		keyJSON.Address = common.Address{}
+		keyJSON.Address = ""
 		err = json.NewDecoder(buf).Decode(&keyJSON)
+		addr := common.HexToAddress(keyJSON.Address)
 		switch {
 		case err != nil:
 			glog.V(logger.Debug).Infof("can't decode key %s: %v", path, err)
-		case (keyJSON.Address == common.Address{}):
+		case (addr == common.Address{}):
 			glog.V(logger.Debug).Infof("can't decode key %s: missing or zero address", path)
 		default:
-			addrs = append(addrs, Account{Address: keyJSON.Address, File: path})
+			addrs = append(addrs, Account{Address: addr, File: path})
 		}
 		fd.Close()
 	}

+ 232 - 0
common/hexutil/hexutil.go

@@ -0,0 +1,232 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+/*
+Package hexutil implements hex encoding with 0x prefix.
+This encoding is used by the Ethereum RPC API to transport binary data in JSON payloads.
+
+Encoding Rules
+
+All hex data must have prefix "0x".
+
+For byte slices, the hex data must be of even length. An empty byte slice
+encodes as "0x".
+
+Integers are encoded using the least amount of digits (no leading zero digits). Their
+encoding may be of uneven length. The number zero encodes as "0x0".
+*/
+package hexutil
+
+import (
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"math/big"
+	"strconv"
+)
+
+const uintBits = 32 << (uint64(^uint(0)) >> 63)
+
+var (
+	ErrEmptyString   = errors.New("empty hex string")
+	ErrMissingPrefix = errors.New("missing 0x prefix for hex data")
+	ErrSyntax        = errors.New("invalid hex")
+	ErrEmptyNumber   = errors.New("hex number has no digits after 0x")
+	ErrLeadingZero   = errors.New("hex number has leading zero digits after 0x")
+	ErrOddLength     = errors.New("hex string has odd length")
+	ErrUint64Range   = errors.New("hex number does not fit into 64 bits")
+	ErrUintRange     = fmt.Errorf("hex number does not fit into %d bits", uintBits)
+)
+
+// Decode decodes a hex string with 0x prefix.
+func Decode(input string) ([]byte, error) {
+	if len(input) == 0 {
+		return nil, ErrEmptyString
+	}
+	if !has0xPrefix(input) {
+		return nil, ErrMissingPrefix
+	}
+	return hex.DecodeString(input[2:])
+}
+
+// MustDecode decodes a hex string with 0x prefix. It panics for invalid input.
+func MustDecode(input string) []byte {
+	dec, err := Decode(input)
+	if err != nil {
+		panic(err)
+	}
+	return dec
+}
+
+// Encode encodes b as a hex string with 0x prefix.
+func Encode(b []byte) string {
+	enc := make([]byte, len(b)*2+2)
+	copy(enc, "0x")
+	hex.Encode(enc[2:], b)
+	return string(enc)
+}
+
+// DecodeUint64 decodes a hex string with 0x prefix as a quantity.
+func DecodeUint64(input string) (uint64, error) {
+	raw, err := checkNumber(input)
+	if err != nil {
+		return 0, err
+	}
+	dec, err := strconv.ParseUint(raw, 16, 64)
+	if err != nil {
+		err = mapError(err)
+	}
+	return dec, err
+}
+
+// MustDecodeUint64 decodes a hex string with 0x prefix as a quantity.
+// It panics for invalid input.
+func MustDecodeUint64(input string) uint64 {
+	dec, err := DecodeUint64(input)
+	if err != nil {
+		panic(err)
+	}
+	return dec
+}
+
+// EncodeUint64 encodes i as a hex string with 0x prefix.
+func EncodeUint64(i uint64) string {
+	enc := make([]byte, 2, 10)
+	copy(enc, "0x")
+	return string(strconv.AppendUint(enc, i, 16))
+}
+
+var bigWordNibbles int
+
+func init() {
+	// This is a weird way to compute the number of nibbles required for big.Word.
+	// The usual way would be to use constant arithmetic but go vet can't handle that.
+	b, _ := new(big.Int).SetString("FFFFFFFFFF", 16)
+	switch len(b.Bits()) {
+	case 1:
+		bigWordNibbles = 16
+	case 2:
+		bigWordNibbles = 8
+	default:
+		panic("weird big.Word size")
+	}
+}
+
+// DecodeBig decodes a hex string with 0x prefix as a quantity.
+func DecodeBig(input string) (*big.Int, error) {
+	raw, err := checkNumber(input)
+	if err != nil {
+		return nil, err
+	}
+	words := make([]big.Word, len(raw)/bigWordNibbles+1)
+	end := len(raw)
+	for i := range words {
+		start := end - bigWordNibbles
+		if start < 0 {
+			start = 0
+		}
+		for ri := start; ri < end; ri++ {
+			nib := decodeNibble(raw[ri])
+			if nib == badNibble {
+				return nil, ErrSyntax
+			}
+			words[i] *= 16
+			words[i] += big.Word(nib)
+		}
+		end = start
+	}
+	dec := new(big.Int).SetBits(words)
+	return dec, nil
+}
+
+// MustDecodeBig decodes a hex string with 0x prefix as a quantity.
+// It panics for invalid input.
+func MustDecodeBig(input string) *big.Int {
+	dec, err := DecodeBig(input)
+	if err != nil {
+		panic(err)
+	}
+	return dec
+}
+
+// EncodeBig encodes bigint as a hex string with 0x prefix.
+// The sign of the integer is ignored.
+func EncodeBig(bigint *big.Int) string {
+	nbits := bigint.BitLen()
+	if nbits == 0 {
+		return "0x0"
+	}
+	enc := make([]byte, 2, (nbits/8)*2+2)
+	copy(enc, "0x")
+	for i := len(bigint.Bits()) - 1; i >= 0; i-- {
+		enc = strconv.AppendUint(enc, uint64(bigint.Bits()[i]), 16)
+	}
+	return string(enc)
+}
+
+func has0xPrefix(input string) bool {
+	return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
+}
+
+func checkNumber(input string) (raw string, err error) {
+	if len(input) == 0 {
+		return "", ErrEmptyString
+	}
+	if !has0xPrefix(input) {
+		return "", ErrMissingPrefix
+	}
+	input = input[2:]
+	if len(input) == 0 {
+		return "", ErrEmptyNumber
+	}
+	if len(input) > 1 && input[0] == '0' {
+		return "", ErrLeadingZero
+	}
+	return input, nil
+}
+
+const badNibble = ^uint64(0)
+
+func decodeNibble(in byte) uint64 {
+	switch {
+	case in >= '0' && in <= '9':
+		return uint64(in - '0')
+	case in >= 'A' && in <= 'F':
+		return uint64(in - 'A' + 10)
+	case in >= 'a' && in <= 'f':
+		return uint64(in - 'a' + 10)
+	default:
+		return badNibble
+	}
+}
+
+func mapError(err error) error {
+	if err, ok := err.(*strconv.NumError); ok {
+		switch err.Err {
+		case strconv.ErrRange:
+			return ErrUint64Range
+		case strconv.ErrSyntax:
+			return ErrSyntax
+		}
+	}
+	if _, ok := err.(hex.InvalidByteError); ok {
+		return ErrSyntax
+	}
+	if err == hex.ErrLength {
+		return ErrOddLength
+	}
+	return err
+}

+ 186 - 0
common/hexutil/hexutil_test.go

@@ -0,0 +1,186 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package hexutil
+
+import (
+	"bytes"
+	"encoding/hex"
+	"math/big"
+	"testing"
+)
+
+type marshalTest struct {
+	input interface{}
+	want  string
+}
+
+type unmarshalTest struct {
+	input   string
+	want    interface{}
+	wantErr error
+}
+
+var (
+	encodeBytesTests = []marshalTest{
+		{[]byte{}, "0x"},
+		{[]byte{0}, "0x00"},
+		{[]byte{0, 0, 1, 2}, "0x00000102"},
+	}
+
+	encodeBigTests = []marshalTest{
+		{referenceBig("0"), "0x0"},
+		{referenceBig("1"), "0x1"},
+		{referenceBig("ff"), "0xff"},
+		{referenceBig("112233445566778899aabbccddeeff"), "0x112233445566778899aabbccddeeff"},
+	}
+
+	encodeUint64Tests = []marshalTest{
+		{uint64(0), "0x0"},
+		{uint64(1), "0x1"},
+		{uint64(0xff), "0xff"},
+		{uint64(0x1122334455667788), "0x1122334455667788"},
+	}
+
+	decodeBytesTests = []unmarshalTest{
+		// invalid
+		{input: ``, wantErr: ErrEmptyString},
+		{input: `0`, wantErr: ErrMissingPrefix},
+		{input: `0x0`, wantErr: hex.ErrLength},
+		{input: `0x023`, wantErr: hex.ErrLength},
+		{input: `0xxx`, wantErr: hex.InvalidByteError('x')},
+		{input: `0x01zz01`, wantErr: hex.InvalidByteError('z')},
+		// valid
+		{input: `0x`, want: []byte{}},
+		{input: `0X`, want: []byte{}},
+		{input: `0x02`, want: []byte{0x02}},
+		{input: `0X02`, want: []byte{0x02}},
+		{input: `0xffffffffff`, want: []byte{0xff, 0xff, 0xff, 0xff, 0xff}},
+		{
+			input: `0xffffffffffffffffffffffffffffffffffff`,
+			want:  []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		},
+	}
+
+	decodeBigTests = []unmarshalTest{
+		// invalid
+		{input: `0`, wantErr: ErrMissingPrefix},
+		{input: `0x`, wantErr: ErrEmptyNumber},
+		{input: `0x01`, wantErr: ErrLeadingZero},
+		{input: `0xx`, wantErr: ErrSyntax},
+		{input: `0x1zz01`, wantErr: ErrSyntax},
+		// valid
+		{input: `0x0`, want: big.NewInt(0)},
+		{input: `0x2`, want: big.NewInt(0x2)},
+		{input: `0x2F2`, want: big.NewInt(0x2f2)},
+		{input: `0X2F2`, want: big.NewInt(0x2f2)},
+		{input: `0x1122aaff`, want: big.NewInt(0x1122aaff)},
+		{input: `0xbBb`, want: big.NewInt(0xbbb)},
+		{input: `0xfffffffff`, want: big.NewInt(0xfffffffff)},
+		{
+			input: `0x112233445566778899aabbccddeeff`,
+			want:  referenceBig("112233445566778899aabbccddeeff"),
+		},
+		{
+			input: `0xffffffffffffffffffffffffffffffffffff`,
+			want:  referenceBig("ffffffffffffffffffffffffffffffffffff"),
+		},
+	}
+
+	decodeUint64Tests = []unmarshalTest{
+		// invalid
+		{input: `0`, wantErr: ErrMissingPrefix},
+		{input: `0x`, wantErr: ErrEmptyNumber},
+		{input: `0x01`, wantErr: ErrLeadingZero},
+		{input: `0xfffffffffffffffff`, wantErr: ErrUintRange},
+		{input: `0xx`, wantErr: ErrSyntax},
+		{input: `0x1zz01`, wantErr: ErrSyntax},
+		// valid
+		{input: `0x0`, want: uint64(0)},
+		{input: `0x2`, want: uint64(0x2)},
+		{input: `0x2F2`, want: uint64(0x2f2)},
+		{input: `0X2F2`, want: uint64(0x2f2)},
+		{input: `0x1122aaff`, want: uint64(0x1122aaff)},
+		{input: `0xbbb`, want: uint64(0xbbb)},
+		{input: `0xffffffffffffffff`, want: uint64(0xffffffffffffffff)},
+	}
+)
+
+func TestEncode(t *testing.T) {
+	for _, test := range encodeBytesTests {
+		enc := Encode(test.input.([]byte))
+		if enc != test.want {
+			t.Errorf("input %x: wrong encoding %s", test.input, enc)
+		}
+	}
+}
+
+func TestDecode(t *testing.T) {
+	for _, test := range decodeBytesTests {
+		dec, err := Decode(test.input)
+		if !checkError(t, test.input, err, test.wantErr) {
+			continue
+		}
+		if !bytes.Equal(test.want.([]byte), dec) {
+			t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
+			continue
+		}
+	}
+}
+
+func TestEncodeBig(t *testing.T) {
+	for _, test := range encodeBigTests {
+		enc := EncodeBig(test.input.(*big.Int))
+		if enc != test.want {
+			t.Errorf("input %x: wrong encoding %s", test.input, enc)
+		}
+	}
+}
+
+func TestDecodeBig(t *testing.T) {
+	for _, test := range decodeBigTests {
+		dec, err := DecodeBig(test.input)
+		if !checkError(t, test.input, err, test.wantErr) {
+			continue
+		}
+		if dec.Cmp(test.want.(*big.Int)) != 0 {
+			t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
+			continue
+		}
+	}
+}
+
+func TestEncodeUint64(t *testing.T) {
+	for _, test := range encodeUint64Tests {
+		enc := EncodeUint64(test.input.(uint64))
+		if enc != test.want {
+			t.Errorf("input %x: wrong encoding %s", test.input, enc)
+		}
+	}
+}
+
+func TestDecodeUint64(t *testing.T) {
+	for _, test := range decodeUint64Tests {
+		dec, err := DecodeUint64(test.input)
+		if !checkError(t, test.input, err, test.wantErr) {
+			continue
+		}
+		if dec != test.want.(uint64) {
+			t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
+			continue
+		}
+	}
+}

+ 271 - 0
common/hexutil/json.go

@@ -0,0 +1,271 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package hexutil
+
+import (
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"math/big"
+	"strconv"
+)
+
+var (
+	jsonNull          = []byte("null")
+	jsonZero          = []byte(`"0x0"`)
+	errNonString      = errors.New("cannot unmarshal non-string as hex data")
+	errNegativeBigInt = errors.New("hexutil.Big: can't marshal negative integer")
+)
+
+// Bytes marshals/unmarshals as a JSON string with 0x prefix.
+// The empty slice marshals as "0x".
+type Bytes []byte
+
+// MarshalJSON implements json.Marshaler.
+func (b Bytes) MarshalJSON() ([]byte, error) {
+	result := make([]byte, len(b)*2+4)
+	copy(result, `"0x`)
+	hex.Encode(result[3:], b)
+	result[len(result)-1] = '"'
+	return result, nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *Bytes) UnmarshalJSON(input []byte) error {
+	raw, err := checkJSON(input)
+	if err != nil {
+		return err
+	}
+	dec := make([]byte, len(raw)/2)
+	if _, err = hex.Decode(dec, raw); err != nil {
+		err = mapError(err)
+	} else {
+		*b = dec
+	}
+	return err
+}
+
+// String returns the hex encoding of b.
+func (b Bytes) String() string {
+	return Encode(b)
+}
+
+// UnmarshalJSON decodes input as a JSON string with 0x prefix. The length of out
+// determines the required input length. This function is commonly used to implement the
+// UnmarshalJSON method for fixed-size types:
+//
+//     type Foo [8]byte
+//
+//     func (f *Foo) UnmarshalJSON(input []byte) error {
+//         return hexutil.UnmarshalJSON("Foo", input, f[:])
+//     }
+func UnmarshalJSON(typname string, input, out []byte) error {
+	raw, err := checkJSON(input)
+	if err != nil {
+		return err
+	}
+	if len(raw)/2 != len(out) {
+		return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname)
+	}
+	// Pre-verify syntax before modifying out.
+	for _, b := range raw {
+		if decodeNibble(b) == badNibble {
+			return ErrSyntax
+		}
+	}
+	hex.Decode(out, raw)
+	return nil
+}
+
+// Big marshals/unmarshals as a JSON string with 0x prefix. The zero value marshals as
+// "0x0". Negative integers are not supported at this time. Attempting to marshal them
+// will return an error.
+type Big big.Int
+
+// MarshalJSON implements json.Marshaler.
+func (b *Big) MarshalJSON() ([]byte, error) {
+	if b == nil {
+		return jsonNull, nil
+	}
+	bigint := (*big.Int)(b)
+	if bigint.Sign() == -1 {
+		return nil, errNegativeBigInt
+	}
+	nbits := bigint.BitLen()
+	if nbits == 0 {
+		return jsonZero, nil
+	}
+	enc := make([]byte, 3, (nbits/8)*2+4)
+	copy(enc, `"0x`)
+	for i := len(bigint.Bits()) - 1; i >= 0; i-- {
+		enc = strconv.AppendUint(enc, uint64(bigint.Bits()[i]), 16)
+	}
+	enc = append(enc, '"')
+	return enc, nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *Big) UnmarshalJSON(input []byte) error {
+	raw, err := checkNumberJSON(input)
+	if err != nil {
+		return err
+	}
+	words := make([]big.Word, len(raw)/bigWordNibbles+1)
+	end := len(raw)
+	for i := range words {
+		start := end - bigWordNibbles
+		if start < 0 {
+			start = 0
+		}
+		for ri := start; ri < end; ri++ {
+			nib := decodeNibble(raw[ri])
+			if nib == badNibble {
+				return ErrSyntax
+			}
+			words[i] *= 16
+			words[i] += big.Word(nib)
+		}
+		end = start
+	}
+	var dec big.Int
+	dec.SetBits(words)
+	*b = (Big)(dec)
+	return nil
+}
+
+// ToInt converts b to a big.Int.
+func (b *Big) ToInt() *big.Int {
+	return (*big.Int)(b)
+}
+
+// String returns the hex encoding of b.
+func (b *Big) String() string {
+	return EncodeBig(b.ToInt())
+}
+
+// Uint64 marshals/unmarshals as a JSON string with 0x prefix.
+// The zero value marshals as "0x0".
+type Uint64 uint64
+
+// MarshalJSON implements json.Marshaler.
+func (b Uint64) MarshalJSON() ([]byte, error) {
+	buf := make([]byte, 3, 12)
+	copy(buf, `"0x`)
+	buf = strconv.AppendUint(buf, uint64(b), 16)
+	buf = append(buf, '"')
+	return buf, nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *Uint64) UnmarshalJSON(input []byte) error {
+	raw, err := checkNumberJSON(input)
+	if err != nil {
+		return err
+	}
+	if len(raw) > 16 {
+		return ErrUint64Range
+	}
+	var dec uint64
+	for _, byte := range raw {
+		nib := decodeNibble(byte)
+		if nib == badNibble {
+			return ErrSyntax
+		}
+		dec *= 16
+		dec += uint64(nib)
+	}
+	*b = Uint64(dec)
+	return nil
+}
+
+// String returns the hex encoding of b.
+func (b Uint64) String() string {
+	return EncodeUint64(uint64(b))
+}
+
+// Uint marshals/unmarshals as a JSON string with 0x prefix.
+// The zero value marshals as "0x0".
+type Uint uint
+
+// MarshalJSON implements json.Marshaler.
+func (b Uint) MarshalJSON() ([]byte, error) {
+	return Uint64(b).MarshalJSON()
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *Uint) UnmarshalJSON(input []byte) error {
+	var u64 Uint64
+	err := u64.UnmarshalJSON(input)
+	if err != nil {
+		return err
+	} else if u64 > Uint64(^uint(0)) {
+		return ErrUintRange
+	}
+	*b = Uint(u64)
+	return nil
+}
+
+// String returns the hex encoding of b.
+func (b Uint) String() string {
+	return EncodeUint64(uint64(b))
+}
+
+func isString(input []byte) bool {
+	return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"'
+}
+
+func bytesHave0xPrefix(input []byte) bool {
+	return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
+}
+
+func checkJSON(input []byte) (raw []byte, err error) {
+	if !isString(input) {
+		return nil, errNonString
+	}
+	if len(input) == 2 {
+		return nil, ErrEmptyString
+	}
+	if !bytesHave0xPrefix(input[1:]) {
+		return nil, ErrMissingPrefix
+	}
+	input = input[3 : len(input)-1]
+	if len(input)%2 != 0 {
+		return nil, ErrOddLength
+	}
+	return input, nil
+}
+
+func checkNumberJSON(input []byte) (raw []byte, err error) {
+	if !isString(input) {
+		return nil, errNonString
+	}
+	input = input[1 : len(input)-1]
+	if len(input) == 0 {
+		return nil, ErrEmptyString
+	}
+	if !bytesHave0xPrefix(input) {
+		return nil, ErrMissingPrefix
+	}
+	input = input[2:]
+	if len(input) == 0 {
+		return nil, ErrEmptyNumber
+	}
+	if len(input) > 1 && input[0] == '0' {
+		return nil, ErrLeadingZero
+	}
+	return input, nil
+}

+ 258 - 0
common/hexutil/json_test.go

@@ -0,0 +1,258 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package hexutil
+
+import (
+	"bytes"
+	"encoding/hex"
+	"math/big"
+	"testing"
+)
+
+func checkError(t *testing.T, input string, got, want error) bool {
+	if got == nil {
+		if want != nil {
+			t.Errorf("input %s: got no error, want %q", input, want)
+			return false
+		}
+		return true
+	}
+	if want == nil {
+		t.Errorf("input %s: unexpected error %q", input, got)
+	} else if got.Error() != want.Error() {
+		t.Errorf("input %s: got error %q, want %q", input, got, want)
+	}
+	return false
+}
+
+func referenceBig(s string) *big.Int {
+	b, ok := new(big.Int).SetString(s, 16)
+	if !ok {
+		panic("invalid")
+	}
+	return b
+}
+
+func referenceBytes(s string) []byte {
+	b, err := hex.DecodeString(s)
+	if err != nil {
+		panic(err)
+	}
+	return b
+}
+
+var unmarshalBytesTests = []unmarshalTest{
+	// invalid encoding
+	{input: "", wantErr: errNonString},
+	{input: "null", wantErr: errNonString},
+	{input: "10", wantErr: errNonString},
+	{input: `""`, wantErr: ErrEmptyString},
+	{input: `"0"`, wantErr: ErrMissingPrefix},
+	{input: `"0x0"`, wantErr: ErrOddLength},
+	{input: `"0xxx"`, wantErr: ErrSyntax},
+	{input: `"0x01zz01"`, wantErr: ErrSyntax},
+
+	// valid encoding
+	{input: `"0x"`, want: referenceBytes("")},
+	{input: `"0x02"`, want: referenceBytes("02")},
+	{input: `"0X02"`, want: referenceBytes("02")},
+	{input: `"0xffffffffff"`, want: referenceBytes("ffffffffff")},
+	{
+		input: `"0xffffffffffffffffffffffffffffffffffff"`,
+		want:  referenceBytes("ffffffffffffffffffffffffffffffffffff"),
+	},
+}
+
+func TestUnmarshalBytes(t *testing.T) {
+	for _, test := range unmarshalBytesTests {
+		var v Bytes
+		err := v.UnmarshalJSON([]byte(test.input))
+		if !checkError(t, test.input, err, test.wantErr) {
+			continue
+		}
+		if !bytes.Equal(test.want.([]byte), []byte(v)) {
+			t.Errorf("input %s: value mismatch: got %x, want %x", test.input, &v, test.want)
+			continue
+		}
+	}
+}
+
+func BenchmarkUnmarshalBytes(b *testing.B) {
+	input := []byte(`"0x123456789abcdef123456789abcdef"`)
+	for i := 0; i < b.N; i++ {
+		var v Bytes
+		if err := v.UnmarshalJSON(input); err != nil {
+			b.Fatal(err)
+		}
+	}
+}
+
+func TestMarshalBytes(t *testing.T) {
+	for _, test := range encodeBytesTests {
+		in := test.input.([]byte)
+		out, err := Bytes(in).MarshalJSON()
+		if err != nil {
+			t.Errorf("%x: %v", in, err)
+			continue
+		}
+		if want := `"` + test.want + `"`; string(out) != want {
+			t.Errorf("%x: MarshalJSON output mismatch: got %q, want %q", in, out, want)
+			continue
+		}
+		if out := Bytes(in).String(); out != test.want {
+			t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
+			continue
+		}
+	}
+}
+
+var unmarshalBigTests = []unmarshalTest{
+	// invalid encoding
+	{input: "", wantErr: errNonString},
+	{input: "null", wantErr: errNonString},
+	{input: "10", wantErr: errNonString},
+	{input: `""`, wantErr: ErrEmptyString},
+	{input: `"0"`, wantErr: ErrMissingPrefix},
+	{input: `"0x"`, wantErr: ErrEmptyNumber},
+	{input: `"0x01"`, wantErr: ErrLeadingZero},
+	{input: `"0xx"`, wantErr: ErrSyntax},
+	{input: `"0x1zz01"`, wantErr: ErrSyntax},
+
+	// valid encoding
+	{input: `"0x0"`, want: big.NewInt(0)},
+	{input: `"0x2"`, want: big.NewInt(0x2)},
+	{input: `"0x2F2"`, want: big.NewInt(0x2f2)},
+	{input: `"0X2F2"`, want: big.NewInt(0x2f2)},
+	{input: `"0x1122aaff"`, want: big.NewInt(0x1122aaff)},
+	{input: `"0xbBb"`, want: big.NewInt(0xbbb)},
+	{input: `"0xfffffffff"`, want: big.NewInt(0xfffffffff)},
+	{
+		input: `"0x112233445566778899aabbccddeeff"`,
+		want:  referenceBig("112233445566778899aabbccddeeff"),
+	},
+	{
+		input: `"0xffffffffffffffffffffffffffffffffffff"`,
+		want:  referenceBig("ffffffffffffffffffffffffffffffffffff"),
+	},
+}
+
+func TestUnmarshalBig(t *testing.T) {
+	for _, test := range unmarshalBigTests {
+		var v Big
+		err := v.UnmarshalJSON([]byte(test.input))
+		if !checkError(t, test.input, err, test.wantErr) {
+			continue
+		}
+		if test.want != nil && test.want.(*big.Int).Cmp((*big.Int)(&v)) != 0 {
+			t.Errorf("input %s: value mismatch: got %x, want %x", test.input, (*big.Int)(&v), test.want)
+			continue
+		}
+	}
+}
+
+func BenchmarkUnmarshalBig(b *testing.B) {
+	input := []byte(`"0x123456789abcdef123456789abcdef"`)
+	for i := 0; i < b.N; i++ {
+		var v Big
+		if err := v.UnmarshalJSON(input); err != nil {
+			b.Fatal(err)
+		}
+	}
+}
+
+func TestMarshalBig(t *testing.T) {
+	for _, test := range encodeBigTests {
+		in := test.input.(*big.Int)
+		out, err := (*Big)(in).MarshalJSON()
+		if err != nil {
+			t.Errorf("%d: %v", in, err)
+			continue
+		}
+		if want := `"` + test.want + `"`; string(out) != want {
+			t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want)
+			continue
+		}
+		if out := (*Big)(in).String(); out != test.want {
+			t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
+			continue
+		}
+	}
+}
+
+var unmarshalUint64Tests = []unmarshalTest{
+	// invalid encoding
+	{input: "", wantErr: errNonString},
+	{input: "null", wantErr: errNonString},
+	{input: "10", wantErr: errNonString},
+	{input: `""`, wantErr: ErrEmptyString},
+	{input: `"0"`, wantErr: ErrMissingPrefix},
+	{input: `"0x"`, wantErr: ErrEmptyNumber},
+	{input: `"0x01"`, wantErr: ErrLeadingZero},
+	{input: `"0xfffffffffffffffff"`, wantErr: ErrUintRange},
+	{input: `"0xx"`, wantErr: ErrSyntax},
+	{input: `"0x1zz01"`, wantErr: ErrSyntax},
+
+	// valid encoding
+	{input: `"0x0"`, want: uint64(0)},
+	{input: `"0x2"`, want: uint64(0x2)},
+	{input: `"0x2F2"`, want: uint64(0x2f2)},
+	{input: `"0X2F2"`, want: uint64(0x2f2)},
+	{input: `"0x1122aaff"`, want: uint64(0x1122aaff)},
+	{input: `"0xbbb"`, want: uint64(0xbbb)},
+	{input: `"0xffffffffffffffff"`, want: uint64(0xffffffffffffffff)},
+}
+
+func TestUnmarshalUint64(t *testing.T) {
+	for _, test := range unmarshalUint64Tests {
+		var v Uint64
+		err := v.UnmarshalJSON([]byte(test.input))
+		if !checkError(t, test.input, err, test.wantErr) {
+			continue
+		}
+		if uint64(v) != test.want.(uint64) {
+			t.Errorf("input %s: value mismatch: got %d, want %d", test.input, v, test.want)
+			continue
+		}
+	}
+}
+
+func BenchmarkUnmarshalUint64(b *testing.B) {
+	input := []byte(`"0x123456789abcdf"`)
+	for i := 0; i < b.N; i++ {
+		var v Uint64
+		v.UnmarshalJSON(input)
+	}
+}
+
+func TestMarshalUint64(t *testing.T) {
+	for _, test := range encodeUint64Tests {
+		in := test.input.(uint64)
+		out, err := Uint64(in).MarshalJSON()
+		if err != nil {
+			t.Errorf("%d: %v", in, err)
+			continue
+		}
+		if want := `"` + test.want + `"`; string(out) != want {
+			t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want)
+			continue
+		}
+		if out := (Uint64)(in).String(); out != test.want {
+			t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
+			continue
+		}
+	}
+}

+ 9 - 49
common/types.go

@@ -17,14 +17,12 @@
 package common
 
 import (
-	"encoding/hex"
-	"encoding/json"
-	"errors"
 	"fmt"
 	"math/big"
 	"math/rand"
 	"reflect"
-	"strings"
+
+	"github.com/ethereum/go-ethereum/common/hexutil"
 )
 
 const (
@@ -32,8 +30,6 @@ const (
 	AddressLength = 20
 )
 
-var hashJsonLengthErr = errors.New("common: unmarshalJSON failed: hash must be exactly 32 bytes")
-
 type (
 	// Hash represents the 32 byte Keccak256 hash of arbitrary data.
 	Hash [HashLength]byte
@@ -57,30 +53,16 @@ func HexToHash(s string) Hash    { return BytesToHash(FromHex(s)) }
 func (h Hash) Str() string   { return string(h[:]) }
 func (h Hash) Bytes() []byte { return h[:] }
 func (h Hash) Big() *big.Int { return Bytes2Big(h[:]) }
-func (h Hash) Hex() string   { return "0x" + Bytes2Hex(h[:]) }
+func (h Hash) Hex() string   { return hexutil.Encode(h[:]) }
 
 // UnmarshalJSON parses a hash in its hex from to a hash.
 func (h *Hash) UnmarshalJSON(input []byte) error {
-	length := len(input)
-	if length >= 2 && input[0] == '"' && input[length-1] == '"' {
-		input = input[1 : length-1]
-	}
-	// strip "0x" for length check
-	if len(input) > 1 && strings.ToLower(string(input[:2])) == "0x" {
-		input = input[2:]
-	}
-
-	// validate the length of the input hash
-	if len(input) != HashLength*2 {
-		return hashJsonLengthErr
-	}
-	h.SetBytes(FromHex(string(input)))
-	return nil
+	return hexutil.UnmarshalJSON("Hash", input, h[:])
 }
 
 // Serialize given hash to JSON
 func (h Hash) MarshalJSON() ([]byte, error) {
-	return json.Marshal(h.Hex())
+	return hexutil.Bytes(h[:]).MarshalJSON()
 }
 
 // Sets the hash to the value of b. If b is larger than len(h) it will panic
@@ -142,7 +124,7 @@ func (a Address) Str() string   { return string(a[:]) }
 func (a Address) Bytes() []byte { return a[:] }
 func (a Address) Big() *big.Int { return Bytes2Big(a[:]) }
 func (a Address) Hash() Hash    { return BytesToHash(a[:]) }
-func (a Address) Hex() string   { return "0x" + Bytes2Hex(a[:]) }
+func (a Address) Hex() string   { return hexutil.Encode(a[:]) }
 
 // Sets the address to the value of b. If b is larger than len(a) it will panic
 func (a *Address) SetBytes(b []byte) {
@@ -164,34 +146,12 @@ func (a *Address) Set(other Address) {
 
 // Serialize given address to JSON
 func (a Address) MarshalJSON() ([]byte, error) {
-	return json.Marshal(a.Hex())
+	return hexutil.Bytes(a[:]).MarshalJSON()
 }
 
 // Parse address from raw json data
-func (a *Address) UnmarshalJSON(data []byte) error {
-	if len(data) > 2 && data[0] == '"' && data[len(data)-1] == '"' {
-		data = data[1 : len(data)-1]
-	}
-
-	if len(data) > 2 && data[0] == '0' && data[1] == 'x' {
-		data = data[2:]
-	}
-
-	if len(data) != 2*AddressLength {
-		return fmt.Errorf("Invalid address length, expected %d got %d bytes", 2*AddressLength, len(data))
-	}
-
-	n, err := hex.Decode(a[:], data)
-	if err != nil {
-		return err
-	}
-
-	if n != AddressLength {
-		return fmt.Errorf("Invalid address")
-	}
-
-	a.Set(HexToAddress(string(data)))
-	return nil
+func (a *Address) UnmarshalJSON(input []byte) error {
+	return hexutil.UnmarshalJSON("Address", input, a[:])
 }
 
 // PP Pretty Prints a byte slice in the following format:

+ 21 - 11
common/types_test.go

@@ -18,7 +18,10 @@ package common
 
 import (
 	"math/big"
+	"strings"
 	"testing"
+
+	"github.com/ethereum/go-ethereum/common/hexutil"
 )
 
 func TestBytesConversion(t *testing.T) {
@@ -38,19 +41,26 @@ func TestHashJsonValidation(t *testing.T) {
 	var tests = []struct {
 		Prefix string
 		Size   int
-		Error  error
+		Error  string
 	}{
-		{"", 2, hashJsonLengthErr},
-		{"", 62, hashJsonLengthErr},
-		{"", 66, hashJsonLengthErr},
-		{"", 65, hashJsonLengthErr},
-		{"0X", 64, nil},
-		{"0x", 64, nil},
-		{"0x", 62, hashJsonLengthErr},
+		{"", 62, hexutil.ErrMissingPrefix.Error()},
+		{"0x", 66, "hex string has length 66, want 64 for Hash"},
+		{"0x", 63, hexutil.ErrOddLength.Error()},
+		{"0x", 0, "hex string has length 0, want 64 for Hash"},
+		{"0x", 64, ""},
+		{"0X", 64, ""},
 	}
-	for i, test := range tests {
-		if err := h.UnmarshalJSON(append([]byte(test.Prefix), make([]byte, test.Size)...)); err != test.Error {
-			t.Errorf("test #%d: error mismatch: have %v, want %v", i, err, test.Error)
+	for _, test := range tests {
+		input := `"` + test.Prefix + strings.Repeat("0", test.Size) + `"`
+		err := h.UnmarshalJSON([]byte(input))
+		if err == nil {
+			if test.Error != "" {
+				t.Errorf("%s: error mismatch: have nil, want %q", input, test.Error)
+			}
+		} else {
+			if err.Error() != test.Error {
+				t.Errorf("%s: error mismatch: have %q, want %q", input, err, test.Error)
+			}
 		}
 	}
 }

+ 15 - 22
core/types/block.go

@@ -29,6 +29,7 @@ import (
 	"time"
 
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
 	"github.com/ethereum/go-ethereum/crypto/sha3"
 	"github.com/ethereum/go-ethereum/rlp"
 )
@@ -63,20 +64,12 @@ func (n BlockNonce) Uint64() uint64 {
 
 // MarshalJSON implements json.Marshaler
 func (n BlockNonce) MarshalJSON() ([]byte, error) {
-	return []byte(fmt.Sprintf(`"0x%x"`, n)), nil
+	return hexutil.Bytes(n[:]).MarshalJSON()
 }
 
 // UnmarshalJSON implements json.Unmarshaler
 func (n *BlockNonce) UnmarshalJSON(input []byte) error {
-	var b hexBytes
-	if err := b.UnmarshalJSON(input); err != nil {
-		return err
-	}
-	if len(b) != 8 {
-		return errBadNonceSize
-	}
-	copy((*n)[:], b)
-	return nil
+	return hexutil.UnmarshalJSON("BlockNonce", input, n[:])
 }
 
 // Header represents a block header in the Ethereum blockchain.
@@ -106,12 +99,12 @@ type jsonHeader struct {
 	TxHash      *common.Hash    `json:"transactionsRoot"`
 	ReceiptHash *common.Hash    `json:"receiptsRoot"`
 	Bloom       *Bloom          `json:"logsBloom"`
-	Difficulty  *hexBig         `json:"difficulty"`
-	Number      *hexBig         `json:"number"`
-	GasLimit    *hexBig         `json:"gasLimit"`
-	GasUsed     *hexBig         `json:"gasUsed"`
-	Time        *hexBig         `json:"timestamp"`
-	Extra       *hexBytes       `json:"extraData"`
+	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"`
 }
@@ -151,12 +144,12 @@ func (h *Header) MarshalJSON() ([]byte, error) {
 		TxHash:      &h.TxHash,
 		ReceiptHash: &h.ReceiptHash,
 		Bloom:       &h.Bloom,
-		Difficulty:  (*hexBig)(h.Difficulty),
-		Number:      (*hexBig)(h.Number),
-		GasLimit:    (*hexBig)(h.GasLimit),
-		GasUsed:     (*hexBig)(h.GasUsed),
-		Time:        (*hexBig)(h.Time),
-		Extra:       (*hexBytes)(&h.Extra),
+		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,
 	})

+ 3 - 10
core/types/bloom9.go

@@ -21,6 +21,7 @@ import (
 	"math/big"
 
 	"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/crypto"
 )
@@ -77,20 +78,12 @@ func (b Bloom) TestBytes(test []byte) bool {
 
 // MarshalJSON encodes b as a hex string with 0x prefix.
 func (b Bloom) MarshalJSON() ([]byte, error) {
-	return []byte(fmt.Sprintf(`"%#x"`, b[:])), nil
+	return hexutil.Bytes(b[:]).MarshalJSON()
 }
 
 // UnmarshalJSON b as a hex string with 0x prefix.
 func (b *Bloom) UnmarshalJSON(input []byte) error {
-	var dec hexBytes
-	if err := dec.UnmarshalJSON(input); err != nil {
-		return err
-	}
-	if len(dec) != bloomLength {
-		return fmt.Errorf("invalid bloom size, want %d bytes", bloomLength)
-	}
-	copy((*b)[:], dec)
-	return nil
+	return hexutil.UnmarshalJSON("Bloom", input, b[:])
 }
 
 func CreateBloom(receipts Receipts) Bloom {

+ 0 - 108
core/types/json.go

@@ -1,108 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package types
-
-import (
-	"encoding/hex"
-	"fmt"
-	"math/big"
-)
-
-// JSON unmarshaling utilities.
-
-type hexBytes []byte
-
-func (b *hexBytes) MarshalJSON() ([]byte, error) {
-	if b != nil {
-		return []byte(fmt.Sprintf(`"0x%x"`, []byte(*b))), nil
-	}
-	return nil, nil
-}
-
-func (b *hexBytes) UnmarshalJSON(input []byte) error {
-	if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' {
-		return fmt.Errorf("cannot unmarshal non-string into hexBytes")
-	}
-	input = input[1 : len(input)-1]
-	if len(input) < 2 || input[0] != '0' || input[1] != 'x' {
-		return fmt.Errorf("missing 0x prefix in hexBytes input %q", input)
-	}
-	dec := make(hexBytes, (len(input)-2)/2)
-	if _, err := hex.Decode(dec, input[2:]); err != nil {
-		return err
-	}
-	*b = dec
-	return nil
-}
-
-type hexBig big.Int
-
-func (b *hexBig) MarshalJSON() ([]byte, error) {
-	if b != nil {
-		return []byte(fmt.Sprintf(`"0x%x"`, (*big.Int)(b))), nil
-	}
-	return nil, nil
-}
-
-func (b *hexBig) UnmarshalJSON(input []byte) error {
-	raw, err := checkHexNumber(input)
-	if err != nil {
-		return err
-	}
-	dec, ok := new(big.Int).SetString(string(raw), 16)
-	if !ok {
-		return fmt.Errorf("invalid hex number")
-	}
-	*b = (hexBig)(*dec)
-	return nil
-}
-
-type hexUint64 uint64
-
-func (b *hexUint64) MarshalJSON() ([]byte, error) {
-	if b != nil {
-		return []byte(fmt.Sprintf(`"0x%x"`, *(*uint64)(b))), nil
-	}
-	return nil, nil
-}
-
-func (b *hexUint64) UnmarshalJSON(input []byte) error {
-	raw, err := checkHexNumber(input)
-	if err != nil {
-		return err
-	}
-	_, err = fmt.Sscanf(string(raw), "%x", b)
-	return err
-}
-
-func checkHexNumber(input []byte) (raw []byte, err error) {
-	if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' {
-		return nil, fmt.Errorf("cannot unmarshal non-string into hex number")
-	}
-	input = input[1 : len(input)-1]
-	if len(input) < 2 || input[0] != '0' || input[1] != 'x' {
-		return nil, fmt.Errorf("missing 0x prefix in hex number input %q", input)
-	}
-	if len(input) == 2 {
-		return nil, fmt.Errorf("empty hex number")
-	}
-	raw = input[2:]
-	if len(raw)%2 != 0 {
-		raw = append([]byte{'0'}, raw...)
-	}
-	return raw, nil
-}

+ 0 - 232
core/types/json_test.go

@@ -1,232 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package types
-
-import (
-	"encoding/json"
-	"reflect"
-	"testing"
-
-	"github.com/ethereum/go-ethereum/common"
-)
-
-var unmarshalHeaderTests = map[string]struct {
-	input     string
-	wantHash  common.Hash
-	wantError error
-}{
-	"block 0x1e2200": {
-		input:    `{"difficulty":"0x311ca98cebfe","extraData":"0x7777772e62772e636f6d","gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","mixHash":"0x1ccfddb506dac5afc09b6f92eb09a043ffc8e08f7592250af57b9c64c20f9b25","nonce":"0x670bd98c79585197","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptsRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
-		wantHash: common.HexToHash("0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48"),
-	},
-	"bad nonce": {
-		input:     `{"difficulty":"0x311ca98cebfe","extraData":"0x7777772e62772e636f6d","gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","mixHash":"0x1ccfddb506dac5afc09b6f92eb09a043ffc8e08f7592250af57b9c64c20f9b25","nonce":"0x670bd98c7958","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptsRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
-		wantError: errBadNonceSize,
-	},
-	"missing mixHash": {
-		input:     `{"difficulty":"0x311ca98cebfe","extraData":"0x7777772e62772e636f6d","gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","nonce":"0x670bd98c79585197","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptsRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
-		wantError: errMissingHeaderMixDigest,
-	},
-	"missing fields": {
-		input:     `{"gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","mixHash":"0x1ccfddb506dac5afc09b6f92eb09a043ffc8e08f7592250af57b9c64c20f9b25","nonce":"0x670bd98c79585197","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptsRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
-		wantError: errMissingHeaderFields,
-	},
-}
-
-func TestUnmarshalHeader(t *testing.T) {
-	for name, test := range unmarshalHeaderTests {
-		var head *Header
-		err := json.Unmarshal([]byte(test.input), &head)
-		if !checkError(t, name, err, test.wantError) {
-			continue
-		}
-		if head.Hash() != test.wantHash {
-			t.Errorf("test %q: got hash %x, want %x", name, head.Hash(), test.wantHash)
-			continue
-		}
-	}
-}
-
-func TestMarshalHeader(t *testing.T) {
-	for name, test := range unmarshalHeaderTests {
-		if test.wantError != nil {
-			continue
-		}
-		var original *Header
-		json.Unmarshal([]byte(test.input), &original)
-
-		blob, err := json.Marshal(original)
-		if err != nil {
-			t.Errorf("test %q: failed to marshal header: %v", name, err)
-			continue
-		}
-		var proced *Header
-		if err := json.Unmarshal(blob, &proced); err != nil {
-			t.Errorf("Test %q: failed to unmarshal marhsalled header: %v", name, err)
-			continue
-		}
-		if !reflect.DeepEqual(original, proced) {
-			t.Errorf("test %q: header mismatch: have %+v, want %+v", name, proced, original)
-			continue
-		}
-	}
-}
-
-var unmarshalTransactionTests = map[string]struct {
-	input     string
-	wantHash  common.Hash
-	wantFrom  common.Address
-	wantError error
-}{
-	"value transfer": {
-		input:    `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x1c","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
-		wantHash: common.HexToHash("0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9"),
-		wantFrom: common.HexToAddress("0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689"),
-	},
-	/* TODO skipping this test as this type can not be tested with the current signing approach
-	"bad signature fields": {
-		input:     `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x58","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
-		wantError: ErrInvalidSig,
-	},
-	*/
-	"missing signature v": {
-		input:     `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
-		wantError: errMissingTxSignatureFields,
-	},
-	"missing signature fields": {
-		input:     `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00"}`,
-		wantError: errMissingTxSignatureFields,
-	},
-	"missing fields": {
-		input:     `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x1c","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
-		wantError: errMissingTxFields,
-	},
-}
-
-func TestUnmarshalTransaction(t *testing.T) {
-	for name, test := range unmarshalTransactionTests {
-		var tx *Transaction
-		err := json.Unmarshal([]byte(test.input), &tx)
-		if !checkError(t, name, err, test.wantError) {
-			continue
-		}
-
-		if tx.Hash() != test.wantHash {
-			t.Errorf("test %q: got hash %x, want %x", name, tx.Hash(), test.wantHash)
-			continue
-		}
-		from, err := Sender(HomesteadSigner{}, tx)
-		if err != nil {
-			t.Errorf("test %q: From error %v", name, err)
-		}
-		if from != test.wantFrom {
-			t.Errorf("test %q: sender mismatch: got %x, want %x", name, from, test.wantFrom)
-		}
-	}
-}
-
-func TestMarshalTransaction(t *testing.T) {
-	for name, test := range unmarshalTransactionTests {
-		if test.wantError != nil {
-			continue
-		}
-		var original *Transaction
-		json.Unmarshal([]byte(test.input), &original)
-
-		blob, err := json.Marshal(original)
-		if err != nil {
-			t.Errorf("test %q: failed to marshal transaction: %v", name, err)
-			continue
-		}
-		var proced *Transaction
-		if err := json.Unmarshal(blob, &proced); err != nil {
-			t.Errorf("Test %q: failed to unmarshal marhsalled transaction: %v", name, err)
-			continue
-		}
-		proced.Hash() // hack private fields to pass deep equal
-		if !reflect.DeepEqual(original, proced) {
-			t.Errorf("test %q: transaction mismatch: have %+v, want %+v", name, proced, original)
-			continue
-		}
-	}
-}
-
-var unmarshalReceiptTests = map[string]struct {
-	input     string
-	wantError error
-}{
-	"ok": {
-		input: `{"blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","blockNumber":"0x1e773b","contractAddress":null,"cumulativeGasUsed":"0x10cea","from":"0xdf21fa922215b1a56f5a6d6294e6e36c85a0acfb","gasUsed":"0xbae2","logs":[{"address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000df21fa922215b1a56f5a6d6294e6e36c85a0acfb","0x00000000000000000000000032be343b94f860124dc4fee278fdcbd38c102d88"],"data":"0x0000000000000000000000000000000000000000000000027cfefc4f3f392700","blockNumber":"0x1e773b","transactionIndex":"0x1","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","logIndex":"0x0"}],"logsBloom":"0x00000000000000020000000000020000000000000000000000000000000000000000000000000000000000000000000000040000000000000100000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000010000000000000000000000000000000000000000000000010000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000002002000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000","root":"0x6e8a06b2dac39ac5c9d4db5fb2a2a94ef7a6e5ec1c554079112112caf162998a","to":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","transactionIndex":"0x1"}`,
-	},
-	"missing post state": {
-		input:     `{"blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","blockNumber":"0x1e773b","contractAddress":null,"cumulativeGasUsed":"0x10cea","from":"0xdf21fa922215b1a56f5a6d6294e6e36c85a0acfb","gasUsed":"0xbae2","logs":[{"address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000df21fa922215b1a56f5a6d6294e6e36c85a0acfb","0x00000000000000000000000032be343b94f860124dc4fee278fdcbd38c102d88"],"data":"0x0000000000000000000000000000000000000000000000027cfefc4f3f392700","blockNumber":"0x1e773b","transactionIndex":"0x1","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","logIndex":"0x0"}],"logsBloom":"0x00000000000000020000000000020000000000000000000000000000000000000000000000000000000000000000000000040000000000000100000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000010000000000000000000000000000000000000000000000010000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000002002000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000","to":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","transactionIndex":"0x1"}`,
-		wantError: errMissingReceiptPostState,
-	},
-	"missing fields": {
-		input:     `{"blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","blockNumber":"0x1e773b","contractAddress":null,"cumulativeGasUsed":"0x10cea","from":"0xdf21fa922215b1a56f5a6d6294e6e36c85a0acfb","gasUsed":"0xbae2","logs":[{"address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000df21fa922215b1a56f5a6d6294e6e36c85a0acfb","0x00000000000000000000000032be343b94f860124dc4fee278fdcbd38c102d88"],"data":"0x0000000000000000000000000000000000000000000000027cfefc4f3f392700","blockNumber":"0x1e773b","transactionIndex":"0x1","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","logIndex":"0x0"}],"logsBloom":"0x00000000000000020000000000020000000000000000000000000000000000000000000000000000000000000000000000040000000000000100000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000010000000000000000000000000000000000000000000000010000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000002002000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000","root":"0x6e8a06b2dac39ac5c9d4db5fb2a2a94ef7a6e5ec1c554079112112caf162998a","to":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413"}`,
-		wantError: errMissingReceiptFields,
-	},
-}
-
-func TestUnmarshalReceipt(t *testing.T) {
-	for name, test := range unmarshalReceiptTests {
-		var r *Receipt
-		err := json.Unmarshal([]byte(test.input), &r)
-		checkError(t, name, err, test.wantError)
-	}
-}
-
-func TestMarshalReceipt(t *testing.T) {
-	for name, test := range unmarshalReceiptTests {
-		if test.wantError != nil {
-			continue
-		}
-		var original *Receipt
-		json.Unmarshal([]byte(test.input), &original)
-
-		blob, err := json.Marshal(original)
-		if err != nil {
-			t.Errorf("test %q: failed to marshal receipt: %v", name, err)
-			continue
-		}
-		var proced *Receipt
-		if err := json.Unmarshal(blob, &proced); err != nil {
-			t.Errorf("Test %q: failed to unmarshal marhsalled receipt: %v", name, err)
-			continue
-		}
-		if !reflect.DeepEqual(original, proced) {
-			t.Errorf("test %q: receipt mismatch: have %+v, want %+v", name, proced, original)
-			continue
-		}
-	}
-}
-
-func checkError(t *testing.T, testname string, got, want error) bool {
-	if got == nil {
-		if want != nil {
-			t.Errorf("test %q: got no error, want %q", testname, want)
-			return false
-		}
-		return true
-	}
-	if want == nil {
-		t.Errorf("test %q: unexpected error %q", testname, got)
-	} else if got.Error() != want.Error() {
-		t.Errorf("test %q: got error %q, want %q", testname, got, want)
-	}
-	return false
-}

+ 5 - 4
core/types/receipt.go

@@ -24,6 +24,7 @@ import (
 	"math/big"
 
 	"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/rlp"
 )
@@ -49,12 +50,12 @@ type Receipt struct {
 
 type jsonReceipt struct {
 	PostState         *common.Hash    `json:"root"`
-	CumulativeGasUsed *hexBig         `json:"cumulativeGasUsed"`
+	CumulativeGasUsed *hexutil.Big    `json:"cumulativeGasUsed"`
 	Bloom             *Bloom          `json:"logsBloom"`
 	Logs              *vm.Logs        `json:"logs"`
 	TxHash            *common.Hash    `json:"transactionHash"`
 	ContractAddress   *common.Address `json:"contractAddress"`
-	GasUsed           *hexBig         `json:"gasUsed"`
+	GasUsed           *hexutil.Big    `json:"gasUsed"`
 }
 
 // NewReceipt creates a barebone transaction receipt, copying the init fields.
@@ -90,12 +91,12 @@ func (r *Receipt) MarshalJSON() ([]byte, error) {
 
 	return json.Marshal(&jsonReceipt{
 		PostState:         &root,
-		CumulativeGasUsed: (*hexBig)(r.CumulativeGasUsed),
+		CumulativeGasUsed: (*hexutil.Big)(r.CumulativeGasUsed),
 		Bloom:             &r.Bloom,
 		Logs:              &r.Logs,
 		TxHash:            &r.TxHash,
 		ContractAddress:   &r.ContractAddress,
-		GasUsed:           (*hexBig)(r.GasUsed),
+		GasUsed:           (*hexutil.Big)(r.GasUsed),
 	})
 }
 

+ 17 - 16
core/types/transaction.go

@@ -27,6 +27,7 @@ import (
 	"sync/atomic"
 
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/params"
 	"github.com/ethereum/go-ethereum/rlp"
@@ -69,15 +70,15 @@ type txdata struct {
 
 type jsonTransaction struct {
 	Hash         *common.Hash    `json:"hash"`
-	AccountNonce *hexUint64      `json:"nonce"`
-	Price        *hexBig         `json:"gasPrice"`
-	GasLimit     *hexBig         `json:"gas"`
+	AccountNonce *hexutil.Uint64 `json:"nonce"`
+	Price        *hexutil.Big    `json:"gasPrice"`
+	GasLimit     *hexutil.Big    `json:"gas"`
 	Recipient    *common.Address `json:"to"`
-	Amount       *hexBig         `json:"value"`
-	Payload      *hexBytes       `json:"input"`
-	V            *hexBig         `json:"v"`
-	R            *hexBig         `json:"r"`
-	S            *hexBig         `json:"s"`
+	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"`
 }
 
 func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
@@ -170,15 +171,15 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
 
 	return json.Marshal(&jsonTransaction{
 		Hash:         &hash,
-		AccountNonce: (*hexUint64)(&tx.data.AccountNonce),
-		Price:        (*hexBig)(tx.data.Price),
-		GasLimit:     (*hexBig)(tx.data.GasLimit),
+		AccountNonce: (*hexutil.Uint64)(&tx.data.AccountNonce),
+		Price:        (*hexutil.Big)(tx.data.Price),
+		GasLimit:     (*hexutil.Big)(tx.data.GasLimit),
 		Recipient:    tx.data.Recipient,
-		Amount:       (*hexBig)(tx.data.Amount),
-		Payload:      (*hexBytes)(&tx.data.Payload),
-		V:            (*hexBig)(tx.data.V),
-		R:            (*hexBig)(tx.data.R),
-		S:            (*hexBig)(tx.data.S),
+		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),
 	})
 }
 

+ 20 - 28
core/vm/log.go

@@ -23,6 +23,7 @@ import (
 	"io"
 
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
 	"github.com/ethereum/go-ethereum/rlp"
 )
 
@@ -47,12 +48,12 @@ type Log struct {
 type jsonLog struct {
 	Address     *common.Address `json:"address"`
 	Topics      *[]common.Hash  `json:"topics"`
-	Data        string          `json:"data"`
-	BlockNumber string          `json:"blockNumber"`
-	TxIndex     string          `json:"transactionIndex"`
+	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       string          `json:"logIndex"`
+	Index       *hexutil.Uint   `json:"logIndex"`
 }
 
 func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *Log {
@@ -85,12 +86,12 @@ func (r *Log) MarshalJSON() ([]byte, error) {
 	return json.Marshal(&jsonLog{
 		Address:     &r.Address,
 		Topics:      &r.Topics,
-		Data:        fmt.Sprintf("0x%x", r.Data),
-		BlockNumber: fmt.Sprintf("0x%x", r.BlockNumber),
-		TxIndex:     fmt.Sprintf("0x%x", r.TxIndex),
+		Data:        (*hexutil.Bytes)(&r.Data),
+		BlockNumber: (*hexutil.Uint64)(&r.BlockNumber),
+		TxIndex:     (*hexutil.Uint)(&r.TxIndex),
 		TxHash:      &r.TxHash,
 		BlockHash:   &r.BlockHash,
-		Index:       fmt.Sprintf("0x%x", r.Index),
+		Index:       (*hexutil.Uint)(&r.Index),
 	})
 }
 
@@ -100,29 +101,20 @@ func (r *Log) UnmarshalJSON(input []byte) error {
 	if err := json.Unmarshal(input, &dec); err != nil {
 		return err
 	}
-	if dec.Address == nil || dec.Topics == nil || dec.Data == "" || dec.BlockNumber == "" ||
-		dec.TxIndex == "" || dec.TxHash == nil || dec.BlockHash == nil || dec.Index == "" {
+	if dec.Address == nil || dec.Topics == nil || dec.Data == nil || dec.BlockNumber == nil ||
+		dec.TxIndex == nil || dec.TxHash == nil || dec.BlockHash == nil || dec.Index == nil {
 		return errMissingLogFields
 	}
-	declog := Log{
-		Address:   *dec.Address,
-		Topics:    *dec.Topics,
-		TxHash:    *dec.TxHash,
-		BlockHash: *dec.BlockHash,
+	*r = Log{
+		Address:     *dec.Address,
+		Topics:      *dec.Topics,
+		Data:        *dec.Data,
+		BlockNumber: uint64(*dec.BlockNumber),
+		TxHash:      *dec.TxHash,
+		TxIndex:     uint(*dec.TxIndex),
+		BlockHash:   *dec.BlockHash,
+		Index:       uint(*dec.Index),
 	}
-	if _, err := fmt.Sscanf(dec.Data, "0x%x", &declog.Data); err != nil {
-		return fmt.Errorf("invalid hex log data")
-	}
-	if _, err := fmt.Sscanf(dec.BlockNumber, "0x%x", &declog.BlockNumber); err != nil {
-		return fmt.Errorf("invalid hex log block number")
-	}
-	if _, err := fmt.Sscanf(dec.TxIndex, "0x%x", &declog.TxIndex); err != nil {
-		return fmt.Errorf("invalid hex log tx index")
-	}
-	if _, err := fmt.Sscanf(dec.Index, "0x%x", &declog.Index); err != nil {
-		return fmt.Errorf("invalid hex log index")
-	}
-	*r = declog
 	return nil
 }
 

+ 3 - 0
core/vm/log_test.go

@@ -28,6 +28,9 @@ var unmarshalLogTests = map[string]struct {
 	"ok": {
 		input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x000000000000000000000000000000000000000000000001a055690d9db80000","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
 	},
+	"empty data": {
+		input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
+	},
 	"missing data": {
 		input:     `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
 		wantError: errMissingLogFields,

+ 44 - 47
eth/filters/api.go

@@ -17,7 +17,6 @@
 package filters
 
 import (
-	"encoding/hex"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -28,6 +27,7 @@ import (
 	"golang.org/x/net/context"
 
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/ethdb"
 	"github.com/ethereum/go-ethereum/event"
@@ -459,52 +459,28 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
 
 	if raw.Addresses != nil {
 		// raw.Address can contain a single address or an array of addresses
-		var addresses []common.Address
-		if strAddrs, ok := raw.Addresses.([]interface{}); ok {
-			for i, addr := range strAddrs {
+		switch rawAddr := raw.Addresses.(type) {
+		case []interface{}:
+			for i, addr := range rawAddr {
 				if strAddr, ok := addr.(string); ok {
-					if len(strAddr) >= 2 && strAddr[0] == '0' && (strAddr[1] == 'x' || strAddr[1] == 'X') {
-						strAddr = strAddr[2:]
-					}
-					if decAddr, err := hex.DecodeString(strAddr); err == nil {
-						addresses = append(addresses, common.BytesToAddress(decAddr))
-					} else {
-						return fmt.Errorf("invalid address given")
+					addr, err := decodeAddress(strAddr)
+					if err != nil {
+						return fmt.Errorf("invalid address at index %d: %v", i, err)
 					}
+					args.Addresses = append(args.Addresses, addr)
 				} else {
-					return fmt.Errorf("invalid address on index %d", i)
+					return fmt.Errorf("non-string address at index %d", i)
 				}
 			}
-		} else if singleAddr, ok := raw.Addresses.(string); ok {
-			if len(singleAddr) >= 2 && singleAddr[0] == '0' && (singleAddr[1] == 'x' || singleAddr[1] == 'X') {
-				singleAddr = singleAddr[2:]
-			}
-			if decAddr, err := hex.DecodeString(singleAddr); err == nil {
-				addresses = append(addresses, common.BytesToAddress(decAddr))
-			} else {
-				return fmt.Errorf("invalid address given")
+		case string:
+			addr, err := decodeAddress(rawAddr)
+			if err != nil {
+				return fmt.Errorf("invalid address: %v", err)
 			}
-		} else {
-			return errors.New("invalid address(es) given")
-		}
-		args.Addresses = addresses
-	}
-
-	// helper function which parses a string to a topic hash
-	topicConverter := func(raw string) (common.Hash, error) {
-		if len(raw) == 0 {
-			return common.Hash{}, nil
-		}
-		if len(raw) >= 2 && raw[0] == '0' && (raw[1] == 'x' || raw[1] == 'X') {
-			raw = raw[2:]
-		}
-		if len(raw) != 2*common.HashLength {
-			return common.Hash{}, errors.New("invalid topic(s)")
-		}
-		if decAddr, err := hex.DecodeString(raw); err == nil {
-			return common.BytesToHash(decAddr), nil
+			args.Addresses = []common.Address{addr}
+		default:
+			return errors.New("invalid addresses in query")
 		}
-		return common.Hash{}, errors.New("invalid topic(s)")
 	}
 
 	// topics is an array consisting of strings and/or arrays of strings.
@@ -512,20 +488,25 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
 	if len(raw.Topics) > 0 {
 		args.Topics = make([][]common.Hash, len(raw.Topics))
 		for i, t := range raw.Topics {
-			if t == nil { // ignore topic when matching logs
+			switch topic := t.(type) {
+			case nil:
+				// ignore topic when matching logs
 				args.Topics[i] = []common.Hash{common.Hash{}}
-			} else if topic, ok := t.(string); ok { // match specific topic
-				top, err := topicConverter(topic)
+
+			case string:
+				// match specific topic
+				top, err := decodeTopic(topic)
 				if err != nil {
 					return err
 				}
 				args.Topics[i] = []common.Hash{top}
-			} else if topics, ok := t.([]interface{}); ok { // or case e.g. [null, "topic0", "topic1"]
-				for _, rawTopic := range topics {
+			case []interface{}:
+				// or case e.g. [null, "topic0", "topic1"]
+				for _, rawTopic := range topic {
 					if rawTopic == nil {
 						args.Topics[i] = append(args.Topics[i], common.Hash{})
 					} else if topic, ok := rawTopic.(string); ok {
-						parsed, err := topicConverter(topic)
+						parsed, err := decodeTopic(topic)
 						if err != nil {
 							return err
 						}
@@ -534,7 +515,7 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
 						return fmt.Errorf("invalid topic(s)")
 					}
 				}
-			} else {
+			default:
 				return fmt.Errorf("invalid topic(s)")
 			}
 		}
@@ -542,3 +523,19 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
 
 	return nil
 }
+
+func decodeAddress(s string) (common.Address, error) {
+	b, err := hexutil.Decode(s)
+	if err == nil && len(b) != common.AddressLength {
+		err = fmt.Errorf("hex has invalid length %d after decoding", len(b))
+	}
+	return common.BytesToAddress(b), err
+}
+
+func decodeTopic(s string) (common.Hash, error) {
+	b, err := hexutil.Decode(s)
+	if err == nil && len(b) != common.HashLength {
+		err = fmt.Errorf("hex has invalid length %d after decoding", len(b))
+	}
+	return common.BytesToHash(b), err
+}

+ 36 - 35
ethclient/ethclient.go

@@ -24,6 +24,7 @@ import (
 
 	"github.com/ethereum/go-ethereum"
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/core/vm"
 	"github.com/ethereum/go-ethereum/rlp"
@@ -156,9 +157,9 @@ func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (*typ
 
 // TransactionCount returns the total number of transactions in the given block.
 func (ec *Client) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
-	var num rpc.HexNumber
+	var num hexutil.Uint
 	err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByHash", blockHash)
-	return num.Uint(), err
+	return uint(num), err
 }
 
 // TransactionInBlock returns a single transaction at index in the given block.
@@ -196,11 +197,11 @@ func toBlockNumArg(number *big.Int) string {
 }
 
 type rpcProgress struct {
-	StartingBlock rpc.HexNumber
-	CurrentBlock  rpc.HexNumber
-	HighestBlock  rpc.HexNumber
-	PulledStates  rpc.HexNumber
-	KnownStates   rpc.HexNumber
+	StartingBlock hexutil.Uint64
+	CurrentBlock  hexutil.Uint64
+	HighestBlock  hexutil.Uint64
+	PulledStates  hexutil.Uint64
+	KnownStates   hexutil.Uint64
 }
 
 // SyncProgress retrieves the current progress of the sync algorithm. If there's
@@ -220,11 +221,11 @@ func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, err
 		return nil, err
 	}
 	return &ethereum.SyncProgress{
-		StartingBlock: progress.StartingBlock.Uint64(),
-		CurrentBlock:  progress.CurrentBlock.Uint64(),
-		HighestBlock:  progress.HighestBlock.Uint64(),
-		PulledStates:  progress.PulledStates.Uint64(),
-		KnownStates:   progress.KnownStates.Uint64(),
+		StartingBlock: uint64(progress.StartingBlock),
+		CurrentBlock:  uint64(progress.CurrentBlock),
+		HighestBlock:  uint64(progress.HighestBlock),
+		PulledStates:  uint64(progress.PulledStates),
+		KnownStates:   uint64(progress.KnownStates),
 	}, nil
 }
 
@@ -239,7 +240,7 @@ func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header)
 // BalanceAt returns the wei balance of the given account.
 // The block number can be nil, in which case the balance is taken from the latest known block.
 func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
-	var result rpc.HexNumber
+	var result hexutil.Big
 	err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, toBlockNumArg(blockNumber))
 	return (*big.Int)(&result), err
 }
@@ -247,7 +248,7 @@ func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNu
 // StorageAt returns the value of key in the contract storage of the given account.
 // The block number can be nil, in which case the value is taken from the latest known block.
 func (ec *Client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
-	var result rpc.HexBytes
+	var result hexutil.Bytes
 	err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, toBlockNumArg(blockNumber))
 	return result, err
 }
@@ -255,7 +256,7 @@ func (ec *Client) StorageAt(ctx context.Context, account common.Address, key com
 // CodeAt returns the contract code of the given account.
 // The block number can be nil, in which case the code is taken from the latest known block.
 func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
-	var result rpc.HexBytes
+	var result hexutil.Bytes
 	err := ec.c.CallContext(ctx, &result, "eth_getCode", account, toBlockNumArg(blockNumber))
 	return result, err
 }
@@ -263,9 +264,9 @@ func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumbe
 // NonceAt returns the account nonce of the given account.
 // The block number can be nil, in which case the nonce is taken from the latest known block.
 func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
-	var result rpc.HexNumber
+	var result hexutil.Uint64
 	err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, toBlockNumArg(blockNumber))
-	return result.Uint64(), err
+	return uint64(result), err
 }
 
 // Filters
@@ -286,7 +287,7 @@ func toFilterArg(q ethereum.FilterQuery) interface{} {
 	arg := map[string]interface{}{
 		"fromBlock": toBlockNumArg(q.FromBlock),
 		"toBlock":   toBlockNumArg(q.ToBlock),
-		"addresses": q.Addresses,
+		"address":   q.Addresses,
 		"topics":    q.Topics,
 	}
 	if q.FromBlock == nil {
@@ -299,21 +300,21 @@ func toFilterArg(q ethereum.FilterQuery) interface{} {
 
 // PendingBalanceAt returns the wei balance of the given account in the pending state.
 func (ec *Client) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) {
-	var result rpc.HexNumber
+	var result hexutil.Big
 	err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, "pending")
 	return (*big.Int)(&result), err
 }
 
 // PendingStorageAt returns the value of key in the contract storage of the given account in the pending state.
 func (ec *Client) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) {
-	var result rpc.HexBytes
+	var result hexutil.Bytes
 	err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, "pending")
 	return result, err
 }
 
 // PendingCodeAt returns the contract code of the given account in the pending state.
 func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
-	var result rpc.HexBytes
+	var result hexutil.Bytes
 	err := ec.c.CallContext(ctx, &result, "eth_getCode", account, "pending")
 	return result, err
 }
@@ -321,16 +322,16 @@ func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]
 // PendingNonceAt returns the account nonce of the given account in the pending state.
 // This is the nonce that should be used for the next transaction.
 func (ec *Client) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
-	var result rpc.HexNumber
+	var result hexutil.Uint64
 	err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, "pending")
-	return result.Uint64(), err
+	return uint64(result), err
 }
 
 // PendingTransactionCount returns the total number of transactions in the pending state.
 func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) {
-	var num rpc.HexNumber
+	var num hexutil.Uint
 	err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByNumber", "pending")
-	return num.Uint(), err
+	return uint(num), err
 }
 
 // TODO: SubscribePendingTransactions (needs server side)
@@ -344,29 +345,29 @@ func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) {
 // case the code is taken from the latest known block. Note that state from very old
 // blocks might not be available.
 func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
-	var hex string
+	var hex hexutil.Bytes
 	err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber))
 	if err != nil {
 		return nil, err
 	}
-	return common.FromHex(hex), nil
+	return hex, nil
 }
 
 // PendingCallContract executes a message call transaction using the EVM.
 // The state seen by the contract call is the pending state.
 func (ec *Client) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
-	var hex string
+	var hex hexutil.Bytes
 	err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "pending")
 	if err != nil {
 		return nil, err
 	}
-	return common.FromHex(hex), nil
+	return hex, nil
 }
 
 // SuggestGasPrice retrieves the currently suggested gas price to allow a timely
 // execution of a transaction.
 func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
-	var hex rpc.HexNumber
+	var hex hexutil.Big
 	if err := ec.c.CallContext(ctx, &hex, "eth_gasPrice"); err != nil {
 		return nil, err
 	}
@@ -378,7 +379,7 @@ func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
 // the true gas limit requirement as other transactions may be added or removed by miners,
 // but it should provide a basis for setting a reasonable default.
 func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) {
-	var hex rpc.HexNumber
+	var hex hexutil.Big
 	err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg))
 	if err != nil {
 		return nil, err
@@ -404,16 +405,16 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
 		"to":   msg.To,
 	}
 	if len(msg.Data) > 0 {
-		arg["data"] = fmt.Sprintf("%#x", msg.Data)
+		arg["data"] = hexutil.Bytes(msg.Data)
 	}
 	if msg.Value != nil {
-		arg["value"] = fmt.Sprintf("%#x", msg.Value)
+		arg["value"] = (*hexutil.Big)(msg.Value)
 	}
 	if msg.Gas != nil {
-		arg["gas"] = fmt.Sprintf("%#x", msg.Gas)
+		arg["gas"] = (*hexutil.Big)(msg.Gas)
 	}
 	if msg.GasPrice != nil {
-		arg["gasPrice"] = fmt.Sprintf("%#x", msg.GasPrice)
+		arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
 	}
 	return arg
 }