mirror of https://github.com/ethereum/go-ethereum
commit
8d0108fc5d
@ -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 |
||||||
|
} |
@ -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 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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 |
||||||
|
} |
@ -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 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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 |
|
||||||
} |
|
@ -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 |
|
||||||
} |
|
Loading…
Reference in new issue