Update project structure and build process

This commit is contained in:
Juan Pablo Civile
2025-05-13 11:10:08 -03:00
parent 124e9fa1bc
commit d9f3e925a4
277 changed files with 15321 additions and 930 deletions

View File

@@ -0,0 +1,127 @@
package btcutilw
// This package wraps some methods from btcutil, using the same interface and delegating all
// supported cases to that module. It's written to be both compatible and similar in implementation,
// so it's easy to swap out in the future.
import (
"fmt"
"strings"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil"
)
// DecodeAddress uses btcutil.DecodeAddress for all cases except SegWit version 1, which is handled
// by this wrapper.
func DecodeAddress(addr string, defaultNet *chaincfg.Params) (btcutil.Address, error) {
// Try to decode the address using btcutil:
decoded, libErr := btcutil.DecodeAddress(addr, defaultNet)
if libErr == nil {
return decoded, nil
}
// If this is a Taproot address, we're here because the bech32 checksum failed. The easiest way
// to know is to try:
witnessVer, witnessProg, err := decodeSegWitAddressV1(addr)
if err != nil {
return nil, fmt.Errorf("failed to decode %s (%v after %w)", addr, err, libErr)
}
if witnessVer != 1 {
return nil, btcutil.UnsupportedWitnessVerError(witnessVer)
}
if len(witnessProg) != 32 {
return nil, btcutil.UnsupportedWitnessProgLenError(len(witnessProg))
}
oneIndex := strings.LastIndexByte(addr, '1')
hrp := addr[:oneIndex]
return newAddressTaprootKey(hrp, witnessProg)
}
// AddressTaprootKey is an Address for a keyspend-only P2TR output.
type AddressTaprootKey struct {
hrp string
witnessVersion byte
witnessProgram [32]byte
}
// NewAddressTaprootKey returns a new AddressTaprootKey.
func NewAddressTaprootKey(xOnlyPubKey []byte, net *chaincfg.Params) (*AddressTaprootKey, error) {
if len(xOnlyPubKey) != 32 {
return nil, fmt.Errorf("witness program must be 32 bytes for p2tr, not %d", len(xOnlyPubKey))
}
addr := &AddressTaprootKey{
hrp: net.Bech32HRPSegwit,
witnessVersion: 0x01,
witnessProgram: [32]byte{},
}
copy(addr.witnessProgram[:], xOnlyPubKey)
return addr, nil
}
// EncodeAddress returns the bech32m string encoding of an AddressTaprootKey.
// Part of the Address interface.
func (a *AddressTaprootKey) EncodeAddress() string {
str, err := encodeSegWitAddressV1(a.hrp, a.witnessVersion, a.witnessProgram[:])
if err != nil {
return ""
}
return str
}
// ScriptAddress returns the witness program for this address.
// Part of the Address interface.
func (a *AddressTaprootKey) ScriptAddress() []byte {
return a.witnessProgram[:]
}
// IsForNet returns whether or not the AddressTaprootKey is associated with a network.
// Part of the Address interface.
func (a *AddressTaprootKey) IsForNet(net *chaincfg.Params) bool {
return a.hrp == net.Bech32HRPSegwit
}
// String returns a human-readable string for the AddressTaprootKey.
// This is equivalent to calling EncodeAddress, but allows use of fmt.Stringer.
// Part of the Address interface.
func (a *AddressTaprootKey) String() string {
return a.EncodeAddress()
}
// Hrp returns the human-readable part of the bech32 encoded AddressTaprootKey.
func (a *AddressTaprootKey) Hrp() string {
return a.hrp
}
// WitnessVersion returns the witness version of the AddressTaprootKey.
func (a *AddressTaprootKey) WitnessVersion() byte {
return a.witnessVersion
}
// WitnessProgram returns the witness program of the AddressTaprootKey.
func (a *AddressTaprootKey) WitnessProgram() []byte {
return a.witnessProgram[:]
}
func newAddressTaprootKey(hrp string, witnessProg []byte) (*AddressTaprootKey, error) {
if len(witnessProg) != 32 {
return nil, fmt.Errorf("witness program must be 32 bytes for p2tr")
}
addr := &AddressTaprootKey{
hrp: strings.ToLower(hrp),
witnessVersion: 0x01,
}
copy(addr.witnessProgram[:], witnessProg)
return addr, nil
}

View File

@@ -0,0 +1,84 @@
package btcutilw
import (
"bytes"
"fmt"
"github.com/muun/libwallet/btcsuitew/bech32m"
)
// -------------------------------------------------------------------------------------------------
// Methods below copied from btcd (address.go), but using our bech32m module instead of their bech32.
// Only that change was made. Some comments inside this code are not correct.
func encodeSegWitAddressV1(hrp string, witnessVersion byte, witnessProgram []byte) (string, error) {
// Group the address bytes into 5 bit groups, as this is what is used to
// encode each character in the address string.
converted, err := bech32m.ConvertBits(witnessProgram, 8, 5, true)
if err != nil {
return "", err
}
// Concatenate the witness version and program, and encode the resulting
// bytes using bech32 encoding.
combined := make([]byte, len(converted)+1)
combined[0] = witnessVersion
copy(combined[1:], converted)
bech, err := bech32m.Encode(hrp, combined)
if err != nil {
return "", err
}
// Check validity by decoding the created address.
version, program, err := decodeSegWitAddressV1(bech)
if err != nil {
return "", fmt.Errorf("invalid taproot address: %v", err)
}
if version != witnessVersion || !bytes.Equal(program, witnessProgram) {
return "", fmt.Errorf("invalid taproot address")
}
return bech, nil
}
func decodeSegWitAddressV1(address string) (byte, []byte, error) {
// Decode the bech32 encoded address.
_, data, err := bech32m.Decode(address)
if err != nil {
return 0, nil, err
}
// The first byte of the decoded address is the witness version, it must
// exist.
if len(data) < 1 {
return 0, nil, fmt.Errorf("no witness version")
}
// ...and be <= 16.
version := data[0]
if version > 16 {
return 0, nil, fmt.Errorf("invalid witness version for taproot: %v", version)
}
// The remaining characters of the address returned are grouped into
// words of 5 bits. In order to restore the original witness program
// bytes, we'll need to regroup into 8 bit words.
regrouped, err := bech32m.ConvertBits(data[1:], 5, 8, false)
if err != nil {
return 0, nil, err
}
// The regrouped data must be between 2 and 40 bytes.
if len(regrouped) < 2 || len(regrouped) > 40 {
return 0, nil, fmt.Errorf("invalid data length")
}
// For witness version 0, address MUST be exactly 20 or 32 bytes.
if version == 0 && len(regrouped) != 20 && len(regrouped) != 32 {
return 0, nil, fmt.Errorf("invalid data length for witness "+
"version 0: %v", len(regrouped))
}
return version, regrouped, nil
}