提交 8d0108fc 编写于 作者: P Péter Szilágyi 提交者: GitHub

Merge pull request #3355 from fjl/hexutil-2

Improve hex encoding/decoding
......@@ -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()
}
......
// 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
}
// 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
}
}
}
// 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
}
// 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
}
}
}
......@@ -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:
......
......@@ -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)
}
}
}
}
......
......@@ -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,
})
......
......@@ -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 {
......
// 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
}
// 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
}
......@@ -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),
})
}
......
......@@ -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),
})
}
......
......@@ -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
}
......
......@@ -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,
......
......@@ -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
}
......@@ -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
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册