stdaddr: Add v0 p2pkh-ed25519 support.

This adds a new type to fully support version 0
pay-to-pubkey-hash-ed25519 addresses along with associated tests.

This is part of a series of commits to fully implement the stdaddr
package.
This commit is contained in:
Dave Collins 2021-03-05 08:34:16 -06:00
parent c6d7cd71ac
commit 48222bd7ba
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
3 changed files with 188 additions and 1 deletions

View File

@ -307,9 +307,16 @@ func NewAddressPubKeyHashEcdsaSecp256k1(scriptVersion uint16, pkHash []byte,
// 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.
//
// NOTE: Version 0 scripts are the only currently supported version.
func NewAddressPubKeyHashEd25519(scriptVersion uint16, pkHash []byte,
params AddressParams) (Address, error) {
switch scriptVersion {
case 0:
return NewAddressPubKeyHashEd25519V0(pkHash, params)
}
str := fmt.Sprintf("pubkey hash addresses for version %d are not "+
"supported", scriptVersion)
return nil, makeError(ErrUnsupportedScriptVersion, str)

View File

@ -753,6 +753,80 @@ func TestAddresses(t *testing.T) {
commitScript: "bb76a914f15da1cb8d1bcb162c6ab446c95757a6e791c91688ac",
revokeScript: "bc76a914f15da1cb8d1bcb162c6ab446c95757a6e791c91688ac",
trsyScript: "c376a914f15da1cb8d1bcb162c6ab446c95757a6e791c91688ac",
}, {
// ---------------------------------------------------------------------
// Negative P2PKH ed25519 tests.
// ---------------------------------------------------------------------
name: "p2pkh-ed25519 wrong hash length",
makeAddr: func() (Address, error) {
hash := hexToBytes("000ef030107fd26e0b6bf40512bca2ceb1dd80adaa")
return NewAddressPubKeyHashEd25519(0, hash, mainNetParams)
},
makeErr: ErrInvalidHashLen,
}, {
name: "p2pkh-ed25519 unsupported script version",
makeAddr: func() (Address, error) {
hash := hexToBytes("0ef030107fd26e0b6bf40512bca2ceb1dd80adaa")
return NewAddressPubKeyHashEd25519(9999, hash, mainNetParams)
},
makeErr: ErrUnsupportedScriptVersion,
}, {
// ---------------------------------------------------------------------
// Positive P2PKH Ed25519 tests.
// ---------------------------------------------------------------------
name: "mainnet p2pkh-ed25519",
makeAddr: func() (Address, error) {
// From pubkey for privkey 0x00...01.
hash := hexToBytes("456d8ee57a4b9121987b4ecab8c3bcb5797e8a53")
return NewAddressPubKeyHashEd25519(0, hash, mainNetParams)
},
makeErr: nil,
addr: "DeeUhrRoTp4DftsqddVW96yMGMW4sgQFYUE",
net: mainNetParams,
decodeErr: nil,
version: 0,
payScript: "76a914456d8ee57a4b9121987b4ecab8c3bcb5797e8a538851be",
}, {
name: "mainnet p2pkh-ed25519 2",
makeAddr: func() (Address, error) {
// From pubkey for privkey 0x00...02.
hash := hexToBytes("09788a8dcb216efa354e487d57d76255b1af4320")
return NewAddressPubKeyHashEd25519(0, hash, mainNetParams)
},
makeErr: nil,
addr: "DeZ1gU2ta8auk5et79R74GYR3pnF31KXbFo",
net: mainNetParams,
decodeErr: nil,
version: 0,
payScript: "76a91409788a8dcb216efa354e487d57d76255b1af43208851be",
}, {
name: "testnet p2pkh-ed25519",
makeAddr: func() (Address, error) {
// From pubkey for privkey 0x00...01.
hash := hexToBytes("456d8ee57a4b9121987b4ecab8c3bcb5797e8a53")
return NewAddressPubKeyHashEd25519(0, hash, testNetParams)
},
makeErr: nil,
addr: "TeeXvqZJrc7KnFZCT27fHfzcrTTzSF1aSRG",
net: testNetParams,
decodeErr: nil,
version: 0,
payScript: "76a914456d8ee57a4b9121987b4ecab8c3bcb5797e8a538851be",
}, {
name: "regnet p2pkh-ed25519",
makeAddr: func() (Address, error) {
// From pubkey for privkey 0x00...01.
hash := hexToBytes("456d8ee57a4b9121987b4ecab8c3bcb5797e8a53")
return NewAddressPubKeyHashEd25519(0, hash, regNetParams)
},
makeErr: nil,
addr: "ReMrcHBAbQyQZuHuQwsQBXkxVZgXpnbqX72",
net: regNetParams,
decodeErr: nil,
version: 0,
payScript: "76a914456d8ee57a4b9121987b4ecab8c3bcb5797e8a538851be",
}}
for _, test := range tests {
@ -951,6 +1025,11 @@ func TestAddresses(t *testing.T) {
id := test.net.AddrIDPubKeyHashECDSAV0()
wantPkhAddr = base58.CheckEncode(Hash160(a.serializedPubKey), id)
pkhAddr = a.AddressPubKeyHash()
case *AddressPubKeyEd25519V0:
id := test.net.AddrIDPubKeyHashEd25519V0()
wantPkhAddr = base58.CheckEncode(Hash160(a.serializedPubKey), id)
pkhAddr = a.AddressPubKeyHash()
}
if pkhAddr != nil {
gotAddr := pkhAddr.Address()

View File

@ -273,8 +273,10 @@ type AddressPubKeyEd25519V0 struct {
serializedPubKey []byte
}
// Ensure AddressPubKeyEd25519V0 implements the Address interfaces.
// 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
@ -356,6 +358,17 @@ func (addr *AddressPubKeyEd25519V0) PaymentScript() (uint16, []byte) {
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
}
// String returns a human-readable string for the address.
//
// This is equivalent to calling Address, but is provided so the type can be
@ -697,6 +710,94 @@ func (addr *AddressPubKeyHashEcdsaSecp256k1V0) String() string {
return addr.Address()
}
// 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
}
// Address 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) Address() 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] = opDup
script[1] = opHash160
script[2] = opData20
copy(script[3:23], addr.hash[:])
script[23] = opEqualVerify
script[24] = opPushSTEd25519
script[25] = opCheckSigAlt
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
}
// String returns a human-readable string for the address.
//
// This is equivalent to calling Address, but is provided so the type can be
// used as a fmt.Stringer.
func (addr *AddressPubKeyHashEd25519V0) String() string {
return addr.Address()
}
// 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.