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
							 | 
						||
| 
								 | 
							
								}
							 |