2022-05-10 08:26:06 +01:00
|
|
|
// Copyright (C) 2022 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package blockchain
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/hex"
|
|
|
|
"encoding/json"
|
2022-05-10 13:13:06 +01:00
|
|
|
"reflect"
|
2022-05-10 08:26:06 +01:00
|
|
|
|
|
|
|
"github.com/zeebo/errs"
|
2022-10-30 14:19:16 +00:00
|
|
|
"golang.org/x/crypto/sha3"
|
2022-05-10 08:26:06 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// Error for the package (mainly parsing errors).
|
|
|
|
var Error = errs.Class("blockchain")
|
|
|
|
|
|
|
|
// Lengths of hashes and addresses in bytes.
|
|
|
|
const (
|
|
|
|
// HashLength is the expected length of the hash.
|
|
|
|
HashLength = 32
|
|
|
|
// AddressLength is the expected length of the address.
|
|
|
|
AddressLength = 20
|
|
|
|
)
|
|
|
|
|
|
|
|
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
|
|
|
type Hash [HashLength]byte
|
|
|
|
|
|
|
|
var _ json.Marshaler = Hash{}
|
|
|
|
|
|
|
|
// Bytes gets the byte representation of the underlying hash.
|
|
|
|
func (h Hash) Bytes() []byte { return h[:] }
|
|
|
|
|
|
|
|
// Hex gets the hex string representation of the underlying hash.
|
|
|
|
func (h Hash) Hex() string {
|
2022-10-06 15:22:45 +01:00
|
|
|
var buf [len(h)*2 + 2]byte
|
|
|
|
copy(buf[:2], "0x")
|
|
|
|
hex.Encode(buf[2:], h[:])
|
|
|
|
return string(buf[:])
|
2022-05-10 08:26:06 +01:00
|
|
|
}
|
|
|
|
|
2022-05-10 13:13:06 +01:00
|
|
|
// MarshalJSON implements json marshalling interface.
|
|
|
|
func (h Hash) MarshalJSON() ([]byte, error) {
|
|
|
|
return json.Marshal(h.Hex())
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalJSON unmarshal JSON into Hash.
|
|
|
|
func (h *Hash) UnmarshalJSON(bytes []byte) error {
|
|
|
|
return unmarshalHexString(h[:], bytes, reflect.TypeOf(Hash{}))
|
|
|
|
}
|
|
|
|
|
2022-05-10 08:26:06 +01:00
|
|
|
// Address is wallet address.
|
|
|
|
type Address [AddressLength]byte
|
|
|
|
|
|
|
|
var _ json.Marshaler = Address{}
|
|
|
|
|
|
|
|
// Bytes gets the byte representation of the underlying address.
|
|
|
|
func (a Address) Bytes() []byte { return a[:] }
|
|
|
|
|
|
|
|
// Hex gets string representation of the underlying address.
|
|
|
|
func (a Address) Hex() string {
|
2022-10-06 15:22:45 +01:00
|
|
|
var buf [len(a)*2 + 2]byte
|
|
|
|
copy(buf[:2], "0x")
|
|
|
|
hex.Encode(buf[2:], a[:])
|
2022-10-30 14:19:16 +00:00
|
|
|
|
|
|
|
// https://eips.ethereum.org/EIPS/eip-55
|
|
|
|
sha := sha3.NewLegacyKeccak256()
|
|
|
|
sha.Write(buf[2:])
|
|
|
|
hash := sha.Sum(nil)
|
|
|
|
for i := 2; i < len(buf); i++ {
|
|
|
|
hashByte := hash[(i-2)/2]
|
|
|
|
if i%2 == 0 {
|
|
|
|
hashByte >>= 4
|
|
|
|
} else {
|
|
|
|
hashByte &= 0xf
|
|
|
|
}
|
|
|
|
if buf[i] > '9' && hashByte > 7 {
|
|
|
|
buf[i] -= 32
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-06 15:22:45 +01:00
|
|
|
return string(buf[:])
|
2022-05-10 08:26:06 +01:00
|
|
|
}
|
2022-05-10 13:13:06 +01:00
|
|
|
|
|
|
|
// MarshalJSON implements json marshalling interface.
|
|
|
|
func (a Address) MarshalJSON() ([]byte, error) {
|
|
|
|
return json.Marshal(a.Hex())
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalJSON unmarshal JSON into Address.
|
|
|
|
func (a *Address) UnmarshalJSON(bytes []byte) error {
|
|
|
|
return unmarshalHexString(a[:], bytes, reflect.TypeOf(Address{}))
|
|
|
|
}
|
|
|
|
|
2022-10-06 15:22:45 +01:00
|
|
|
// BytesToHash create a new hash from raw bytes.
|
|
|
|
func BytesToHash(bytes []byte) (h Hash, err error) {
|
|
|
|
copy(h[:], bytes)
|
|
|
|
return h, err
|
|
|
|
}
|
|
|
|
|
2022-05-10 13:13:06 +01:00
|
|
|
// unmarshalHexString decodes JSON string containing hex string into bytes.
|
|
|
|
// Copies result into dst byte slice.
|
|
|
|
func unmarshalHexString(dst, src []byte, typ reflect.Type) error {
|
|
|
|
if !isString(src) {
|
|
|
|
return &json.UnmarshalTypeError{Value: "non-string", Type: reflect.TypeOf(typ)}
|
|
|
|
}
|
|
|
|
src = src[1 : len(src)-1]
|
|
|
|
|
|
|
|
if bytesHave0xPrefix(src) {
|
|
|
|
src = src[2:]
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := hex.Decode(dst, src)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// isString checks if JSON value is a string.
|
|
|
|
func isString(input []byte) bool {
|
|
|
|
return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"'
|
|
|
|
}
|
|
|
|
|
|
|
|
// bytesHave0xPrefix checks if string bytes representation contains 0x prefix.
|
|
|
|
func bytesHave0xPrefix(input []byte) bool {
|
|
|
|
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
|
|
|
|
}
|
2022-05-20 10:18:59 +01:00
|
|
|
|
|
|
|
// BytesToAddress create a new address from raw bytes.
|
|
|
|
func BytesToAddress(bytes []byte) (a Address, err error) {
|
|
|
|
if len(bytes) != AddressLength {
|
|
|
|
return a, errs.New("Invalid address length: %d instead of %d", len(bytes), AddressLength)
|
|
|
|
}
|
|
|
|
copy(a[:], bytes)
|
|
|
|
return a, err
|
|
|
|
}
|