forked from cerc-io/plugeth
Merge pull request #14686 from fjl/hexutil-json-type-error
common/hexutil: wrap errors in json.UnmarshalTypeError
This commit is contained in:
commit
bb366271fe
@ -32,7 +32,6 @@ package hexutil
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -41,17 +40,23 @@ import (
|
|||||||
const uintBits = 32 << (uint64(^uint(0)) >> 63)
|
const uintBits = 32 << (uint64(^uint(0)) >> 63)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrEmptyString = errors.New("empty hex string")
|
ErrEmptyString = &decError{"empty hex string"}
|
||||||
ErrMissingPrefix = errors.New("missing 0x prefix for hex data")
|
ErrSyntax = &decError{"invalid hex string"}
|
||||||
ErrSyntax = errors.New("invalid hex")
|
ErrMissingPrefix = &decError{"hex string without 0x prefix"}
|
||||||
ErrEmptyNumber = errors.New("hex number has no digits after 0x")
|
ErrOddLength = &decError{"hex string of odd length"}
|
||||||
ErrLeadingZero = errors.New("hex number has leading zero digits after 0x")
|
ErrEmptyNumber = &decError{"hex string \"0x\""}
|
||||||
ErrOddLength = errors.New("hex string has odd length")
|
ErrLeadingZero = &decError{"hex number with leading zero digits"}
|
||||||
ErrUint64Range = errors.New("hex number does not fit into 64 bits")
|
ErrUint64Range = &decError{"hex number > 64 bits"}
|
||||||
ErrUintRange = fmt.Errorf("hex number does not fit into %d bits", uintBits)
|
ErrUintRange = &decError{fmt.Sprintf("hex number > %d bits", uintBits)}
|
||||||
ErrBig256Range = errors.New("hex number does not fit into 256 bits")
|
ErrBig256Range = &decError{"hex number > 256 bits"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type decError struct{ msg string }
|
||||||
|
|
||||||
|
func (err decError) Error() string {
|
||||||
|
return string(err.msg)
|
||||||
|
}
|
||||||
|
|
||||||
// Decode decodes a hex string with 0x prefix.
|
// Decode decodes a hex string with 0x prefix.
|
||||||
func Decode(input string) ([]byte, error) {
|
func Decode(input string) ([]byte, error) {
|
||||||
if len(input) == 0 {
|
if len(input) == 0 {
|
||||||
|
@ -18,15 +18,19 @@ package hexutil
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
textZero = []byte(`0x0`)
|
textZero = []byte(`0x0`)
|
||||||
errNonString = errors.New("cannot unmarshal non-string as hex data")
|
bytesT = reflect.TypeOf(Bytes(nil))
|
||||||
|
bigT = reflect.TypeOf((*Big)(nil))
|
||||||
|
uintT = reflect.TypeOf(Uint(0))
|
||||||
|
uint64T = reflect.TypeOf(Uint64(0))
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
|
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
|
||||||
@ -44,9 +48,9 @@ func (b Bytes) MarshalText() ([]byte, error) {
|
|||||||
// UnmarshalJSON implements json.Unmarshaler.
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
func (b *Bytes) UnmarshalJSON(input []byte) error {
|
func (b *Bytes) UnmarshalJSON(input []byte) error {
|
||||||
if !isString(input) {
|
if !isString(input) {
|
||||||
return errNonString
|
return errNonString(bytesT)
|
||||||
}
|
}
|
||||||
return b.UnmarshalText(input[1 : len(input)-1])
|
return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bytesT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
@ -69,6 +73,16 @@ func (b Bytes) String() string {
|
|||||||
return Encode(b)
|
return Encode(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalFixedJSON decodes the input as a 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.
|
||||||
|
func UnmarshalFixedJSON(typ reflect.Type, input, out []byte) error {
|
||||||
|
if !isString(input) {
|
||||||
|
return errNonString(typ)
|
||||||
|
}
|
||||||
|
return wrapTypeError(UnmarshalFixedText(typ.String(), input[1:len(input)-1], out), typ)
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalFixedText decodes the input as a string with 0x prefix. The length of out
|
// UnmarshalFixedText decodes the input as a string with 0x prefix. The length of out
|
||||||
// determines the required input length. This function is commonly used to implement the
|
// determines the required input length. This function is commonly used to implement the
|
||||||
// UnmarshalText method for fixed-size types.
|
// UnmarshalText method for fixed-size types.
|
||||||
@ -127,9 +141,9 @@ func (b Big) MarshalText() ([]byte, error) {
|
|||||||
// UnmarshalJSON implements json.Unmarshaler.
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
func (b *Big) UnmarshalJSON(input []byte) error {
|
func (b *Big) UnmarshalJSON(input []byte) error {
|
||||||
if !isString(input) {
|
if !isString(input) {
|
||||||
return errNonString
|
return errNonString(bigT)
|
||||||
}
|
}
|
||||||
return b.UnmarshalText(input[1 : len(input)-1])
|
return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bigT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalText implements encoding.TextUnmarshaler
|
// UnmarshalText implements encoding.TextUnmarshaler
|
||||||
@ -189,9 +203,9 @@ func (b Uint64) MarshalText() ([]byte, error) {
|
|||||||
// UnmarshalJSON implements json.Unmarshaler.
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
func (b *Uint64) UnmarshalJSON(input []byte) error {
|
func (b *Uint64) UnmarshalJSON(input []byte) error {
|
||||||
if !isString(input) {
|
if !isString(input) {
|
||||||
return errNonString
|
return errNonString(uint64T)
|
||||||
}
|
}
|
||||||
return b.UnmarshalText(input[1 : len(input)-1])
|
return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uint64T)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalText implements encoding.TextUnmarshaler
|
// UnmarshalText implements encoding.TextUnmarshaler
|
||||||
@ -233,9 +247,9 @@ func (b Uint) MarshalText() ([]byte, error) {
|
|||||||
// UnmarshalJSON implements json.Unmarshaler.
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
func (b *Uint) UnmarshalJSON(input []byte) error {
|
func (b *Uint) UnmarshalJSON(input []byte) error {
|
||||||
if !isString(input) {
|
if !isString(input) {
|
||||||
return errNonString
|
return errNonString(uintT)
|
||||||
}
|
}
|
||||||
return b.UnmarshalText(input[1 : len(input)-1])
|
return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uintT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
@ -295,3 +309,14 @@ func checkNumberText(input []byte) (raw []byte, err error) {
|
|||||||
}
|
}
|
||||||
return input, nil
|
return input, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wrapTypeError(err error, typ reflect.Type) error {
|
||||||
|
if _, ok := err.(*decError); ok {
|
||||||
|
return &json.UnmarshalTypeError{Value: err.Error(), Type: typ}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func errNonString(typ reflect.Type) error {
|
||||||
|
return &json.UnmarshalTypeError{Value: "non-string", Type: typ}
|
||||||
|
}
|
||||||
|
@ -62,12 +62,12 @@ var errJSONEOF = errors.New("unexpected end of JSON input")
|
|||||||
var unmarshalBytesTests = []unmarshalTest{
|
var unmarshalBytesTests = []unmarshalTest{
|
||||||
// invalid encoding
|
// invalid encoding
|
||||||
{input: "", wantErr: errJSONEOF},
|
{input: "", wantErr: errJSONEOF},
|
||||||
{input: "null", wantErr: errNonString},
|
{input: "null", wantErr: errNonString(bytesT)},
|
||||||
{input: "10", wantErr: errNonString},
|
{input: "10", wantErr: errNonString(bytesT)},
|
||||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
{input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, bytesT)},
|
||||||
{input: `"0x0"`, wantErr: ErrOddLength},
|
{input: `"0x0"`, wantErr: wrapTypeError(ErrOddLength, bytesT)},
|
||||||
{input: `"0xxx"`, wantErr: ErrSyntax},
|
{input: `"0xxx"`, wantErr: wrapTypeError(ErrSyntax, bytesT)},
|
||||||
{input: `"0x01zz01"`, wantErr: ErrSyntax},
|
{input: `"0x01zz01"`, wantErr: wrapTypeError(ErrSyntax, bytesT)},
|
||||||
|
|
||||||
// valid encoding
|
// valid encoding
|
||||||
{input: `""`, want: referenceBytes("")},
|
{input: `""`, want: referenceBytes("")},
|
||||||
@ -127,16 +127,16 @@ func TestMarshalBytes(t *testing.T) {
|
|||||||
var unmarshalBigTests = []unmarshalTest{
|
var unmarshalBigTests = []unmarshalTest{
|
||||||
// invalid encoding
|
// invalid encoding
|
||||||
{input: "", wantErr: errJSONEOF},
|
{input: "", wantErr: errJSONEOF},
|
||||||
{input: "null", wantErr: errNonString},
|
{input: "null", wantErr: errNonString(bigT)},
|
||||||
{input: "10", wantErr: errNonString},
|
{input: "10", wantErr: errNonString(bigT)},
|
||||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
{input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, bigT)},
|
||||||
{input: `"0x"`, wantErr: ErrEmptyNumber},
|
{input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, bigT)},
|
||||||
{input: `"0x01"`, wantErr: ErrLeadingZero},
|
{input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, bigT)},
|
||||||
{input: `"0xx"`, wantErr: ErrSyntax},
|
{input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, bigT)},
|
||||||
{input: `"0x1zz01"`, wantErr: ErrSyntax},
|
{input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, bigT)},
|
||||||
{
|
{
|
||||||
input: `"0x10000000000000000000000000000000000000000000000000000000000000000"`,
|
input: `"0x10000000000000000000000000000000000000000000000000000000000000000"`,
|
||||||
wantErr: ErrBig256Range,
|
wantErr: wrapTypeError(ErrBig256Range, bigT),
|
||||||
},
|
},
|
||||||
|
|
||||||
// valid encoding
|
// valid encoding
|
||||||
@ -208,14 +208,14 @@ func TestMarshalBig(t *testing.T) {
|
|||||||
var unmarshalUint64Tests = []unmarshalTest{
|
var unmarshalUint64Tests = []unmarshalTest{
|
||||||
// invalid encoding
|
// invalid encoding
|
||||||
{input: "", wantErr: errJSONEOF},
|
{input: "", wantErr: errJSONEOF},
|
||||||
{input: "null", wantErr: errNonString},
|
{input: "null", wantErr: errNonString(uint64T)},
|
||||||
{input: "10", wantErr: errNonString},
|
{input: "10", wantErr: errNonString(uint64T)},
|
||||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
{input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, uint64T)},
|
||||||
{input: `"0x"`, wantErr: ErrEmptyNumber},
|
{input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, uint64T)},
|
||||||
{input: `"0x01"`, wantErr: ErrLeadingZero},
|
{input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, uint64T)},
|
||||||
{input: `"0xfffffffffffffffff"`, wantErr: ErrUint64Range},
|
{input: `"0xfffffffffffffffff"`, wantErr: wrapTypeError(ErrUint64Range, uint64T)},
|
||||||
{input: `"0xx"`, wantErr: ErrSyntax},
|
{input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, uint64T)},
|
||||||
{input: `"0x1zz01"`, wantErr: ErrSyntax},
|
{input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, uint64T)},
|
||||||
|
|
||||||
// valid encoding
|
// valid encoding
|
||||||
{input: `""`, want: uint64(0)},
|
{input: `""`, want: uint64(0)},
|
||||||
@ -298,15 +298,15 @@ var (
|
|||||||
var unmarshalUintTests = []unmarshalTest{
|
var unmarshalUintTests = []unmarshalTest{
|
||||||
// invalid encoding
|
// invalid encoding
|
||||||
{input: "", wantErr: errJSONEOF},
|
{input: "", wantErr: errJSONEOF},
|
||||||
{input: "null", wantErr: errNonString},
|
{input: "null", wantErr: errNonString(uintT)},
|
||||||
{input: "10", wantErr: errNonString},
|
{input: "10", wantErr: errNonString(uintT)},
|
||||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
{input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, uintT)},
|
||||||
{input: `"0x"`, wantErr: ErrEmptyNumber},
|
{input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, uintT)},
|
||||||
{input: `"0x01"`, wantErr: ErrLeadingZero},
|
{input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, uintT)},
|
||||||
{input: `"0x100000000"`, want: uint(maxUint33bits), wantErr32bit: ErrUintRange},
|
{input: `"0x100000000"`, want: uint(maxUint33bits), wantErr32bit: wrapTypeError(ErrUintRange, uintT)},
|
||||||
{input: `"0xfffffffffffffffff"`, wantErr: ErrUintRange},
|
{input: `"0xfffffffffffffffff"`, wantErr: wrapTypeError(ErrUintRange, uintT)},
|
||||||
{input: `"0xx"`, wantErr: ErrSyntax},
|
{input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, uintT)},
|
||||||
{input: `"0x1zz01"`, wantErr: ErrSyntax},
|
{input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, uintT)},
|
||||||
|
|
||||||
// valid encoding
|
// valid encoding
|
||||||
{input: `""`, want: uint(0)},
|
{input: `""`, want: uint(0)},
|
||||||
@ -317,7 +317,7 @@ var unmarshalUintTests = []unmarshalTest{
|
|||||||
{input: `"0x1122aaff"`, want: uint(0x1122aaff)},
|
{input: `"0x1122aaff"`, want: uint(0x1122aaff)},
|
||||||
{input: `"0xbbb"`, want: uint(0xbbb)},
|
{input: `"0xbbb"`, want: uint(0xbbb)},
|
||||||
{input: `"0xffffffff"`, want: uint(0xffffffff)},
|
{input: `"0xffffffff"`, want: uint(0xffffffff)},
|
||||||
{input: `"0xffffffffffffffff"`, want: uint(maxUint64bits), wantErr32bit: ErrUintRange},
|
{input: `"0xffffffffffffffff"`, want: uint(maxUint64bits), wantErr32bit: wrapTypeError(ErrUintRange, uintT)},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalUint(t *testing.T) {
|
func TestUnmarshalUint(t *testing.T) {
|
||||||
|
@ -31,6 +31,11 @@ const (
|
|||||||
AddressLength = 20
|
AddressLength = 20
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
hashT = reflect.TypeOf(Hash{})
|
||||||
|
addressT = reflect.TypeOf(Address{})
|
||||||
|
)
|
||||||
|
|
||||||
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
||||||
type Hash [HashLength]byte
|
type Hash [HashLength]byte
|
||||||
|
|
||||||
@ -72,6 +77,11 @@ func (h *Hash) UnmarshalText(input []byte) error {
|
|||||||
return hexutil.UnmarshalFixedText("Hash", input, h[:])
|
return hexutil.UnmarshalFixedText("Hash", input, h[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON parses a hash in hex syntax.
|
||||||
|
func (h *Hash) UnmarshalJSON(input []byte) error {
|
||||||
|
return hexutil.UnmarshalFixedJSON(hashT, input, h[:])
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalText returns the hex representation of h.
|
// MarshalText returns the hex representation of h.
|
||||||
func (h Hash) MarshalText() ([]byte, error) {
|
func (h Hash) MarshalText() ([]byte, error) {
|
||||||
return hexutil.Bytes(h[:]).MarshalText()
|
return hexutil.Bytes(h[:]).MarshalText()
|
||||||
@ -194,6 +204,11 @@ func (a *Address) UnmarshalText(input []byte) error {
|
|||||||
return hexutil.UnmarshalFixedText("Address", input, a[:])
|
return hexutil.UnmarshalFixedText("Address", input, a[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON parses a hash in hex syntax.
|
||||||
|
func (a *Address) UnmarshalJSON(input []byte) error {
|
||||||
|
return hexutil.UnmarshalFixedJSON(addressT, input, a[:])
|
||||||
|
}
|
||||||
|
|
||||||
// UnprefixedHash allows marshaling an Address without 0x prefix.
|
// UnprefixedHash allows marshaling an Address without 0x prefix.
|
||||||
type UnprefixedAddress Address
|
type UnprefixedAddress Address
|
||||||
|
|
||||||
|
@ -21,8 +21,6 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBytesConversion(t *testing.T) {
|
func TestBytesConversion(t *testing.T) {
|
||||||
@ -43,10 +41,10 @@ func TestHashJsonValidation(t *testing.T) {
|
|||||||
Size int
|
Size int
|
||||||
Error string
|
Error string
|
||||||
}{
|
}{
|
||||||
{"", 62, hexutil.ErrMissingPrefix.Error()},
|
{"", 62, "json: cannot unmarshal hex string without 0x prefix into Go value of type common.Hash"},
|
||||||
{"0x", 66, "hex string has length 66, want 64 for Hash"},
|
{"0x", 66, "hex string has length 66, want 64 for common.Hash"},
|
||||||
{"0x", 63, hexutil.ErrOddLength.Error()},
|
{"0x", 63, "json: cannot unmarshal hex string of odd length into Go value of type common.Hash"},
|
||||||
{"0x", 0, "hex string has length 0, want 64 for Hash"},
|
{"0x", 0, "hex string has length 0, want 64 for common.Hash"},
|
||||||
{"0x", 64, ""},
|
{"0x", 64, ""},
|
||||||
{"0X", 64, ""},
|
{"0X", 64, ""},
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user