1235 lines
50 KiB
Go
1235 lines
50 KiB
Go
// Copyright (c) 2021-2023 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package stdaddr
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
|
|
"github.com/decred/base58"
|
|
"github.com/decred/dcrd/crypto/blake256"
|
|
"github.com/decred/dcrd/crypto/ripemd160"
|
|
"github.com/decred/dcrd/dcrec"
|
|
"github.com/decred/dcrd/dcrec/edwards/v2"
|
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
|
"github.com/decred/dcrd/txscript/v4"
|
|
)
|
|
|
|
const (
|
|
// opPushSTEd25519 is the dcrec.STEd25519 signature type converted to the
|
|
// associated small integer data push opcode.
|
|
opPushSTEd25519 = txscript.OP_1
|
|
|
|
// opPushSTSchnorrSecp256k1 is the dcrec.STSchnorrSecp256k1 signature type
|
|
// converted to the associated small integer data push opcode.
|
|
opPushSTSchnorrSecp256k1 = txscript.OP_2
|
|
|
|
// sigTypeSecp256k1PubKeyCompOddFlag specifies the bitmask to apply to the
|
|
// pubkey address signature type byte for those that deal with compressed
|
|
// secp256k1 pubkeys to specify the omitted y coordinate is odd.
|
|
sigTypeSecp256k1PubKeyCompOddFlag = uint8(1 << 7)
|
|
|
|
// commitP2SHFlag specifies the bitmask to apply to an amount in a ticket
|
|
// commitment in order to specify if it is a pay-to-script-hash commitment.
|
|
// The value is derived from the fact it is encoded as the most significant
|
|
// bit in the amount.
|
|
commitP2SHFlag = uint64(1 << 63)
|
|
|
|
// p2pkhPaymentScriptLen is the length of a standard version 0 P2PKH script
|
|
// for secp256k1+ecdsa.
|
|
p2pkhPaymentScriptLen = 25
|
|
|
|
// p2shPaymentScriptLen is the length of a standard version 0 P2SH script.
|
|
p2shPaymentScriptLen = 23
|
|
)
|
|
|
|
// AddressParamsV0 defines an interface that is used to provide the parameters
|
|
// required when encoding and decoding addresses for version 0 scripts. These
|
|
// values are typically well-defined and unique per network.
|
|
type AddressParamsV0 interface {
|
|
// AddrIDPubKeyV0 returns the magic prefix bytes for version 0 pay-to-pubkey
|
|
// addresses.
|
|
AddrIDPubKeyV0() [2]byte
|
|
|
|
// AddrIDPubKeyHashECDSAV0 returns the magic prefix bytes for version 0
|
|
// pay-to-pubkey-hash addresses where the underlying pubkey is secp256k1 and
|
|
// the signature algorithm is ECDSA.
|
|
AddrIDPubKeyHashECDSAV0() [2]byte
|
|
|
|
// AddrIDPubKeyHashEd25519V0 returns the magic prefix bytes for version 0
|
|
// pay-to-pubkey-hash addresses where the underlying pubkey and signature
|
|
// algorithm are Ed25519.
|
|
AddrIDPubKeyHashEd25519V0() [2]byte
|
|
|
|
// AddrIDPubKeyHashSchnorrV0 returns the magic prefix bytes for version 0
|
|
// pay-to-pubkey-hash addresses where the underlying pubkey is secp256k1 and
|
|
// the signature algorithm is Schnorr.
|
|
AddrIDPubKeyHashSchnorrV0() [2]byte
|
|
|
|
// AddrIDScriptHashV0 returns the magic prefix bytes for version 0
|
|
// pay-to-script-hash addresses.
|
|
AddrIDScriptHashV0() [2]byte
|
|
}
|
|
|
|
// encodeAddressV0 returns a human-readable payment address for the data and
|
|
// netID which encodes the network and address type using the format for version
|
|
// 0 scripts.
|
|
func encodeAddressV0(data []byte, netID [2]byte) string {
|
|
// The overall format for an address for version 0 scripts is the base58
|
|
// check encoding of data which varies by address type. In other words, it
|
|
// is:
|
|
//
|
|
// 2-byte network and address type || data || 4-byte checksum
|
|
return base58.CheckEncode(data, netID)
|
|
}
|
|
|
|
// Hash160 calculates the hash ripemd160(blake256(b)).
|
|
func Hash160(buf []byte) []byte {
|
|
b256Hash := blake256.Sum256(buf)
|
|
hasher := ripemd160.New()
|
|
hasher.Write(b256Hash[:])
|
|
return hasher.Sum(nil)
|
|
}
|
|
|
|
// AddressPubKeyEcdsaSecp256k1V0 specifies an address that represents a payment
|
|
// destination which imposes an encumbrance that requires a valid ECDSA
|
|
// signature for a specific secp256k1 public key.
|
|
//
|
|
// This is commonly referred to as pay-to-pubkey (P2PK) for legacy reasons,
|
|
// however, since it is possible to support multiple algorithm and signature
|
|
// scheme combinations, it is technically more accurate to refer to it as
|
|
// pay-to-pubkey-ecdsa-secp256k1.
|
|
type AddressPubKeyEcdsaSecp256k1V0 struct {
|
|
pubKeyID [2]byte
|
|
pubKeyHashID [2]byte
|
|
serializedPubKey []byte
|
|
}
|
|
|
|
// Ensure AddressPubKeyEcdsaSecp256k1V0 implements the Address and
|
|
// AddressPubKeyHasher interfaces.
|
|
var _ Address = (*AddressPubKeyEcdsaSecp256k1V0)(nil)
|
|
var _ AddressPubKeyHasher = (*AddressPubKeyEcdsaSecp256k1V0)(nil)
|
|
|
|
// NewAddressPubKeyEcdsaSecp256k1V0Raw returns an address that represents a
|
|
// payment destination which imposes an encumbrance that requires a valid ECDSA
|
|
// signature for a specific secp256k1 public key using version 0 scripts.
|
|
//
|
|
// The provided public key MUST be a valid secp256k1 public key serialized in
|
|
// the _compressed_ format or an error will be returned.
|
|
//
|
|
// See NewAddressPubKeyEcdsaSecp256k1V0 for a variant that accepts the public
|
|
// key as a concrete type instance instead.
|
|
//
|
|
// This function can be useful to callers who already need the serialized public
|
|
// key for other purposes to avoid the need to serialize it multiple times.
|
|
func NewAddressPubKeyEcdsaSecp256k1V0Raw(serializedPubKey []byte,
|
|
params AddressParamsV0) (*AddressPubKeyEcdsaSecp256k1V0, error) {
|
|
|
|
// Attempt to parse the provided public key to ensure it is both a valid
|
|
// serialization and that it is a valid point on the secp256k1 curve.
|
|
_, err := secp256k1.ParsePubKey(serializedPubKey)
|
|
if err != nil {
|
|
str := fmt.Sprintf("failed to parse public key: %v", err)
|
|
return nil, makeError(ErrInvalidPubKey, str)
|
|
}
|
|
|
|
// Ensure the provided serialized public key is in the compressed format.
|
|
// This probably should be returned from secp256k1, but do it here to avoid
|
|
// API churn. The pubkey is known to be valid since it parsed above, so
|
|
// it's safe to simply examine the leading byte to get the format.
|
|
//
|
|
// Notice that both the uncompressed and hybrid forms are intentionally not
|
|
// supported.
|
|
switch serializedPubKey[0] {
|
|
case secp256k1.PubKeyFormatCompressedEven:
|
|
case secp256k1.PubKeyFormatCompressedOdd:
|
|
default:
|
|
str := fmt.Sprintf("serialized public key %x is not a valid format",
|
|
serializedPubKey)
|
|
return nil, makeError(ErrInvalidPubKeyFormat, str)
|
|
}
|
|
|
|
return &AddressPubKeyEcdsaSecp256k1V0{
|
|
pubKeyID: params.AddrIDPubKeyV0(),
|
|
pubKeyHashID: params.AddrIDPubKeyHashECDSAV0(),
|
|
serializedPubKey: serializedPubKey,
|
|
}, nil
|
|
}
|
|
|
|
// NewAddressPubKeyEcdsaSecp256k1V0 returns an address that represents a
|
|
// payment destination which imposes an encumbrance that requires a valid ECDSA
|
|
// signature for a specific secp256k1 public key using version 0 scripts.
|
|
//
|
|
// See NewAddressPubKeyEcdsaSecp256k1V0Raw for a variant that accepts the public
|
|
// key already serialized in the _compressed_ format instead of a concrete type.
|
|
// It can be useful to callers who already need the serialized public key for
|
|
// other purposes to avoid the need to serialize it multiple times.
|
|
func NewAddressPubKeyEcdsaSecp256k1V0(pubKey Secp256k1PublicKey,
|
|
params AddressParamsV0) (*AddressPubKeyEcdsaSecp256k1V0, error) {
|
|
|
|
return &AddressPubKeyEcdsaSecp256k1V0{
|
|
pubKeyID: params.AddrIDPubKeyV0(),
|
|
pubKeyHashID: params.AddrIDPubKeyHashECDSAV0(),
|
|
serializedPubKey: pubKey.SerializeCompressed(),
|
|
}, nil
|
|
}
|
|
|
|
// String returns the string encoding of the payment address for the associated
|
|
// script version and payment script.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressPubKeyEcdsaSecp256k1V0) String() string {
|
|
// The format for the data portion of a public key address used with
|
|
// elliptic curves is:
|
|
// identifier byte || 32-byte X coordinate
|
|
//
|
|
// The identifier byte specifies the curve and signature scheme combination
|
|
// as well as encoding the oddness of the Y coordinate for secp256k1 public
|
|
// keys in the high bit.
|
|
var data [33]byte
|
|
data[0] = byte(dcrec.STEcdsaSecp256k1)
|
|
if addr.serializedPubKey[0] == secp256k1.PubKeyFormatCompressedOdd {
|
|
data[0] |= sigTypeSecp256k1PubKeyCompOddFlag
|
|
}
|
|
copy(data[1:], addr.serializedPubKey[1:])
|
|
return encodeAddressV0(data[:], addr.pubKeyID)
|
|
}
|
|
|
|
// PaymentScript returns the script version associated with the address along
|
|
// with a script to pay a transaction output to the address.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressPubKeyEcdsaSecp256k1V0) PaymentScript() (uint16, []byte) {
|
|
// A pay-to-pubkey-ecdsa-secp256k1 script is one of the following forms:
|
|
// <33-byte compressed pubkey> CHECKSIG
|
|
// <65-byte uncompressed pubkey> CHECKSIG
|
|
//
|
|
// However, this address type intentionally only supports the compressed
|
|
// form.
|
|
var script [35]byte
|
|
script[0] = txscript.OP_DATA_33
|
|
copy(script[1:34], addr.serializedPubKey)
|
|
script[34] = txscript.OP_CHECKSIG
|
|
return 0, script[:]
|
|
}
|
|
|
|
// AddressPubKeyHash returns the address converted to a
|
|
// pay-to-pubkey-hash-ecdsa-secp256k1 address.
|
|
//
|
|
// Note that the hash used in resulting address is the hash of the serialized
|
|
// public key and the address constructor intentionally only supports public
|
|
// keys in the compressed format. In other words, the resulting address will
|
|
// impose an encumbrance that requires the public key to be provided in the
|
|
// compressed format.
|
|
func (addr *AddressPubKeyEcdsaSecp256k1V0) AddressPubKeyHash() Address {
|
|
pkHash := Hash160(addr.serializedPubKey)
|
|
addrPKH := &AddressPubKeyHashEcdsaSecp256k1V0{
|
|
netID: addr.pubKeyHashID,
|
|
}
|
|
copy(addrPKH.hash[:], pkHash)
|
|
return addrPKH
|
|
}
|
|
|
|
// SerializedPubKey returns the compressed serialization of the secp256k1 public
|
|
// key. The bytes must not be modified.
|
|
func (addr *AddressPubKeyEcdsaSecp256k1V0) SerializedPubKey() []byte {
|
|
return addr.serializedPubKey
|
|
}
|
|
|
|
// AddressPubKeyEd25519V0 specifies an address that represents a payment
|
|
// destination which imposes an encumbrance that requires a valid Ed25519
|
|
// signature for a specific Ed25519 public key.
|
|
//
|
|
// This is commonly referred to as pay-to-pubkey-ed25519.
|
|
type AddressPubKeyEd25519V0 struct {
|
|
pubKeyID [2]byte
|
|
pubKeyHashID [2]byte
|
|
serializedPubKey []byte
|
|
}
|
|
|
|
// Ensure AddressPubKeyEd25519V0 implements the Address and AddressPubKeyHasher
|
|
// interfaces.
|
|
var _ Address = (*AddressPubKeyEd25519V0)(nil)
|
|
var _ AddressPubKeyHasher = (*AddressPubKeyEd25519V0)(nil)
|
|
|
|
// NewAddressPubKeyEd25519V0Raw returns an address that represents a payment
|
|
// destination which imposes an encumbrance that requires a valid Ed25519
|
|
// signature for a specific Ed25519 public key using version 0 scripts.
|
|
//
|
|
// See NewAddressPubKeyEd25519V0 for a variant that accepts the public key as a
|
|
// concrete type instance instead.
|
|
func NewAddressPubKeyEd25519V0Raw(serializedPubKey []byte,
|
|
params AddressParamsV0) (*AddressPubKeyEd25519V0, error) {
|
|
|
|
// Attempt to parse the provided public key to ensure it is both a valid
|
|
// serialization and that it is a valid point on the underlying curve.
|
|
_, err := edwards.ParsePubKey(serializedPubKey)
|
|
if err != nil {
|
|
str := fmt.Sprintf("failed to parse public key: %v", err)
|
|
return nil, makeError(ErrInvalidPubKey, str)
|
|
}
|
|
|
|
return &AddressPubKeyEd25519V0{
|
|
pubKeyID: params.AddrIDPubKeyV0(),
|
|
pubKeyHashID: params.AddrIDPubKeyHashEd25519V0(),
|
|
serializedPubKey: serializedPubKey,
|
|
}, nil
|
|
}
|
|
|
|
// NewAddressPubKeyEd25519V0 returns an address that represents a payment
|
|
// destination which imposes an encumbrance that requires a valid Ed25519
|
|
// signature for a specific Ed25519 public key using version 0 scripts.
|
|
//
|
|
// See NewAddressPubKeyEd25519Raw for a variant that accepts the public key
|
|
// already serialized instead of a concrete type. It can be useful to callers
|
|
// who already need the serialized public key for other purposes to avoid the
|
|
// need to serialize it multiple times.
|
|
func NewAddressPubKeyEd25519V0(pubKey Ed25519PublicKey,
|
|
params AddressParamsV0) (*AddressPubKeyEd25519V0, error) {
|
|
|
|
return &AddressPubKeyEd25519V0{
|
|
pubKeyID: params.AddrIDPubKeyV0(),
|
|
pubKeyHashID: params.AddrIDPubKeyHashEd25519V0(),
|
|
serializedPubKey: pubKey.Serialize(),
|
|
}, nil
|
|
}
|
|
|
|
// String returns the string encoding of the payment address for the associated
|
|
// script version and payment script.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressPubKeyEd25519V0) String() string {
|
|
// The format for the data portion of a public key address used with
|
|
// elliptic curves is:
|
|
// identifier byte || 32-byte X coordinate
|
|
//
|
|
// The identifier byte specifies the curve and signature scheme combination
|
|
// as well as encoding the oddness of the Y coordinate for secp256k1 public
|
|
// keys in the high bit.
|
|
//
|
|
// Since this address is for an ed25519 public key, the oddness bit is not
|
|
// used/encoded.
|
|
var data [33]byte
|
|
data[0] = byte(dcrec.STEd25519)
|
|
copy(data[1:], addr.serializedPubKey)
|
|
return encodeAddressV0(data[:], addr.pubKeyID)
|
|
}
|
|
|
|
// PaymentScript returns the script version associated with the address along
|
|
// with a script to pay a transaction output to the address.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressPubKeyEd25519V0) PaymentScript() (uint16, []byte) {
|
|
// A pay-to-pubkey-ed25519 script is one of the form:
|
|
// <32-byte pubkey> <1-byte sigtype> CHECKSIGALT
|
|
//
|
|
// Since the signature type is 1, it is pushed as a small integer.
|
|
var script [35]byte
|
|
script[0] = txscript.OP_DATA_32
|
|
copy(script[1:33], addr.serializedPubKey)
|
|
script[33] = opPushSTEd25519
|
|
script[34] = txscript.OP_CHECKSIGALT
|
|
return 0, script[:]
|
|
}
|
|
|
|
// AddressPubKeyHash returns the address converted to a
|
|
// pay-to-pubkey-hash-ed25519 address.
|
|
func (addr *AddressPubKeyEd25519V0) AddressPubKeyHash() Address {
|
|
pkHash := Hash160(addr.serializedPubKey)
|
|
addrPKH := &AddressPubKeyHashEd25519V0{
|
|
netID: addr.pubKeyHashID,
|
|
}
|
|
copy(addrPKH.hash[:], pkHash)
|
|
return addrPKH
|
|
}
|
|
|
|
// SerializedPubKey returns the serialization of the ed25519 public key. The
|
|
// bytes must not be modified.
|
|
func (addr *AddressPubKeyEd25519V0) SerializedPubKey() []byte {
|
|
return addr.serializedPubKey
|
|
}
|
|
|
|
// AddressPubKeySchnorrSecp256k1V0 specifies an address that represents a
|
|
// payment destination which imposes an encumbrance that requires a valid
|
|
// EC-Schnorr-DCRv0 signature for a specific secp256k1 public key.
|
|
//
|
|
// This is commonly referred to as pay-to-pubkey-schnorr-secp256k1.
|
|
type AddressPubKeySchnorrSecp256k1V0 struct {
|
|
pubKeyID [2]byte
|
|
pubKeyHashID [2]byte
|
|
serializedPubKey []byte
|
|
}
|
|
|
|
// Ensure AddressPubKeySchnorrSecp256k1V0 implements the Address and
|
|
// AddressPubKeyHasher interface.
|
|
var _ Address = (*AddressPubKeySchnorrSecp256k1V0)(nil)
|
|
var _ AddressPubKeyHasher = (*AddressPubKeySchnorrSecp256k1V0)(nil)
|
|
|
|
// NewAddressPubKeySchnorrSecp256k1V0Raw returns an address that represents a
|
|
// payment destination which imposes an encumbrance that requires a valid
|
|
// EC-Schnorr-DCRv0 signature for a specific secp256k1 public key using version
|
|
// 0 scripts.
|
|
//
|
|
// The provided public key MUST be a valid secp256k1 public key serialized in
|
|
// the _compressed_ format or an error will be returned.
|
|
//
|
|
// See NewAddressPubKeySchnorrSecp256k1V0 for a variant that accepts the public
|
|
// key as a concrete type instance instead.
|
|
//
|
|
// This function can be useful to callers who already need the serialized public
|
|
// key for other purposes to avoid the need to serialize it multiple times.
|
|
func NewAddressPubKeySchnorrSecp256k1V0Raw(serializedPubKey []byte,
|
|
params AddressParamsV0) (*AddressPubKeySchnorrSecp256k1V0, error) {
|
|
|
|
// Attempt to parse the provided public key to ensure it is both a valid
|
|
// serialization and that it is a valid point on the secp256k1 curve.
|
|
_, err := secp256k1.ParsePubKey(serializedPubKey)
|
|
if err != nil {
|
|
str := fmt.Sprintf("failed to parse public key: %v", err)
|
|
return nil, makeError(ErrInvalidPubKey, str)
|
|
}
|
|
|
|
// Ensure the provided serialized public key is in the compressed format.
|
|
// This probably should be returned from secp256k1, but do it here to avoid
|
|
// API churn. The pubkey is known to be valid since it parsed above, so
|
|
// it's safe to simply examine the leading byte to get the format.
|
|
//
|
|
// Notice that both the uncompressed and hybrid forms are intentionally not
|
|
// supported.
|
|
switch serializedPubKey[0] {
|
|
case secp256k1.PubKeyFormatCompressedEven:
|
|
case secp256k1.PubKeyFormatCompressedOdd:
|
|
default:
|
|
str := fmt.Sprintf("serialized public key %x is not a valid format",
|
|
serializedPubKey)
|
|
return nil, makeError(ErrInvalidPubKeyFormat, str)
|
|
}
|
|
|
|
return &AddressPubKeySchnorrSecp256k1V0{
|
|
pubKeyID: params.AddrIDPubKeyV0(),
|
|
pubKeyHashID: params.AddrIDPubKeyHashSchnorrV0(),
|
|
serializedPubKey: serializedPubKey,
|
|
}, nil
|
|
}
|
|
|
|
// NewAddressPubKeySchnorrSecp256k1V0 returns an address that represents a
|
|
// payment destination which imposes an encumbrance that requires a valid
|
|
// EC-Schnorr-DCRv0 signature for a specific secp256k1 public key using version
|
|
// 0 scripts.
|
|
//
|
|
// See NewAddressPubKeySchnorrSecp256k1V0Raw for a variant that accepts the public
|
|
// key already serialized in the _compressed_ format instead of a concrete type.
|
|
// It can be useful to callers who already need the serialized public key for
|
|
// other purposes to avoid the need to serialize it multiple times.
|
|
func NewAddressPubKeySchnorrSecp256k1V0(pubKey Secp256k1PublicKey,
|
|
params AddressParamsV0) (*AddressPubKeySchnorrSecp256k1V0, error) {
|
|
|
|
return &AddressPubKeySchnorrSecp256k1V0{
|
|
pubKeyID: params.AddrIDPubKeyV0(),
|
|
pubKeyHashID: params.AddrIDPubKeyHashSchnorrV0(),
|
|
serializedPubKey: pubKey.SerializeCompressed(),
|
|
}, nil
|
|
}
|
|
|
|
// String returns the string encoding of the payment address for the associated
|
|
// script version and payment script.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressPubKeySchnorrSecp256k1V0) String() string {
|
|
// The format for the data portion of a public key address used with
|
|
// elliptic curves is:
|
|
// identifier byte || 32-byte X coordinate
|
|
//
|
|
// The identifier byte specifies the curve and signature scheme combination
|
|
// as well as encoding the oddness of the Y coordinate for secp256k1 public
|
|
// keys in the high bit.
|
|
var data [33]byte
|
|
data[0] = byte(dcrec.STSchnorrSecp256k1)
|
|
if addr.serializedPubKey[0] == secp256k1.PubKeyFormatCompressedOdd {
|
|
data[0] |= sigTypeSecp256k1PubKeyCompOddFlag
|
|
}
|
|
copy(data[1:], addr.serializedPubKey[1:])
|
|
return encodeAddressV0(data[:], addr.pubKeyID)
|
|
}
|
|
|
|
// PaymentScript returns the script version associated with the address along
|
|
// with a script to pay a transaction output to the address.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressPubKeySchnorrSecp256k1V0) PaymentScript() (uint16, []byte) {
|
|
// A pay-to-pubkey-schnorr-secp256k1 script is of the following form:
|
|
// <33-byte compressed pubkey> <1-byte sigtype> CHECKSIGALT
|
|
//
|
|
// Since the signature type is 2, it is pushed as a small integer.
|
|
var script [36]byte
|
|
script[0] = txscript.OP_DATA_33
|
|
copy(script[1:34], addr.serializedPubKey)
|
|
script[34] = opPushSTSchnorrSecp256k1
|
|
script[35] = txscript.OP_CHECKSIGALT
|
|
return 0, script[:]
|
|
}
|
|
|
|
// AddressPubKeyHash returns the address converted to a
|
|
// pay-to-pubkey-hash-schnorr-secp256k1 address.
|
|
//
|
|
// Note that the hash used in resulting address is the hash of the serialized
|
|
// public key and only public keys in the compressed format are supported. In
|
|
// other words, the resulting address will impose an encumbrance that requires
|
|
// the public key to be provided in the compressed format.
|
|
func (addr *AddressPubKeySchnorrSecp256k1V0) AddressPubKeyHash() Address {
|
|
pkHash := Hash160(addr.serializedPubKey)
|
|
addrPKH := &AddressPubKeyHashSchnorrSecp256k1V0{
|
|
netID: addr.pubKeyHashID,
|
|
}
|
|
copy(addrPKH.hash[:], pkHash)
|
|
return addrPKH
|
|
}
|
|
|
|
// SerializedPubKey returns the compressed serialization of the secp256k1 public
|
|
// key. The bytes must not be modified.
|
|
func (addr *AddressPubKeySchnorrSecp256k1V0) SerializedPubKey() []byte {
|
|
return addr.serializedPubKey
|
|
}
|
|
|
|
// AddressPubKeyHashEcdsaSecp256k1V0 specifies an address that represents a
|
|
// payment destination which imposes an encumbrance that requires a secp256k1
|
|
// public key that hashes to the given public key hash along with a valid ECDSA
|
|
// signature for that public key.
|
|
//
|
|
// This is commonly referred to as pay-to-pubkey-hash (P2PKH) for legacy
|
|
// reasons, however, since it is possible to support multiple algorithm and
|
|
// signature scheme combinations, it is technically more accurate to refer to it
|
|
// as pay-to-pubkey-hash-ecdsa-secp256k1.
|
|
type AddressPubKeyHashEcdsaSecp256k1V0 struct {
|
|
netID [2]byte
|
|
hash [ripemd160.Size]byte
|
|
}
|
|
|
|
// Ensure AddressPubKeyHashEcdsaSecp256k1V0 implements the Address,
|
|
// StakeAddress, and Hash160er interfaces.
|
|
var _ Address = (*AddressPubKeyHashEcdsaSecp256k1V0)(nil)
|
|
var _ StakeAddress = (*AddressPubKeyHashEcdsaSecp256k1V0)(nil)
|
|
var _ Hash160er = (*AddressPubKeyHashEcdsaSecp256k1V0)(nil)
|
|
|
|
// NewAddressPubKeyHashEcdsaSecp256k1V0 returns an address that represents a
|
|
// payment destination which imposes an encumbrance that requires a secp256k1
|
|
// public key that hashes to the provided public key hash along with a valid
|
|
// ECDSA signature for that public key using version 0 scripts.
|
|
//
|
|
// The provided public key hash must be 20 bytes and is expected to be the
|
|
// Hash160 of the associated secp256k1 public key serialized in the _compressed_
|
|
// format.
|
|
//
|
|
// It is important to note that while it is technically possible for legacy
|
|
// reasons to create this specific type of address based on the hash of a public
|
|
// key in the uncompressed format, so long as it is also redeemed with that same
|
|
// public key in uncompressed format, it is *HIGHLY* recommended to use the
|
|
// compressed format since it occupies less space on the chain and is more
|
|
// consistent with other address formats where uncompressed public keys are NOT
|
|
// supported.
|
|
func NewAddressPubKeyHashEcdsaSecp256k1V0(pkHash []byte,
|
|
params AddressParamsV0) (*AddressPubKeyHashEcdsaSecp256k1V0, error) {
|
|
|
|
// Check for a valid script hash length.
|
|
if len(pkHash) != ripemd160.Size {
|
|
str := fmt.Sprintf("public key hash is %d bytes vs required %d bytes",
|
|
len(pkHash), ripemd160.Size)
|
|
return nil, makeError(ErrInvalidHashLen, str)
|
|
}
|
|
|
|
addr := &AddressPubKeyHashEcdsaSecp256k1V0{
|
|
netID: params.AddrIDPubKeyHashECDSAV0(),
|
|
}
|
|
copy(addr.hash[:], pkHash)
|
|
return addr, nil
|
|
}
|
|
|
|
// String returns the string encoding of the payment address for the associated
|
|
// script version and payment script.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressPubKeyHashEcdsaSecp256k1V0) String() string {
|
|
// The format for the data portion of addresses that encode 160-bit hashes
|
|
// is merely the hash itself:
|
|
// 20-byte ripemd160 hash
|
|
return encodeAddressV0(addr.hash[:ripemd160.Size], addr.netID)
|
|
}
|
|
|
|
// putPaymentScript serializes the payment script associated with the address
|
|
// directly into the passed byte slice which must be at least
|
|
// p2pkhPaymentScriptLen bytes in length or it will panic.
|
|
func (addr *AddressPubKeyHashEcdsaSecp256k1V0) putPaymentScript(script []byte) {
|
|
// A pay-to-pubkey-hash-ecdsa-secp256k1 script is of the form:
|
|
// DUP HASH160 <20-byte hash> EQUALVERIFY CHECKSIG
|
|
script[0] = txscript.OP_DUP
|
|
script[1] = txscript.OP_HASH160
|
|
script[2] = txscript.OP_DATA_20
|
|
copy(script[3:23], addr.hash[:])
|
|
script[23] = txscript.OP_EQUALVERIFY
|
|
script[24] = txscript.OP_CHECKSIG
|
|
}
|
|
|
|
// PaymentScript returns the script version associated with the address along
|
|
// with a script to pay a transaction output to the address.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressPubKeyHashEcdsaSecp256k1V0) PaymentScript() (uint16, []byte) {
|
|
// A pay-to-pubkey-hash-ecdsa-secp256k1 script is of the form:
|
|
// DUP HASH160 <20-byte hash> EQUALVERIFY CHECKSIG
|
|
var script [p2pkhPaymentScriptLen]byte
|
|
addr.putPaymentScript(script[:])
|
|
return 0, script[:]
|
|
}
|
|
|
|
// VotingRightsScript returns the script version associated with the address
|
|
// along with a script to give voting rights to the address. It is only valid
|
|
// when used in stake ticket purchase transactions.
|
|
//
|
|
// This is part of the StakeAddress interface implementation.
|
|
func (addr *AddressPubKeyHashEcdsaSecp256k1V0) VotingRightsScript() (uint16, []byte) {
|
|
// A script that assigns voting rights for a ticket to this address type is
|
|
// of the form:
|
|
// SSTX [standard pay-to-pubkey-hash-ecdsa-secp256k1 script]
|
|
var script [p2pkhPaymentScriptLen + 1]byte
|
|
script[0] = txscript.OP_SSTX
|
|
addr.putPaymentScript(script[1:])
|
|
return 0, script[:]
|
|
}
|
|
|
|
// calcRewardCommitScriptLimits calculates the encoded limits to impose on fees
|
|
// applied to votes and revocations via the reward commitment script of a ticket
|
|
// purchase.
|
|
func calcRewardCommitScriptLimits(voteFeeLimit, revocationFeeLimit int64) uint16 {
|
|
// The limits are defined in terms of the closest base 2 exponent and
|
|
// a bit that must be set to specify the limit is to be applied. The
|
|
// vote fee exponent is in the bottom 8 bits, while the revocation fee
|
|
// exponent is in the upper 8 bits.
|
|
limits := uint16(0)
|
|
if voteFeeLimit != 0 {
|
|
exp := uint16(math.Ceil(math.Log2(float64(voteFeeLimit))))
|
|
limits |= (exp | 0x40)
|
|
}
|
|
if revocationFeeLimit != 0 {
|
|
exp := uint16(math.Ceil(math.Log2(float64(revocationFeeLimit))))
|
|
limits |= ((exp | 0x40) << 8)
|
|
}
|
|
return limits
|
|
}
|
|
|
|
// RewardCommitmentScript returns the script version associated with the address
|
|
// along with a script that commits the original funds locked to purchase a
|
|
// ticket plus the reward to the address along with limits to impose on any
|
|
// fees (in atoms).
|
|
//
|
|
// Note that fee limits are encoded in the commitment script in terms of the
|
|
// closest base 2 exponent that results in a limit that is >= the provided
|
|
// limit. In other words, the limits are rounded up to the next power of 2
|
|
// when they are not already an exact power of 2. For example, a revocation
|
|
// limit of 2^23 + 1 will result in allowing a revocation fee of up to 2^24
|
|
// atoms.
|
|
//
|
|
// This is part of the StakeAddress interface implementation.
|
|
func (addr *AddressPubKeyHashEcdsaSecp256k1V0) RewardCommitmentScript(amount, voteFeeLimit, revocationFeeLimit int64) (uint16, []byte) {
|
|
// The reward commitment output of a ticket purchase is a provably pruneable
|
|
// script of the form:
|
|
// RETURN <20-byte hash || 8-byte amount || 2-byte fee limits>
|
|
//
|
|
// The high bit of the amount is used to indicate whether the provided hash
|
|
// is a public key hash that represents a pay-to-pubkey-hash-ecdsa-secp256k1
|
|
// script or a script hash that represents a pay-to-script-hash script. It
|
|
// is NOT set for a public key hash.
|
|
limits := calcRewardCommitScriptLimits(voteFeeLimit, revocationFeeLimit)
|
|
var script [32]byte
|
|
script[0] = txscript.OP_RETURN
|
|
script[1] = txscript.OP_DATA_30
|
|
copy(script[2:22], addr.hash[:])
|
|
binary.LittleEndian.PutUint64(script[22:30], uint64(amount) & ^commitP2SHFlag)
|
|
binary.LittleEndian.PutUint16(script[30:32], limits)
|
|
return 0, script[:]
|
|
}
|
|
|
|
// StakeChangeScript returns the script version associated with the address
|
|
// along with a script to pay change to the address. It is only valid when used
|
|
// in stake ticket purchase and treasury add transactions.
|
|
//
|
|
// This is part of the StakeAddress interface implementation.
|
|
func (addr *AddressPubKeyHashEcdsaSecp256k1V0) StakeChangeScript() (uint16, []byte) {
|
|
// A stake change script to this address type is of the form:
|
|
// SSTXCHANGE [standard pay-to-pubkey-hash-ecdsa-secp256k1 script]
|
|
var script [p2pkhPaymentScriptLen + 1]byte
|
|
script[0] = txscript.OP_SSTXCHANGE
|
|
addr.putPaymentScript(script[1:])
|
|
return 0, script[:]
|
|
}
|
|
|
|
// PayVoteCommitmentScript returns the script version associated with the
|
|
// address along with a script to pay the original funds locked to purchase a
|
|
// ticket plus the reward to the address. The address must have previously been
|
|
// committed to by the ticket purchase. The script is only valid when used in
|
|
// stake vote transactions whose associated tickets are eligible to vote.
|
|
//
|
|
// This is part of the StakeAddress interface implementation.
|
|
func (addr *AddressPubKeyHashEcdsaSecp256k1V0) PayVoteCommitmentScript() (uint16, []byte) {
|
|
// A script that pays a ticket commitment as part of a vote to this address
|
|
// type is of the form:
|
|
// SSGEN [standard pay-to-pubkey-hash-ecdsa-secp256k1 script]
|
|
var script [p2pkhPaymentScriptLen + 1]byte
|
|
script[0] = txscript.OP_SSGEN
|
|
addr.putPaymentScript(script[1:])
|
|
return 0, script[:]
|
|
}
|
|
|
|
// PayRevokeCommitmentScript returns the script version associated with the
|
|
// address along with a script to revoke an expired or missed ticket which pays
|
|
// the original funds locked to purchase a ticket to the address. The address
|
|
// must have previously been committed to by the ticket purchase. The script is
|
|
// only valid when used in stake revocation transactions whose associated
|
|
// tickets have been missed or expired.
|
|
//
|
|
// This is part of the StakeAddress interface implementation.
|
|
func (addr *AddressPubKeyHashEcdsaSecp256k1V0) PayRevokeCommitmentScript() (uint16, []byte) {
|
|
// A ticket revocation script to this address type is of the form:
|
|
// SSRTX [standard pay-to-pubkey-hash-ecdsa-secp256k1 script]
|
|
var script [p2pkhPaymentScriptLen + 1]byte
|
|
script[0] = txscript.OP_SSRTX
|
|
addr.putPaymentScript(script[1:])
|
|
return 0, script[:]
|
|
}
|
|
|
|
// PayFromTreasuryScript returns the script version associated with the address
|
|
// along with a script that pays funds from the treasury to the address. The
|
|
// script is only valid when used in treasury spend transactions.
|
|
//
|
|
// This is part of the StakeAddress interface implementation.
|
|
func (addr *AddressPubKeyHashEcdsaSecp256k1V0) PayFromTreasuryScript() (uint16, []byte) {
|
|
// A script that pays from the treasury as a part of a treasury spend to
|
|
// this address type is of the form:
|
|
// TGEN [standard pay-to-pubkey-hash-ecdsa-secp256k1 script]
|
|
var script [p2pkhPaymentScriptLen + 1]byte
|
|
script[0] = txscript.OP_TGEN
|
|
addr.putPaymentScript(script[1:])
|
|
return 0, script[:]
|
|
}
|
|
|
|
// Hash160 returns the underlying array of the pubkey hash. This can be useful
|
|
// when an array is more appropriate than a slice (for example, when used as map
|
|
// keys).
|
|
func (addr *AddressPubKeyHashEcdsaSecp256k1V0) Hash160() *[ripemd160.Size]byte {
|
|
return &addr.hash
|
|
}
|
|
|
|
// AddressPubKeyHashEd25519V0 specifies an address that represents a payment
|
|
// destination which imposes an encumbrance that requires an Ed25519 public key
|
|
// that hashes to the given public key hash along with a valid Ed25519 signature
|
|
// for that public key.
|
|
//
|
|
// This is commonly referred to as pay-to-pubkey-hash-ed25519.
|
|
type AddressPubKeyHashEd25519V0 struct {
|
|
netID [2]byte
|
|
hash [ripemd160.Size]byte
|
|
}
|
|
|
|
// Ensure AddressPubKeyHashEd25519V0 implements the Address and Hash160er
|
|
// interfaces.
|
|
var _ Address = (*AddressPubKeyHashEd25519V0)(nil)
|
|
var _ Hash160er = (*AddressPubKeyHashEd25519V0)(nil)
|
|
|
|
// NewAddressPubKeyHashEd25519V0 returns an address that represents a payment
|
|
// destination which imposes an encumbrance that requires an Ed25519 public key
|
|
// that hashes to the provided public key hash along with a valid Ed25519
|
|
// signature for that public key using version 0 scripts.
|
|
//
|
|
// The provided public key hash must be 20 bytes and be the Hash160 of the
|
|
// correct public key or it will not be redeemable with the expected public key
|
|
// because it would hash to a different value than the payment script generated
|
|
// for the provided incorrect public key hash expects.
|
|
func NewAddressPubKeyHashEd25519V0(pkHash []byte,
|
|
params AddressParamsV0) (*AddressPubKeyHashEd25519V0, error) {
|
|
|
|
// Check for a valid script hash length.
|
|
if len(pkHash) != ripemd160.Size {
|
|
str := fmt.Sprintf("public key hash is %d bytes vs required %d bytes",
|
|
len(pkHash), ripemd160.Size)
|
|
return nil, makeError(ErrInvalidHashLen, str)
|
|
}
|
|
|
|
addr := &AddressPubKeyHashEd25519V0{
|
|
netID: params.AddrIDPubKeyHashEd25519V0(),
|
|
}
|
|
copy(addr.hash[:], pkHash)
|
|
return addr, nil
|
|
}
|
|
|
|
// String returns the string encoding of the payment address for the associated
|
|
// script version and payment script.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressPubKeyHashEd25519V0) String() string {
|
|
// The format for the data portion of addresses that encode 160-bit hashes
|
|
// is merely the hash itself:
|
|
// 20-byte ripemd160 hash
|
|
return encodeAddressV0(addr.hash[:ripemd160.Size], addr.netID)
|
|
}
|
|
|
|
// PaymentScript returns the script version associated with the address along
|
|
// with a script to pay a transaction output to the address.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressPubKeyHashEd25519V0) PaymentScript() (uint16, []byte) {
|
|
// A pay-to-pubkey-hash-ed25519 script is of the form:
|
|
// DUP HASH160 <20-byte hash> EQUALVERIFY <1-byte sigtype> CHECKSIGALT
|
|
//
|
|
// Since the signature type is 1, it is pushed as a small integer.
|
|
var script [26]byte
|
|
script[0] = txscript.OP_DUP
|
|
script[1] = txscript.OP_HASH160
|
|
script[2] = txscript.OP_DATA_20
|
|
copy(script[3:23], addr.hash[:])
|
|
script[23] = txscript.OP_EQUALVERIFY
|
|
script[24] = opPushSTEd25519
|
|
script[25] = txscript.OP_CHECKSIGALT
|
|
return 0, script[:]
|
|
}
|
|
|
|
// Hash160 returns the underlying array of the pubkey hash. This can be useful
|
|
// when an array is more appropriate than a slice (for example, when used as map
|
|
// keys).
|
|
func (addr *AddressPubKeyHashEd25519V0) Hash160() *[ripemd160.Size]byte {
|
|
return &addr.hash
|
|
}
|
|
|
|
// AddressPubKeyHashSchnorrSecp256k1V0 specifies address that represents a
|
|
// payment destination which imposes an encumbrance that requires a secp256k1
|
|
// public key in the _compressed_ format that hashes to the given public key
|
|
// hash along with a valid EC-Schnorr-DCRv0 signature for that public key.
|
|
//
|
|
// This is commonly referred to as pay-to-pubkey-hash-schnorr-secp256k1.
|
|
type AddressPubKeyHashSchnorrSecp256k1V0 struct {
|
|
netID [2]byte
|
|
hash [ripemd160.Size]byte
|
|
}
|
|
|
|
// Ensure AddressPubKeyHashSchnorrSecp256k1V0 implements the Address and
|
|
// Hash160er interfaces.
|
|
var _ Address = (*AddressPubKeyHashSchnorrSecp256k1V0)(nil)
|
|
var _ Hash160er = (*AddressPubKeyHashSchnorrSecp256k1V0)(nil)
|
|
|
|
// NewAddressPubKeyHashSchnorrSecp256k1V0 returns an address that represents a
|
|
// payment destination which imposes an encumbrance that requires a secp256k1
|
|
// public key in the _compressed_ format that hashes to the provided public key
|
|
// hash along with a valid EC-Schnorr-DCRv0 signature for that public key using
|
|
// version 0 scripts.
|
|
//
|
|
// The provided public key hash must be 20 bytes and is expected to be the
|
|
// Hash160 of the associated secp256k1 public key serialized in the _compressed_
|
|
// format.
|
|
//
|
|
// WARNING: It is important to note that, unlike in the case of the ECDSA
|
|
// variant of this type of address, redemption via a public key in the
|
|
// uncompressed format is NOT supported by the consensus rules for this type, so
|
|
// it is *EXTREMELY* important to ensure the provided hash is of the serialized
|
|
// public key in the compressed format or the associated coins will NOT be
|
|
// redeemable.
|
|
func NewAddressPubKeyHashSchnorrSecp256k1V0(pkHash []byte,
|
|
params AddressParamsV0) (*AddressPubKeyHashSchnorrSecp256k1V0, error) {
|
|
|
|
// Check for a valid script hash length.
|
|
if len(pkHash) != ripemd160.Size {
|
|
str := fmt.Sprintf("public key hash is %d bytes vs required %d bytes",
|
|
len(pkHash), ripemd160.Size)
|
|
return nil, makeError(ErrInvalidHashLen, str)
|
|
}
|
|
|
|
addr := &AddressPubKeyHashSchnorrSecp256k1V0{
|
|
netID: params.AddrIDPubKeyHashSchnorrV0(),
|
|
}
|
|
copy(addr.hash[:], pkHash)
|
|
return addr, nil
|
|
}
|
|
|
|
// String returns the string encoding of the payment address for the associated
|
|
// script version and payment script.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressPubKeyHashSchnorrSecp256k1V0) String() string {
|
|
// The format for the data portion of addresses that encode 160-bit hashes
|
|
// is merely the hash itself:
|
|
// 20-byte ripemd160 hash
|
|
return encodeAddressV0(addr.hash[:ripemd160.Size], addr.netID)
|
|
}
|
|
|
|
// PaymentScript returns the script version associated with the address along
|
|
// with a script to pay a transaction output to the address.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressPubKeyHashSchnorrSecp256k1V0) PaymentScript() (uint16, []byte) {
|
|
// A pay-to-pubkey-hash-schnorr-secp256k1 script is of the form:
|
|
// DUP HASH160 <20-byte hash> EQUALVERIFY <1-byte sigtype> CHECKSIGALT
|
|
//
|
|
// Since the signature type is 2, it is pushed as a small integer.
|
|
var script [26]byte
|
|
script[0] = txscript.OP_DUP
|
|
script[1] = txscript.OP_HASH160
|
|
script[2] = txscript.OP_DATA_20
|
|
copy(script[3:23], addr.hash[:])
|
|
script[23] = txscript.OP_EQUALVERIFY
|
|
script[24] = opPushSTSchnorrSecp256k1
|
|
script[25] = txscript.OP_CHECKSIGALT
|
|
return 0, script[:]
|
|
}
|
|
|
|
// Hash160 returns the underlying array of the pubkey hash. This can be useful
|
|
// when an array is more appropriate than a slice (for example, when used as map
|
|
// keys).
|
|
func (addr *AddressPubKeyHashSchnorrSecp256k1V0) Hash160() *[ripemd160.Size]byte {
|
|
return &addr.hash
|
|
}
|
|
|
|
// AddressScriptHashV0 specifies an address that represents a payment
|
|
// destination which imposes an encumbrance that requires a script that hashes
|
|
// to the provided script hash along with all of the encumbrances that script
|
|
// itself imposes. The script is commonly referred to as a redeem script.
|
|
//
|
|
// This is commonly referred to as pay-to-script-hash (P2SH).
|
|
type AddressScriptHashV0 struct {
|
|
netID [2]byte
|
|
hash [ripemd160.Size]byte
|
|
}
|
|
|
|
// Ensure AddressScriptHashV0 implements the Address and StakeAddress
|
|
// interfaces.
|
|
var _ Address = (*AddressScriptHashV0)(nil)
|
|
var _ StakeAddress = (*AddressScriptHashV0)(nil)
|
|
|
|
// NewAddressScriptHashV0FromHash returns an address that represents a payment
|
|
// destination which imposes an encumbrance that requires a script that hashes
|
|
// to the provided script hash along with all of the encumbrances that script
|
|
// itself imposes using version 0 scripts. The script is commonly referred to
|
|
// as a redeem script.
|
|
//
|
|
// The provided script hash must be 20 bytes and is expected to be the Hash160
|
|
// of the associated redeem script.
|
|
//
|
|
// See NewAddressScriptHashV0 for a variant that accepts the redeem script instead
|
|
// of its hash. It can be used as a convenience for callers that have the
|
|
// redeem script available.
|
|
func NewAddressScriptHashV0FromHash(scriptHash []byte,
|
|
params AddressParamsV0) (*AddressScriptHashV0, error) {
|
|
|
|
// Check for a valid script hash length.
|
|
if len(scriptHash) != ripemd160.Size {
|
|
str := fmt.Sprintf("script hash is %d bytes vs required %d bytes",
|
|
len(scriptHash), ripemd160.Size)
|
|
return nil, makeError(ErrInvalidHashLen, str)
|
|
}
|
|
|
|
addr := &AddressScriptHashV0{
|
|
netID: params.AddrIDScriptHashV0(),
|
|
}
|
|
copy(addr.hash[:], scriptHash)
|
|
return addr, nil
|
|
}
|
|
|
|
// NewAddressScriptHashV0 returns an address that represents a payment
|
|
// destination which imposes an encumbrance that requires a script that hashes
|
|
// to the same value as the provided script along with all of the encumbrances
|
|
// that script itself imposes using version 0 scripts. The script is commonly
|
|
// referred to as a redeem script.
|
|
//
|
|
// See NewAddressScriptHashV0FromHash for a variant that accepts the hash of the
|
|
// script directly instead of the script. It can be useful to callers that
|
|
// either already have the script hash available or do not know the associated
|
|
// script.
|
|
func NewAddressScriptHashV0(redeemScript []byte,
|
|
params AddressParamsV0) (*AddressScriptHashV0, error) {
|
|
|
|
scriptHash := Hash160(redeemScript)
|
|
return NewAddressScriptHashV0FromHash(scriptHash, params)
|
|
}
|
|
|
|
// String returns the string encoding of the payment address for the associated
|
|
// script version and payment script.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressScriptHashV0) String() string {
|
|
// The format for the data portion of addresses that encode 160-bit hashes
|
|
// is merely the hash itself:
|
|
// 20-byte ripemd160 hash
|
|
return encodeAddressV0(addr.hash[:ripemd160.Size], addr.netID)
|
|
}
|
|
|
|
// putPaymentScript serializes the payment script associated with the address
|
|
// directly into the passed byte slice which must be at least
|
|
// p2shPaymentScriptLen bytes in length or it will panic.
|
|
func (addr *AddressScriptHashV0) putPaymentScript(script []byte) {
|
|
// A pay-to-script-hash script is of the form:
|
|
// HASH160 <20-byte hash> EQUAL
|
|
script[0] = txscript.OP_HASH160
|
|
script[1] = txscript.OP_DATA_20
|
|
copy(script[2:22], addr.hash[:])
|
|
script[22] = txscript.OP_EQUAL
|
|
}
|
|
|
|
// PaymentScript returns the script version associated with the address along
|
|
// with a script to pay a transaction output to the address.
|
|
//
|
|
// This is part of the Address interface implementation.
|
|
func (addr *AddressScriptHashV0) PaymentScript() (uint16, []byte) {
|
|
// A pay-to-script-hash script is of the form:
|
|
// HASH160 <20-byte hash> EQUAL
|
|
var script [p2shPaymentScriptLen]byte
|
|
addr.putPaymentScript(script[:])
|
|
return 0, script[:]
|
|
}
|
|
|
|
// VotingRightsScript returns the script version associated with the address
|
|
// along with a script to give voting rights to the address. It is only
|
|
// valid when used in stake ticket purchase transactions.
|
|
//
|
|
// This is part of the StakeAddress interface implementation.
|
|
func (addr *AddressScriptHashV0) VotingRightsScript() (uint16, []byte) {
|
|
// A script that assigns voting rights for a ticket to this address type is
|
|
// of the form:
|
|
// SSTX [standard pay-to-script-hash script]
|
|
var script [p2shPaymentScriptLen + 1]byte
|
|
script[0] = txscript.OP_SSTX
|
|
addr.putPaymentScript(script[1:])
|
|
return 0, script[:]
|
|
}
|
|
|
|
// RewardCommitmentScript returns the script version associated with the address
|
|
// along with a script that commits the original funds locked to purchase a
|
|
// ticket plus the reward to the address along with limits to impose on any
|
|
// fees (in atoms).
|
|
//
|
|
// Note that fee limits are encoded in the commitment script in terms of the
|
|
// closest base 2 exponent that results in a limit that is >= the provided
|
|
// limit. In other words, the limits are rounded up to the next power of 2
|
|
// when they are not already an exact power of 2. For example, a revocation
|
|
// limit of 2^23 + 1 will result in allowing a revocation fee of up to 2^24
|
|
// atoms.
|
|
//
|
|
// This is part of the StakeAddress interface implementation.
|
|
func (addr *AddressScriptHashV0) RewardCommitmentScript(amount, voteFeeLimit, revocationFeeLimit int64) (uint16, []byte) {
|
|
// The reward commitment output of a ticket purchase is a provably pruneable
|
|
// script of the form:
|
|
// RETURN <20-byte hash || 8-byte amount || 2-byte fee limits>
|
|
//
|
|
// The high bit of the amount is used to indicate whether the provided hash
|
|
// is a public key hash that represents a pay-to-pubkey-hash-ecdsa-secp256k1
|
|
// script or a script hash that represents a pay-to-script-hash script. It
|
|
// is set for a script hash.
|
|
limits := calcRewardCommitScriptLimits(voteFeeLimit, revocationFeeLimit)
|
|
var script [32]byte
|
|
script[0] = txscript.OP_RETURN
|
|
script[1] = txscript.OP_DATA_30
|
|
copy(script[2:22], addr.hash[:])
|
|
binary.LittleEndian.PutUint64(script[22:30], uint64(amount)|commitP2SHFlag)
|
|
binary.LittleEndian.PutUint16(script[30:32], limits)
|
|
return 0, script[:]
|
|
}
|
|
|
|
// StakeChangeScript returns the script version associated with the address
|
|
// along with a script to pay change to the address. It is only valid when used
|
|
// in stake ticket purchase and treasury add transactions.
|
|
//
|
|
// This is part of the StakeAddress interface implementation.
|
|
func (addr *AddressScriptHashV0) StakeChangeScript() (uint16, []byte) {
|
|
// A stake change script to this address type is of the form:
|
|
// SSTXCHANGE [standard pay-to-script-hash script]
|
|
var script [p2shPaymentScriptLen + 1]byte
|
|
script[0] = txscript.OP_SSTXCHANGE
|
|
addr.putPaymentScript(script[1:])
|
|
return 0, script[:]
|
|
}
|
|
|
|
// PayVoteCommitmentScript returns the script version associated with the
|
|
// address along with a script to pay the original funds locked to purchase a
|
|
// ticket plus the reward to the address. The address must have previously been
|
|
// committed to by the ticket purchase. The script is only valid when used in
|
|
// stake vote transactions whose associated tickets are eligible to vote.
|
|
//
|
|
// This is part of the StakeAddress interface implementation.
|
|
func (addr *AddressScriptHashV0) PayVoteCommitmentScript() (uint16, []byte) {
|
|
// A script that pays a ticket commitment as part of a vote to this address
|
|
// type is of the form:
|
|
// SSGEN [standard pay-to-script-hash script]
|
|
var script [p2shPaymentScriptLen + 1]byte
|
|
script[0] = txscript.OP_SSGEN
|
|
addr.putPaymentScript(script[1:])
|
|
return 0, script[:]
|
|
}
|
|
|
|
// PayRevokeCommitmentScript returns the script version associated with the
|
|
// address along with a script to revoke an expired or missed ticket which pays
|
|
// the original funds locked to purchase a ticket to the address. The address
|
|
// must have previously been committed to by the ticket purchase. The script is
|
|
// only valid when used in stake revocation transactions whose associated
|
|
// tickets have been missed or expired.
|
|
//
|
|
// This is part of the StakeAddress interface implementation.
|
|
func (addr *AddressScriptHashV0) PayRevokeCommitmentScript() (uint16, []byte) {
|
|
// A ticket revocation script to this address type is of the form:
|
|
// SSRTX [standard pay-to-script-hash script]
|
|
var script [p2shPaymentScriptLen + 1]byte
|
|
script[0] = txscript.OP_SSRTX
|
|
addr.putPaymentScript(script[1:])
|
|
return 0, script[:]
|
|
}
|
|
|
|
// PayFromTreasuryScript returns the script version associated with the address
|
|
// along with a script that pays funds from the treasury to the address. The
|
|
// script is only valid when used in treasury spend transactions.
|
|
//
|
|
// This is part of the StakeAddress interface implementation.
|
|
func (addr *AddressScriptHashV0) PayFromTreasuryScript() (uint16, []byte) {
|
|
// A script that pays from the treasury as a part of a treasury spend to
|
|
// this address type is of the form:
|
|
// TGEN [standard pay-to-script-hash script]
|
|
var script [p2shPaymentScriptLen + 1]byte
|
|
script[0] = txscript.OP_TGEN
|
|
addr.putPaymentScript(script[1:])
|
|
return 0, script[:]
|
|
}
|
|
|
|
// Hash160 returns the underlying script hash. This can be useful when an array
|
|
// is more appropriate than a slice (for example, when used as map keys).
|
|
func (addr *AddressScriptHashV0) Hash160() *[ripemd160.Size]byte {
|
|
return &addr.hash
|
|
}
|
|
|
|
// DecodeAddressV0 decodes the string encoding of an address and returns the
|
|
// relevant Address if it is a valid encoding for a known version 0 address type
|
|
// and is for the network identified by the provided parameters.
|
|
func DecodeAddressV0(addr string, params AddressParamsV0) (Address, error) {
|
|
// The provided address must not be larger than the maximum possible size.
|
|
//
|
|
// The largest supported version 0 decoded address data consists of 33 bytes
|
|
// for the public key, 2 bytes for the network identifier, and 4 bytes for
|
|
// the checksum.
|
|
//
|
|
// Since the encoding converts from base256 to base58, the max possible
|
|
// number of bytes of output per input byte is log_58(256) ~= 1.37. Thus, a
|
|
// reasonable estimate for the max possible encoded size is
|
|
// ceil(max decoded data len * 1.37).
|
|
//
|
|
// Note that the actual max address size in practice is one less than this
|
|
// value due to network prefixes in use, however, this uses the theoretical
|
|
// max so the code works properly with all prefixes since they are
|
|
// parameterized.
|
|
const maxV0AddrLen = 54
|
|
if len(addr) > maxV0AddrLen {
|
|
str := fmt.Sprintf("failed to decode address %q...: len %d exceeds "+
|
|
"max allowed %d", addr[:maxV0AddrLen], len(addr), maxV0AddrLen)
|
|
return nil, makeError(ErrMalformedAddress, str)
|
|
}
|
|
|
|
// Attempt to decode the address and address type.
|
|
decoded, addrID, err := base58.CheckDecode(addr)
|
|
if err != nil {
|
|
kind := ErrMalformedAddress
|
|
if errors.Is(err, base58.ErrChecksum) {
|
|
kind = ErrBadAddressChecksum
|
|
}
|
|
str := fmt.Sprintf("failed to decode address %q: %v", addr, err)
|
|
return nil, makeError(kind, str)
|
|
}
|
|
|
|
// Decode the address according to the address type.
|
|
switch addrID {
|
|
case params.AddrIDScriptHashV0():
|
|
return NewAddressScriptHashFromHash(0, decoded, params)
|
|
|
|
case params.AddrIDPubKeyHashECDSAV0():
|
|
return NewAddressPubKeyHashEcdsaSecp256k1(0, decoded, params)
|
|
|
|
case params.AddrIDPubKeyHashSchnorrV0():
|
|
return NewAddressPubKeyHashSchnorrSecp256k1(0, decoded, params)
|
|
|
|
case params.AddrIDPubKeyHashEd25519V0():
|
|
return NewAddressPubKeyHashEd25519(0, decoded, params)
|
|
|
|
case params.AddrIDPubKeyV0():
|
|
// Ensure the decoded data has the expected signature type identifier
|
|
// byte.
|
|
if len(decoded) < 1 {
|
|
str := fmt.Sprintf("address %q decoded data is empty", addr)
|
|
return nil, makeError(ErrMalformedAddressData, str)
|
|
}
|
|
|
|
// Decode according to the crypto algorithm and signature scheme.
|
|
sigType := decoded[0] & ^sigTypeSecp256k1PubKeyCompOddFlag
|
|
switch dcrec.SignatureType(sigType) {
|
|
case dcrec.STEcdsaSecp256k1:
|
|
// The encoded data for this case is the 32-byte X coordinate for a
|
|
// secp256k1 public key along with the oddness of the Y coordinate
|
|
// encoded via the high bit of the first byte.
|
|
//
|
|
// Reconstruct the standard compressed serialized public key format
|
|
// by choosing the correct prefix byte depending on the encoded
|
|
// Y-coordinate oddness pass it along to the constructor of the
|
|
// appropriate type to validate and return the relevant address
|
|
// instance.
|
|
const reqPubKeyLen = 33
|
|
if len(decoded) != reqPubKeyLen {
|
|
str := fmt.Sprintf("public key is %d bytes vs required %d bytes",
|
|
len(decoded), reqPubKeyLen)
|
|
return nil, makeError(ErrMalformedAddressData, str)
|
|
}
|
|
isOddY := decoded[0]&sigTypeSecp256k1PubKeyCompOddFlag != 0
|
|
prefix := secp256k1.PubKeyFormatCompressedEven
|
|
if isOddY {
|
|
prefix = secp256k1.PubKeyFormatCompressedOdd
|
|
}
|
|
decoded[0] = prefix
|
|
return NewAddressPubKeyEcdsaSecp256k1Raw(0, decoded, params)
|
|
|
|
case dcrec.STEd25519:
|
|
const reqPubKeyLen = 32
|
|
pubKey := decoded[1:]
|
|
if len(pubKey) != reqPubKeyLen {
|
|
str := fmt.Sprintf("public key is %d bytes vs required %d bytes",
|
|
len(pubKey), reqPubKeyLen)
|
|
return nil, makeError(ErrMalformedAddressData, str)
|
|
}
|
|
|
|
// The encoded data for this case is the actual Ed25519 public key,
|
|
// so just pass it along unaltered to the constructor of the
|
|
// appropriate type to validate and return the relevant address
|
|
// instance.
|
|
return NewAddressPubKeyEd25519Raw(0, pubKey, params)
|
|
|
|
case dcrec.STSchnorrSecp256k1:
|
|
// The encoded data for this case is the 32-byte X coordinate for a
|
|
// secp256k1 public key along with the oddness of the Y coordinate
|
|
// encoded via the high bit of the first byte.
|
|
//
|
|
// Reconstruct the standard compressed serialized public key format
|
|
// by choosing the correct prefix byte depending on the encoded
|
|
// Y-coordinate oddness pass it along to the constructor of the
|
|
// appropriate type to validate and return the relevant address
|
|
// instance.
|
|
const reqPubKeyLen = 33
|
|
if len(decoded) != reqPubKeyLen {
|
|
str := fmt.Sprintf("public key is %d bytes vs required %d bytes",
|
|
len(decoded), reqPubKeyLen)
|
|
return nil, makeError(ErrMalformedAddressData, str)
|
|
}
|
|
isOddY := decoded[0]&sigTypeSecp256k1PubKeyCompOddFlag != 0
|
|
prefix := secp256k1.PubKeyFormatCompressedEven
|
|
if isOddY {
|
|
prefix = secp256k1.PubKeyFormatCompressedOdd
|
|
}
|
|
decoded[0] = prefix
|
|
return NewAddressPubKeySchnorrSecp256k1Raw(0, decoded, params)
|
|
}
|
|
}
|
|
|
|
str := fmt.Sprintf("address %q is not a supported type", addr)
|
|
return nil, makeError(ErrUnsupportedAddress, str)
|
|
}
|