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".
		
			
				
	
	
		
			242 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			242 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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"
 | 
						|
	"fmt"
 | 
						|
	"math/big"
 | 
						|
	"strconv"
 | 
						|
)
 | 
						|
 | 
						|
const uintBits = 32 << (uint64(^uint(0)) >> 63)
 | 
						|
 | 
						|
var (
 | 
						|
	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 {
 | 
						|
		return nil, ErrEmptyString
 | 
						|
	}
 | 
						|
	if !has0xPrefix(input) {
 | 
						|
		return nil, ErrMissingPrefix
 | 
						|
	}
 | 
						|
	b, err := hex.DecodeString(input[2:])
 | 
						|
	if err != nil {
 | 
						|
		err = mapError(err)
 | 
						|
	}
 | 
						|
	return b, err
 | 
						|
}
 | 
						|
 | 
						|
// 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.
 | 
						|
// Numbers larger than 256 bits are not accepted.
 | 
						|
func DecodeBig(input string) (*big.Int, error) {
 | 
						|
	raw, err := checkNumber(input)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if len(raw) > 64 {
 | 
						|
		return nil, ErrBig256Range
 | 
						|
	}
 | 
						|
	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"
 | 
						|
	}
 | 
						|
	return fmt.Sprintf("%#x", bigint)
 | 
						|
}
 | 
						|
 | 
						|
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
 | 
						|
}
 |