kaspad/util/address.go
stasatdaglabs 6dd3b815c1 [DEV-17] Move Bech32Prefix-related code from btcd/chaincfg/params.go to btcdutil (#62)
* [DEV-17] Moved Bech32 stuff from params.go to address.go.

* [DEV-17] Removed dagconfig dependency from address_test.go.

* [DEV-17] Removed dagconfig dependency from builder_test.go.

* [DEV-17] Removed dagconfig dependency from internal_test.go.

* [DEV-17] Removed dagconfig dependency from wif.go.

* [DEV-17] Removed dagconfig dependency from externdedkey.go.

* [DEV-17] Fixed compilation errors outside of hdkeychain.

* [DEV-17] Resolved circular dependencies.

* [DEV-17] Fixed failing tests.

* [DEV-17] Renamed DagXxx to Bech32PrefixDAGXxx

* [DEV-17] Fixed register_test.go.

* [DEV-17] Renamed HDKeyIDPairXxxNet to XxxNetHDKeyPair.

* [DEV-17] Renamed IsForNet to IsForPrefix.
2018-09-23 15:49:50 +03:00

419 lines
14 KiB
Go

// Copyright (c) 2013-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package util
import (
"encoding/hex"
"errors"
"fmt"
"github.com/daglabs/btcd/btcec"
"github.com/daglabs/btcd/util/bech32"
"golang.org/x/crypto/ripemd160"
)
var (
// ErrChecksumMismatch describes an error where decoding failed due
// to a bad checksum.
ErrChecksumMismatch = errors.New("checksum mismatch")
// ErrUnknownAddressType describes an error where an address can not
// decoded as a specific address type due to the string encoding
// begining with an identifier byte unknown to any standard or
// registered (via chaincfg.Register) network.
ErrUnknownAddressType = errors.New("unknown address type")
)
const (
// PubKeyHash addresses always have the version byte set to 0.
pubKeyHashAddrID = 0x00
// ScriptHash addresses always have the version byte set to 8.
scriptHashAddrID = 0x08
)
// Bech32Prefix is the human-readable prefix for a Bech32 address.
type Bech32Prefix int
// Constants that define Bech32 address prefixes. Every network is assigned
// a unique prefix.
const (
// Unknown/Erroneous prefix
Bech32PrefixUnknown Bech32Prefix = iota
// Prefix for the main network.
Bech32PrefixDAGCoin
// Prefix for the regression test network.
Bech32PrefixDAGReg
// Prefix for the test network.
Bech32PrefixDAGTest
// Prefix for the simulation network.
Bech32PrefixDAGSim
)
// Map from strings to Bech32 address prefix constants for parsing purposes.
var stringsToBech32Prefixes = map[string]Bech32Prefix{
"dagcoin": Bech32PrefixDAGCoin,
"dagreg": Bech32PrefixDAGReg,
"dagtest": Bech32PrefixDAGTest,
"dagsim": Bech32PrefixDAGSim,
}
// ParsePrefix attempts to parse a Bech32 address prefix.
func ParsePrefix(prefixString string) (Bech32Prefix, error) {
prefix, ok := stringsToBech32Prefixes[prefixString]
if !ok {
return Bech32PrefixUnknown, fmt.Errorf("could not parse prefix %v", prefixString)
}
return prefix, nil
}
// Converts from Bech32 address prefixes to their string values
func (prefix Bech32Prefix) String() string {
for key, value := range stringsToBech32Prefixes {
if prefix == value {
return key
}
}
return ""
}
// encodeAddress returns a human-readable payment address given a network prefix
// and a ripemd160 hash which encodes the bitcoin network and address type. It is used
// in both pay-to-pubkey-hash (P2PKH) and pay-to-script-hash (P2SH) address
// encoding.
func encodeAddress(prefix Bech32Prefix, hash160 []byte, version byte) string {
return bech32.Encode(prefix.String(), hash160[:ripemd160.Size], version)
}
// Address is an interface type for any type of destination a transaction
// output may spend to. This includes pay-to-pubkey (P2PK), pay-to-pubkey-hash
// (P2PKH), and pay-to-script-hash (P2SH). Address is designed to be generic
// enough that other kinds of addresses may be added in the future without
// changing the decoding and encoding API.
type Address interface {
// String returns the string encoding of the transaction output
// destination.
//
// Please note that String differs subtly from EncodeAddress: String
// will return the value as a string without any conversion, while
// EncodeAddress may convert destination types (for example,
// converting pubkeys to P2PKH addresses) before encoding as a
// payment address string.
String() string
// EncodeAddress returns the string encoding of the payment address
// associated with the Address value. See the comment on String
// for how this method differs from String.
EncodeAddress() string
// ScriptAddress returns the raw bytes of the address to be used
// when inserting the address into a txout's script.
ScriptAddress() []byte
// IsForPrefix returns whether or not the address is associated with the
// passed bitcoin network.
IsForPrefix(prefix Bech32Prefix) bool
}
// DecodeAddress decodes the string encoding of an address and returns
// the Address if addr is a valid encoding for a known address type.
//
// The bitcoin network address is associated with is extracted if possible.
// When the address does not encode the network, such as in the case of a raw
// public key, the address will be associated with the passed defaultNet.
func DecodeAddress(addr string, defaultPrefix Bech32Prefix) (Address, error) {
prefixString, decoded, version, err := bech32.Decode(addr)
if err != nil {
return nil, fmt.Errorf("decoded address is of unknown format: %v", err)
}
prefix, err := ParsePrefix(prefixString)
if err != nil {
return nil, fmt.Errorf("decoded address's prefix could not be parsed: %v", err)
}
if defaultPrefix != prefix {
return nil, fmt.Errorf("decoded address is of wrong network: %v", err)
}
// Switch on decoded length to determine the type.
switch len(decoded) {
case ripemd160.Size: // P2PKH or P2SH
switch version {
case pubKeyHashAddrID:
return newAddressPubKeyHash(defaultPrefix, decoded)
case scriptHashAddrID:
return newAddressScriptHashFromHash(defaultPrefix, decoded)
default:
return nil, ErrUnknownAddressType
}
default:
return nil, errors.New("decoded address is of unknown size")
}
}
// AddressPubKeyHash is an Address for a pay-to-pubkey-hash (P2PKH)
// transaction.
type AddressPubKeyHash struct {
prefix Bech32Prefix
hash [ripemd160.Size]byte
}
// NewAddressPubKeyHash returns a new AddressPubKeyHash. pkHash mustbe 20
// bytes.
func NewAddressPubKeyHash(pkHash []byte, prefix Bech32Prefix) (*AddressPubKeyHash, error) {
return newAddressPubKeyHash(prefix, pkHash)
}
// newAddressPubKeyHash is the internal API to create a pubkey hash address
// with a known leading identifier byte for a network, rather than looking
// it up through its parameters. This is useful when creating a new address
// structure from a string encoding where the identifer byte is already
// known.
func newAddressPubKeyHash(prefix Bech32Prefix, pkHash []byte) (*AddressPubKeyHash, error) {
// Check for a valid pubkey hash length.
if len(pkHash) != ripemd160.Size {
return nil, errors.New("pkHash must be 20 bytes")
}
addr := &AddressPubKeyHash{prefix: prefix}
copy(addr.hash[:], pkHash)
return addr, nil
}
// EncodeAddress returns the string encoding of a pay-to-pubkey-hash
// address. Part of the Address interface.
func (a *AddressPubKeyHash) EncodeAddress() string {
return encodeAddress(a.prefix, a.hash[:], pubKeyHashAddrID)
}
// ScriptAddress returns the bytes to be included in a txout script to pay
// to a pubkey hash. Part of the Address interface.
func (a *AddressPubKeyHash) ScriptAddress() []byte {
return a.hash[:]
}
// IsForPrefix returns whether or not the pay-to-pubkey-hash address is associated
// with the passed bitcoin network.
func (a *AddressPubKeyHash) IsForPrefix(prefix Bech32Prefix) bool {
return a.prefix == prefix
}
// String returns a human-readable string for the pay-to-pubkey-hash address.
// This is equivalent to calling EncodeAddress, but is provided so the type can
// be used as a fmt.Stringer.
func (a *AddressPubKeyHash) String() string {
return a.EncodeAddress()
}
// Hash160 returns the underlying array of the pubkey hash. This can be useful
// when an array is more appropiate than a slice (for example, when used as map
// keys).
func (a *AddressPubKeyHash) Hash160() *[ripemd160.Size]byte {
return &a.hash
}
// AddressScriptHash is an Address for a pay-to-script-hash (P2SH)
// transaction.
type AddressScriptHash struct {
prefix Bech32Prefix
hash [ripemd160.Size]byte
}
// NewAddressScriptHash returns a new AddressScriptHash.
func NewAddressScriptHash(serializedScript []byte, prefix Bech32Prefix) (*AddressScriptHash, error) {
scriptHash := Hash160(serializedScript)
return newAddressScriptHashFromHash(prefix, scriptHash)
}
// NewAddressScriptHashFromHash returns a new AddressScriptHash. scriptHash
// must be 20 bytes.
func NewAddressScriptHashFromHash(scriptHash []byte, prefix Bech32Prefix) (*AddressScriptHash, error) {
return newAddressScriptHashFromHash(prefix, scriptHash)
}
// newAddressScriptHashFromHash is the internal API to create a script hash
// address with a known leading identifier byte for a network, rather than
// looking it up through its parameters. This is useful when creating a new
// address structure from a string encoding where the identifer byte is already
// known.
func newAddressScriptHashFromHash(prefix Bech32Prefix, scriptHash []byte) (*AddressScriptHash, error) {
// Check for a valid script hash length.
if len(scriptHash) != ripemd160.Size {
return nil, errors.New("scriptHash must be 20 bytes")
}
addr := &AddressScriptHash{prefix: prefix}
copy(addr.hash[:], scriptHash)
return addr, nil
}
// EncodeAddress returns the string encoding of a pay-to-script-hash
// address. Part of the Address interface.
func (a *AddressScriptHash) EncodeAddress() string {
return encodeAddress(a.prefix, a.hash[:], scriptHashAddrID)
}
// ScriptAddress returns the bytes to be included in a txout script to pay
// to a script hash. Part of the Address interface.
func (a *AddressScriptHash) ScriptAddress() []byte {
return a.hash[:]
}
// IsForPrefix returns whether or not the pay-to-script-hash address is associated
// with the passed bitcoin network.
func (a *AddressScriptHash) IsForPrefix(prefix Bech32Prefix) bool {
return a.prefix == prefix
}
// String returns a human-readable string for the pay-to-script-hash address.
// This is equivalent to calling EncodeAddress, but is provided so the type can
// be used as a fmt.Stringer.
func (a *AddressScriptHash) String() string {
return a.EncodeAddress()
}
// Hash160 returns the underlying array of the script hash. This can be useful
// when an array is more appropiate than a slice (for example, when used as map
// keys).
func (a *AddressScriptHash) Hash160() *[ripemd160.Size]byte {
return &a.hash
}
// PubKeyFormat describes what format to use for a pay-to-pubkey address.
type PubKeyFormat int
const (
// PKFUncompressed indicates the pay-to-pubkey address format is an
// uncompressed public key.
PKFUncompressed PubKeyFormat = iota
// PKFCompressed indicates the pay-to-pubkey address format is a
// compressed public key.
PKFCompressed
// PKFHybrid indicates the pay-to-pubkey address format is a hybrid
// public key.
PKFHybrid
)
// AddressPubKey is an Address for a pay-to-pubkey transaction.
type AddressPubKey struct {
prefix Bech32Prefix
pubKeyFormat PubKeyFormat
pubKey *btcec.PublicKey
pubKeyHashID byte
}
// NewAddressPubKey returns a new AddressPubKey which represents a pay-to-pubkey
// address. The serializedPubKey parameter must be a valid pubkey and can be
// uncompressed, compressed, or hybrid.
func NewAddressPubKey(serializedPubKey []byte, prefix Bech32Prefix) (*AddressPubKey, error) {
pubKey, err := btcec.ParsePubKey(serializedPubKey, btcec.S256())
if err != nil {
return nil, err
}
// Set the format of the pubkey. This probably should be returned
// from btcec, but do it here to avoid API churn. We already know the
// pubkey is valid since it parsed above, so it's safe to simply examine
// the leading byte to get the format.
pkFormat := PKFUncompressed
switch serializedPubKey[0] {
case 0x02, 0x03:
pkFormat = PKFCompressed
case 0x06, 0x07:
pkFormat = PKFHybrid
}
return &AddressPubKey{
pubKeyFormat: pkFormat,
pubKey: pubKey,
pubKeyHashID: pubKeyHashAddrID,
prefix: prefix,
}, nil
}
// serialize returns the serialization of the public key according to the
// format associated with the address.
func (a *AddressPubKey) serialize() []byte {
switch a.pubKeyFormat {
default:
fallthrough
case PKFUncompressed:
return a.pubKey.SerializeUncompressed()
case PKFCompressed:
return a.pubKey.SerializeCompressed()
case PKFHybrid:
return a.pubKey.SerializeHybrid()
}
}
// EncodeAddress returns the string encoding of the public key as a
// pay-to-pubkey-hash. Note that the public key format (uncompressed,
// compressed, etc) will change the resulting address. This is expected since
// pay-to-pubkey-hash is a hash of the serialized public key which obviously
// differs with the format. At the time of this writing, most Bitcoin addresses
// are pay-to-pubkey-hash constructed from the uncompressed public key.
//
// Part of the Address interface.
func (a *AddressPubKey) EncodeAddress() string {
return encodeAddress(a.prefix, Hash160(a.serialize()), a.pubKeyHashID)
}
// ScriptAddress returns the bytes to be included in a txout script to pay
// to a public key. Setting the public key format will affect the output of
// this function accordingly. Part of the Address interface.
func (a *AddressPubKey) ScriptAddress() []byte {
return a.serialize()
}
// IsForPrefix returns whether or not the pay-to-pubkey address is associated
// with the passed bitcoin network.
func (a *AddressPubKey) IsForPrefix(prefix Bech32Prefix) bool {
return a.prefix == prefix
}
// String returns the hex-encoded human-readable string for the pay-to-pubkey
// address. This is not the same as calling EncodeAddress.
func (a *AddressPubKey) String() string {
return hex.EncodeToString(a.serialize())
}
// Format returns the format (uncompressed, compressed, etc) of the
// pay-to-pubkey address.
func (a *AddressPubKey) Format() PubKeyFormat {
return a.pubKeyFormat
}
// SetFormat sets the format (uncompressed, compressed, etc) of the
// pay-to-pubkey address.
func (a *AddressPubKey) SetFormat(pkFormat PubKeyFormat) {
a.pubKeyFormat = pkFormat
}
// AddressPubKeyHash returns the pay-to-pubkey address converted to a
// pay-to-pubkey-hash address. Note that the public key format (uncompressed,
// compressed, etc) will change the resulting address. This is expected since
// pay-to-pubkey-hash is a hash of the serialized public key which obviously
// differs with the format. At the time of this writing, most Bitcoin addresses
// are pay-to-pubkey-hash constructed from the uncompressed public key.
func (a *AddressPubKey) AddressPubKeyHash() *AddressPubKeyHash {
addr := &AddressPubKeyHash{prefix: a.prefix}
copy(addr.hash[:], Hash160(a.serialize()))
return addr
}
// PubKey returns the underlying public key for the address.
func (a *AddressPubKey) PubKey() *btcec.PublicKey {
return a.pubKey
}