// Copyright (c) 2021 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 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 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) { // 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 decoded 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) }