// 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 . package hexutil import ( "encoding/hex" "encoding/json" "fmt" "math/big" "reflect" "strconv" ) var ( 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. // The empty slice marshals as "0x". type Bytes []byte // MarshalText implements encoding.TextMarshaler func (b Bytes) MarshalText() ([]byte, error) { result := make([]byte, len(b)*2+2) copy(result, `0x`) hex.Encode(result[2:], b) return result, nil } // UnmarshalJSON implements json.Unmarshaler. func (b *Bytes) UnmarshalJSON(input []byte) error { if !isString(input) { return errNonString(bytesT) } return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bytesT) } // UnmarshalText implements encoding.TextUnmarshaler. func (b *Bytes) UnmarshalText(input []byte) error { raw, err := checkText(input, true) if err != nil { return err } dec := make([]byte, len(raw)/2) if _, err = hex.Decode(dec, raw); err != nil { err = mapError(err) } else { *b = dec } return err } // String returns the hex encoding of b. func (b Bytes) String() string { return Encode(b) } // ImplementsGraphQLType returns true if Bytes implements the specified GraphQL type. func (b Bytes) ImplementsGraphQLType(name string) bool { return name == "Bytes" } // UnmarshalGraphQL unmarshals the provided GraphQL query data. func (b *Bytes) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { case string: data, err := Decode(input) if err != nil { return err } *b = data default: err = fmt.Errorf("Unexpected type for Bytes: %v", input) } return err } // 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. func UnmarshalFixedText(typname string, input, out []byte) error { raw, err := checkText(input, true) if err != nil { return err } if len(raw)/2 != len(out) { return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname) } // Pre-verify syntax before modifying out. for _, b := range raw { if decodeNibble(b) == badNibble { return ErrSyntax } } hex.Decode(out, raw) return nil } // UnmarshalFixedUnprefixedText decodes the input as a string with optional 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. func UnmarshalFixedUnprefixedText(typname string, input, out []byte) error { raw, err := checkText(input, false) if err != nil { return err } if len(raw)/2 != len(out) { return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname) } // Pre-verify syntax before modifying out. for _, b := range raw { if decodeNibble(b) == badNibble { return ErrSyntax } } hex.Decode(out, raw) return nil } // Big marshals/unmarshals as a JSON string with 0x prefix. // The zero value marshals as "0x0". // // Negative integers are not supported at this time. Attempting to marshal them will // return an error. Values larger than 256bits are rejected by Unmarshal but will be // marshaled without error. type Big big.Int // MarshalText implements encoding.TextMarshaler func (b Big) MarshalText() ([]byte, error) { return []byte(EncodeBig((*big.Int)(&b))), nil } // UnmarshalJSON implements json.Unmarshaler. func (b *Big) UnmarshalJSON(input []byte) error { if !isString(input) { return errNonString(bigT) } return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bigT) } // UnmarshalText implements encoding.TextUnmarshaler func (b *Big) UnmarshalText(input []byte) error { raw, err := checkNumberText(input) if err != nil { return err } if len(raw) > 64 { return 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 ErrSyntax } words[i] *= 16 words[i] += big.Word(nib) } end = start } var dec big.Int dec.SetBits(words) *b = (Big)(dec) return nil } // ToInt converts b to a big.Int. func (b *Big) ToInt() *big.Int { return (*big.Int)(b) } // String returns the hex encoding of b. func (b *Big) String() string { return EncodeBig(b.ToInt()) } // ImplementsGraphQLType returns true if Big implements the provided GraphQL type. func (b Big) ImplementsGraphQLType(name string) bool { return name == "BigInt" } // UnmarshalGraphQL unmarshals the provided GraphQL query data. func (b *Big) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { case string: return b.UnmarshalText([]byte(input)) case int32: var num big.Int num.SetInt64(int64(input)) *b = Big(num) default: err = fmt.Errorf("Unexpected type for BigInt: %v", input) } return err } // Uint64 marshals/unmarshals as a JSON string with 0x prefix. // The zero value marshals as "0x0". type Uint64 uint64 // MarshalText implements encoding.TextMarshaler. func (b Uint64) MarshalText() ([]byte, error) { buf := make([]byte, 2, 10) copy(buf, `0x`) buf = strconv.AppendUint(buf, uint64(b), 16) return buf, nil } // UnmarshalJSON implements json.Unmarshaler. func (b *Uint64) UnmarshalJSON(input []byte) error { if !isString(input) { return errNonString(uint64T) } return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uint64T) } // UnmarshalText implements encoding.TextUnmarshaler func (b *Uint64) UnmarshalText(input []byte) error { raw, err := checkNumberText(input) if err != nil { return err } if len(raw) > 16 { return ErrUint64Range } var dec uint64 for _, byte := range raw { nib := decodeNibble(byte) if nib == badNibble { return ErrSyntax } dec *= 16 dec += nib } *b = Uint64(dec) return nil } // String returns the hex encoding of b. func (b Uint64) String() string { return EncodeUint64(uint64(b)) } // ImplementsGraphQLType returns true if Uint64 implements the provided GraphQL type. func (b Uint64) ImplementsGraphQLType(name string) bool { return name == "Long" } // UnmarshalGraphQL unmarshals the provided GraphQL query data. func (b *Uint64) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { case string: return b.UnmarshalText([]byte(input)) case int32: *b = Uint64(input) default: err = fmt.Errorf("Unexpected type for Long: %v", input) } return err } // Uint marshals/unmarshals as a JSON string with 0x prefix. // The zero value marshals as "0x0". type Uint uint // MarshalText implements encoding.TextMarshaler. func (b Uint) MarshalText() ([]byte, error) { return Uint64(b).MarshalText() } // UnmarshalJSON implements json.Unmarshaler. func (b *Uint) UnmarshalJSON(input []byte) error { if !isString(input) { return errNonString(uintT) } return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uintT) } // UnmarshalText implements encoding.TextUnmarshaler. func (b *Uint) UnmarshalText(input []byte) error { var u64 Uint64 err := u64.UnmarshalText(input) if u64 > Uint64(^uint(0)) || err == ErrUint64Range { return ErrUintRange } else if err != nil { return err } *b = Uint(u64) return nil } // String returns the hex encoding of b. func (b Uint) String() string { return EncodeUint64(uint64(b)) } func isString(input []byte) bool { return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' } func bytesHave0xPrefix(input []byte) bool { return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') } func checkText(input []byte, wantPrefix bool) ([]byte, error) { if len(input) == 0 { return nil, nil // empty strings are allowed } if bytesHave0xPrefix(input) { input = input[2:] } else if wantPrefix { return nil, ErrMissingPrefix } if len(input)%2 != 0 { return nil, ErrOddLength } return input, nil } func checkNumberText(input []byte) (raw []byte, err error) { if len(input) == 0 { return nil, nil // empty strings are allowed } if !bytesHave0xPrefix(input) { return nil, ErrMissingPrefix } input = input[2:] if len(input) == 0 { return nil, ErrEmptyNumber } if len(input) > 1 && input[0] == '0' { return nil, ErrLeadingZero } 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} }