aboutsummaryrefslogtreecommitdiffstats
path: root/common/hexutil
diff options
context:
space:
mode:
authorFelix Lange <fjl@twurst.com>2017-06-23 16:50:49 +0800
committerFelix Lange <fjl@twurst.com>2017-06-27 19:15:12 +0800
commit4a741df757cf6b1d1b4c0e8c875c34b650f46167 (patch)
tree057d995a3847f9f983a4dcdf41cf3bc9fb9b82a2 /common/hexutil
parentb664bedcf21f30b54af542e0b4ba4e24c01968fd (diff)
downloaddexon-4a741df757cf6b1d1b4c0e8c875c34b650f46167.tar.gz
dexon-4a741df757cf6b1d1b4c0e8c875c34b650f46167.tar.zst
dexon-4a741df757cf6b1d1b4c0e8c875c34b650f46167.zip
common/hexutil: wrap errors in json.UnmarshalTypeError
This adds type and struct field context to error messages. Instead of "hex string of odd length" users will now see "json: cannot unmarshal hex string of odd length into Go struct field SendTxArgs.from of type common.Address".
Diffstat (limited to 'common/hexutil')
-rw-r--r--common/hexutil/hexutil.go25
-rw-r--r--common/hexutil/json.go47
-rw-r--r--common/hexutil/json_test.go64
3 files changed, 83 insertions, 53 deletions
diff --git a/common/hexutil/hexutil.go b/common/hexutil/hexutil.go
index 6b128ae36..582a67c22 100644
--- a/common/hexutil/hexutil.go
+++ b/common/hexutil/hexutil.go
@@ -32,7 +32,6 @@ package hexutil
import (
"encoding/hex"
- "errors"
"fmt"
"math/big"
"strconv"
@@ -41,17 +40,23 @@ import (
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)
- ErrBig256Range = errors.New("hex number does not fit into 256 bits")
+ ErrEmptyString = &decError{"empty hex string"}
+ ErrSyntax = &decError{"invalid hex string"}
+ ErrMissingPrefix = &decError{"hex string without 0x prefix"}
+ ErrOddLength = &decError{"hex string of odd length"}
+ ErrEmptyNumber = &decError{"hex string \"0x\""}
+ ErrLeadingZero = &decError{"hex number with leading zero digits"}
+ ErrUint64Range = &decError{"hex number > 64 bits"}
+ ErrUintRange = &decError{fmt.Sprintf("hex number > %d bits", uintBits)}
+ 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.
func Decode(input string) ([]byte, error) {
if len(input) == 0 {
diff --git a/common/hexutil/json.go b/common/hexutil/json.go
index 1bc1d014c..943288fad 100644
--- a/common/hexutil/json.go
+++ b/common/hexutil/json.go
@@ -18,15 +18,19 @@ package hexutil
import (
"encoding/hex"
- "errors"
+ "encoding/json"
"fmt"
"math/big"
+ "reflect"
"strconv"
)
var (
- textZero = []byte(`0x0`)
- errNonString = errors.New("cannot unmarshal non-string as hex data")
+ textZero = []byte(`0x0`)
+ 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.
@@ -44,9 +48,9 @@ func (b Bytes) MarshalText() ([]byte, error) {
// UnmarshalJSON implements json.Unmarshaler.
func (b *Bytes) UnmarshalJSON(input []byte) error {
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.
@@ -69,6 +73,16 @@ func (b Bytes) String() string {
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
// determines the required input length. This function is commonly used to implement the
// UnmarshalText method for fixed-size types.
@@ -127,9 +141,9 @@ func (b Big) MarshalText() ([]byte, error) {
// UnmarshalJSON implements json.Unmarshaler.
func (b *Big) UnmarshalJSON(input []byte) error {
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
@@ -189,9 +203,9 @@ func (b Uint64) MarshalText() ([]byte, error) {
// UnmarshalJSON implements json.Unmarshaler.
func (b *Uint64) UnmarshalJSON(input []byte) error {
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
@@ -233,9 +247,9 @@ func (b Uint) MarshalText() ([]byte, error) {
// UnmarshalJSON implements json.Unmarshaler.
func (b *Uint) UnmarshalJSON(input []byte) error {
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.
@@ -295,3 +309,14 @@ func checkNumberText(input []byte) (raw []byte, err error) {
}
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}
+}
diff --git a/common/hexutil/json_test.go b/common/hexutil/json_test.go
index e4e827491..8a6b8643a 100644
--- a/common/hexutil/json_test.go
+++ b/common/hexutil/json_test.go
@@ -62,12 +62,12 @@ var errJSONEOF = errors.New("unexpected end of JSON input")
var unmarshalBytesTests = []unmarshalTest{
// invalid encoding
{input: "", wantErr: errJSONEOF},
- {input: "null", wantErr: errNonString},
- {input: "10", wantErr: errNonString},
- {input: `"0"`, wantErr: ErrMissingPrefix},
- {input: `"0x0"`, wantErr: ErrOddLength},
- {input: `"0xxx"`, wantErr: ErrSyntax},
- {input: `"0x01zz01"`, wantErr: ErrSyntax},
+ {input: "null", wantErr: errNonString(bytesT)},
+ {input: "10", wantErr: errNonString(bytesT)},
+ {input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, bytesT)},
+ {input: `"0x0"`, wantErr: wrapTypeError(ErrOddLength, bytesT)},
+ {input: `"0xxx"`, wantErr: wrapTypeError(ErrSyntax, bytesT)},
+ {input: `"0x01zz01"`, wantErr: wrapTypeError(ErrSyntax, bytesT)},
// valid encoding
{input: `""`, want: referenceBytes("")},
@@ -127,16 +127,16 @@ func TestMarshalBytes(t *testing.T) {
var unmarshalBigTests = []unmarshalTest{
// invalid encoding
{input: "", wantErr: errJSONEOF},
- {input: "null", wantErr: errNonString},
- {input: "10", wantErr: errNonString},
- {input: `"0"`, wantErr: ErrMissingPrefix},
- {input: `"0x"`, wantErr: ErrEmptyNumber},
- {input: `"0x01"`, wantErr: ErrLeadingZero},
- {input: `"0xx"`, wantErr: ErrSyntax},
- {input: `"0x1zz01"`, wantErr: ErrSyntax},
+ {input: "null", wantErr: errNonString(bigT)},
+ {input: "10", wantErr: errNonString(bigT)},
+ {input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, bigT)},
+ {input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, bigT)},
+ {input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, bigT)},
+ {input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, bigT)},
+ {input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, bigT)},
{
input: `"0x10000000000000000000000000000000000000000000000000000000000000000"`,
- wantErr: ErrBig256Range,
+ wantErr: wrapTypeError(ErrBig256Range, bigT),
},
// valid encoding
@@ -208,14 +208,14 @@ func TestMarshalBig(t *testing.T) {
var unmarshalUint64Tests = []unmarshalTest{
// invalid encoding
{input: "", wantErr: errJSONEOF},
- {input: "null", wantErr: errNonString},
- {input: "10", wantErr: errNonString},
- {input: `"0"`, wantErr: ErrMissingPrefix},
- {input: `"0x"`, wantErr: ErrEmptyNumber},
- {input: `"0x01"`, wantErr: ErrLeadingZero},
- {input: `"0xfffffffffffffffff"`, wantErr: ErrUint64Range},
- {input: `"0xx"`, wantErr: ErrSyntax},
- {input: `"0x1zz01"`, wantErr: ErrSyntax},
+ {input: "null", wantErr: errNonString(uint64T)},
+ {input: "10", wantErr: errNonString(uint64T)},
+ {input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, uint64T)},
+ {input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, uint64T)},
+ {input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, uint64T)},
+ {input: `"0xfffffffffffffffff"`, wantErr: wrapTypeError(ErrUint64Range, uint64T)},
+ {input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, uint64T)},
+ {input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, uint64T)},
// valid encoding
{input: `""`, want: uint64(0)},
@@ -298,15 +298,15 @@ var (
var unmarshalUintTests = []unmarshalTest{
// invalid encoding
{input: "", wantErr: errJSONEOF},
- {input: "null", wantErr: errNonString},
- {input: "10", wantErr: errNonString},
- {input: `"0"`, wantErr: ErrMissingPrefix},
- {input: `"0x"`, wantErr: ErrEmptyNumber},
- {input: `"0x01"`, wantErr: ErrLeadingZero},
- {input: `"0x100000000"`, want: uint(maxUint33bits), wantErr32bit: ErrUintRange},
- {input: `"0xfffffffffffffffff"`, wantErr: ErrUintRange},
- {input: `"0xx"`, wantErr: ErrSyntax},
- {input: `"0x1zz01"`, wantErr: ErrSyntax},
+ {input: "null", wantErr: errNonString(uintT)},
+ {input: "10", wantErr: errNonString(uintT)},
+ {input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, uintT)},
+ {input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, uintT)},
+ {input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, uintT)},
+ {input: `"0x100000000"`, want: uint(maxUint33bits), wantErr32bit: wrapTypeError(ErrUintRange, uintT)},
+ {input: `"0xfffffffffffffffff"`, wantErr: wrapTypeError(ErrUintRange, uintT)},
+ {input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, uintT)},
+ {input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, uintT)},
// valid encoding
{input: `""`, want: uint(0)},
@@ -317,7 +317,7 @@ var unmarshalUintTests = []unmarshalTest{
{input: `"0x1122aaff"`, want: uint(0x1122aaff)},
{input: `"0xbbb"`, want: uint(0xbbb)},
{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) {