Skip to content

Commit b1c8819

Browse files
authored
Merge pull request #101 from kcalvinalvin/2024-01-02-address-taproot
address: add AddressTaproot
2 parents b79bf9c + 637a92a commit b1c8819

File tree

3 files changed

+299
-117
lines changed

3 files changed

+299
-117
lines changed

btcutil/address.go

Lines changed: 145 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ func encodeAddress(hash160 []byte, netID byte) string {
6464
return base58.CheckEncode(hash160[:ripemd160.Size], netID)
6565
}
6666

67-
// encodeSegWitAddress creates a bech32 encoded address string representation
68-
// from witness version and witness program.
67+
// encodeSegWitAddress creates a bech32 (or bech32m for SegWit v1) encoded
68+
// address string representation from witness version and witness program.
6969
func encodeSegWitAddress(hrp string, witnessVersion byte, witnessProgram []byte) (string, error) {
7070
// Group the address bytes into 5 bit groups, as this is what is used to
7171
// encode each character in the address string.
@@ -79,7 +79,19 @@ func encodeSegWitAddress(hrp string, witnessVersion byte, witnessProgram []byte)
7979
combined := make([]byte, len(converted)+1)
8080
combined[0] = witnessVersion
8181
copy(combined[1:], converted)
82-
bech, err := bech32.Encode(hrp, combined)
82+
83+
var bech string
84+
switch witnessVersion {
85+
case 0:
86+
bech, err = bech32.Encode(hrp, combined)
87+
88+
case 1:
89+
bech, err = bech32.EncodeM(hrp, combined)
90+
91+
default:
92+
return "", fmt.Errorf("unsupported witness version %d",
93+
witnessVersion)
94+
}
8395
if err != nil {
8496
return "", err
8597
}
@@ -149,8 +161,9 @@ func DecodeAddress(addr string, defaultNet *chaincfg.Params) (Address, error) {
149161
}
150162

151163
// We currently only support P2WPKH and P2WSH, which is
152-
// witness version 0.
153-
if witnessVer != 0 {
164+
// witness version 0 and P2TR which is witness version
165+
// 1.
166+
if witnessVer != 0 && witnessVer != 1 {
154167
return nil, UnsupportedWitnessVerError(witnessVer)
155168
}
156169

@@ -161,6 +174,10 @@ func DecodeAddress(addr string, defaultNet *chaincfg.Params) (Address, error) {
161174
case 20:
162175
return newAddressWitnessPubKeyHash(hrp, witnessProg)
163176
case 32:
177+
if witnessVer == 1 {
178+
return newAddressTaproot(hrp, witnessProg)
179+
}
180+
164181
return newAddressWitnessScriptHash(hrp, witnessProg)
165182
default:
166183
return nil, UnsupportedWitnessProgLenError(len(witnessProg))
@@ -210,7 +227,7 @@ func DecodeAddress(addr string, defaultNet *chaincfg.Params) (Address, error) {
210227
// returns the witness version and witness program byte representation.
211228
func decodeSegWitAddress(address string) (byte, []byte, error) {
212229
// Decode the bech32 encoded address.
213-
_, data, err := bech32.Decode(address)
230+
_, data, bech32version, err := bech32.DecodeGeneric(address)
214231
if err != nil {
215232
return 0, nil, err
216233
}
@@ -246,6 +263,18 @@ func decodeSegWitAddress(address string) (byte, []byte, error) {
246263
"version 0: %v", len(regrouped))
247264
}
248265

266+
// For witness version 0, the bech32 encoding must be used.
267+
if version == 0 && bech32version != bech32.Version0 {
268+
return 0, nil, fmt.Errorf("invalid checksum expected bech32 " +
269+
"encoding for address with witness version 0")
270+
}
271+
272+
// For witness version 1, the bech32m encoding must be used.
273+
if version == 1 && bech32version != bech32.VersionM {
274+
return 0, nil, fmt.Errorf("invalid checksum expected bech32m " +
275+
"encoding for address with witness version 1")
276+
}
277+
249278
return version, regrouped, nil
250279
}
251280

@@ -506,116 +535,135 @@ func (a *AddressPubKey) PubKey() *btcec.PublicKey {
506535
return a.pubKey
507536
}
508537

509-
// AddressWitnessPubKeyHash is an Address for a pay-to-witness-pubkey-hash
510-
// (P2WPKH) output. See BIP 173 for further details regarding native segregated
511-
// witness address encoding:
512-
// https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
513-
type AddressWitnessPubKeyHash struct {
538+
// AddressSegWit is the base address type for all SegWit addresses.
539+
type AddressSegWit struct {
514540
hrp string
515541
witnessVersion byte
516-
witnessProgram [20]byte
517-
}
518-
519-
// NewAddressWitnessPubKeyHash returns a new AddressWitnessPubKeyHash.
520-
func NewAddressWitnessPubKeyHash(witnessProg []byte, net *chaincfg.Params) (*AddressWitnessPubKeyHash, error) {
521-
return newAddressWitnessPubKeyHash(net.Bech32HRPSegwit, witnessProg)
522-
}
523-
524-
// newAddressWitnessPubKeyHash is an internal helper function to create an
525-
// AddressWitnessPubKeyHash with a known human-readable part, rather than
526-
// looking it up through its parameters.
527-
func newAddressWitnessPubKeyHash(hrp string, witnessProg []byte) (*AddressWitnessPubKeyHash, error) {
528-
// Check for valid program length for witness version 0, which is 20
529-
// for P2WPKH.
530-
if len(witnessProg) != 20 {
531-
return nil, errors.New("witness program must be 20 " +
532-
"bytes for p2wpkh")
533-
}
534-
535-
addr := &AddressWitnessPubKeyHash{
536-
hrp: strings.ToLower(hrp),
537-
witnessVersion: 0x00,
538-
}
539-
540-
copy(addr.witnessProgram[:], witnessProg)
541-
542-
return addr, nil
542+
witnessProgram []byte
543543
}
544544

545-
// EncodeAddress returns the bech32 string encoding of an
546-
// AddressWitnessPubKeyHash.
547-
// Part of the Address interface.
548-
func (a *AddressWitnessPubKeyHash) EncodeAddress() string {
549-
str, err := encodeSegWitAddress(a.hrp, a.witnessVersion,
550-
a.witnessProgram[:])
545+
// EncodeAddress returns the bech32 (or bech32m for SegWit v1) string encoding
546+
// of an AddressSegWit.
547+
//
548+
// NOTE: This method is part of the Address interface.
549+
func (a *AddressSegWit) EncodeAddress() string {
550+
str, err := encodeSegWitAddress(
551+
a.hrp, a.witnessVersion, a.witnessProgram[:],
552+
)
551553
if err != nil {
552554
return ""
553555
}
554556
return str
555557
}
556558

557559
// ScriptAddress returns the witness program for this address.
558-
// Part of the Address interface.
559-
func (a *AddressWitnessPubKeyHash) ScriptAddress() []byte {
560+
//
561+
// NOTE: This method is part of the Address interface.
562+
func (a *AddressSegWit) ScriptAddress() []byte {
560563
return a.witnessProgram[:]
561564
}
562565

563-
// IsForNet returns whether or not the AddressWitnessPubKeyHash is associated
564-
// with the passed bitcoin network.
565-
// Part of the Address interface.
566-
func (a *AddressWitnessPubKeyHash) IsForNet(net *chaincfg.Params) bool {
566+
// IsForNet returns whether the AddressSegWit is associated with the passed
567+
// bitcoin network.
568+
//
569+
// NOTE: This method is part of the Address interface.
570+
func (a *AddressSegWit) IsForNet(net *chaincfg.Params) bool {
567571
return a.hrp == net.Bech32HRPSegwit
568572
}
569573

570574
// String returns a human-readable string for the AddressWitnessPubKeyHash.
571575
// This is equivalent to calling EncodeAddress, but is provided so the type
572576
// can be used as a fmt.Stringer.
573-
// Part of the Address interface.
574-
func (a *AddressWitnessPubKeyHash) String() string {
577+
//
578+
// NOTE: This method is part of the Address interface.
579+
func (a *AddressSegWit) String() string {
575580
return a.EncodeAddress()
576581
}
577582

578-
// Hrp returns the human-readable part of the bech32 encoded
579-
// AddressWitnessPubKeyHash.
580-
func (a *AddressWitnessPubKeyHash) Hrp() string {
583+
// Hrp returns the human-readable part of the bech32 (or bech32m for SegWit v1)
584+
// encoded AddressSegWit.
585+
func (a *AddressSegWit) Hrp() string {
581586
return a.hrp
582587
}
583588

584-
// WitnessVersion returns the witness version of the AddressWitnessPubKeyHash.
585-
func (a *AddressWitnessPubKeyHash) WitnessVersion() byte {
589+
// WitnessVersion returns the witness version of the AddressSegWit.
590+
func (a *AddressSegWit) WitnessVersion() byte {
586591
return a.witnessVersion
587592
}
588593

589-
// WitnessProgram returns the witness program of the AddressWitnessPubKeyHash.
590-
func (a *AddressWitnessPubKeyHash) WitnessProgram() []byte {
594+
// WitnessProgram returns the witness program of the AddressSegWit.
595+
func (a *AddressSegWit) WitnessProgram() []byte {
591596
return a.witnessProgram[:]
592597
}
593598

599+
// AddressWitnessPubKeyHash is an Address for a pay-to-witness-pubkey-hash
600+
// (P2WPKH) output. See BIP 173 for further details regarding native segregated
601+
// witness address encoding:
602+
// https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
603+
type AddressWitnessPubKeyHash struct {
604+
AddressSegWit
605+
}
606+
607+
// NewAddressWitnessPubKeyHash returns a new AddressWitnessPubKeyHash.
608+
func NewAddressWitnessPubKeyHash(witnessProg []byte,
609+
net *chaincfg.Params) (*AddressWitnessPubKeyHash, error) {
610+
611+
return newAddressWitnessPubKeyHash(net.Bech32HRPSegwit, witnessProg)
612+
}
613+
614+
// newAddressWitnessPubKeyHash is an internal helper function to create an
615+
// AddressWitnessPubKeyHash with a known human-readable part, rather than
616+
// looking it up through its parameters.
617+
func newAddressWitnessPubKeyHash(hrp string,
618+
witnessProg []byte) (*AddressWitnessPubKeyHash, error) {
619+
620+
// Check for valid program length for witness version 0, which is 20
621+
// for P2WPKH.
622+
if len(witnessProg) != 20 {
623+
return nil, errors.New("witness program must be 20 " +
624+
"bytes for p2wpkh")
625+
}
626+
627+
addr := &AddressWitnessPubKeyHash{
628+
AddressSegWit{
629+
hrp: strings.ToLower(hrp),
630+
witnessVersion: 0x00,
631+
witnessProgram: witnessProg,
632+
},
633+
}
634+
635+
return addr, nil
636+
}
637+
594638
// Hash160 returns the witness program of the AddressWitnessPubKeyHash as a
595639
// byte array.
596640
func (a *AddressWitnessPubKeyHash) Hash160() *[20]byte {
597-
return &a.witnessProgram
641+
var pubKeyHashWitnessProgram [20]byte
642+
copy(pubKeyHashWitnessProgram[:], a.witnessProgram)
643+
return &pubKeyHashWitnessProgram
598644
}
599645

600646
// AddressWitnessScriptHash is an Address for a pay-to-witness-script-hash
601647
// (P2WSH) output. See BIP 173 for further details regarding native segregated
602648
// witness address encoding:
603649
// https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
604650
type AddressWitnessScriptHash struct {
605-
hrp string
606-
witnessVersion byte
607-
witnessProgram [32]byte
651+
AddressSegWit
608652
}
609653

610654
// NewAddressWitnessScriptHash returns a new AddressWitnessPubKeyHash.
611-
func NewAddressWitnessScriptHash(witnessProg []byte, net *chaincfg.Params) (*AddressWitnessScriptHash, error) {
655+
func NewAddressWitnessScriptHash(witnessProg []byte,
656+
net *chaincfg.Params) (*AddressWitnessScriptHash, error) {
657+
612658
return newAddressWitnessScriptHash(net.Bech32HRPSegwit, witnessProg)
613659
}
614660

615661
// newAddressWitnessScriptHash is an internal helper function to create an
616662
// AddressWitnessScriptHash with a known human-readable part, rather than
617663
// looking it up through its parameters.
618-
func newAddressWitnessScriptHash(hrp string, witnessProg []byte) (*AddressWitnessScriptHash, error) {
664+
func newAddressWitnessScriptHash(hrp string,
665+
witnessProg []byte) (*AddressWitnessScriptHash, error) {
666+
619667
// Check for valid program length for witness version 0, which is 32
620668
// for P2WSH.
621669
if len(witnessProg) != 32 {
@@ -624,60 +672,49 @@ func newAddressWitnessScriptHash(hrp string, witnessProg []byte) (*AddressWitnes
624672
}
625673

626674
addr := &AddressWitnessScriptHash{
627-
hrp: strings.ToLower(hrp),
628-
witnessVersion: 0x00,
675+
AddressSegWit{
676+
hrp: strings.ToLower(hrp),
677+
witnessVersion: 0x00,
678+
witnessProgram: witnessProg,
679+
},
629680
}
630681

631-
copy(addr.witnessProgram[:], witnessProg)
632-
633682
return addr, nil
634683
}
635684

636-
// EncodeAddress returns the bech32 string encoding of an
637-
// AddressWitnessScriptHash.
638-
// Part of the Address interface.
639-
func (a *AddressWitnessScriptHash) EncodeAddress() string {
640-
str, err := encodeSegWitAddress(a.hrp, a.witnessVersion,
641-
a.witnessProgram[:])
642-
if err != nil {
643-
return ""
644-
}
645-
return str
685+
// AddressTaproot is an Address for a pay-to-taproot (P2TR) output. See BIP 341
686+
// for further details.
687+
type AddressTaproot struct {
688+
AddressSegWit
646689
}
647690

648-
// ScriptAddress returns the witness program for this address.
649-
// Part of the Address interface.
650-
func (a *AddressWitnessScriptHash) ScriptAddress() []byte {
651-
return a.witnessProgram[:]
652-
}
691+
// NewAddressTaproot returns a new AddressTaproot.
692+
func NewAddressTaproot(witnessProg []byte,
693+
net *chaincfg.Params) (*AddressTaproot, error) {
653694

654-
// IsForNet returns whether or not the AddressWitnessScriptHash is associated
655-
// with the passed bitcoin network.
656-
// Part of the Address interface.
657-
func (a *AddressWitnessScriptHash) IsForNet(net *chaincfg.Params) bool {
658-
return a.hrp == net.Bech32HRPSegwit
695+
return newAddressTaproot(net.Bech32HRPSegwit, witnessProg)
659696
}
660697

661-
// String returns a human-readable string for the AddressWitnessScriptHash.
662-
// This is equivalent to calling EncodeAddress, but is provided so the type
663-
// can be used as a fmt.Stringer.
664-
// Part of the Address interface.
665-
func (a *AddressWitnessScriptHash) String() string {
666-
return a.EncodeAddress()
667-
}
698+
// newAddressWitnessScriptHash is an internal helper function to create an
699+
// AddressWitnessScriptHash with a known human-readable part, rather than
700+
// looking it up through its parameters.
701+
func newAddressTaproot(hrp string,
702+
witnessProg []byte) (*AddressTaproot, error) {
668703

669-
// Hrp returns the human-readable part of the bech32 encoded
670-
// AddressWitnessScriptHash.
671-
func (a *AddressWitnessScriptHash) Hrp() string {
672-
return a.hrp
673-
}
704+
// Check for valid program length for witness version 1, which is 32
705+
// for P2TR.
706+
if len(witnessProg) != 32 {
707+
return nil, errors.New("witness program must be 32 bytes for " +
708+
"p2tr")
709+
}
674710

675-
// WitnessVersion returns the witness version of the AddressWitnessScriptHash.
676-
func (a *AddressWitnessScriptHash) WitnessVersion() byte {
677-
return a.witnessVersion
678-
}
711+
addr := &AddressTaproot{
712+
AddressSegWit{
713+
hrp: strings.ToLower(hrp),
714+
witnessVersion: 0x01,
715+
witnessProgram: witnessProg,
716+
},
717+
}
679718

680-
// WitnessProgram returns the witness program of the AddressWitnessScriptHash.
681-
func (a *AddressWitnessScriptHash) WitnessProgram() []byte {
682-
return a.witnessProgram[:]
719+
return addr, nil
683720
}

0 commit comments

Comments
 (0)