233 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			233 lines
		
	
	
		
			5.8 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" | ||
|  | 	"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 | ||
|  | } |