diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml deleted file mode 100644 index 541cc82a2..000000000 --- a/.github/workflows/go.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Build and Test -on: [push, pull_request] - -env: - # go needs absolute directories, using the $HOME variable doesn't work here. - GOCACHE: /home/runner/work/go/pkg/build - GOPATH: /home/runner/work/go - GO_VERSION: 1.16.8 - -jobs: - build: - name: Build - runs-on: ubuntu-latest - steps: - - name: Set up Go - uses: actions/setup-go@v2 - with: - go-version: ${{ env.GO_VERSION }} - - - name: Check out source - uses: actions/checkout@v2 - - - name: Build - run: make build - - test-cover: - name: Unit coverage - runs-on: ubuntu-latest - steps: - - name: Set up Go - uses: actions/setup-go@v2 - with: - go-version: ${{ env.GO_VERSION }} - - - name: Check out source - uses: actions/checkout@v2 - - - name: Test - run: make unit-cover - - - name: Send btcutil coverage - uses: shogo82148/actions-goveralls@v1 - with: - path-to-profile: coverage.txt - - - name: Send btcutil coverage for psbt package - uses: shogo82148/actions-goveralls@v1 - with: - path-to-profile: psbt/coverage.txt - - test-race: - name: Unit race - runs-on: ubuntu-latest - steps: - - name: Set up Go - uses: actions/setup-go@v2 - with: - go-version: ${{ env.GO_VERSION }} - - - name: Check out source - uses: actions/checkout@v2 - - - name: Test - run: make unit-race diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 3e7b16791..000000000 --- a/LICENSE +++ /dev/null @@ -1,16 +0,0 @@ -ISC License - -Copyright (c) 2013-2017 The btcsuite developers -Copyright (c) 2016-2017 The Lightning Network Developers - -Permission to use, copy, modify, and distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile deleted file mode 100644 index c627e885a..000000000 --- a/Makefile +++ /dev/null @@ -1,116 +0,0 @@ -PKG := github.com/btcsuite/btcutil - -LINT_PKG := github.com/golangci/golangci-lint/cmd/golangci-lint -GOACC_PKG := github.com/ory/go-acc -GOIMPORTS_PKG := golang.org/x/tools/cmd/goimports - -GO_BIN := ${GOPATH}/bin -LINT_BIN := $(GO_BIN)/golangci-lint -GOACC_BIN := $(GO_BIN)/go-acc - -LINT_COMMIT := v1.18.0 -GOACC_COMMIT := 80342ae2e0fcf265e99e76bcc4efd022c7c3811b - -DEPGET := cd /tmp && GO111MODULE=on go get -v -GOBUILD := GO111MODULE=on go build -v -GOINSTALL := GO111MODULE=on go install -v -GOTEST := GO111MODULE=on go test - -GOFILES_NOVENDOR = $(shell find . -type f -name '*.go' -not -path "./vendor/*") - -RM := rm -f -CP := cp -MAKE := make -XARGS := xargs -L 1 - -# Linting uses a lot of memory, so keep it under control by limiting the number -# of workers if requested. -ifneq ($(workers),) -LINT_WORKERS = --concurrency=$(workers) -endif - -LINT = $(LINT_BIN) run -v $(LINT_WORKERS) - -GREEN := "\\033[0;32m" -NC := "\\033[0m" -define print - echo $(GREEN)$1$(NC) -endef - -default: build - -all: build check - -# ============ -# DEPENDENCIES -# ============ - -$(LINT_BIN): - @$(call print, "Fetching linter") - $(DEPGET) $(LINT_PKG)@$(LINT_COMMIT) - -$(GOACC_BIN): - @$(call print, "Fetching go-acc") - $(DEPGET) $(GOACC_PKG)@$(GOACC_COMMIT) - -goimports: - @$(call print, "Installing goimports.") - $(DEPGET) $(GOIMPORTS_PKG) - -# ============ -# INSTALLATION -# ============ - -build: - @$(call print, "Compiling btcutil.") - $(GOBUILD) $(PKG)/... - -# ======= -# TESTING -# ======= - -check: unit - -unit: - @$(call print, "Running unit tests.") - $(GOTEST) ./... -test.timeout=20m - cd psbt; $(GOTEST) ./... -test.timeout=20m - -unit-cover: $(GOACC_BIN) - @$(call print, "Running unit coverage tests.") - $(GOACC_BIN) ./... - cd psbt; $(GOACC_BIN) ./... - -unit-race: - @$(call print, "Running unit race tests.") - env CGO_ENABLED=1 GORACE="history_size=7 halt_on_errors=1" $(GOTEST) -race -test.timeout=20m ./... - cd psbt; env CGO_ENABLED=1 GORACE="history_size=7 halt_on_errors=1" $(GOTEST) -race -test.timeout=20m ./... - -# ========= -# UTILITIES -# ========= - -fmt: goimports - @$(call print, "Fixing imports.") - goimports -w $(GOFILES_NOVENDOR) - @$(call print, "Formatting source.") - gofmt -l -w -s $(GOFILES_NOVENDOR) - -lint: $(LINT_BIN) - @$(call print, "Linting source.") - $(LINT) - -clean: - @$(call print, "Cleaning source.$(NC)") - $(RM) coverage.txt psbt/coverage.txt - -.PHONY: all \ - default \ - build \ - check \ - unit \ - unit-cover \ - unit-race \ - fmt \ - lint \ - clean diff --git a/README.md b/README.md index fffb0f7b4..3859c1201 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,4 @@ -btcutil -======= - -[![Build Status](https://github.com/btcsuite/btcutil/workflows/Build%20and%20Test/badge.svg)](https://github.com/btcsuite/btcutil/actions) -[![ISC License](https://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) -[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/btcsuite/btcutil) - -Package btcutil provides bitcoin-specific convenience functions and types. -A comprehensive suite of tests is provided to ensure proper functionality. See -`test_coverage.txt` for the gocov coverage report. Alternatively, if you are -running a POSIX OS, you can run the `cov_report.sh` script for a real-time -report. - -This package was developed for btcd, an alternative full-node implementation of -bitcoin which is under active development by Conformal. Although it was -primarily written for btcd, this package has intentionally been designed so it -can be used as a standalone package for any projects needing the functionality -provided. - -## Installation and Updating - -```bash -$ go get -u github.com/btcsuite/btcutil -``` - -## GPG Verification Key - -All official release tags are signed by Conformal so users can ensure the code -has not been tampered with and is coming from the btcsuite developers. To -verify the signature perform the following: - -- Download the public key from the Conformal website at - https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt - -- Import the public key into your GPG keyring: - ```bash - gpg --import GIT-GPG-KEY-conformal.txt - ``` - -- Verify the release tag with the following command where `TAG_NAME` is a - placeholder for the specific tag: - ```bash - git tag -v TAG_NAME - ``` - -## License - -Package btcutil is licensed under the [copyfree](http://copyfree.org) ISC -License. +This package is now a sub-package in the +[btcutil](https://github.com/btcsuite/btcd/tree/master/btcutil) directory +of the [btcd](https://github.com/btcsuite/btcd) repository. + diff --git a/address.go b/address.go deleted file mode 100644 index 8eaf2db3a..000000000 --- a/address.go +++ /dev/null @@ -1,720 +0,0 @@ -// Copyright (c) 2013-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil - -import ( - "bytes" - "encoding/hex" - "errors" - "fmt" - "strings" - - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil/base58" - "github.com/btcsuite/btcutil/bech32" - "golang.org/x/crypto/ripemd160" -) - -// UnsupportedWitnessVerError describes an error where a segwit address being -// decoded has an unsupported witness version. -type UnsupportedWitnessVerError byte - -func (e UnsupportedWitnessVerError) Error() string { - return fmt.Sprintf("unsupported witness version: %#x", byte(e)) -} - -// UnsupportedWitnessProgLenError describes an error where a segwit address -// being decoded has an unsupported witness program length. -type UnsupportedWitnessProgLenError int - -func (e UnsupportedWitnessProgLenError) Error() string { - return fmt.Sprintf("unsupported witness program length: %d", int(e)) -} - -var ( - // ErrChecksumMismatch describes an error where decoding failed due - // to a bad checksum. - ErrChecksumMismatch = errors.New("checksum mismatch") - - // ErrUnknownAddressType describes an error where an address can not - // decoded as a specific address type due to the string encoding - // beginning with an identifier byte unknown to any standard or - // registered (via chaincfg.Register) network. - ErrUnknownAddressType = errors.New("unknown address type") - - // ErrAddressCollision describes an error where an address can not - // be uniquely determined as either a pay-to-pubkey-hash or - // pay-to-script-hash address since the leading identifier is used for - // describing both address kinds, but for different networks. Rather - // than assuming or defaulting to one or the other, this error is - // returned and the caller must decide how to decode the address. - ErrAddressCollision = errors.New("address collision") -) - -// encodeAddress returns a human-readable payment address given a ripemd160 hash -// and netID which encodes the bitcoin network and address type. It is used -// in both pay-to-pubkey-hash (P2PKH) and pay-to-script-hash (P2SH) address -// encoding. -func encodeAddress(hash160 []byte, netID byte) string { - // Format is 1 byte for a network and address class (i.e. P2PKH vs - // P2SH), 20 bytes for a RIPEMD160 hash, and 4 bytes of checksum. - return base58.CheckEncode(hash160[:ripemd160.Size], netID) -} - -// encodeSegWitAddress creates a bech32 (or bech32m for SegWit v1) encoded -// address string representation from witness version and witness program. -func encodeSegWitAddress(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 := bech32.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) - - var bech string - switch witnessVersion { - case 0: - bech, err = bech32.Encode(hrp, combined) - - case 1: - bech, err = bech32.EncodeM(hrp, combined) - - default: - return "", fmt.Errorf("unsupported witness version %d", - witnessVersion) - } - if err != nil { - return "", err - } - - // Check validity by decoding the created address. - version, program, err := decodeSegWitAddress(bech) - if err != nil { - return "", fmt.Errorf("invalid segwit address: %v", err) - } - - if version != witnessVersion || !bytes.Equal(program, witnessProgram) { - return "", fmt.Errorf("invalid segwit address") - } - - return bech, nil -} - -// Address is an interface type for any type of destination a transaction -// output may spend to. This includes pay-to-pubkey (P2PK), pay-to-pubkey-hash -// (P2PKH), and pay-to-script-hash (P2SH). Address is designed to be generic -// enough that other kinds of addresses may be added in the future without -// changing the decoding and encoding API. -type Address interface { - // String returns the string encoding of the transaction output - // destination. - // - // Please note that String differs subtly from EncodeAddress: String - // will return the value as a string without any conversion, while - // EncodeAddress may convert destination types (for example, - // converting pubkeys to P2PKH addresses) before encoding as a - // payment address string. - String() string - - // EncodeAddress returns the string encoding of the payment address - // associated with the Address value. See the comment on String - // for how this method differs from String. - EncodeAddress() string - - // ScriptAddress returns the raw bytes of the address to be used - // when inserting the address into a txout's script. - ScriptAddress() []byte - - // IsForNet returns whether or not the address is associated with the - // passed bitcoin network. - IsForNet(*chaincfg.Params) bool -} - -// DecodeAddress decodes the string encoding of an address and returns -// the Address if addr is a valid encoding for a known address type. -// -// The bitcoin network the address is associated with is extracted if possible. -// When the address does not encode the network, such as in the case of a raw -// public key, the address will be associated with the passed defaultNet. -func DecodeAddress(addr string, defaultNet *chaincfg.Params) (Address, error) { - // Bech32 encoded segwit addresses start with a human-readable part - // (hrp) followed by '1'. For Bitcoin mainnet the hrp is "bc", and for - // testnet it is "tb". If the address string has a prefix that matches - // one of the prefixes for the known networks, we try to decode it as - // a segwit address. - oneIndex := strings.LastIndexByte(addr, '1') - if oneIndex > 1 { - prefix := addr[:oneIndex+1] - if chaincfg.IsBech32SegwitPrefix(prefix) { - witnessVer, witnessProg, err := decodeSegWitAddress(addr) - if err != nil { - return nil, err - } - - // We currently only support P2WPKH and P2WSH, which is - // witness version 0 and P2TR which is witness version - // 1. - if witnessVer != 0 && witnessVer != 1 { - return nil, UnsupportedWitnessVerError(witnessVer) - } - - // The HRP is everything before the found '1'. - hrp := prefix[:len(prefix)-1] - - switch len(witnessProg) { - case 20: - return newAddressWitnessPubKeyHash(hrp, witnessProg) - case 32: - if witnessVer == 1 { - return newAddressTaproot(hrp, witnessProg) - } - - return newAddressWitnessScriptHash(hrp, witnessProg) - default: - return nil, UnsupportedWitnessProgLenError(len(witnessProg)) - } - } - } - - // Serialized public keys are either 65 bytes (130 hex chars) if - // uncompressed/hybrid or 33 bytes (66 hex chars) if compressed. - if len(addr) == 130 || len(addr) == 66 { - serializedPubKey, err := hex.DecodeString(addr) - if err != nil { - return nil, err - } - return NewAddressPubKey(serializedPubKey, defaultNet) - } - - // Switch on decoded length to determine the type. - decoded, netID, err := base58.CheckDecode(addr) - if err != nil { - if err == base58.ErrChecksum { - return nil, ErrChecksumMismatch - } - return nil, errors.New("decoded address is of unknown format") - } - switch len(decoded) { - case ripemd160.Size: // P2PKH or P2SH - isP2PKH := netID == defaultNet.PubKeyHashAddrID - isP2SH := netID == defaultNet.ScriptHashAddrID - switch hash160 := decoded; { - case isP2PKH && isP2SH: - return nil, ErrAddressCollision - case isP2PKH: - return newAddressPubKeyHash(hash160, netID) - case isP2SH: - return newAddressScriptHashFromHash(hash160, netID) - default: - return nil, ErrUnknownAddressType - } - - default: - return nil, errors.New("decoded address is of unknown size") - } -} - -// decodeSegWitAddress parses a bech32 encoded segwit address string and -// returns the witness version and witness program byte representation. -func decodeSegWitAddress(address string) (byte, []byte, error) { - // Decode the bech32 encoded address. - _, data, bech32version, err := bech32.DecodeGeneric(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: %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 := bech32.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)) - } - - // For witness version 0, the bech32 encoding must be used. - if version == 0 && bech32version != bech32.Version0 { - return 0, nil, fmt.Errorf("invalid checksum expected bech32 " + - "encoding for address with witness version 0") - } - - // For witness version 1, the bech32m encoding must be used. - if version == 1 && bech32version != bech32.VersionM { - return 0, nil, fmt.Errorf("invalid checksum expected bech32m " + - "encoding for address with witness version 1") - } - - return version, regrouped, nil -} - -// AddressPubKeyHash is an Address for a pay-to-pubkey-hash (P2PKH) -// transaction. -type AddressPubKeyHash struct { - hash [ripemd160.Size]byte - netID byte -} - -// NewAddressPubKeyHash returns a new AddressPubKeyHash. pkHash mustbe 20 -// bytes. -func NewAddressPubKeyHash(pkHash []byte, net *chaincfg.Params) (*AddressPubKeyHash, error) { - return newAddressPubKeyHash(pkHash, net.PubKeyHashAddrID) -} - -// newAddressPubKeyHash is the internal API to create a pubkey hash address -// with a known leading identifier byte for a network, rather than looking -// it up through its parameters. This is useful when creating a new address -// structure from a string encoding where the identifier byte is already -// known. -func newAddressPubKeyHash(pkHash []byte, netID byte) (*AddressPubKeyHash, error) { - // Check for a valid pubkey hash length. - if len(pkHash) != ripemd160.Size { - return nil, errors.New("pkHash must be 20 bytes") - } - - addr := &AddressPubKeyHash{netID: netID} - copy(addr.hash[:], pkHash) - return addr, nil -} - -// EncodeAddress returns the string encoding of a pay-to-pubkey-hash -// address. Part of the Address interface. -func (a *AddressPubKeyHash) EncodeAddress() string { - return encodeAddress(a.hash[:], a.netID) -} - -// ScriptAddress returns the bytes to be included in a txout script to pay -// to a pubkey hash. Part of the Address interface. -func (a *AddressPubKeyHash) ScriptAddress() []byte { - return a.hash[:] -} - -// IsForNet returns whether or not the pay-to-pubkey-hash address is associated -// with the passed bitcoin network. -func (a *AddressPubKeyHash) IsForNet(net *chaincfg.Params) bool { - return a.netID == net.PubKeyHashAddrID -} - -// String returns a human-readable string for the pay-to-pubkey-hash address. -// This is equivalent to calling EncodeAddress, but is provided so the type can -// be used as a fmt.Stringer. -func (a *AddressPubKeyHash) String() string { - return a.EncodeAddress() -} - -// 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 (a *AddressPubKeyHash) Hash160() *[ripemd160.Size]byte { - return &a.hash -} - -// AddressScriptHash is an Address for a pay-to-script-hash (P2SH) -// transaction. -type AddressScriptHash struct { - hash [ripemd160.Size]byte - netID byte -} - -// NewAddressScriptHash returns a new AddressScriptHash. -func NewAddressScriptHash(serializedScript []byte, net *chaincfg.Params) (*AddressScriptHash, error) { - scriptHash := Hash160(serializedScript) - return newAddressScriptHashFromHash(scriptHash, net.ScriptHashAddrID) -} - -// NewAddressScriptHashFromHash returns a new AddressScriptHash. scriptHash -// must be 20 bytes. -func NewAddressScriptHashFromHash(scriptHash []byte, net *chaincfg.Params) (*AddressScriptHash, error) { - return newAddressScriptHashFromHash(scriptHash, net.ScriptHashAddrID) -} - -// newAddressScriptHashFromHash is the internal API to create a script hash -// address with a known leading identifier byte for a network, rather than -// looking it up through its parameters. This is useful when creating a new -// address structure from a string encoding where the identifier byte is already -// known. -func newAddressScriptHashFromHash(scriptHash []byte, netID byte) (*AddressScriptHash, error) { - // Check for a valid script hash length. - if len(scriptHash) != ripemd160.Size { - return nil, errors.New("scriptHash must be 20 bytes") - } - - addr := &AddressScriptHash{netID: netID} - copy(addr.hash[:], scriptHash) - return addr, nil -} - -// EncodeAddress returns the string encoding of a pay-to-script-hash -// address. Part of the Address interface. -func (a *AddressScriptHash) EncodeAddress() string { - return encodeAddress(a.hash[:], a.netID) -} - -// ScriptAddress returns the bytes to be included in a txout script to pay -// to a script hash. Part of the Address interface. -func (a *AddressScriptHash) ScriptAddress() []byte { - return a.hash[:] -} - -// IsForNet returns whether or not the pay-to-script-hash address is associated -// with the passed bitcoin network. -func (a *AddressScriptHash) IsForNet(net *chaincfg.Params) bool { - return a.netID == net.ScriptHashAddrID -} - -// String returns a human-readable string for the pay-to-script-hash address. -// This is equivalent to calling EncodeAddress, but is provided so the type can -// be used as a fmt.Stringer. -func (a *AddressScriptHash) String() string { - return a.EncodeAddress() -} - -// Hash160 returns the underlying array of the script hash. This can be useful -// when an array is more appropriate than a slice (for example, when used as map -// keys). -func (a *AddressScriptHash) Hash160() *[ripemd160.Size]byte { - return &a.hash -} - -// PubKeyFormat describes what format to use for a pay-to-pubkey address. -type PubKeyFormat int - -const ( - // PKFUncompressed indicates the pay-to-pubkey address format is an - // uncompressed public key. - PKFUncompressed PubKeyFormat = iota - - // PKFCompressed indicates the pay-to-pubkey address format is a - // compressed public key. - PKFCompressed - - // PKFHybrid indicates the pay-to-pubkey address format is a hybrid - // public key. - PKFHybrid -) - -// AddressPubKey is an Address for a pay-to-pubkey transaction. -type AddressPubKey struct { - pubKeyFormat PubKeyFormat - pubKey *btcec.PublicKey - pubKeyHashID byte -} - -// NewAddressPubKey returns a new AddressPubKey which represents a pay-to-pubkey -// address. The serializedPubKey parameter must be a valid pubkey and can be -// uncompressed, compressed, or hybrid. -func NewAddressPubKey(serializedPubKey []byte, net *chaincfg.Params) (*AddressPubKey, error) { - pubKey, err := btcec.ParsePubKey(serializedPubKey, btcec.S256()) - if err != nil { - return nil, err - } - - // Set the format of the pubkey. This probably should be returned - // from btcec, but do it here to avoid API churn. We already know the - // pubkey is valid since it parsed above, so it's safe to simply examine - // the leading byte to get the format. - pkFormat := PKFUncompressed - switch serializedPubKey[0] { - case 0x02, 0x03: - pkFormat = PKFCompressed - case 0x06, 0x07: - pkFormat = PKFHybrid - } - - return &AddressPubKey{ - pubKeyFormat: pkFormat, - pubKey: pubKey, - pubKeyHashID: net.PubKeyHashAddrID, - }, nil -} - -// serialize returns the serialization of the public key according to the -// format associated with the address. -func (a *AddressPubKey) serialize() []byte { - switch a.pubKeyFormat { - default: - fallthrough - case PKFUncompressed: - return a.pubKey.SerializeUncompressed() - - case PKFCompressed: - return a.pubKey.SerializeCompressed() - - case PKFHybrid: - return a.pubKey.SerializeHybrid() - } -} - -// EncodeAddress returns the string encoding of the public key as a -// pay-to-pubkey-hash. Note that the public key format (uncompressed, -// compressed, etc) will change the resulting address. This is expected since -// pay-to-pubkey-hash is a hash of the serialized public key which obviously -// differs with the format. At the time of this writing, most Bitcoin addresses -// are pay-to-pubkey-hash constructed from the uncompressed public key. -// -// Part of the Address interface. -func (a *AddressPubKey) EncodeAddress() string { - return encodeAddress(Hash160(a.serialize()), a.pubKeyHashID) -} - -// ScriptAddress returns the bytes to be included in a txout script to pay -// to a public key. Setting the public key format will affect the output of -// this function accordingly. Part of the Address interface. -func (a *AddressPubKey) ScriptAddress() []byte { - return a.serialize() -} - -// IsForNet returns whether or not the pay-to-pubkey address is associated -// with the passed bitcoin network. -func (a *AddressPubKey) IsForNet(net *chaincfg.Params) bool { - return a.pubKeyHashID == net.PubKeyHashAddrID -} - -// String returns the hex-encoded human-readable string for the pay-to-pubkey -// address. This is not the same as calling EncodeAddress. -func (a *AddressPubKey) String() string { - return hex.EncodeToString(a.serialize()) -} - -// Format returns the format (uncompressed, compressed, etc) of the -// pay-to-pubkey address. -func (a *AddressPubKey) Format() PubKeyFormat { - return a.pubKeyFormat -} - -// SetFormat sets the format (uncompressed, compressed, etc) of the -// pay-to-pubkey address. -func (a *AddressPubKey) SetFormat(pkFormat PubKeyFormat) { - a.pubKeyFormat = pkFormat -} - -// AddressPubKeyHash returns the pay-to-pubkey address converted to a -// pay-to-pubkey-hash address. Note that the public key format (uncompressed, -// compressed, etc) will change the resulting address. This is expected since -// pay-to-pubkey-hash is a hash of the serialized public key which obviously -// differs with the format. At the time of this writing, most Bitcoin addresses -// are pay-to-pubkey-hash constructed from the uncompressed public key. -func (a *AddressPubKey) AddressPubKeyHash() *AddressPubKeyHash { - addr := &AddressPubKeyHash{netID: a.pubKeyHashID} - copy(addr.hash[:], Hash160(a.serialize())) - return addr -} - -// PubKey returns the underlying public key for the address. -func (a *AddressPubKey) PubKey() *btcec.PublicKey { - return a.pubKey -} - -// AddressSegWit is the base address type for all SegWit addresses. -type AddressSegWit struct { - hrp string - witnessVersion byte - witnessProgram []byte -} - -// EncodeAddress returns the bech32 (or bech32m for SegWit v1) string encoding -// of an AddressSegWit. -// -// NOTE: This method is part of the Address interface. -func (a *AddressSegWit) EncodeAddress() string { - str, err := encodeSegWitAddress( - a.hrp, a.witnessVersion, a.witnessProgram[:], - ) - if err != nil { - return "" - } - return str -} - -// ScriptAddress returns the witness program for this address. -// -// NOTE: This method is part of the Address interface. -func (a *AddressSegWit) ScriptAddress() []byte { - return a.witnessProgram[:] -} - -// IsForNet returns whether the AddressSegWit is associated with the passed -// bitcoin network. -// -// NOTE: This method is part of the Address interface. -func (a *AddressSegWit) IsForNet(net *chaincfg.Params) bool { - return a.hrp == net.Bech32HRPSegwit -} - -// String returns a human-readable string for the AddressWitnessPubKeyHash. -// This is equivalent to calling EncodeAddress, but is provided so the type -// can be used as a fmt.Stringer. -// -// NOTE: This method is part of the Address interface. -func (a *AddressSegWit) String() string { - return a.EncodeAddress() -} - -// Hrp returns the human-readable part of the bech32 (or bech32m for SegWit v1) -// encoded AddressSegWit. -func (a *AddressSegWit) Hrp() string { - return a.hrp -} - -// WitnessVersion returns the witness version of the AddressSegWit. -func (a *AddressSegWit) WitnessVersion() byte { - return a.witnessVersion -} - -// WitnessProgram returns the witness program of the AddressSegWit. -func (a *AddressSegWit) WitnessProgram() []byte { - return a.witnessProgram[:] -} - -// AddressWitnessPubKeyHash is an Address for a pay-to-witness-pubkey-hash -// (P2WPKH) output. See BIP 173 for further details regarding native segregated -// witness address encoding: -// https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki -type AddressWitnessPubKeyHash struct { - AddressSegWit -} - -// NewAddressWitnessPubKeyHash returns a new AddressWitnessPubKeyHash. -func NewAddressWitnessPubKeyHash(witnessProg []byte, - net *chaincfg.Params) (*AddressWitnessPubKeyHash, error) { - - return newAddressWitnessPubKeyHash(net.Bech32HRPSegwit, witnessProg) -} - -// newAddressWitnessPubKeyHash is an internal helper function to create an -// AddressWitnessPubKeyHash with a known human-readable part, rather than -// looking it up through its parameters. -func newAddressWitnessPubKeyHash(hrp string, - witnessProg []byte) (*AddressWitnessPubKeyHash, error) { - - // Check for valid program length for witness version 0, which is 20 - // for P2WPKH. - if len(witnessProg) != 20 { - return nil, errors.New("witness program must be 20 " + - "bytes for p2wpkh") - } - - addr := &AddressWitnessPubKeyHash{ - AddressSegWit{ - hrp: strings.ToLower(hrp), - witnessVersion: 0x00, - witnessProgram: witnessProg, - }, - } - - return addr, nil -} - -// Hash160 returns the witness program of the AddressWitnessPubKeyHash as a -// byte array. -func (a *AddressWitnessPubKeyHash) Hash160() *[20]byte { - var pubKeyHashWitnessProgram [20]byte - copy(pubKeyHashWitnessProgram[:], a.witnessProgram) - return &pubKeyHashWitnessProgram -} - -// AddressWitnessScriptHash is an Address for a pay-to-witness-script-hash -// (P2WSH) output. See BIP 173 for further details regarding native segregated -// witness address encoding: -// https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki -type AddressWitnessScriptHash struct { - AddressSegWit -} - -// NewAddressWitnessScriptHash returns a new AddressWitnessPubKeyHash. -func NewAddressWitnessScriptHash(witnessProg []byte, - net *chaincfg.Params) (*AddressWitnessScriptHash, error) { - - return newAddressWitnessScriptHash(net.Bech32HRPSegwit, witnessProg) -} - -// newAddressWitnessScriptHash is an internal helper function to create an -// AddressWitnessScriptHash with a known human-readable part, rather than -// looking it up through its parameters. -func newAddressWitnessScriptHash(hrp string, - witnessProg []byte) (*AddressWitnessScriptHash, error) { - - // Check for valid program length for witness version 0, which is 32 - // for P2WSH. - if len(witnessProg) != 32 { - return nil, errors.New("witness program must be 32 " + - "bytes for p2wsh") - } - - addr := &AddressWitnessScriptHash{ - AddressSegWit{ - hrp: strings.ToLower(hrp), - witnessVersion: 0x00, - witnessProgram: witnessProg, - }, - } - - return addr, nil -} - -// AddressTaproot is an Address for a pay-to-taproot (P2TR) output. See BIP 341 -// for further details. -type AddressTaproot struct { - AddressSegWit -} - -// NewAddressTaproot returns a new AddressTaproot. -func NewAddressTaproot(witnessProg []byte, - net *chaincfg.Params) (*AddressTaproot, error) { - - return newAddressTaproot(net.Bech32HRPSegwit, witnessProg) -} - -// newAddressWitnessScriptHash is an internal helper function to create an -// AddressWitnessScriptHash with a known human-readable part, rather than -// looking it up through its parameters. -func newAddressTaproot(hrp string, - witnessProg []byte) (*AddressTaproot, error) { - - // Check for valid program length for witness version 1, which is 32 - // for P2TR. - if len(witnessProg) != 32 { - return nil, errors.New("witness program must be 32 bytes for " + - "p2tr") - } - - addr := &AddressTaproot{ - AddressSegWit{ - hrp: strings.ToLower(hrp), - witnessVersion: 0x01, - witnessProgram: witnessProg, - }, - } - - return addr, nil -} diff --git a/address_test.go b/address_test.go deleted file mode 100644 index 754fec893..000000000 --- a/address_test.go +++ /dev/null @@ -1,1012 +0,0 @@ -// Copyright (c) 2013-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil_test - -import ( - "bytes" - "encoding/hex" - "fmt" - "reflect" - "strings" - "testing" - - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" - "golang.org/x/crypto/ripemd160" -) - -type CustomParamStruct struct { - Net wire.BitcoinNet - PubKeyHashAddrID byte - ScriptHashAddrID byte - Bech32HRPSegwit string -} - -var CustomParams = CustomParamStruct{ - Net: 0xdbb6c0fb, // litecoin mainnet HD version bytes - PubKeyHashAddrID: 0x30, // starts with L - ScriptHashAddrID: 0x32, // starts with M - Bech32HRPSegwit: "ltc", // starts with ltc -} - -// We use this function to be able to test functionality in DecodeAddress for -// defaultNet addresses -func applyCustomParams(params chaincfg.Params, customParams CustomParamStruct) chaincfg.Params { - params.Net = customParams.Net - params.PubKeyHashAddrID = customParams.PubKeyHashAddrID - params.ScriptHashAddrID = customParams.ScriptHashAddrID - params.Bech32HRPSegwit = customParams.Bech32HRPSegwit - return params -} - -var customParams = applyCustomParams(chaincfg.MainNetParams, CustomParams) - -func TestAddresses(t *testing.T) { - tests := []struct { - name string - addr string - encoded string - valid bool - result btcutil.Address - f func() (btcutil.Address, error) - net *chaincfg.Params - }{ - // Positive P2PKH tests. - { - name: "mainnet p2pkh", - addr: "1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX", - encoded: "1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX", - valid: true, - result: btcutil.TstAddressPubKeyHash( - [ripemd160.Size]byte{ - 0xe3, 0x4c, 0xce, 0x70, 0xc8, 0x63, 0x73, 0x27, 0x3e, 0xfc, - 0xc5, 0x4c, 0xe7, 0xd2, 0xa4, 0x91, 0xbb, 0x4a, 0x0e, 0x84}, - chaincfg.MainNetParams.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - pkHash := []byte{ - 0xe3, 0x4c, 0xce, 0x70, 0xc8, 0x63, 0x73, 0x27, 0x3e, 0xfc, - 0xc5, 0x4c, 0xe7, 0xd2, 0xa4, 0x91, 0xbb, 0x4a, 0x0e, 0x84} - return btcutil.NewAddressPubKeyHash(pkHash, &chaincfg.MainNetParams) - }, - net: &chaincfg.MainNetParams, - }, - { - name: "mainnet p2pkh 2", - addr: "12MzCDwodF9G1e7jfwLXfR164RNtx4BRVG", - encoded: "12MzCDwodF9G1e7jfwLXfR164RNtx4BRVG", - valid: true, - result: btcutil.TstAddressPubKeyHash( - [ripemd160.Size]byte{ - 0x0e, 0xf0, 0x30, 0x10, 0x7f, 0xd2, 0x6e, 0x0b, 0x6b, 0xf4, - 0x05, 0x12, 0xbc, 0xa2, 0xce, 0xb1, 0xdd, 0x80, 0xad, 0xaa}, - chaincfg.MainNetParams.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - pkHash := []byte{ - 0x0e, 0xf0, 0x30, 0x10, 0x7f, 0xd2, 0x6e, 0x0b, 0x6b, 0xf4, - 0x05, 0x12, 0xbc, 0xa2, 0xce, 0xb1, 0xdd, 0x80, 0xad, 0xaa} - return btcutil.NewAddressPubKeyHash(pkHash, &chaincfg.MainNetParams) - }, - net: &chaincfg.MainNetParams, - }, - { - name: "litecoin mainnet p2pkh", - addr: "LM2WMpR1Rp6j3Sa59cMXMs1SPzj9eXpGc1", - encoded: "LM2WMpR1Rp6j3Sa59cMXMs1SPzj9eXpGc1", - valid: true, - result: btcutil.TstAddressPubKeyHash( - [ripemd160.Size]byte{ - 0x13, 0xc6, 0x0d, 0x8e, 0x68, 0xd7, 0x34, 0x9f, 0x5b, 0x4c, - 0xa3, 0x62, 0xc3, 0x95, 0x4b, 0x15, 0x04, 0x50, 0x61, 0xb1}, - CustomParams.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - pkHash := []byte{ - 0x13, 0xc6, 0x0d, 0x8e, 0x68, 0xd7, 0x34, 0x9f, 0x5b, 0x4c, - 0xa3, 0x62, 0xc3, 0x95, 0x4b, 0x15, 0x04, 0x50, 0x61, 0xb1} - return btcutil.NewAddressPubKeyHash(pkHash, &customParams) - }, - net: &customParams, - }, - { - name: "testnet p2pkh", - addr: "mrX9vMRYLfVy1BnZbc5gZjuyaqH3ZW2ZHz", - encoded: "mrX9vMRYLfVy1BnZbc5gZjuyaqH3ZW2ZHz", - valid: true, - result: btcutil.TstAddressPubKeyHash( - [ripemd160.Size]byte{ - 0x78, 0xb3, 0x16, 0xa0, 0x86, 0x47, 0xd5, 0xb7, 0x72, 0x83, - 0xe5, 0x12, 0xd3, 0x60, 0x3f, 0x1f, 0x1c, 0x8d, 0xe6, 0x8f}, - chaincfg.TestNet3Params.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - pkHash := []byte{ - 0x78, 0xb3, 0x16, 0xa0, 0x86, 0x47, 0xd5, 0xb7, 0x72, 0x83, - 0xe5, 0x12, 0xd3, 0x60, 0x3f, 0x1f, 0x1c, 0x8d, 0xe6, 0x8f} - return btcutil.NewAddressPubKeyHash(pkHash, &chaincfg.TestNet3Params) - }, - net: &chaincfg.TestNet3Params, - }, - - // Negative P2PKH tests. - { - name: "p2pkh wrong hash length", - addr: "", - valid: false, - f: func() (btcutil.Address, error) { - pkHash := []byte{ - 0x00, 0x0e, 0xf0, 0x30, 0x10, 0x7f, 0xd2, 0x6e, 0x0b, 0x6b, - 0xf4, 0x05, 0x12, 0xbc, 0xa2, 0xce, 0xb1, 0xdd, 0x80, 0xad, - 0xaa} - return btcutil.NewAddressPubKeyHash(pkHash, &chaincfg.MainNetParams) - }, - net: &chaincfg.MainNetParams, - }, - { - name: "p2pkh bad checksum", - addr: "1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gY", - valid: false, - net: &chaincfg.MainNetParams, - }, - - // Positive P2SH tests. - { - // Taken from transactions: - // output: 3c9018e8d5615c306d72397f8f5eef44308c98fb576a88e030c25456b4f3a7ac - // input: 837dea37ddc8b1e3ce646f1a656e79bbd8cc7f558ac56a169626d649ebe2a3ba. - name: "mainnet p2sh", - addr: "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC", - encoded: "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC", - valid: true, - result: btcutil.TstAddressScriptHash( - [ripemd160.Size]byte{ - 0xf8, 0x15, 0xb0, 0x36, 0xd9, 0xbb, 0xbc, 0xe5, 0xe9, 0xf2, - 0xa0, 0x0a, 0xbd, 0x1b, 0xf3, 0xdc, 0x91, 0xe9, 0x55, 0x10}, - chaincfg.MainNetParams.ScriptHashAddrID), - f: func() (btcutil.Address, error) { - script := []byte{ - 0x52, 0x41, 0x04, 0x91, 0xbb, 0xa2, 0x51, 0x09, 0x12, 0xa5, - 0xbd, 0x37, 0xda, 0x1f, 0xb5, 0xb1, 0x67, 0x30, 0x10, 0xe4, - 0x3d, 0x2c, 0x6d, 0x81, 0x2c, 0x51, 0x4e, 0x91, 0xbf, 0xa9, - 0xf2, 0xeb, 0x12, 0x9e, 0x1c, 0x18, 0x33, 0x29, 0xdb, 0x55, - 0xbd, 0x86, 0x8e, 0x20, 0x9a, 0xac, 0x2f, 0xbc, 0x02, 0xcb, - 0x33, 0xd9, 0x8f, 0xe7, 0x4b, 0xf2, 0x3f, 0x0c, 0x23, 0x5d, - 0x61, 0x26, 0xb1, 0xd8, 0x33, 0x4f, 0x86, 0x41, 0x04, 0x86, - 0x5c, 0x40, 0x29, 0x3a, 0x68, 0x0c, 0xb9, 0xc0, 0x20, 0xe7, - 0xb1, 0xe1, 0x06, 0xd8, 0xc1, 0x91, 0x6d, 0x3c, 0xef, 0x99, - 0xaa, 0x43, 0x1a, 0x56, 0xd2, 0x53, 0xe6, 0x92, 0x56, 0xda, - 0xc0, 0x9e, 0xf1, 0x22, 0xb1, 0xa9, 0x86, 0x81, 0x8a, 0x7c, - 0xb6, 0x24, 0x53, 0x2f, 0x06, 0x2c, 0x1d, 0x1f, 0x87, 0x22, - 0x08, 0x48, 0x61, 0xc5, 0xc3, 0x29, 0x1c, 0xcf, 0xfe, 0xf4, - 0xec, 0x68, 0x74, 0x41, 0x04, 0x8d, 0x24, 0x55, 0xd2, 0x40, - 0x3e, 0x08, 0x70, 0x8f, 0xc1, 0xf5, 0x56, 0x00, 0x2f, 0x1b, - 0x6c, 0xd8, 0x3f, 0x99, 0x2d, 0x08, 0x50, 0x97, 0xf9, 0x97, - 0x4a, 0xb0, 0x8a, 0x28, 0x83, 0x8f, 0x07, 0x89, 0x6f, 0xba, - 0xb0, 0x8f, 0x39, 0x49, 0x5e, 0x15, 0xfa, 0x6f, 0xad, 0x6e, - 0xdb, 0xfb, 0x1e, 0x75, 0x4e, 0x35, 0xfa, 0x1c, 0x78, 0x44, - 0xc4, 0x1f, 0x32, 0x2a, 0x18, 0x63, 0xd4, 0x62, 0x13, 0x53, - 0xae} - return btcutil.NewAddressScriptHash(script, &chaincfg.MainNetParams) - }, - net: &chaincfg.MainNetParams, - }, - { - name: "litecoin mainnet P2SH ", - addr: "MVcg9uEvtWuP5N6V48EHfEtbz48qR8TKZ9", - encoded: "MVcg9uEvtWuP5N6V48EHfEtbz48qR8TKZ9", - valid: true, - result: btcutil.TstAddressScriptHash( - [ripemd160.Size]byte{ - 0xee, 0x34, 0xac, 0x67, 0x6b, 0xda, 0xf6, 0xe3, 0x70, 0xc8, - 0xc8, 0x20, 0xb9, 0x48, 0xed, 0xfa, 0xd3, 0xa8, 0x73, 0xd8}, - CustomParams.ScriptHashAddrID), - f: func() (btcutil.Address, error) { - pkHash := []byte{ - 0xEE, 0x34, 0xAC, 0x67, 0x6B, 0xDA, 0xF6, 0xE3, 0x70, 0xC8, - 0xC8, 0x20, 0xB9, 0x48, 0xED, 0xFA, 0xD3, 0xA8, 0x73, 0xD8} - return btcutil.NewAddressScriptHashFromHash(pkHash, &customParams) - }, - net: &customParams, - }, - { - // Taken from transactions: - // output: b0539a45de13b3e0403909b8bd1a555b8cbe45fd4e3f3fda76f3a5f52835c29d - // input: (not yet redeemed at time test was written) - name: "mainnet p2sh 2", - addr: "3NukJ6fYZJ5Kk8bPjycAnruZkE5Q7UW7i8", - encoded: "3NukJ6fYZJ5Kk8bPjycAnruZkE5Q7UW7i8", - valid: true, - result: btcutil.TstAddressScriptHash( - [ripemd160.Size]byte{ - 0xe8, 0xc3, 0x00, 0xc8, 0x79, 0x86, 0xef, 0xa8, 0x4c, 0x37, - 0xc0, 0x51, 0x99, 0x29, 0x01, 0x9e, 0xf8, 0x6e, 0xb5, 0xb4}, - chaincfg.MainNetParams.ScriptHashAddrID), - f: func() (btcutil.Address, error) { - hash := []byte{ - 0xe8, 0xc3, 0x00, 0xc8, 0x79, 0x86, 0xef, 0xa8, 0x4c, 0x37, - 0xc0, 0x51, 0x99, 0x29, 0x01, 0x9e, 0xf8, 0x6e, 0xb5, 0xb4} - return btcutil.NewAddressScriptHashFromHash(hash, &chaincfg.MainNetParams) - }, - net: &chaincfg.MainNetParams, - }, - { - // Taken from bitcoind base58_keys_valid. - name: "testnet p2sh", - addr: "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", - encoded: "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", - valid: true, - result: btcutil.TstAddressScriptHash( - [ripemd160.Size]byte{ - 0xc5, 0x79, 0x34, 0x2c, 0x2c, 0x4c, 0x92, 0x20, 0x20, 0x5e, - 0x2c, 0xdc, 0x28, 0x56, 0x17, 0x04, 0x0c, 0x92, 0x4a, 0x0a}, - chaincfg.TestNet3Params.ScriptHashAddrID), - f: func() (btcutil.Address, error) { - hash := []byte{ - 0xc5, 0x79, 0x34, 0x2c, 0x2c, 0x4c, 0x92, 0x20, 0x20, 0x5e, - 0x2c, 0xdc, 0x28, 0x56, 0x17, 0x04, 0x0c, 0x92, 0x4a, 0x0a} - return btcutil.NewAddressScriptHashFromHash(hash, &chaincfg.TestNet3Params) - }, - net: &chaincfg.TestNet3Params, - }, - - // Negative P2SH tests. - { - name: "p2sh wrong hash length", - addr: "", - valid: false, - f: func() (btcutil.Address, error) { - hash := []byte{ - 0x00, 0xf8, 0x15, 0xb0, 0x36, 0xd9, 0xbb, 0xbc, 0xe5, 0xe9, - 0xf2, 0xa0, 0x0a, 0xbd, 0x1b, 0xf3, 0xdc, 0x91, 0xe9, 0x55, - 0x10} - return btcutil.NewAddressScriptHashFromHash(hash, &chaincfg.MainNetParams) - }, - net: &chaincfg.MainNetParams, - }, - - // Positive P2PK tests. - { - name: "mainnet p2pk compressed (0x02)", - addr: "02192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4", - encoded: "13CG6SJ3yHUXo4Cr2RY4THLLJrNFuG3gUg", - valid: true, - result: btcutil.TstAddressPubKey( - []byte{ - 0x02, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95, - 0x69, 0xc2, 0xe7, 0x79, 0x01, 0x57, 0x3d, 0x8d, 0x79, 0x03, - 0xc3, 0xeb, 0xec, 0x3a, 0x95, 0x77, 0x24, 0x89, 0x5d, 0xca, - 0x52, 0xc6, 0xb4}, - btcutil.PKFCompressed, chaincfg.MainNetParams.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - serializedPubKey := []byte{ - 0x02, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95, - 0x69, 0xc2, 0xe7, 0x79, 0x01, 0x57, 0x3d, 0x8d, 0x79, 0x03, - 0xc3, 0xeb, 0xec, 0x3a, 0x95, 0x77, 0x24, 0x89, 0x5d, 0xca, - 0x52, 0xc6, 0xb4} - return btcutil.NewAddressPubKey(serializedPubKey, &chaincfg.MainNetParams) - }, - net: &chaincfg.MainNetParams, - }, - { - name: "mainnet p2pk compressed (0x03)", - addr: "03b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65", - encoded: "15sHANNUBSh6nDp8XkDPmQcW6n3EFwmvE6", - valid: true, - result: btcutil.TstAddressPubKey( - []byte{ - 0x03, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1, - 0xe9, 0x86, 0xe8, 0x84, 0x18, 0x5c, 0x61, 0xcf, 0x43, 0xe0, - 0x01, 0xf9, 0x13, 0x7f, 0x23, 0xc2, 0xc4, 0x09, 0x27, 0x3e, - 0xb1, 0x6e, 0x65}, - btcutil.PKFCompressed, chaincfg.MainNetParams.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - serializedPubKey := []byte{ - 0x03, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1, - 0xe9, 0x86, 0xe8, 0x84, 0x18, 0x5c, 0x61, 0xcf, 0x43, 0xe0, - 0x01, 0xf9, 0x13, 0x7f, 0x23, 0xc2, 0xc4, 0x09, 0x27, 0x3e, - 0xb1, 0x6e, 0x65} - return btcutil.NewAddressPubKey(serializedPubKey, &chaincfg.MainNetParams) - }, - net: &chaincfg.MainNetParams, - }, - { - name: "mainnet p2pk uncompressed (0x04)", - addr: "0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2" + - "e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", - encoded: "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S", - valid: true, - result: btcutil.TstAddressPubKey( - []byte{ - 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, 0x6b, - 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 0xb6, 0x8a, 0x38, - 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 0xd7, 0xb1, 0x48, 0xa6, - 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 0xea, 0xdd, 0xfb, 0x84, 0xcc, - 0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, - 0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, - 0xf6, 0x56, 0xb4, 0x12, 0xa3}, - btcutil.PKFUncompressed, chaincfg.MainNetParams.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - serializedPubKey := []byte{ - 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, 0x6b, - 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 0xb6, 0x8a, 0x38, - 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 0xd7, 0xb1, 0x48, 0xa6, - 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 0xea, 0xdd, 0xfb, 0x84, 0xcc, - 0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, - 0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, - 0xf6, 0x56, 0xb4, 0x12, 0xa3} - return btcutil.NewAddressPubKey(serializedPubKey, &chaincfg.MainNetParams) - }, - net: &chaincfg.MainNetParams, - }, - { - name: "mainnet p2pk hybrid (0x06)", - addr: "06192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4" + - "0d45264838c0bd96852662ce6a847b197376830160c6d2eb5e6a4c44d33f453e", - encoded: "1Ja5rs7XBZnK88EuLVcFqYGMEbBitzchmX", - valid: true, - result: btcutil.TstAddressPubKey( - []byte{ - 0x06, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95, - 0x69, 0xc2, 0xe7, 0x79, 0x01, 0x57, 0x3d, 0x8d, 0x79, 0x03, - 0xc3, 0xeb, 0xec, 0x3a, 0x95, 0x77, 0x24, 0x89, 0x5d, 0xca, - 0x52, 0xc6, 0xb4, 0x0d, 0x45, 0x26, 0x48, 0x38, 0xc0, 0xbd, - 0x96, 0x85, 0x26, 0x62, 0xce, 0x6a, 0x84, 0x7b, 0x19, 0x73, - 0x76, 0x83, 0x01, 0x60, 0xc6, 0xd2, 0xeb, 0x5e, 0x6a, 0x4c, - 0x44, 0xd3, 0x3f, 0x45, 0x3e}, - btcutil.PKFHybrid, chaincfg.MainNetParams.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - serializedPubKey := []byte{ - 0x06, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95, - 0x69, 0xc2, 0xe7, 0x79, 0x01, 0x57, 0x3d, 0x8d, 0x79, 0x03, - 0xc3, 0xeb, 0xec, 0x3a, 0x95, 0x77, 0x24, 0x89, 0x5d, 0xca, - 0x52, 0xc6, 0xb4, 0x0d, 0x45, 0x26, 0x48, 0x38, 0xc0, 0xbd, - 0x96, 0x85, 0x26, 0x62, 0xce, 0x6a, 0x84, 0x7b, 0x19, 0x73, - 0x76, 0x83, 0x01, 0x60, 0xc6, 0xd2, 0xeb, 0x5e, 0x6a, 0x4c, - 0x44, 0xd3, 0x3f, 0x45, 0x3e} - return btcutil.NewAddressPubKey(serializedPubKey, &chaincfg.MainNetParams) - }, - net: &chaincfg.MainNetParams, - }, - { - name: "mainnet p2pk hybrid (0x07)", - addr: "07b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65" + - "37a576782eba668a7ef8bd3b3cfb1edb7117ab65129b8a2e681f3c1e0908ef7b", - encoded: "1ExqMmf6yMxcBMzHjbj41wbqYuqoX6uBLG", - valid: true, - result: btcutil.TstAddressPubKey( - []byte{ - 0x07, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1, - 0xe9, 0x86, 0xe8, 0x84, 0x18, 0x5c, 0x61, 0xcf, 0x43, 0xe0, - 0x01, 0xf9, 0x13, 0x7f, 0x23, 0xc2, 0xc4, 0x09, 0x27, 0x3e, - 0xb1, 0x6e, 0x65, 0x37, 0xa5, 0x76, 0x78, 0x2e, 0xba, 0x66, - 0x8a, 0x7e, 0xf8, 0xbd, 0x3b, 0x3c, 0xfb, 0x1e, 0xdb, 0x71, - 0x17, 0xab, 0x65, 0x12, 0x9b, 0x8a, 0x2e, 0x68, 0x1f, 0x3c, - 0x1e, 0x09, 0x08, 0xef, 0x7b}, - btcutil.PKFHybrid, chaincfg.MainNetParams.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - serializedPubKey := []byte{ - 0x07, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1, - 0xe9, 0x86, 0xe8, 0x84, 0x18, 0x5c, 0x61, 0xcf, 0x43, 0xe0, - 0x01, 0xf9, 0x13, 0x7f, 0x23, 0xc2, 0xc4, 0x09, 0x27, 0x3e, - 0xb1, 0x6e, 0x65, 0x37, 0xa5, 0x76, 0x78, 0x2e, 0xba, 0x66, - 0x8a, 0x7e, 0xf8, 0xbd, 0x3b, 0x3c, 0xfb, 0x1e, 0xdb, 0x71, - 0x17, 0xab, 0x65, 0x12, 0x9b, 0x8a, 0x2e, 0x68, 0x1f, 0x3c, - 0x1e, 0x09, 0x08, 0xef, 0x7b} - return btcutil.NewAddressPubKey(serializedPubKey, &chaincfg.MainNetParams) - }, - net: &chaincfg.MainNetParams, - }, - { - name: "testnet p2pk compressed (0x02)", - addr: "02192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4", - encoded: "mhiDPVP2nJunaAgTjzWSHCYfAqxxrxzjmo", - valid: true, - result: btcutil.TstAddressPubKey( - []byte{ - 0x02, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95, - 0x69, 0xc2, 0xe7, 0x79, 0x01, 0x57, 0x3d, 0x8d, 0x79, 0x03, - 0xc3, 0xeb, 0xec, 0x3a, 0x95, 0x77, 0x24, 0x89, 0x5d, 0xca, - 0x52, 0xc6, 0xb4}, - btcutil.PKFCompressed, chaincfg.TestNet3Params.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - serializedPubKey := []byte{ - 0x02, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95, - 0x69, 0xc2, 0xe7, 0x79, 0x01, 0x57, 0x3d, 0x8d, 0x79, 0x03, - 0xc3, 0xeb, 0xec, 0x3a, 0x95, 0x77, 0x24, 0x89, 0x5d, 0xca, - 0x52, 0xc6, 0xb4} - return btcutil.NewAddressPubKey(serializedPubKey, &chaincfg.TestNet3Params) - }, - net: &chaincfg.TestNet3Params, - }, - { - name: "testnet p2pk compressed (0x03)", - addr: "03b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65", - encoded: "mkPETRTSzU8MZLHkFKBmbKppxmdw9qT42t", - valid: true, - result: btcutil.TstAddressPubKey( - []byte{ - 0x03, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1, - 0xe9, 0x86, 0xe8, 0x84, 0x18, 0x5c, 0x61, 0xcf, 0x43, 0xe0, - 0x01, 0xf9, 0x13, 0x7f, 0x23, 0xc2, 0xc4, 0x09, 0x27, 0x3e, - 0xb1, 0x6e, 0x65}, - btcutil.PKFCompressed, chaincfg.TestNet3Params.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - serializedPubKey := []byte{ - 0x03, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1, - 0xe9, 0x86, 0xe8, 0x84, 0x18, 0x5c, 0x61, 0xcf, 0x43, 0xe0, - 0x01, 0xf9, 0x13, 0x7f, 0x23, 0xc2, 0xc4, 0x09, 0x27, 0x3e, - 0xb1, 0x6e, 0x65} - return btcutil.NewAddressPubKey(serializedPubKey, &chaincfg.TestNet3Params) - }, - net: &chaincfg.TestNet3Params, - }, - { - name: "testnet p2pk uncompressed (0x04)", - addr: "0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5" + - "cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", - encoded: "mh8YhPYEAYs3E7EVyKtB5xrcfMExkkdEMF", - valid: true, - result: btcutil.TstAddressPubKey( - []byte{ - 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, 0x6b, - 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 0xb6, 0x8a, 0x38, - 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 0xd7, 0xb1, 0x48, 0xa6, - 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 0xea, 0xdd, 0xfb, 0x84, 0xcc, - 0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, - 0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, - 0xf6, 0x56, 0xb4, 0x12, 0xa3}, - btcutil.PKFUncompressed, chaincfg.TestNet3Params.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - serializedPubKey := []byte{ - 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, 0x6b, - 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 0xb6, 0x8a, 0x38, - 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 0xd7, 0xb1, 0x48, 0xa6, - 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 0xea, 0xdd, 0xfb, 0x84, 0xcc, - 0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, - 0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, - 0xf6, 0x56, 0xb4, 0x12, 0xa3} - return btcutil.NewAddressPubKey(serializedPubKey, &chaincfg.TestNet3Params) - }, - net: &chaincfg.TestNet3Params, - }, - { - name: "testnet p2pk hybrid (0x06)", - addr: "06192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b" + - "40d45264838c0bd96852662ce6a847b197376830160c6d2eb5e6a4c44d33f453e", - encoded: "my639vCVzbDZuEiX44adfTUg6anRomZLEP", - valid: true, - result: btcutil.TstAddressPubKey( - []byte{ - 0x06, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95, - 0x69, 0xc2, 0xe7, 0x79, 0x01, 0x57, 0x3d, 0x8d, 0x79, 0x03, - 0xc3, 0xeb, 0xec, 0x3a, 0x95, 0x77, 0x24, 0x89, 0x5d, 0xca, - 0x52, 0xc6, 0xb4, 0x0d, 0x45, 0x26, 0x48, 0x38, 0xc0, 0xbd, - 0x96, 0x85, 0x26, 0x62, 0xce, 0x6a, 0x84, 0x7b, 0x19, 0x73, - 0x76, 0x83, 0x01, 0x60, 0xc6, 0xd2, 0xeb, 0x5e, 0x6a, 0x4c, - 0x44, 0xd3, 0x3f, 0x45, 0x3e}, - btcutil.PKFHybrid, chaincfg.TestNet3Params.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - serializedPubKey := []byte{ - 0x06, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95, - 0x69, 0xc2, 0xe7, 0x79, 0x01, 0x57, 0x3d, 0x8d, 0x79, 0x03, - 0xc3, 0xeb, 0xec, 0x3a, 0x95, 0x77, 0x24, 0x89, 0x5d, 0xca, - 0x52, 0xc6, 0xb4, 0x0d, 0x45, 0x26, 0x48, 0x38, 0xc0, 0xbd, - 0x96, 0x85, 0x26, 0x62, 0xce, 0x6a, 0x84, 0x7b, 0x19, 0x73, - 0x76, 0x83, 0x01, 0x60, 0xc6, 0xd2, 0xeb, 0x5e, 0x6a, 0x4c, - 0x44, 0xd3, 0x3f, 0x45, 0x3e} - return btcutil.NewAddressPubKey(serializedPubKey, &chaincfg.TestNet3Params) - }, - net: &chaincfg.TestNet3Params, - }, - { - name: "testnet p2pk hybrid (0x07)", - addr: "07b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e6" + - "537a576782eba668a7ef8bd3b3cfb1edb7117ab65129b8a2e681f3c1e0908ef7b", - encoded: "muUnepk5nPPrxUTuTAhRqrpAQuSWS5fVii", - valid: true, - result: btcutil.TstAddressPubKey( - []byte{ - 0x07, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1, - 0xe9, 0x86, 0xe8, 0x84, 0x18, 0x5c, 0x61, 0xcf, 0x43, 0xe0, - 0x01, 0xf9, 0x13, 0x7f, 0x23, 0xc2, 0xc4, 0x09, 0x27, 0x3e, - 0xb1, 0x6e, 0x65, 0x37, 0xa5, 0x76, 0x78, 0x2e, 0xba, 0x66, - 0x8a, 0x7e, 0xf8, 0xbd, 0x3b, 0x3c, 0xfb, 0x1e, 0xdb, 0x71, - 0x17, 0xab, 0x65, 0x12, 0x9b, 0x8a, 0x2e, 0x68, 0x1f, 0x3c, - 0x1e, 0x09, 0x08, 0xef, 0x7b}, - btcutil.PKFHybrid, chaincfg.TestNet3Params.PubKeyHashAddrID), - f: func() (btcutil.Address, error) { - serializedPubKey := []byte{ - 0x07, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1, - 0xe9, 0x86, 0xe8, 0x84, 0x18, 0x5c, 0x61, 0xcf, 0x43, 0xe0, - 0x01, 0xf9, 0x13, 0x7f, 0x23, 0xc2, 0xc4, 0x09, 0x27, 0x3e, - 0xb1, 0x6e, 0x65, 0x37, 0xa5, 0x76, 0x78, 0x2e, 0xba, 0x66, - 0x8a, 0x7e, 0xf8, 0xbd, 0x3b, 0x3c, 0xfb, 0x1e, 0xdb, 0x71, - 0x17, 0xab, 0x65, 0x12, 0x9b, 0x8a, 0x2e, 0x68, 0x1f, 0x3c, - 0x1e, 0x09, 0x08, 0xef, 0x7b} - return btcutil.NewAddressPubKey(serializedPubKey, &chaincfg.TestNet3Params) - }, - net: &chaincfg.TestNet3Params, - }, - // Segwit address tests. - { - name: "segwit mainnet p2wpkh v0", - addr: "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", - encoded: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", - valid: true, - result: btcutil.TstAddressWitnessPubKeyHash( - 0, - [20]byte{ - 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, - 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6}, - chaincfg.MainNetParams.Bech32HRPSegwit), - f: func() (btcutil.Address, error) { - pkHash := []byte{ - 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, - 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6} - return btcutil.NewAddressWitnessPubKeyHash(pkHash, &chaincfg.MainNetParams) - }, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit mainnet p2wsh v0", - addr: "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", - encoded: "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", - valid: true, - result: btcutil.TstAddressWitnessScriptHash( - 0, - [32]byte{ - 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, - 0x04, 0xbd, 0x19, 0x20, 0x33, 0x56, 0xda, 0x13, - 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1, - 0xb8, 0xc6, 0x32, 0x96, 0x04, 0x90, 0x32, 0x62}, - chaincfg.MainNetParams.Bech32HRPSegwit), - f: func() (btcutil.Address, error) { - scriptHash := []byte{ - 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, - 0x04, 0xbd, 0x19, 0x20, 0x33, 0x56, 0xda, 0x13, - 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1, - 0xb8, 0xc6, 0x32, 0x96, 0x04, 0x90, 0x32, 0x62} - return btcutil.NewAddressWitnessScriptHash(scriptHash, &chaincfg.MainNetParams) - }, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit testnet p2wpkh v0", - addr: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx", - encoded: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx", - valid: true, - result: btcutil.TstAddressWitnessPubKeyHash( - 0, - [20]byte{ - 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, - 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6}, - chaincfg.TestNet3Params.Bech32HRPSegwit), - f: func() (btcutil.Address, error) { - pkHash := []byte{ - 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, - 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6} - return btcutil.NewAddressWitnessPubKeyHash(pkHash, &chaincfg.TestNet3Params) - }, - net: &chaincfg.TestNet3Params, - }, - { - name: "segwit testnet p2wsh v0", - addr: "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", - encoded: "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", - valid: true, - result: btcutil.TstAddressWitnessScriptHash( - 0, - [32]byte{ - 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, - 0x04, 0xbd, 0x19, 0x20, 0x33, 0x56, 0xda, 0x13, - 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1, - 0xb8, 0xc6, 0x32, 0x96, 0x04, 0x90, 0x32, 0x62}, - chaincfg.TestNet3Params.Bech32HRPSegwit), - f: func() (btcutil.Address, error) { - scriptHash := []byte{ - 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, - 0x04, 0xbd, 0x19, 0x20, 0x33, 0x56, 0xda, 0x13, - 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1, - 0xb8, 0xc6, 0x32, 0x96, 0x04, 0x90, 0x32, 0x62} - return btcutil.NewAddressWitnessScriptHash(scriptHash, &chaincfg.TestNet3Params) - }, - net: &chaincfg.TestNet3Params, - }, - { - name: "segwit testnet p2wsh witness v0", - addr: "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", - encoded: "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", - valid: true, - result: btcutil.TstAddressWitnessScriptHash( - 0, - [32]byte{ - 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, - 0x21, 0xb2, 0xa1, 0x87, 0x90, 0x5e, 0x52, 0x66, - 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2, - 0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64, 0x33}, - chaincfg.TestNet3Params.Bech32HRPSegwit), - f: func() (btcutil.Address, error) { - scriptHash := []byte{ - 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, - 0x21, 0xb2, 0xa1, 0x87, 0x90, 0x5e, 0x52, 0x66, - 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2, - 0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64, 0x33} - return btcutil.NewAddressWitnessScriptHash(scriptHash, &chaincfg.TestNet3Params) - }, - net: &chaincfg.TestNet3Params, - }, - { - name: "segwit litecoin mainnet p2wpkh v0", - addr: "LTC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KGMN4N9", - encoded: "ltc1qw508d6qejxtdg4y5r3zarvary0c5xw7kgmn4n9", - valid: true, - result: btcutil.TstAddressWitnessPubKeyHash( - 0, - [20]byte{ - 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, - 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6}, - CustomParams.Bech32HRPSegwit, - ), - f: func() (btcutil.Address, error) { - pkHash := []byte{ - 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, - 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6} - return btcutil.NewAddressWitnessPubKeyHash(pkHash, &customParams) - }, - net: &customParams, - }, - - // P2TR address tests. - { - name: "segwit v1 mainnet p2tr", - addr: "bc1paardr2nczq0rx5rqpfwnvpzm497zvux64y0f7wjgcs7xuuuh2nnqwr2d5c", - encoded: "bc1paardr2nczq0rx5rqpfwnvpzm497zvux64y0f7wjgcs7xuuuh2nnqwr2d5c", - valid: true, - result: btcutil.TstAddressTaproot( - 1, [32]byte{ - 0xef, 0x46, 0xd1, 0xaa, 0x78, 0x10, 0x1e, 0x33, - 0x50, 0x60, 0x0a, 0x5d, 0x36, 0x04, 0x5b, 0xa9, - 0x7c, 0x26, 0x70, 0xda, 0xa9, 0x1e, 0x9f, 0x3a, - 0x48, 0xc4, 0x3c, 0x6e, 0x73, 0x97, 0x54, 0xe6, - }, chaincfg.MainNetParams.Bech32HRPSegwit, - ), - f: func() (btcutil.Address, error) { - scriptHash := []byte{ - 0xef, 0x46, 0xd1, 0xaa, 0x78, 0x10, 0x1e, 0x33, - 0x50, 0x60, 0x0a, 0x5d, 0x36, 0x04, 0x5b, 0xa9, - 0x7c, 0x26, 0x70, 0xda, 0xa9, 0x1e, 0x9f, 0x3a, - 0x48, 0xc4, 0x3c, 0x6e, 0x73, 0x97, 0x54, 0xe6, - } - return btcutil.NewAddressTaproot( - scriptHash, &chaincfg.MainNetParams, - ) - }, - net: &chaincfg.MainNetParams, - }, - - // Invalid bech32m tests. Source: - // https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki - { - name: "segwit v1 invalid human-readable part", - addr: "tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit v1 mainnet bech32 instead of bech32m", - addr: "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit v1 testnet bech32 instead of bech32m", - addr: "tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf", - valid: false, - net: &chaincfg.TestNet3Params, - }, - { - name: "segwit v1 mainnet bech32 instead of bech32m upper case", - addr: "BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit v0 mainnet bech32m instead of bech32", - addr: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit v1 testnet bech32 instead of bech32m second test", - addr: "tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47", - valid: false, - net: &chaincfg.TestNet3Params, - }, - { - name: "segwit v1 mainnet bech32m invalid character in checksum", - addr: "bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit mainnet witness v17", - addr: "BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit v1 mainnet bech32m invalid program length (1 byte)", - addr: "bc1pw5dgrnzv", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit v1 mainnet bech32m invalid program length (41 bytes)", - addr: "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit v1 testnet bech32m mixed case", - addr: "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq", - valid: false, - net: &chaincfg.TestNet3Params, - }, - { - name: "segwit v1 mainnet bech32m zero padding of more than 4 bits", - addr: "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit v1 mainnet bech32m non-zero padding in 8-to-5-conversion", - addr: "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j", - valid: false, - net: &chaincfg.TestNet3Params, - }, - { - name: "segwit v1 mainnet bech32m empty data section", - addr: "bc1gmk9yu", - valid: false, - net: &chaincfg.MainNetParams, - }, - - // Unsupported witness versions (version 0 and 1 only supported at this point) - { - name: "segwit mainnet witness v16", - addr: "BC1SW50QA3JX3S", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit mainnet witness v2", - addr: "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", - valid: false, - net: &chaincfg.MainNetParams, - }, - // Invalid segwit addresses - { - name: "segwit invalid hrp", - addr: "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", - valid: false, - net: &chaincfg.TestNet3Params, - }, - { - name: "segwit invalid checksum", - addr: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit invalid witness version", - addr: "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit invalid program length", - addr: "bc1rw5uspcuh", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit invalid program length", - addr: "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit invalid program length for witness version 0 (per BIP141)", - addr: "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", - valid: false, - net: &chaincfg.MainNetParams, - }, - { - name: "segwit mixed case", - addr: "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7", - valid: false, - net: &chaincfg.TestNet3Params, - }, - { - name: "segwit zero padding of more than 4 bits", - addr: "tb1pw508d6qejxtdg4y5r3zarqfsj6c3", - valid: false, - net: &chaincfg.TestNet3Params, - }, - { - name: "segwit non-zero padding in 8-to-5 conversion", - addr: "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", - valid: false, - net: &chaincfg.TestNet3Params, - }, - } - - if err := chaincfg.Register(&customParams); err != nil { - panic(err) - } - - for _, test := range tests { - // Decode addr and compare error against valid. - decoded, err := btcutil.DecodeAddress(test.addr, test.net) - if (err == nil) != test.valid { - t.Errorf("%v: decoding test failed: %v", test.name, err) - return - } - - if err == nil { - // Ensure the stringer returns the same address as the - // original. - if decodedStringer, ok := decoded.(fmt.Stringer); ok { - addr := test.addr - - // For Segwit addresses the string representation - // will always be lower case, so in that case we - // convert the original to lower case first. - if strings.Contains(test.name, "segwit") { - addr = strings.ToLower(addr) - } - - if addr != decodedStringer.String() { - t.Errorf("%v: String on decoded value does not match expected value: %v != %v", - test.name, test.addr, decodedStringer.String()) - return - } - } - - // Encode again and compare against the original. - encoded := decoded.EncodeAddress() - if test.encoded != encoded { - t.Errorf("%v: decoding and encoding produced different addresses: %v != %v", - test.name, test.encoded, encoded) - return - } - - // Perform type-specific calculations. - var saddr []byte - switch d := decoded.(type) { - case *btcutil.AddressPubKeyHash: - saddr = btcutil.TstAddressSAddr(encoded) - - case *btcutil.AddressScriptHash: - saddr = btcutil.TstAddressSAddr(encoded) - - case *btcutil.AddressPubKey: - // Ignore the error here since the script - // address is checked below. - saddr, _ = hex.DecodeString(d.String()) - case *btcutil.AddressWitnessPubKeyHash: - saddr = btcutil.TstAddressSegwitSAddr(encoded) - case *btcutil.AddressWitnessScriptHash: - saddr = btcutil.TstAddressSegwitSAddr(encoded) - case *btcutil.AddressTaproot: - saddr = btcutil.TstAddressTaprootSAddr(encoded) - } - - // Check script address, as well as the Hash160 method for P2PKH and - // P2SH addresses. - if !bytes.Equal(saddr, decoded.ScriptAddress()) { - t.Errorf("%v: script addresses do not match:\n%x != \n%x", - test.name, saddr, decoded.ScriptAddress()) - return - } - switch a := decoded.(type) { - case *btcutil.AddressPubKeyHash: - if h := a.Hash160()[:]; !bytes.Equal(saddr, h) { - t.Errorf("%v: hashes do not match:\n%x != \n%x", - test.name, saddr, h) - return - } - - case *btcutil.AddressScriptHash: - if h := a.Hash160()[:]; !bytes.Equal(saddr, h) { - t.Errorf("%v: hashes do not match:\n%x != \n%x", - test.name, saddr, h) - return - } - - case *btcutil.AddressWitnessPubKeyHash: - if hrp := a.Hrp(); test.net.Bech32HRPSegwit != hrp { - t.Errorf("%v: hrps do not match:\n%x != \n%x", - test.name, test.net.Bech32HRPSegwit, hrp) - return - } - - expVer := test.result.(*btcutil.AddressWitnessPubKeyHash).WitnessVersion() - if v := a.WitnessVersion(); v != expVer { - t.Errorf("%v: witness versions do not match:\n%x != \n%x", - test.name, expVer, v) - return - } - - if p := a.WitnessProgram(); !bytes.Equal(saddr, p) { - t.Errorf("%v: witness programs do not match:\n%x != \n%x", - test.name, saddr, p) - return - } - - case *btcutil.AddressWitnessScriptHash: - if hrp := a.Hrp(); test.net.Bech32HRPSegwit != hrp { - t.Errorf("%v: hrps do not match:\n%x != \n%x", - test.name, test.net.Bech32HRPSegwit, hrp) - return - } - - expVer := test.result.(*btcutil.AddressWitnessScriptHash).WitnessVersion() - if v := a.WitnessVersion(); v != expVer { - t.Errorf("%v: witness versions do not match:\n%x != \n%x", - test.name, expVer, v) - return - } - - if p := a.WitnessProgram(); !bytes.Equal(saddr, p) { - t.Errorf("%v: witness programs do not match:\n%x != \n%x", - test.name, saddr, p) - return - } - } - - // Ensure the address is for the expected network. - if !decoded.IsForNet(test.net) { - t.Errorf("%v: calculated network does not match expected", - test.name) - return - } - } else { - // If there is an error, make sure we can print it - // correctly. - errStr := err.Error() - if errStr == "" { - t.Errorf("%v: error was non-nil but message is"+ - "empty: %v", test.name, err) - } - } - - if !test.valid { - // If address is invalid, but a creation function exists, - // verify that it returns a nil addr and non-nil error. - if test.f != nil { - _, err := test.f() - if err == nil { - t.Errorf("%v: address is invalid but creating new address succeeded", - test.name) - return - } - } - continue - } - - // Valid test, compare address created with f against expected result. - addr, err := test.f() - if err != nil { - t.Errorf("%v: address is valid but creating new address failed with error %v", - test.name, err) - return - } - - if !reflect.DeepEqual(addr, test.result) { - t.Errorf("%v: created address does not match expected result", - test.name) - return - } - } -} diff --git a/amount.go b/amount.go deleted file mode 100644 index e2f057ecc..000000000 --- a/amount.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2013, 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil - -import ( - "errors" - "math" - "strconv" -) - -// AmountUnit describes a method of converting an Amount to something -// other than the base unit of a bitcoin. The value of the AmountUnit -// is the exponent component of the decadic multiple to convert from -// an amount in bitcoin to an amount counted in units. -type AmountUnit int - -// These constants define various units used when describing a bitcoin -// monetary amount. -const ( - AmountMegaBTC AmountUnit = 6 - AmountKiloBTC AmountUnit = 3 - AmountBTC AmountUnit = 0 - AmountMilliBTC AmountUnit = -3 - AmountMicroBTC AmountUnit = -6 - AmountSatoshi AmountUnit = -8 -) - -// String returns the unit as a string. For recognized units, the SI -// prefix is used, or "Satoshi" for the base unit. For all unrecognized -// units, "1eN BTC" is returned, where N is the AmountUnit. -func (u AmountUnit) String() string { - switch u { - case AmountMegaBTC: - return "MBTC" - case AmountKiloBTC: - return "kBTC" - case AmountBTC: - return "BTC" - case AmountMilliBTC: - return "mBTC" - case AmountMicroBTC: - return "μBTC" - case AmountSatoshi: - return "Satoshi" - default: - return "1e" + strconv.FormatInt(int64(u), 10) + " BTC" - } -} - -// Amount represents the base bitcoin monetary unit (colloquially referred -// to as a `Satoshi'). A single Amount is equal to 1e-8 of a bitcoin. -type Amount int64 - -// round converts a floating point number, which may or may not be representable -// as an integer, to the Amount integer type by rounding to the nearest integer. -// This is performed by adding or subtracting 0.5 depending on the sign, and -// relying on integer truncation to round the value to the nearest Amount. -func round(f float64) Amount { - if f < 0 { - return Amount(f - 0.5) - } - return Amount(f + 0.5) -} - -// NewAmount creates an Amount from a floating point value representing -// some value in bitcoin. NewAmount errors if f is NaN or +-Infinity, but -// does not check that the amount is within the total amount of bitcoin -// producible as f may not refer to an amount at a single moment in time. -// -// NewAmount is for specifically for converting BTC to Satoshi. -// For creating a new Amount with an int64 value which denotes a quantity of Satoshi, -// do a simple type conversion from type int64 to Amount. -// See GoDoc for example: http://godoc.org/github.com/btcsuite/btcutil#example-Amount -func NewAmount(f float64) (Amount, error) { - // The amount is only considered invalid if it cannot be represented - // as an integer type. This may happen if f is NaN or +-Infinity. - switch { - case math.IsNaN(f): - fallthrough - case math.IsInf(f, 1): - fallthrough - case math.IsInf(f, -1): - return 0, errors.New("invalid bitcoin amount") - } - - return round(f * SatoshiPerBitcoin), nil -} - -// ToUnit converts a monetary amount counted in bitcoin base units to a -// floating point value representing an amount of bitcoin. -func (a Amount) ToUnit(u AmountUnit) float64 { - return float64(a) / math.Pow10(int(u+8)) -} - -// ToBTC is the equivalent of calling ToUnit with AmountBTC. -func (a Amount) ToBTC() float64 { - return a.ToUnit(AmountBTC) -} - -// Format formats a monetary amount counted in bitcoin base units as a -// string for a given unit. The conversion will succeed for any unit, -// however, known units will be formated with an appended label describing -// the units with SI notation, or "Satoshi" for the base unit. -func (a Amount) Format(u AmountUnit) string { - units := " " + u.String() - return strconv.FormatFloat(a.ToUnit(u), 'f', -int(u+8), 64) + units -} - -// String is the equivalent of calling Format with AmountBTC. -func (a Amount) String() string { - return a.Format(AmountBTC) -} - -// MulF64 multiplies an Amount by a floating point value. While this is not -// an operation that must typically be done by a full node or wallet, it is -// useful for services that build on top of bitcoin (for example, calculating -// a fee by multiplying by a percentage). -func (a Amount) MulF64(f float64) Amount { - return round(float64(a) * f) -} diff --git a/amount_test.go b/amount_test.go deleted file mode 100644 index 808ea2450..000000000 --- a/amount_test.go +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright (c) 2013, 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil_test - -import ( - "math" - "testing" - - . "github.com/btcsuite/btcutil" -) - -func TestAmountCreation(t *testing.T) { - tests := []struct { - name string - amount float64 - valid bool - expected Amount - }{ - // Positive tests. - { - name: "zero", - amount: 0, - valid: true, - expected: 0, - }, - { - name: "max producible", - amount: 21e6, - valid: true, - expected: MaxSatoshi, - }, - { - name: "min producible", - amount: -21e6, - valid: true, - expected: -MaxSatoshi, - }, - { - name: "exceeds max producible", - amount: 21e6 + 1e-8, - valid: true, - expected: MaxSatoshi + 1, - }, - { - name: "exceeds min producible", - amount: -21e6 - 1e-8, - valid: true, - expected: -MaxSatoshi - 1, - }, - { - name: "one hundred", - amount: 100, - valid: true, - expected: 100 * SatoshiPerBitcoin, - }, - { - name: "fraction", - amount: 0.01234567, - valid: true, - expected: 1234567, - }, - { - name: "rounding up", - amount: 54.999999999999943157, - valid: true, - expected: 55 * SatoshiPerBitcoin, - }, - { - name: "rounding down", - amount: 55.000000000000056843, - valid: true, - expected: 55 * SatoshiPerBitcoin, - }, - - // Negative tests. - { - name: "not-a-number", - amount: math.NaN(), - valid: false, - }, - { - name: "-infinity", - amount: math.Inf(-1), - valid: false, - }, - { - name: "+infinity", - amount: math.Inf(1), - valid: false, - }, - } - - for _, test := range tests { - a, err := NewAmount(test.amount) - switch { - case test.valid && err != nil: - t.Errorf("%v: Positive test Amount creation failed with: %v", test.name, err) - continue - case !test.valid && err == nil: - t.Errorf("%v: Negative test Amount creation succeeded (value %v) when should fail", test.name, a) - continue - } - - if a != test.expected { - t.Errorf("%v: Created amount %v does not match expected %v", test.name, a, test.expected) - continue - } - } -} - -func TestAmountUnitConversions(t *testing.T) { - tests := []struct { - name string - amount Amount - unit AmountUnit - converted float64 - s string - }{ - { - name: "MBTC", - amount: MaxSatoshi, - unit: AmountMegaBTC, - converted: 21, - s: "21 MBTC", - }, - { - name: "kBTC", - amount: 44433322211100, - unit: AmountKiloBTC, - converted: 444.33322211100, - s: "444.333222111 kBTC", - }, - { - name: "BTC", - amount: 44433322211100, - unit: AmountBTC, - converted: 444333.22211100, - s: "444333.222111 BTC", - }, - { - name: "mBTC", - amount: 44433322211100, - unit: AmountMilliBTC, - converted: 444333222.11100, - s: "444333222.111 mBTC", - }, - { - - name: "μBTC", - amount: 44433322211100, - unit: AmountMicroBTC, - converted: 444333222111.00, - s: "444333222111 μBTC", - }, - { - - name: "satoshi", - amount: 44433322211100, - unit: AmountSatoshi, - converted: 44433322211100, - s: "44433322211100 Satoshi", - }, - { - - name: "non-standard unit", - amount: 44433322211100, - unit: AmountUnit(-1), - converted: 4443332.2211100, - s: "4443332.22111 1e-1 BTC", - }, - } - - for _, test := range tests { - f := test.amount.ToUnit(test.unit) - if f != test.converted { - t.Errorf("%v: converted value %v does not match expected %v", test.name, f, test.converted) - continue - } - - s := test.amount.Format(test.unit) - if s != test.s { - t.Errorf("%v: format '%v' does not match expected '%v'", test.name, s, test.s) - continue - } - - // Verify that Amount.ToBTC works as advertised. - f1 := test.amount.ToUnit(AmountBTC) - f2 := test.amount.ToBTC() - if f1 != f2 { - t.Errorf("%v: ToBTC does not match ToUnit(AmountBTC): %v != %v", test.name, f1, f2) - } - - // Verify that Amount.String works as advertised. - s1 := test.amount.Format(AmountBTC) - s2 := test.amount.String() - if s1 != s2 { - t.Errorf("%v: String does not match Format(AmountBitcoin): %v != %v", test.name, s1, s2) - } - } -} - -func TestAmountMulF64(t *testing.T) { - tests := []struct { - name string - amt Amount - mul float64 - res Amount - }{ - { - name: "Multiply 0.1 BTC by 2", - amt: 100e5, // 0.1 BTC - mul: 2, - res: 200e5, // 0.2 BTC - }, - { - name: "Multiply 0.2 BTC by 0.02", - amt: 200e5, // 0.2 BTC - mul: 1.02, - res: 204e5, // 0.204 BTC - }, - { - name: "Multiply 0.1 BTC by -2", - amt: 100e5, // 0.1 BTC - mul: -2, - res: -200e5, // -0.2 BTC - }, - { - name: "Multiply 0.2 BTC by -0.02", - amt: 200e5, // 0.2 BTC - mul: -1.02, - res: -204e5, // -0.204 BTC - }, - { - name: "Multiply -0.1 BTC by 2", - amt: -100e5, // -0.1 BTC - mul: 2, - res: -200e5, // -0.2 BTC - }, - { - name: "Multiply -0.2 BTC by 0.02", - amt: -200e5, // -0.2 BTC - mul: 1.02, - res: -204e5, // -0.204 BTC - }, - { - name: "Multiply -0.1 BTC by -2", - amt: -100e5, // -0.1 BTC - mul: -2, - res: 200e5, // 0.2 BTC - }, - { - name: "Multiply -0.2 BTC by -0.02", - amt: -200e5, // -0.2 BTC - mul: -1.02, - res: 204e5, // 0.204 BTC - }, - { - name: "Round down", - amt: 49, // 49 Satoshis - mul: 0.01, - res: 0, - }, - { - name: "Round up", - amt: 50, // 50 Satoshis - mul: 0.01, - res: 1, // 1 Satoshi - }, - { - name: "Multiply by 0.", - amt: 1e8, // 1 BTC - mul: 0, - res: 0, // 0 BTC - }, - { - name: "Multiply 1 by 0.5.", - amt: 1, // 1 Satoshi - mul: 0.5, - res: 1, // 1 Satoshi - }, - { - name: "Multiply 100 by 66%.", - amt: 100, // 100 Satoshis - mul: 0.66, - res: 66, // 66 Satoshis - }, - { - name: "Multiply 100 by 66.6%.", - amt: 100, // 100 Satoshis - mul: 0.666, - res: 67, // 67 Satoshis - }, - { - name: "Multiply 100 by 2/3.", - amt: 100, // 100 Satoshis - mul: 2.0 / 3, - res: 67, // 67 Satoshis - }, - } - - for _, test := range tests { - a := test.amt.MulF64(test.mul) - if a != test.res { - t.Errorf("%v: expected %v got %v", test.name, test.res, a) - } - } -} diff --git a/appdata.go b/appdata.go deleted file mode 100644 index e36cf7c4a..000000000 --- a/appdata.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) 2013-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil - -import ( - "os" - "os/user" - "path/filepath" - "runtime" - "strings" - "unicode" -) - -// appDataDir returns an operating system specific directory to be used for -// storing application data for an application. See AppDataDir for more -// details. This unexported version takes an operating system argument -// primarily to enable the testing package to properly test the function by -// forcing an operating system that is not the currently one. -func appDataDir(goos, appName string, roaming bool) string { - if appName == "" || appName == "." { - return "." - } - - // The caller really shouldn't prepend the appName with a period, but - // if they do, handle it gracefully by trimming it. - appName = strings.TrimPrefix(appName, ".") - appNameUpper := string(unicode.ToUpper(rune(appName[0]))) + appName[1:] - appNameLower := string(unicode.ToLower(rune(appName[0]))) + appName[1:] - - // Get the OS specific home directory via the Go standard lib. - var homeDir string - usr, err := user.Current() - if err == nil { - homeDir = usr.HomeDir - } - - // Fall back to standard HOME environment variable that works - // for most POSIX OSes if the directory from the Go standard - // lib failed. - if err != nil || homeDir == "" { - homeDir = os.Getenv("HOME") - } - - switch goos { - // Attempt to use the LOCALAPPDATA or APPDATA environment variable on - // Windows. - case "windows": - // Windows XP and before didn't have a LOCALAPPDATA, so fallback - // to regular APPDATA when LOCALAPPDATA is not set. - appData := os.Getenv("LOCALAPPDATA") - if roaming || appData == "" { - appData = os.Getenv("APPDATA") - } - - if appData != "" { - return filepath.Join(appData, appNameUpper) - } - - case "darwin": - if homeDir != "" { - return filepath.Join(homeDir, "Library", - "Application Support", appNameUpper) - } - - case "plan9": - if homeDir != "" { - return filepath.Join(homeDir, appNameLower) - } - - default: - if homeDir != "" { - return filepath.Join(homeDir, "."+appNameLower) - } - } - - // Fall back to the current directory if all else fails. - return "." -} - -// AppDataDir returns an operating system specific directory to be used for -// storing application data for an application. -// -// The appName parameter is the name of the application the data directory is -// being requested for. This function will prepend a period to the appName for -// POSIX style operating systems since that is standard practice. An empty -// appName or one with a single dot is treated as requesting the current -// directory so only "." will be returned. Further, the first character -// of appName will be made lowercase for POSIX style operating systems and -// uppercase for Mac and Windows since that is standard practice. -// -// The roaming parameter only applies to Windows where it specifies the roaming -// application data profile (%APPDATA%) should be used instead of the local one -// (%LOCALAPPDATA%) that is used by default. -// -// Example results: -// dir := AppDataDir("myapp", false) -// POSIX (Linux/BSD): ~/.myapp -// Mac OS: $HOME/Library/Application Support/Myapp -// Windows: %LOCALAPPDATA%\Myapp -// Plan 9: $home/myapp -func AppDataDir(appName string, roaming bool) string { - return appDataDir(runtime.GOOS, appName, roaming) -} diff --git a/appdata_test.go b/appdata_test.go deleted file mode 100644 index dff62d446..000000000 --- a/appdata_test.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) 2013-2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil_test - -import ( - "os" - "os/user" - "path/filepath" - "runtime" - "testing" - "unicode" - - "github.com/btcsuite/btcutil" -) - -// TestAppDataDir tests the API for AppDataDir to ensure it gives expected -// results for various operating systems. -func TestAppDataDir(t *testing.T) { - // App name plus upper and lowercase variants. - appName := "myapp" - appNameUpper := string(unicode.ToUpper(rune(appName[0]))) + appName[1:] - appNameLower := string(unicode.ToLower(rune(appName[0]))) + appName[1:] - - // When we're on Windows, set the expected local and roaming directories - // per the environment vars. When we aren't on Windows, the function - // should return the current directory when forced to provide the - // Windows path since the environment variables won't exist. - winLocal := "." - winRoaming := "." - if runtime.GOOS == "windows" { - localAppData := os.Getenv("LOCALAPPDATA") - roamingAppData := os.Getenv("APPDATA") - if localAppData == "" { - localAppData = roamingAppData - } - winLocal = filepath.Join(localAppData, appNameUpper) - winRoaming = filepath.Join(roamingAppData, appNameUpper) - } - - // Get the home directory to use for testing expected results. - var homeDir string - usr, err := user.Current() - if err != nil { - t.Errorf("user.Current: %v", err) - return - } - homeDir = usr.HomeDir - - // Mac app data directory. - macAppData := filepath.Join(homeDir, "Library", "Application Support") - - tests := []struct { - goos string - appName string - roaming bool - want string - }{ - // Various combinations of application name casing, leading - // period, operating system, and roaming flags. - {"windows", appNameLower, false, winLocal}, - {"windows", appNameUpper, false, winLocal}, - {"windows", "." + appNameLower, false, winLocal}, - {"windows", "." + appNameUpper, false, winLocal}, - {"windows", appNameLower, true, winRoaming}, - {"windows", appNameUpper, true, winRoaming}, - {"windows", "." + appNameLower, true, winRoaming}, - {"windows", "." + appNameUpper, true, winRoaming}, - {"linux", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)}, - {"linux", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)}, - {"linux", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)}, - {"linux", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)}, - {"darwin", appNameLower, false, filepath.Join(macAppData, appNameUpper)}, - {"darwin", appNameUpper, false, filepath.Join(macAppData, appNameUpper)}, - {"darwin", "." + appNameLower, false, filepath.Join(macAppData, appNameUpper)}, - {"darwin", "." + appNameUpper, false, filepath.Join(macAppData, appNameUpper)}, - {"openbsd", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)}, - {"openbsd", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)}, - {"openbsd", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)}, - {"openbsd", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)}, - {"freebsd", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)}, - {"freebsd", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)}, - {"freebsd", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)}, - {"freebsd", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)}, - {"netbsd", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)}, - {"netbsd", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)}, - {"netbsd", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)}, - {"netbsd", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)}, - {"plan9", appNameLower, false, filepath.Join(homeDir, appNameLower)}, - {"plan9", appNameUpper, false, filepath.Join(homeDir, appNameLower)}, - {"plan9", "." + appNameLower, false, filepath.Join(homeDir, appNameLower)}, - {"plan9", "." + appNameUpper, false, filepath.Join(homeDir, appNameLower)}, - {"unrecognized", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)}, - {"unrecognized", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)}, - {"unrecognized", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)}, - {"unrecognized", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)}, - - // No application name provided, so expect current directory. - {"windows", "", false, "."}, - {"windows", "", true, "."}, - {"linux", "", false, "."}, - {"darwin", "", false, "."}, - {"openbsd", "", false, "."}, - {"freebsd", "", false, "."}, - {"netbsd", "", false, "."}, - {"plan9", "", false, "."}, - {"unrecognized", "", false, "."}, - - // Single dot provided for application name, so expect current - // directory. - {"windows", ".", false, "."}, - {"windows", ".", true, "."}, - {"linux", ".", false, "."}, - {"darwin", ".", false, "."}, - {"openbsd", ".", false, "."}, - {"freebsd", ".", false, "."}, - {"netbsd", ".", false, "."}, - {"plan9", ".", false, "."}, - {"unrecognized", ".", false, "."}, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - ret := btcutil.TstAppDataDir(test.goos, test.appName, test.roaming) - if ret != test.want { - t.Errorf("appDataDir #%d (%s) does not match - "+ - "expected got %s, want %s", i, test.goos, ret, - test.want) - continue - } - } -} diff --git a/base58/README.md b/base58/README.md deleted file mode 100644 index 98dfb1db8..000000000 --- a/base58/README.md +++ /dev/null @@ -1,34 +0,0 @@ -base58 -========== - -[![Build Status](http://img.shields.io/travis/btcsuite/btcutil.svg)](https://travis-ci.org/btcsuite/btcutil) -[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) -[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcutil/base58) - -Package base58 provides an API for encoding and decoding to and from the -modified base58 encoding. It also provides an API to do Base58Check encoding, -as described [here](https://en.bitcoin.it/wiki/Base58Check_encoding). - -A comprehensive suite of tests is provided to ensure proper functionality. - -## Installation and Updating - -```bash -$ go get -u github.com/btcsuite/btcutil/base58 -``` - -## Examples - -* [Decode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-Decode) - Demonstrates how to decode modified base58 encoded data. -* [Encode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-Encode) - Demonstrates how to encode data using the modified base58 encoding scheme. -* [CheckDecode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-CheckDecode) - Demonstrates how to decode Base58Check encoded data. -* [CheckEncode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-CheckEncode) - Demonstrates how to encode data using the Base58Check encoding scheme. - -## License - -Package base58 is licensed under the [copyfree](http://copyfree.org) ISC -License. diff --git a/base58/alphabet.go b/base58/alphabet.go deleted file mode 100644 index 6bb39fef1..000000000 --- a/base58/alphabet.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -// AUTOGENERATED by genalphabet.go; do not edit. - -package base58 - -const ( - // alphabet is the modified base58 alphabet used by Bitcoin. - alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" - - alphabetIdx0 = '1' -) - -var b58 = [256]byte{ - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 255, 255, 255, 255, 255, 255, - 255, 9, 10, 11, 12, 13, 14, 15, - 16, 255, 17, 18, 19, 20, 21, 255, - 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 255, 255, 255, 255, 255, - 255, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 255, 44, 45, 46, - 47, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 57, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, -} diff --git a/base58/base58.go b/base58/base58.go deleted file mode 100644 index 8ee595671..000000000 --- a/base58/base58.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (c) 2013-2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package base58 - -import ( - "math/big" -) - -//go:generate go run genalphabet.go - -var bigRadix = [...]*big.Int{ - big.NewInt(0), - big.NewInt(58), - big.NewInt(58 * 58), - big.NewInt(58 * 58 * 58), - big.NewInt(58 * 58 * 58 * 58), - big.NewInt(58 * 58 * 58 * 58 * 58), - big.NewInt(58 * 58 * 58 * 58 * 58 * 58), - big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58), - big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58), - big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58), - bigRadix10, -} - -var bigRadix10 = big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58) // 58^10 - -// Decode decodes a modified base58 string to a byte slice. -func Decode(b string) []byte { - answer := big.NewInt(0) - scratch := new(big.Int) - - // Calculating with big.Int is slow for each iteration. - // x += b58[b[i]] * j - // j *= 58 - // - // Instead we can try to do as much calculations on int64. - // We can represent a 10 digit base58 number using an int64. - // - // Hence we'll try to convert 10, base58 digits at a time. - // The rough idea is to calculate `t`, such that: - // - // t := b58[b[i+9]] * 58^9 ... + b58[b[i+1]] * 58^1 + b58[b[i]] * 58^0 - // x *= 58^10 - // x += t - // - // Of course, in addition, we'll need to handle boundary condition when `b` is not multiple of 58^10. - // In that case we'll use the bigRadix[n] lookup for the appropriate power. - for t := b; len(t) > 0; { - n := len(t) - if n > 10 { - n = 10 - } - - total := uint64(0) - for _, v := range t[:n] { - tmp := b58[v] - if tmp == 255 { - return []byte("") - } - total = total*58 + uint64(tmp) - } - - answer.Mul(answer, bigRadix[n]) - scratch.SetUint64(total) - answer.Add(answer, scratch) - - t = t[n:] - } - - tmpval := answer.Bytes() - - var numZeros int - for numZeros = 0; numZeros < len(b); numZeros++ { - if b[numZeros] != alphabetIdx0 { - break - } - } - flen := numZeros + len(tmpval) - val := make([]byte, flen) - copy(val[numZeros:], tmpval) - - return val -} - -// Encode encodes a byte slice to a modified base58 string. -func Encode(b []byte) string { - x := new(big.Int) - x.SetBytes(b) - - // maximum length of output is log58(2^(8*len(b))) == len(b) * 8 / log(58) - maxlen := int(float64(len(b))*1.365658237309761) + 1 - answer := make([]byte, 0, maxlen) - mod := new(big.Int) - for x.Sign() > 0 { - // Calculating with big.Int is slow for each iteration. - // x, mod = x / 58, x % 58 - // - // Instead we can try to do as much calculations on int64. - // x, mod = x / 58^10, x % 58^10 - // - // Which will give us mod, which is 10 digit base58 number. - // We'll loop that 10 times to convert to the answer. - - x.DivMod(x, bigRadix10, mod) - if x.Sign() == 0 { - // When x = 0, we need to ensure we don't add any extra zeros. - m := mod.Int64() - for m > 0 { - answer = append(answer, alphabet[m%58]) - m /= 58 - } - } else { - m := mod.Int64() - for i := 0; i < 10; i++ { - answer = append(answer, alphabet[m%58]) - m /= 58 - } - } - } - - // leading zero bytes - for _, i := range b { - if i != 0 { - break - } - answer = append(answer, alphabetIdx0) - } - - // reverse - alen := len(answer) - for i := 0; i < alen/2; i++ { - answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i] - } - - return string(answer) -} diff --git a/base58/base58_test.go b/base58/base58_test.go deleted file mode 100644 index c4bba4f64..000000000 --- a/base58/base58_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) 2013-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package base58_test - -import ( - "bytes" - "encoding/hex" - "testing" - - "github.com/btcsuite/btcutil/base58" -) - -var stringTests = []struct { - in string - out string -}{ - {"", ""}, - {" ", "Z"}, - {"-", "n"}, - {"0", "q"}, - {"1", "r"}, - {"-1", "4SU"}, - {"11", "4k8"}, - {"abc", "ZiCa"}, - {"1234598760", "3mJr7AoUXx2Wqd"}, - {"abcdefghijklmnopqrstuvwxyz", "3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f"}, - {"00000000000000000000000000000000000000000000000000000000000000", "3sN2THZeE9Eh9eYrwkvZqNstbHGvrxSAM7gXUXvyFQP8XvQLUqNCS27icwUeDT7ckHm4FUHM2mTVh1vbLmk7y"}, -} - -var invalidStringTests = []struct { - in string - out string -}{ - {"0", ""}, - {"O", ""}, - {"I", ""}, - {"l", ""}, - {"3mJr0", ""}, - {"O3yxU", ""}, - {"3sNI", ""}, - {"4kl8", ""}, - {"0OIl", ""}, - {"!@#$%^&*()-_=+~`", ""}, -} - -var hexTests = []struct { - in string - out string -}{ - {"", ""}, - {"61", "2g"}, - {"626262", "a3gV"}, - {"636363", "aPEr"}, - {"73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2"}, - {"00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"}, - {"516b6fcd0f", "ABnLTmg"}, - {"bf4f89001e670274dd", "3SEo3LWLoPntC"}, - {"572e4794", "3EFU7m"}, - {"ecac89cad93923c02321", "EJDM8drfXA6uyA"}, - {"10c8511e", "Rt5zm"}, - {"00000000000000000000", "1111111111"}, - {"000111d38e5fc9071ffcd20b4a763cc9ae4f252bb4e48fd66a835e252ada93ff480d6dd43dc62a641155a5", "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"}, - {"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", "1cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY"}, -} - -func TestBase58(t *testing.T) { - // Encode tests - for x, test := range stringTests { - tmp := []byte(test.in) - if res := base58.Encode(tmp); res != test.out { - t.Errorf("Encode test #%d failed: got: %s want: %s", - x, res, test.out) - continue - } - } - - // Decode tests - for x, test := range hexTests { - b, err := hex.DecodeString(test.in) - if err != nil { - t.Errorf("hex.DecodeString failed failed #%d: got: %s", x, test.in) - continue - } - if res := base58.Decode(test.out); !bytes.Equal(res, b) { - t.Errorf("Decode test #%d failed: got: %q want: %q", - x, res, test.in) - continue - } - } - - // Decode with invalid input - for x, test := range invalidStringTests { - if res := base58.Decode(test.in); string(res) != test.out { - t.Errorf("Decode invalidString test #%d failed: got: %q want: %q", - x, res, test.out) - continue - } - } -} diff --git a/base58/base58bench_test.go b/base58/base58bench_test.go deleted file mode 100644 index 04c4a0076..000000000 --- a/base58/base58bench_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2013-2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package base58_test - -import ( - "bytes" - "testing" - - "github.com/btcsuite/btcutil/base58" -) - -var ( - raw5k = bytes.Repeat([]byte{0xff}, 5000) - raw100k = bytes.Repeat([]byte{0xff}, 100*1000) - encoded5k = base58.Encode(raw5k) - encoded100k = base58.Encode(raw100k) -) - -func BenchmarkBase58Encode_5K(b *testing.B) { - b.SetBytes(int64(len(raw5k))) - for i := 0; i < b.N; i++ { - base58.Encode(raw5k) - } -} - -func BenchmarkBase58Encode_100K(b *testing.B) { - b.SetBytes(int64(len(raw100k))) - for i := 0; i < b.N; i++ { - base58.Encode(raw100k) - } -} - -func BenchmarkBase58Decode_5K(b *testing.B) { - b.SetBytes(int64(len(encoded5k))) - for i := 0; i < b.N; i++ { - base58.Decode(encoded5k) - } -} - -func BenchmarkBase58Decode_100K(b *testing.B) { - b.SetBytes(int64(len(encoded100k))) - for i := 0; i < b.N; i++ { - base58.Decode(encoded100k) - } -} diff --git a/base58/base58check.go b/base58/base58check.go deleted file mode 100644 index 402c3233f..000000000 --- a/base58/base58check.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2013-2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package base58 - -import ( - "crypto/sha256" - "errors" -) - -// ErrChecksum indicates that the checksum of a check-encoded string does not verify against -// the checksum. -var ErrChecksum = errors.New("checksum error") - -// ErrInvalidFormat indicates that the check-encoded string has an invalid format. -var ErrInvalidFormat = errors.New("invalid format: version and/or checksum bytes missing") - -// checksum: first four bytes of sha256^2 -func checksum(input []byte) (cksum [4]byte) { - h := sha256.Sum256(input) - h2 := sha256.Sum256(h[:]) - copy(cksum[:], h2[:4]) - return -} - -// CheckEncode prepends a version byte and appends a four byte checksum. -func CheckEncode(input []byte, version byte) string { - b := make([]byte, 0, 1+len(input)+4) - b = append(b, version) - b = append(b, input...) - cksum := checksum(b) - b = append(b, cksum[:]...) - return Encode(b) -} - -// CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum. -func CheckDecode(input string) (result []byte, version byte, err error) { - decoded := Decode(input) - if len(decoded) < 5 { - return nil, 0, ErrInvalidFormat - } - version = decoded[0] - var cksum [4]byte - copy(cksum[:], decoded[len(decoded)-4:]) - if checksum(decoded[:len(decoded)-4]) != cksum { - return nil, 0, ErrChecksum - } - payload := decoded[1 : len(decoded)-4] - result = append(result, payload...) - return -} diff --git a/base58/base58check_test.go b/base58/base58check_test.go deleted file mode 100644 index 20a41777e..000000000 --- a/base58/base58check_test.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2013-2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package base58_test - -import ( - "testing" - - "github.com/btcsuite/btcutil/base58" -) - -var checkEncodingStringTests = []struct { - version byte - in string - out string -}{ - {20, "", "3MNQE1X"}, - {20, " ", "B2Kr6dBE"}, - {20, "-", "B3jv1Aft"}, - {20, "0", "B482yuaX"}, - {20, "1", "B4CmeGAC"}, - {20, "-1", "mM7eUf6kB"}, - {20, "11", "mP7BMTDVH"}, - {20, "abc", "4QiVtDjUdeq"}, - {20, "1234598760", "ZmNb8uQn5zvnUohNCEPP"}, - {20, "abcdefghijklmnopqrstuvwxyz", "K2RYDcKfupxwXdWhSAxQPCeiULntKm63UXyx5MvEH2"}, - {20, "00000000000000000000000000000000000000000000000000000000000000", "bi1EWXwJay2udZVxLJozuTb8Meg4W9c6xnmJaRDjg6pri5MBAxb9XwrpQXbtnqEoRV5U2pixnFfwyXC8tRAVC8XxnjK"}, -} - -func TestBase58Check(t *testing.T) { - for x, test := range checkEncodingStringTests { - // test encoding - if res := base58.CheckEncode([]byte(test.in), test.version); res != test.out { - t.Errorf("CheckEncode test #%d failed: got %s, want: %s", x, res, test.out) - } - - // test decoding - res, version, err := base58.CheckDecode(test.out) - switch { - case err != nil: - t.Errorf("CheckDecode test #%d failed with err: %v", x, err) - - case version != test.version: - t.Errorf("CheckDecode test #%d failed: got version: %d want: %d", x, version, test.version) - - case string(res) != test.in: - t.Errorf("CheckDecode test #%d failed: got: %s want: %s", x, res, test.in) - } - } - - // test the two decoding failure cases - // case 1: checksum error - _, _, err := base58.CheckDecode("3MNQE1Y") - if err != base58.ErrChecksum { - t.Error("Checkdecode test failed, expected ErrChecksum") - } - // case 2: invalid formats (string lengths below 5 mean the version byte and/or the checksum - // bytes are missing). - testString := "" - for len := 0; len < 4; len++ { - testString += "x" - _, _, err = base58.CheckDecode(testString) - if err != base58.ErrInvalidFormat { - t.Error("Checkdecode test failed, expected ErrInvalidFormat") - } - } - -} diff --git a/base58/cov_report.sh b/base58/cov_report.sh deleted file mode 100644 index 307f05b76..000000000 --- a/base58/cov_report.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -# This script uses gocov to generate a test coverage report. -# The gocov tool my be obtained with the following command: -# go get github.com/axw/gocov/gocov -# -# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. - -# Check for gocov. -type gocov >/dev/null 2>&1 -if [ $? -ne 0 ]; then - echo >&2 "This script requires the gocov tool." - echo >&2 "You may obtain it with the following command:" - echo >&2 "go get github.com/axw/gocov/gocov" - exit 1 -fi -gocov test | gocov report diff --git a/base58/doc.go b/base58/doc.go deleted file mode 100644 index 9a2c0e6e3..000000000 --- a/base58/doc.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -/* -Package base58 provides an API for working with modified base58 and Base58Check -encodings. - -Modified Base58 Encoding - -Standard base58 encoding is similar to standard base64 encoding except, as the -name implies, it uses a 58 character alphabet which results in an alphanumeric -string and allows some characters which are problematic for humans to be -excluded. Due to this, there can be various base58 alphabets. - -The modified base58 alphabet used by Bitcoin, and hence this package, omits the -0, O, I, and l characters that look the same in many fonts and are therefore -hard to humans to distinguish. - -Base58Check Encoding Scheme - -The Base58Check encoding scheme is primarily used for Bitcoin addresses at the -time of this writing, however it can be used to generically encode arbitrary -byte arrays into human-readable strings along with a version byte that can be -used to differentiate the same payload. For Bitcoin addresses, the extra -version is used to differentiate the network of otherwise identical public keys -which helps prevent using an address intended for one network on another. -*/ -package base58 diff --git a/base58/example_test.go b/base58/example_test.go deleted file mode 100644 index 230a78496..000000000 --- a/base58/example_test.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package base58_test - -import ( - "fmt" - - "github.com/btcsuite/btcutil/base58" -) - -// This example demonstrates how to decode modified base58 encoded data. -func ExampleDecode() { - // Decode example modified base58 encoded data. - encoded := "25JnwSn7XKfNQ" - decoded := base58.Decode(encoded) - - // Show the decoded data. - fmt.Println("Decoded Data:", string(decoded)) - - // Output: - // Decoded Data: Test data -} - -// This example demonstrates how to encode data using the modified base58 -// encoding scheme. -func ExampleEncode() { - // Encode example data with the modified base58 encoding scheme. - data := []byte("Test data") - encoded := base58.Encode(data) - - // Show the encoded data. - fmt.Println("Encoded Data:", encoded) - - // Output: - // Encoded Data: 25JnwSn7XKfNQ -} - -// This example demonstrates how to decode Base58Check encoded data. -func ExampleCheckDecode() { - // Decode an example Base58Check encoded data. - encoded := "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" - decoded, version, err := base58.CheckDecode(encoded) - if err != nil { - fmt.Println(err) - return - } - - // Show the decoded data. - fmt.Printf("Decoded data: %x\n", decoded) - fmt.Println("Version Byte:", version) - - // Output: - // Decoded data: 62e907b15cbf27d5425399ebf6f0fb50ebb88f18 - // Version Byte: 0 -} - -// This example demonstrates how to encode data using the Base58Check encoding -// scheme. -func ExampleCheckEncode() { - // Encode example data with the Base58Check encoding scheme. - data := []byte("Test data") - encoded := base58.CheckEncode(data, 0) - - // Show the encoded data. - fmt.Println("Encoded Data:", encoded) - - // Output: - // Encoded Data: 182iP79GRURMp7oMHDU -} diff --git a/base58/genalphabet.go b/base58/genalphabet.go deleted file mode 100644 index 010cbee39..000000000 --- a/base58/genalphabet.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -//+build ignore - -package main - -import ( - "bytes" - "io" - "log" - "os" - "strconv" -) - -var ( - start = []byte(`// Copyright (c) 2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -// AUTOGENERATED by genalphabet.go; do not edit. - -package base58 - -const ( - // alphabet is the modified base58 alphabet used by Bitcoin. - alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" - - alphabetIdx0 = '1' -) - -var b58 = [256]byte{`) - - end = []byte(`}`) - - alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") - tab = []byte("\t") - invalid = []byte("255") - comma = []byte(",") - space = []byte(" ") - nl = []byte("\n") -) - -func write(w io.Writer, b []byte) { - _, err := w.Write(b) - if err != nil { - log.Fatal(err) - } -} - -func main() { - fi, err := os.Create("alphabet.go") - if err != nil { - log.Fatal(err) - } - defer fi.Close() - - write(fi, start) - write(fi, nl) - for i := byte(0); i < 32; i++ { - write(fi, tab) - for j := byte(0); j < 8; j++ { - idx := bytes.IndexByte(alphabet, i*8+j) - if idx == -1 { - write(fi, invalid) - } else { - write(fi, strconv.AppendInt(nil, int64(idx), 10)) - } - write(fi, comma) - if j != 7 { - write(fi, space) - } - } - write(fi, nl) - } - write(fi, end) - write(fi, nl) -} diff --git a/bech32/README.md b/bech32/README.md deleted file mode 100644 index e6fa43f58..000000000 --- a/bech32/README.md +++ /dev/null @@ -1,29 +0,0 @@ -bech32 -========== - -[![Build Status](http://img.shields.io/travis/btcsuite/btcutil.svg)](https://travis-ci.org/btcsuite/btcutil) -[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) -[![GoDoc](https://godoc.org/github.com/btcsuite/btcutil/bech32?status.png)](http://godoc.org/github.com/btcsuite/btcutil/bech32) - -Package bech32 provides a Go implementation of the bech32 format specified in -[BIP 173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki). - -Test vectors from BIP 173 are added to ensure compatibility with the BIP. - -## Installation and Updating - -```bash -$ go get -u github.com/btcsuite/btcutil/bech32 -``` - -## Examples - -* [Bech32 decode Example](http://godoc.org/github.com/btcsuite/btcutil/bech32#example-Bech32Decode) - Demonstrates how to decode a bech32 encoded string. -* [Bech32 encode Example](http://godoc.org/github.com/btcsuite/btcutil/bech32#example-BechEncode) - Demonstrates how to encode data into a bech32 string. - -## License - -Package bech32 is licensed under the [copyfree](http://copyfree.org) ISC -License. diff --git a/bech32/bech32.go b/bech32/bech32.go deleted file mode 100644 index c1e00106e..000000000 --- a/bech32/bech32.go +++ /dev/null @@ -1,442 +0,0 @@ -// Copyright (c) 2017 The btcsuite developers -// Copyright (c) 2019 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package bech32 - -import ( - "strings" -) - -// charset is the set of characters used in the data section of bech32 strings. -// Note that this is ordered, such that for a given charset[i], i is the binary -// value of the character. -const charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" - -// gen encodes the generator polynomial for the bech32 BCH checksum. -var gen = []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} - -// toBytes converts each character in the string 'chars' to the value of the -// index of the correspoding character in 'charset'. -func toBytes(chars string) ([]byte, error) { - decoded := make([]byte, 0, len(chars)) - for i := 0; i < len(chars); i++ { - index := strings.IndexByte(charset, chars[i]) - if index < 0 { - return nil, ErrNonCharsetChar(chars[i]) - } - decoded = append(decoded, byte(index)) - } - return decoded, nil -} - -// bech32Polymod calculates the BCH checksum for a given hrp, values and -// checksum data. Checksum is optional, and if nil a 0 checksum is assumed. -// -// Values and checksum (if provided) MUST be encoded as 5 bits per element (base -// 32), otherwise the results are undefined. -// -// For more details on the polymod calculation, please refer to BIP 173. -func bech32Polymod(hrp string, values, checksum []byte) int { - chk := 1 - - // Account for the high bits of the HRP in the checksum. - for i := 0; i < len(hrp); i++ { - b := chk >> 25 - hiBits := int(hrp[i]) >> 5 - chk = (chk&0x1ffffff)<<5 ^ hiBits - for i := 0; i < 5; i++ { - if (b>>uint(i))&1 == 1 { - chk ^= gen[i] - } - } - } - - // Account for the separator (0) between high and low bits of the HRP. - // x^0 == x, so we eliminate the redundant xor used in the other rounds. - b := chk >> 25 - chk = (chk & 0x1ffffff) << 5 - for i := 0; i < 5; i++ { - if (b>>uint(i))&1 == 1 { - chk ^= gen[i] - } - } - - // Account for the low bits of the HRP. - for i := 0; i < len(hrp); i++ { - b := chk >> 25 - loBits := int(hrp[i]) & 31 - chk = (chk&0x1ffffff)<<5 ^ loBits - for i := 0; i < 5; i++ { - if (b>>uint(i))&1 == 1 { - chk ^= gen[i] - } - } - } - - // Account for the values. - for _, v := range values { - b := chk >> 25 - chk = (chk&0x1ffffff)<<5 ^ int(v) - for i := 0; i < 5; i++ { - if (b>>uint(i))&1 == 1 { - chk ^= gen[i] - } - } - } - - if checksum == nil { - // A nil checksum is used during encoding, so assume all bytes are zero. - // x^0 == x, so we eliminate the redundant xor used in the other rounds. - for v := 0; v < 6; v++ { - b := chk >> 25 - chk = (chk & 0x1ffffff) << 5 - for i := 0; i < 5; i++ { - if (b>>uint(i))&1 == 1 { - chk ^= gen[i] - } - } - } - } else { - // Checksum is provided during decoding, so use it. - for _, v := range checksum { - b := chk >> 25 - chk = (chk&0x1ffffff)<<5 ^ int(v) - for i := 0; i < 5; i++ { - if (b>>uint(i))&1 == 1 { - chk ^= gen[i] - } - } - } - } - - return chk -} - -// writeBech32Checksum calculates the checksum data expected for a string that -// will have the given hrp and payload data and writes it to the provided string -// builder. -// -// The payload data MUST be encoded as a base 32 (5 bits per element) byte slice -// and the hrp MUST only use the allowed character set (ascii chars between 33 -// and 126), otherwise the results are undefined. -// -// For more details on the checksum calculation, please refer to BIP 173. -func writeBech32Checksum(hrp string, data []byte, bldr *strings.Builder, - version Version) { - - bech32Const := int(VersionToConsts[version]) - polymod := bech32Polymod(hrp, data, nil) ^ bech32Const - for i := 0; i < 6; i++ { - b := byte((polymod >> uint(5*(5-i))) & 31) - - // This can't fail, given we explicitly cap the previous b byte by the - // first 31 bits. - c := charset[b] - bldr.WriteByte(c) - } -} - -// bech32VerifyChecksum verifies whether the bech32 string specified by the -// provided hrp and payload data (encoded as 5 bits per element byte slice) has -// the correct checksum suffix. The version of bech32 used (bech32 OG, or -// bech32m) is also returned to allow the caller to perform proper address -// validation (segwitv0 should use bech32, v1+ should use bech32m). -// -// Data MUST have more than 6 elements, otherwise this function panics. -// -// For more details on the checksum verification, please refer to BIP 173. -func bech32VerifyChecksum(hrp string, data []byte) (Version, bool) { - checksum := data[len(data)-6:] - values := data[:len(data)-6] - polymod := bech32Polymod(hrp, values, checksum) - - // Before BIP-350, we'd always check this against a static constant of - // 1 to know if the checksum was computed properly. As we want to - // generically support decoding for bech32m as well as bech32, we'll - // look up the returned value and compare it to the set of defined - // constants. - bech32Version, ok := ConstsToVersion[ChecksumConst(polymod)] - if ok { - return bech32Version, true - } - - return VersionUnknown, false -} - -// DecodeNoLimit is a bech32 checksum version aware arbitrary string length -// decoder. This function will return the version of the decoded checksum -// constant so higher level validation can be performed to ensure the correct -// version of bech32 was used when encoding. -func decodeNoLimit(bech string) (string, []byte, Version, error) { - // The minimum allowed size of a bech32 string is 8 characters, since it - // needs a non-empty HRP, a separator, and a 6 character checksum. - if len(bech) < 8 { - return "", nil, VersionUnknown, ErrInvalidLength(len(bech)) - } - - // Only ASCII characters between 33 and 126 are allowed. - var hasLower, hasUpper bool - for i := 0; i < len(bech); i++ { - if bech[i] < 33 || bech[i] > 126 { - return "", nil, VersionUnknown, ErrInvalidCharacter(bech[i]) - } - - // The characters must be either all lowercase or all uppercase. Testing - // directly with ascii codes is safe here, given the previous test. - hasLower = hasLower || (bech[i] >= 97 && bech[i] <= 122) - hasUpper = hasUpper || (bech[i] >= 65 && bech[i] <= 90) - if hasLower && hasUpper { - return "", nil, VersionUnknown, ErrMixedCase{} - } - } - - // Bech32 standard uses only the lowercase for of strings for checksum - // calculation. - if hasUpper { - bech = strings.ToLower(bech) - } - - // The string is invalid if the last '1' is non-existent, it is the - // first character of the string (no human-readable part) or one of the - // last 6 characters of the string (since checksum cannot contain '1'). - one := strings.LastIndexByte(bech, '1') - if one < 1 || one+7 > len(bech) { - return "", nil, VersionUnknown, ErrInvalidSeparatorIndex(one) - } - - // The human-readable part is everything before the last '1'. - hrp := bech[:one] - data := bech[one+1:] - - // Each character corresponds to the byte with value of the index in - // 'charset'. - decoded, err := toBytes(data) - if err != nil { - return "", nil, VersionUnknown, err - } - - // Verify if the checksum (stored inside decoded[:]) is valid, given the - // previously decoded hrp. - bech32Version, ok := bech32VerifyChecksum(hrp, decoded) - if !ok { - // Invalid checksum. Calculate what it should have been, so that the - // error contains this information. - - // Extract the payload bytes and actual checksum in the string. - actual := bech[len(bech)-6:] - payload := decoded[:len(decoded)-6] - - // Calculate the expected checksum, given the hrp and payload - // data. We'll actually compute _both_ possibly valid checksum - // to further aide in debugging. - var expectedBldr strings.Builder - expectedBldr.Grow(6) - writeBech32Checksum(hrp, payload, &expectedBldr, Version0) - expectedVersion0 := expectedBldr.String() - - var b strings.Builder - b.Grow(6) - writeBech32Checksum(hrp, payload, &expectedBldr, VersionM) - expectedVersionM := expectedBldr.String() - - err = ErrInvalidChecksum{ - Expected: expectedVersion0, - ExpectedM: expectedVersionM, - Actual: actual, - } - return "", nil, VersionUnknown, err - } - - // We exclude the last 6 bytes, which is the checksum. - return hrp, decoded[:len(decoded)-6], bech32Version, nil -} - -// DecodeNoLimit decodes a bech32 encoded string, returning the human-readable -// part and the data part excluding the checksum. This function does NOT -// validate against the BIP-173 maximum length allowed for bech32 strings and -// is meant for use in custom applications (such as lightning network payment -// requests), NOT on-chain addresses. -// -// Note that the returned data is 5-bit (base32) encoded and the human-readable -// part will be lowercase. -func DecodeNoLimit(bech string) (string, []byte, error) { - hrp, data, _, err := decodeNoLimit(bech) - return hrp, data, err -} - -// Decode decodes a bech32 encoded string, returning the human-readable part and -// the data part excluding the checksum. -// -// Note that the returned data is 5-bit (base32) encoded and the human-readable -// part will be lowercase. -func Decode(bech string) (string, []byte, error) { - // The maximum allowed length for a bech32 string is 90. - if len(bech) > 90 { - return "", nil, ErrInvalidLength(len(bech)) - } - - hrp, data, _, err := decodeNoLimit(bech) - return hrp, data, err -} - -// DecodeGeneric is identical to the existing Decode method, but will also -// return bech32 version that matches the decoded checksum. This method should -// be used when decoding segwit addresses, as it enables additional -// verification to ensure the proper checksum is used. -func DecodeGeneric(bech string) (string, []byte, Version, error) { - // The maximum allowed length for a bech32 string is 90. - if len(bech) > 90 { - return "", nil, VersionUnknown, ErrInvalidLength(len(bech)) - } - - return decodeNoLimit(bech) -} - -// encodeGeneric is the base bech32 encoding function that is aware of the -// existence of the checksum versions. This method is private, as the Encode -// and EncodeM methods are intended to be used instead. -func encodeGeneric(hrp string, data []byte, - version Version) (string, error) { - - // The resulting bech32 string is the concatenation of the lowercase - // hrp, the separator 1, data and the 6-byte checksum. - hrp = strings.ToLower(hrp) - var bldr strings.Builder - bldr.Grow(len(hrp) + 1 + len(data) + 6) - bldr.WriteString(hrp) - bldr.WriteString("1") - - // Write the data part, using the bech32 charset. - for _, b := range data { - if int(b) >= len(charset) { - return "", ErrInvalidDataByte(b) - } - bldr.WriteByte(charset[b]) - } - - // Calculate and write the checksum of the data. - writeBech32Checksum(hrp, data, &bldr, version) - - return bldr.String(), nil -} - -// Encode encodes a byte slice into a bech32 string with the given -// human-readable part (HRP). The HRP will be converted to lowercase if needed -// since mixed cased encodings are not permitted and lowercase is used for -// checksum purposes. Note that the bytes must each encode 5 bits (base32). -func Encode(hrp string, data []byte) (string, error) { - return encodeGeneric(hrp, data, Version0) -} - -// EncodeM is the exactly same as the Encode method, but it uses the new -// bech32m constant instead of the original one. It should be used whenever one -// attempts to encode a segwit address of v1 and beyond. -func EncodeM(hrp string, data []byte) (string, error) { - return encodeGeneric(hrp, data, VersionM) -} - -// ConvertBits converts a byte slice where each byte is encoding fromBits bits, -// to a byte slice where each byte is encoding toBits bits. -func ConvertBits(data []byte, fromBits, toBits uint8, pad bool) ([]byte, error) { - if fromBits < 1 || fromBits > 8 || toBits < 1 || toBits > 8 { - return nil, ErrInvalidBitGroups{} - } - - // Determine the maximum size the resulting array can have after base - // conversion, so that we can size it a single time. This might be off - // by a byte depending on whether padding is used or not and if the input - // data is a multiple of both fromBits and toBits, but we ignore that and - // just size it to the maximum possible. - maxSize := len(data)*int(fromBits)/int(toBits) + 1 - - // The final bytes, each byte encoding toBits bits. - regrouped := make([]byte, 0, maxSize) - - // Keep track of the next byte we create and how many bits we have - // added to it out of the toBits goal. - nextByte := byte(0) - filledBits := uint8(0) - - for _, b := range data { - - // Discard unused bits. - b <<= 8 - fromBits - - // How many bits remaining to extract from the input data. - remFromBits := fromBits - for remFromBits > 0 { - // How many bits remaining to be added to the next byte. - remToBits := toBits - filledBits - - // The number of bytes to next extract is the minimum of - // remFromBits and remToBits. - toExtract := remFromBits - if remToBits < toExtract { - toExtract = remToBits - } - - // Add the next bits to nextByte, shifting the already - // added bits to the left. - nextByte = (nextByte << toExtract) | (b >> (8 - toExtract)) - - // Discard the bits we just extracted and get ready for - // next iteration. - b <<= toExtract - remFromBits -= toExtract - filledBits += toExtract - - // If the nextByte is completely filled, we add it to - // our regrouped bytes and start on the next byte. - if filledBits == toBits { - regrouped = append(regrouped, nextByte) - filledBits = 0 - nextByte = 0 - } - } - } - - // We pad any unfinished group if specified. - if pad && filledBits > 0 { - nextByte <<= toBits - filledBits - regrouped = append(regrouped, nextByte) - filledBits = 0 - nextByte = 0 - } - - // Any incomplete group must be <= 4 bits, and all zeroes. - if filledBits > 0 && (filledBits > 4 || nextByte != 0) { - return nil, ErrInvalidIncompleteGroup{} - } - - return regrouped, nil -} - -// EncodeFromBase256 converts a base256-encoded byte slice into a base32-encoded -// byte slice and then encodes it into a bech32 string with the given -// human-readable part (HRP). The HRP will be converted to lowercase if needed -// since mixed cased encodings are not permitted and lowercase is used for -// checksum purposes. -func EncodeFromBase256(hrp string, data []byte) (string, error) { - converted, err := ConvertBits(data, 8, 5, true) - if err != nil { - return "", err - } - return Encode(hrp, converted) -} - -// DecodeToBase256 decodes a bech32-encoded string into its associated -// human-readable part (HRP) and base32-encoded data, converts that data to a -// base256-encoded byte slice and returns it along with the lowercase HRP. -func DecodeToBase256(bech string) (string, []byte, error) { - hrp, data, err := Decode(bech) - if err != nil { - return "", nil, err - } - converted, err := ConvertBits(data, 5, 8, false) - if err != nil { - return "", nil, err - } - return hrp, converted, nil -} diff --git a/bech32/bech32_test.go b/bech32/bech32_test.go deleted file mode 100644 index 1e04905a6..000000000 --- a/bech32/bech32_test.go +++ /dev/null @@ -1,691 +0,0 @@ -// Copyright (c) 2017-2020 The btcsuite developers -// Copyright (c) 2019 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package bech32 - -import ( - "bytes" - "encoding/hex" - "fmt" - "strings" - "testing" -) - -// TestBech32 tests whether decoding and re-encoding the valid BIP-173 test -// vectors works and if decoding invalid test vectors fails for the correct -// reason. -func TestBech32(t *testing.T) { - tests := []struct { - str string - expectedError error - }{ - {"A12UEL5L", nil}, - {"a12uel5l", nil}, - {"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", nil}, - {"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", nil}, - {"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", nil}, - {"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", nil}, - {"split1checkupstagehandshakeupstreamerranterredcaperred2y9e2w", ErrInvalidChecksum{"2y9e3w", "2y9e3wlc445v", "2y9e2w"}}, // invalid checksum - {"s lit1checkupstagehandshakeupstreamerranterredcaperredp8hs2p", ErrInvalidCharacter(' ')}, // invalid character (space) in hrp - {"spl\x7Ft1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", ErrInvalidCharacter(127)}, // invalid character (DEL) in hrp - {"split1cheo2y9e2w", ErrNonCharsetChar('o')}, // invalid character (o) in data part - {"split1a2y9w", ErrInvalidSeparatorIndex(5)}, // too short data part - {"1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", ErrInvalidSeparatorIndex(0)}, // empty hrp - {"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", ErrInvalidLength(91)}, // too long - - // Additional test vectors used in bitcoin core - {" 1nwldj5", ErrInvalidCharacter(' ')}, - {"\x7f" + "1axkwrx", ErrInvalidCharacter(0x7f)}, - {"\x801eym55h", ErrInvalidCharacter(0x80)}, - {"an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", ErrInvalidLength(91)}, - {"pzry9x0s0muk", ErrInvalidSeparatorIndex(-1)}, - {"1pzry9x0s0muk", ErrInvalidSeparatorIndex(0)}, - {"x1b4n0q5v", ErrNonCharsetChar(98)}, - {"li1dgmt3", ErrInvalidSeparatorIndex(2)}, - {"de1lg7wt\xff", ErrInvalidCharacter(0xff)}, - {"A1G7SGD8", ErrInvalidChecksum{"2uel5l", "2uel5llqfn3a", "g7sgd8"}}, - {"10a06t8", ErrInvalidLength(7)}, - {"1qzzfhee", ErrInvalidSeparatorIndex(0)}, - {"a12UEL5L", ErrMixedCase{}}, - {"A12uEL5L", ErrMixedCase{}}, - } - - for i, test := range tests { - str := test.str - hrp, decoded, err := Decode(str) - if test.expectedError != err { - t.Errorf("%d: expected decoding error %v "+ - "instead got %v", i, test.expectedError, err) - continue - } - - if err != nil { - // End test case here if a decoding error was expected. - continue - } - - // Check that it encodes to the same string - encoded, err := Encode(hrp, decoded) - if err != nil { - t.Errorf("encoding failed: %v", err) - } - - if encoded != strings.ToLower(str) { - t.Errorf("expected data to encode to %v, but got %v", - str, encoded) - } - - // Flip a bit in the string an make sure it is caught. - pos := strings.LastIndexAny(str, "1") - flipped := str[:pos+1] + string((str[pos+1] ^ 1)) + str[pos+2:] - _, _, err = Decode(flipped) - if err == nil { - t.Error("expected decoding to fail") - } - } -} - -// TestBech32M tests that the following set of strings, based on the test -// vectors in BIP-350 are either valid or invalid using the new bech32m -// checksum algo. Some of these strings are similar to the set of above test -// vectors, but end up with different checksums. -func TestBech32M(t *testing.T) { - tests := []struct { - str string - expectedError error - }{ - {"A1LQFN3A", nil}, - {"a1lqfn3a", nil}, - {"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6", nil}, - {"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx", nil}, - {"11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8", nil}, - {"split1checkupstagehandshakeupstreamerranterredcaperredlc445v", nil}, - {"?1v759aa", nil}, - - // Additional test vectors used in bitcoin core - {"\x201xj0phk", ErrInvalidCharacter('\x20')}, - {"\x7f1g6xzxy", ErrInvalidCharacter('\x7f')}, - {"\x801vctc34", ErrInvalidCharacter('\x80')}, - {"an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4", ErrInvalidLength(91)}, - {"qyrz8wqd2c9m", ErrInvalidSeparatorIndex(-1)}, - {"1qyrz8wqd2c9m", ErrInvalidSeparatorIndex(0)}, - {"y1b0jsk6g", ErrNonCharsetChar(98)}, - {"lt1igcx5c0", ErrNonCharsetChar(105)}, - {"in1muywd", ErrInvalidSeparatorIndex(2)}, - {"mm1crxm3i", ErrNonCharsetChar(105)}, - {"au1s5cgom", ErrNonCharsetChar(111)}, - {"M1VUXWEZ", ErrInvalidChecksum{"mzl49c", "mzl49cw70eq6", "vuxwez"}}, - {"16plkw9", ErrInvalidLength(7)}, - {"1p2gdwpf", ErrInvalidSeparatorIndex(0)}, - - {" 1nwldj5", ErrInvalidCharacter(' ')}, - {"\x7f" + "1axkwrx", ErrInvalidCharacter(0x7f)}, - {"\x801eym55h", ErrInvalidCharacter(0x80)}, - } - - for i, test := range tests { - str := test.str - hrp, decoded, err := Decode(str) - if test.expectedError != err { - t.Errorf("%d: (%v) expected decoding error %v "+ - "instead got %v", i, str, test.expectedError, - err) - continue - } - - if err != nil { - // End test case here if a decoding error was expected. - continue - } - - // Check that it encodes to the same string, using bech32 m. - encoded, err := EncodeM(hrp, decoded) - if err != nil { - t.Errorf("encoding failed: %v", err) - } - - if encoded != strings.ToLower(str) { - t.Errorf("expected data to encode to %v, but got %v", - str, encoded) - } - - // Flip a bit in the string an make sure it is caught. - pos := strings.LastIndexAny(str, "1") - flipped := str[:pos+1] + string((str[pos+1] ^ 1)) + str[pos+2:] - _, _, err = Decode(flipped) - if err == nil { - t.Error("expected decoding to fail") - } - } -} - -// TestBech32DecodeGeneric tests that given a bech32 string, or a bech32m -// string, the proper checksum version is returned so that callers can perform -// segwit addr validation. -func TestBech32DecodeGeneric(t *testing.T) { - tests := []struct { - str string - version Version - }{ - {"A1LQFN3A", VersionM}, - {"a1lqfn3a", VersionM}, - {"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6", VersionM}, - {"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx", VersionM}, - {"11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8", VersionM}, - {"split1checkupstagehandshakeupstreamerranterredcaperredlc445v", VersionM}, - {"?1v759aa", VersionM}, - - {"A12UEL5L", Version0}, - {"a12uel5l", Version0}, - {"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", Version0}, - {"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", Version0}, - {"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", Version0}, - {"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", Version0}, - - {"BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", Version0}, - {"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", Version0}, - {"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", VersionM}, - {"BC1SW50QGDZ25J", VersionM}, - {"bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", VersionM}, - {"tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", Version0}, - {"tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c", VersionM}, - {"bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", VersionM}, - } - for i, test := range tests { - _, _, version, err := DecodeGeneric(test.str) - if err != nil { - t.Errorf("%d: (%v) unexpected error during "+ - "decoding: %v", i, test.str, err) - continue - } - - if version != test.version { - t.Errorf("(%v): invalid version: expected %v, got %v", - test.str, test.version, version) - } - } -} - -// TestMixedCaseEncode ensures mixed case HRPs are converted to lowercase as -// expected when encoding and that decoding the produced encoding when converted -// to all uppercase produces the lowercase HRP and original data. -func TestMixedCaseEncode(t *testing.T) { - tests := []struct { - name string - hrp string - data string - encoded string - }{{ - name: "all uppercase HRP with no data", - hrp: "A", - data: "", - encoded: "a12uel5l", - }, { - name: "all uppercase HRP with data", - hrp: "UPPERCASE", - data: "787878", - encoded: "uppercase10pu8sss7kmp", - }, { - name: "mixed case HRP even offsets uppercase", - hrp: "AbCdEf", - data: "00443214c74254b635cf84653a56d7c675be77df", - encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", - }, { - name: "mixed case HRP odd offsets uppercase ", - hrp: "aBcDeF", - data: "00443214c74254b635cf84653a56d7c675be77df", - encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", - }, { - name: "all lowercase HRP", - hrp: "abcdef", - data: "00443214c74254b635cf84653a56d7c675be77df", - encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", - }} - - for _, test := range tests { - // Convert the text hex to bytes, convert those bytes from base256 to - // base32, then ensure the encoded result with the HRP provided in the - // test data is as expected. - data, err := hex.DecodeString(test.data) - if err != nil { - t.Errorf("%q: invalid hex %q: %v", test.name, test.data, err) - continue - } - convertedData, err := ConvertBits(data, 8, 5, true) - if err != nil { - t.Errorf("%q: unexpected convert bits error: %v", test.name, - err) - continue - } - gotEncoded, err := Encode(test.hrp, convertedData) - if err != nil { - t.Errorf("%q: unexpected encode error: %v", test.name, err) - continue - } - if gotEncoded != test.encoded { - t.Errorf("%q: mismatched encoding -- got %q, want %q", test.name, - gotEncoded, test.encoded) - continue - } - - // Ensure the decoding the expected lowercase encoding converted to all - // uppercase produces the lowercase HRP and original data. - gotHRP, gotData, err := Decode(strings.ToUpper(test.encoded)) - if err != nil { - t.Errorf("%q: unexpected decode error: %v", test.name, err) - continue - } - wantHRP := strings.ToLower(test.hrp) - if gotHRP != wantHRP { - t.Errorf("%q: mismatched decoded HRP -- got %q, want %q", test.name, - gotHRP, wantHRP) - continue - } - convertedGotData, err := ConvertBits(gotData, 5, 8, false) - if err != nil { - t.Errorf("%q: unexpected convert bits error: %v", test.name, - err) - continue - } - if !bytes.Equal(convertedGotData, data) { - t.Errorf("%q: mismatched data -- got %x, want %x", test.name, - convertedGotData, data) - continue - } - } -} - -// TestCanDecodeUnlimtedBech32 tests whether decoding a large bech32 string works -// when using the DecodeNoLimit version -func TestCanDecodeUnlimtedBech32(t *testing.T) { - input := "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5kx0yd" - - // Sanity check that an input of this length errors on regular Decode() - _, _, err := Decode(input) - if err == nil { - t.Fatalf("Test vector not appropriate") - } - - // Try and decode it. - hrp, data, err := DecodeNoLimit(input) - if err != nil { - t.Fatalf("Expected decoding of large string to work. Got error: %v", err) - } - - // Verify data for correctness. - if hrp != "1" { - t.Fatalf("Unexpected hrp: %v", hrp) - } - decodedHex := fmt.Sprintf("%x", data) - expected := "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000" - if decodedHex != expected { - t.Fatalf("Unexpected decoded data: %s", decodedHex) - } -} - -// TestBech32Base256 ensures decoding and encoding various bech32, HRPs, and -// data produces the expected results when using EncodeFromBase256 and -// DecodeToBase256. It includes tests for proper handling of case -// manipulations. -func TestBech32Base256(t *testing.T) { - tests := []struct { - name string // test name - encoded string // bech32 string to decode - hrp string // expected human-readable part - data string // expected hex-encoded data - err error // expected error - }{{ - name: "all uppercase, no data", - encoded: "A12UEL5L", - hrp: "a", - data: "", - }, { - name: "long hrp with separator and excluded chars, no data", - encoded: "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", - hrp: "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio", - data: "", - }, { - name: "6 char hrp with data with leading zero", - encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", - hrp: "abcdef", - data: "00443214c74254b635cf84653a56d7c675be77df", - }, { - name: "hrp same as separator and max length encoded string", - encoded: "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", - hrp: "1", - data: "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - }, { - name: "5 char hrp with data chosen to produce human-readable data part", - encoded: "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", - hrp: "split", - data: "c5f38b70305f519bf66d85fb6cf03058f3dde463ecd7918f2dc743918f2d", - }, { - name: "same as previous but with checksum invalidated", - encoded: "split1checkupstagehandshakeupstreamerranterredcaperred2y9e2w", - err: ErrInvalidChecksum{"2y9e3w", "2y9e3wlc445v", "2y9e2w"}, - }, { - name: "hrp with invalid character (space)", - encoded: "s lit1checkupstagehandshakeupstreamerranterredcaperredp8hs2p", - err: ErrInvalidCharacter(' '), - }, { - name: "hrp with invalid character (DEL)", - encoded: "spl\x7ft1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", - err: ErrInvalidCharacter(127), - }, { - name: "data part with invalid character (o)", - encoded: "split1cheo2y9e2w", - err: ErrNonCharsetChar('o'), - }, { - name: "data part too short", - encoded: "split1a2y9w", - err: ErrInvalidSeparatorIndex(5), - }, { - name: "empty hrp", - encoded: "1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", - err: ErrInvalidSeparatorIndex(0), - }, { - name: "no separator", - encoded: "pzry9x0s0muk", - err: ErrInvalidSeparatorIndex(-1), - }, { - name: "too long by one char", - encoded: "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", - err: ErrInvalidLength(91), - }, { - name: "invalid due to mixed case in hrp", - encoded: "aBcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", - err: ErrMixedCase{}, - }, { - name: "invalid due to mixed case in data part", - encoded: "abcdef1Qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", - err: ErrMixedCase{}, - }} - - for _, test := range tests { - // Ensure the decode either produces an error or not as expected. - str := test.encoded - gotHRP, gotData, err := DecodeToBase256(str) - if test.err != err { - t.Errorf("%q: unexpected decode error -- got %v, want %v", - test.name, err, test.err) - continue - } - if err != nil { - // End test case here if a decoding error was expected. - continue - } - - // Ensure the expected HRP and original data are as expected. - if gotHRP != test.hrp { - t.Errorf("%q: mismatched decoded HRP -- got %q, want %q", test.name, - gotHRP, test.hrp) - continue - } - data, err := hex.DecodeString(test.data) - if err != nil { - t.Errorf("%q: invalid hex %q: %v", test.name, test.data, err) - continue - } - if !bytes.Equal(gotData, data) { - t.Errorf("%q: mismatched data -- got %x, want %x", test.name, - gotData, data) - continue - } - - // Encode the same data with the HRP converted to all uppercase and - // ensure the result is the lowercase version of the original encoded - // bech32 string. - gotEncoded, err := EncodeFromBase256(strings.ToUpper(test.hrp), data) - if err != nil { - t.Errorf("%q: unexpected uppercase HRP encode error: %v", test.name, - err) - } - wantEncoded := strings.ToLower(str) - if gotEncoded != wantEncoded { - t.Errorf("%q: mismatched encoding -- got %q, want %q", test.name, - gotEncoded, wantEncoded) - } - - // Encode the same data with the HRP converted to all lowercase and - // ensure the result is the lowercase version of the original encoded - // bech32 string. - gotEncoded, err = EncodeFromBase256(strings.ToLower(test.hrp), data) - if err != nil { - t.Errorf("%q: unexpected lowercase HRP encode error: %v", test.name, - err) - } - if gotEncoded != wantEncoded { - t.Errorf("%q: mismatched encoding -- got %q, want %q", test.name, - gotEncoded, wantEncoded) - } - - // Encode the same data with the HRP converted to mixed upper and - // lowercase and ensure the result is the lowercase version of the - // original encoded bech32 string. - var mixedHRPBuilder strings.Builder - for i, r := range test.hrp { - if i%2 == 0 { - mixedHRPBuilder.WriteString(strings.ToUpper(string(r))) - continue - } - mixedHRPBuilder.WriteRune(r) - } - gotEncoded, err = EncodeFromBase256(mixedHRPBuilder.String(), data) - if err != nil { - t.Errorf("%q: unexpected lowercase HRP encode error: %v", test.name, - err) - } - if gotEncoded != wantEncoded { - t.Errorf("%q: mismatched encoding -- got %q, want %q", test.name, - gotEncoded, wantEncoded) - } - - // Ensure a bit flip in the string is caught. - pos := strings.LastIndexAny(test.encoded, "1") - flipped := str[:pos+1] + string((str[pos+1] ^ 1)) + str[pos+2:] - _, _, err = DecodeToBase256(flipped) - if err == nil { - t.Error("expected decoding to fail") - } - } -} - -// BenchmarkEncodeDecodeCycle performs a benchmark for a full encode/decode -// cycle of a bech32 string. It also reports the allocation count, which we -// expect to be 2 for a fully optimized cycle. -func BenchmarkEncodeDecodeCycle(b *testing.B) { - // Use a fixed, 49-byte raw data for testing. - inputData, err := hex.DecodeString("cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1") - if err != nil { - b.Fatalf("failed to initialize input data: %v", err) - } - - // Convert this into a 79-byte, base 32 byte slice. - base32Input, err := ConvertBits(inputData, 8, 5, true) - if err != nil { - b.Fatalf("failed to convert input to 32 bits-per-element: %v", err) - } - - // Use a fixed hrp for the tests. This should generate an encoded bech32 - // string of size 90 (the maximum allowed by BIP-173). - hrp := "bc" - - // Begin the benchmark. Given that we test one roundtrip per iteration - // (that is, one Encode() and one Decode() operation), we expect at most - // 2 allocations per reported test op. - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - str, err := Encode(hrp, base32Input) - if err != nil { - b.Fatalf("failed to encode input: %v", err) - } - - _, _, err = Decode(str) - if err != nil { - b.Fatalf("failed to decode string: %v", err) - } - } -} - -// TestConvertBits tests whether base conversion works using TestConvertBits(). -func TestConvertBits(t *testing.T) { - tests := []struct { - input string - output string - fromBits uint8 - toBits uint8 - pad bool - }{ - // Trivial empty conversions. - {"", "", 8, 5, false}, - {"", "", 8, 5, true}, - {"", "", 5, 8, false}, - {"", "", 5, 8, true}, - - // Conversions of 0 value with/without padding. - {"00", "00", 8, 5, false}, - {"00", "0000", 8, 5, true}, - {"0000", "00", 5, 8, false}, - {"0000", "0000", 5, 8, true}, - - // Testing when conversion ends exactly at the byte edge. This makes - // both padded and unpadded versions the same. - {"0000000000", "0000000000000000", 8, 5, false}, - {"0000000000", "0000000000000000", 8, 5, true}, - {"0000000000000000", "0000000000", 5, 8, false}, - {"0000000000000000", "0000000000", 5, 8, true}, - - // Conversions of full byte sequences. - {"ffffff", "1f1f1f1f1e", 8, 5, true}, - {"1f1f1f1f1e", "ffffff", 5, 8, false}, - {"1f1f1f1f1e", "ffffff00", 5, 8, true}, - - // Sample random conversions. - {"c9ca", "190705", 8, 5, false}, - {"c9ca", "19070500", 8, 5, true}, - {"19070500", "c9ca", 5, 8, false}, - {"19070500", "c9ca00", 5, 8, true}, - - // Test cases tested on TestConvertBitsFailures with their corresponding - // fixes. - {"ff", "1f1c", 8, 5, true}, - {"1f1c10", "ff20", 5, 8, true}, - - // Large conversions. - { - "cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1", - "190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408", - 8, 5, true, - }, - { - "190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408", - "cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed100", - 5, 8, true, - }, - } - - for i, tc := range tests { - input, err := hex.DecodeString(tc.input) - if err != nil { - t.Fatalf("invalid test input data: %v", err) - } - - expected, err := hex.DecodeString(tc.output) - if err != nil { - t.Fatalf("invalid test output data: %v", err) - } - - actual, err := ConvertBits(input, tc.fromBits, tc.toBits, tc.pad) - if err != nil { - t.Fatalf("test case %d failed: %v", i, err) - } - - if !bytes.Equal(actual, expected) { - t.Fatalf("test case %d has wrong output; expected=%x actual=%x", - i, expected, actual) - } - } -} - -// TestConvertBitsFailures tests for the expected conversion failures of -// ConvertBits(). -func TestConvertBitsFailures(t *testing.T) { - tests := []struct { - input string - fromBits uint8 - toBits uint8 - pad bool - err error - }{ - // Not enough output bytes when not using padding. - {"ff", 8, 5, false, ErrInvalidIncompleteGroup{}}, - {"1f1c10", 5, 8, false, ErrInvalidIncompleteGroup{}}, - - // Unsupported bit conversions. - {"", 0, 5, false, ErrInvalidBitGroups{}}, - {"", 10, 5, false, ErrInvalidBitGroups{}}, - {"", 5, 0, false, ErrInvalidBitGroups{}}, - {"", 5, 10, false, ErrInvalidBitGroups{}}, - } - - for i, tc := range tests { - input, err := hex.DecodeString(tc.input) - if err != nil { - t.Fatalf("invalid test input data: %v", err) - } - - _, err = ConvertBits(input, tc.fromBits, tc.toBits, tc.pad) - if err != tc.err { - t.Fatalf("test case %d failure: expected '%v' got '%v'", i, - tc.err, err) - } - } - -} - -// BenchmarkConvertBitsDown benchmarks the speed and memory allocation behavior -// of ConvertBits when converting from a higher base into a lower base (e.g. 8 -// => 5). -// -// Only a single allocation is expected, which is used for the output array. -func BenchmarkConvertBitsDown(b *testing.B) { - // Use a fixed, 49-byte raw data for testing. - inputData, err := hex.DecodeString("cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1") - if err != nil { - b.Fatalf("failed to initialize input data: %v", err) - } - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := ConvertBits(inputData, 8, 5, true) - if err != nil { - b.Fatalf("error converting bits: %v", err) - } - } -} - -// BenchmarkConvertBitsDown benchmarks the speed and memory allocation behavior -// of ConvertBits when converting from a lower base into a higher base (e.g. 5 -// => 8). -// -// Only a single allocation is expected, which is used for the output array. -func BenchmarkConvertBitsUp(b *testing.B) { - // Use a fixed, 79-byte raw data for testing. - inputData, err := hex.DecodeString("190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408") - if err != nil { - b.Fatalf("failed to initialize input data: %v", err) - } - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := ConvertBits(inputData, 8, 5, true) - if err != nil { - b.Fatalf("error converting bits: %v", err) - } - } -} diff --git a/bech32/doc.go b/bech32/doc.go deleted file mode 100644 index 2d64fbe0b..000000000 --- a/bech32/doc.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -/* -Package bech32 provides a Go implementation of the bech32 format specified in -BIP 173. - -Bech32 strings consist of a human-readable part (hrp), followed by the -separator 1, then a checksummed data part encoded using the 32 characters -"qpzry9x8gf2tvdw0s3jn54khce6mua7l". - -More info: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki -*/ -package bech32 diff --git a/bech32/error.go b/bech32/error.go deleted file mode 100644 index e8b1fe8c3..000000000 --- a/bech32/error.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2019 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package bech32 - -import ( - "fmt" -) - -// ErrMixedCase is returned when the bech32 string has both lower and uppercase -// characters. -type ErrMixedCase struct{} - -func (e ErrMixedCase) Error() string { - return "string not all lowercase or all uppercase" -} - -// ErrInvalidBitGroups is returned when conversion is attempted between byte -// slices using bit-per-element of unsupported value. -type ErrInvalidBitGroups struct{} - -func (e ErrInvalidBitGroups) Error() string { - return "only bit groups between 1 and 8 allowed" -} - -// ErrInvalidIncompleteGroup is returned when then byte slice used as input has -// data of wrong length. -type ErrInvalidIncompleteGroup struct{} - -func (e ErrInvalidIncompleteGroup) Error() string { - return "invalid incomplete group" -} - -// ErrInvalidLength is returned when the bech32 string has an invalid length -// given the BIP-173 defined restrictions. -type ErrInvalidLength int - -func (e ErrInvalidLength) Error() string { - return fmt.Sprintf("invalid bech32 string length %d", int(e)) -} - -// ErrInvalidCharacter is returned when the bech32 string has a character -// outside the range of the supported charset. -type ErrInvalidCharacter rune - -func (e ErrInvalidCharacter) Error() string { - return fmt.Sprintf("invalid character in string: '%c'", rune(e)) -} - -// ErrInvalidSeparatorIndex is returned when the separator character '1' is -// in an invalid position in the bech32 string. -type ErrInvalidSeparatorIndex int - -func (e ErrInvalidSeparatorIndex) Error() string { - return fmt.Sprintf("invalid separator index %d", int(e)) -} - -// ErrNonCharsetChar is returned when a character outside of the specific -// bech32 charset is used in the string. -type ErrNonCharsetChar rune - -func (e ErrNonCharsetChar) Error() string { - return fmt.Sprintf("invalid character not part of charset: %v", int(e)) -} - -// ErrInvalidChecksum is returned when the extracted checksum of the string -// is different than what was expected. Both the original version, as well as -// the new bech32m checksum may be specified. -type ErrInvalidChecksum struct { - Expected string - ExpectedM string - Actual string -} - -func (e ErrInvalidChecksum) Error() string { - return fmt.Sprintf("invalid checksum (expected (bech32=%v, "+ - "bech32m=%v), got %v)", e.Expected, e.ExpectedM, e.Actual) -} - -// ErrInvalidDataByte is returned when a byte outside the range required for -// conversion into a string was found. -type ErrInvalidDataByte byte - -func (e ErrInvalidDataByte) Error() string { - return fmt.Sprintf("invalid data byte: %v", byte(e)) -} diff --git a/bech32/example_test.go b/bech32/example_test.go deleted file mode 100644 index 6a1f69688..000000000 --- a/bech32/example_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package bech32_test - -import ( - "encoding/hex" - "fmt" - - "github.com/btcsuite/btcutil/bech32" -) - -// This example demonstrates how to decode a bech32 encoded string. -func ExampleDecode() { - encoded := "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx" - hrp, decoded, err := bech32.Decode(encoded) - if err != nil { - fmt.Println("Error:", err) - } - - // Show the decoded data. - fmt.Println("Decoded human-readable part:", hrp) - fmt.Println("Decoded Data:", hex.EncodeToString(decoded)) - - // Output: - // Decoded human-readable part: bc - // Decoded Data: 010e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e160e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e16 -} - -// This example demonstrates how to encode data into a bech32 string. -func ExampleEncode() { - data := []byte("Test data") - // Convert test data to base32: - conv, err := bech32.ConvertBits(data, 8, 5, true) - if err != nil { - fmt.Println("Error:", err) - } - encoded, err := bech32.Encode("customHrp!11111q", conv) - if err != nil { - fmt.Println("Error:", err) - } - - // Show the encoded data. - fmt.Println("Encoded Data:", encoded) - - // Output: - // Encoded Data: customhrp!11111q123jhxapqv3shgcgkxpuhe -} diff --git a/bech32/version.go b/bech32/version.go deleted file mode 100644 index 147037db9..000000000 --- a/bech32/version.go +++ /dev/null @@ -1,43 +0,0 @@ -package bech32 - -// ChecksumConst is a type that represents the currently defined bech32 -// checksum constants. -type ChecksumConst int - -const ( - // Version0Const is the original constant used in the checksum - // verification for bech32. - Version0Const ChecksumConst = 1 - - // VersionMConst is the new constant used for bech32m checksum - // verification. - VersionMConst ChecksumConst = 0x2bc830a3 -) - -// Version defines the current set of bech32 versions. -type Version uint8 - -const ( - // Version0 defines the original bech version. - Version0 Version = iota - - // VersionM is the new bech32 version defined in BIP-350, also known as - // bech32m. - VersionM - - // VersionUnknown denotes an unknown bech version. - VersionUnknown -) - -// VersionToConsts maps bech32 versions to the checksum constant to be used -// when encoding, and asserting a particular version when decoding. -var VersionToConsts = map[Version]ChecksumConst{ - Version0: Version0Const, - VersionM: VersionMConst, -} - -// ConstsToVersion maps a bech32 constant to the version it's associated with. -var ConstsToVersion = map[ChecksumConst]Version{ - Version0Const: Version0, - VersionMConst: VersionM, -} diff --git a/block.go b/block.go deleted file mode 100644 index 7d38abc4a..000000000 --- a/block.go +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil - -import ( - "bytes" - "fmt" - "io" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" -) - -// OutOfRangeError describes an error due to accessing an element that is out -// of range. -type OutOfRangeError string - -// BlockHeightUnknown is the value returned for a block height that is unknown. -// This is typically because the block has not been inserted into the main chain -// yet. -const BlockHeightUnknown = int32(-1) - -// Error satisfies the error interface and prints human-readable errors. -func (e OutOfRangeError) Error() string { - return string(e) -} - -// Block defines a bitcoin block that provides easier and more efficient -// manipulation of raw blocks. It also memoizes hashes for the block and its -// transactions on their first access so subsequent accesses don't have to -// repeat the relatively expensive hashing operations. -type Block struct { - msgBlock *wire.MsgBlock // Underlying MsgBlock - serializedBlock []byte // Serialized bytes for the block - serializedBlockNoWitness []byte // Serialized bytes for block w/o witness data - blockHash *chainhash.Hash // Cached block hash - blockHeight int32 // Height in the main block chain - transactions []*Tx // Transactions - txnsGenerated bool // ALL wrapped transactions generated -} - -// MsgBlock returns the underlying wire.MsgBlock for the Block. -func (b *Block) MsgBlock() *wire.MsgBlock { - // Return the cached block. - return b.msgBlock -} - -// Bytes returns the serialized bytes for the Block. This is equivalent to -// calling Serialize on the underlying wire.MsgBlock, however it caches the -// result so subsequent calls are more efficient. -func (b *Block) Bytes() ([]byte, error) { - // Return the cached serialized bytes if it has already been generated. - if len(b.serializedBlock) != 0 { - return b.serializedBlock, nil - } - - // Serialize the MsgBlock. - w := bytes.NewBuffer(make([]byte, 0, b.msgBlock.SerializeSize())) - err := b.msgBlock.Serialize(w) - if err != nil { - return nil, err - } - serializedBlock := w.Bytes() - - // Cache the serialized bytes and return them. - b.serializedBlock = serializedBlock - return serializedBlock, nil -} - -// BytesNoWitness returns the serialized bytes for the block with transactions -// encoded without any witness data. -func (b *Block) BytesNoWitness() ([]byte, error) { - // Return the cached serialized bytes if it has already been generated. - if len(b.serializedBlockNoWitness) != 0 { - return b.serializedBlockNoWitness, nil - } - - // Serialize the MsgBlock. - var w bytes.Buffer - err := b.msgBlock.SerializeNoWitness(&w) - if err != nil { - return nil, err - } - serializedBlock := w.Bytes() - - // Cache the serialized bytes and return them. - b.serializedBlockNoWitness = serializedBlock - return serializedBlock, nil -} - -// Hash returns the block identifier hash for the Block. This is equivalent to -// calling BlockHash on the underlying wire.MsgBlock, however it caches the -// result so subsequent calls are more efficient. -func (b *Block) Hash() *chainhash.Hash { - // Return the cached block hash if it has already been generated. - if b.blockHash != nil { - return b.blockHash - } - - // Cache the block hash and return it. - hash := b.msgBlock.BlockHash() - b.blockHash = &hash - return &hash -} - -// Tx returns a wrapped transaction (btcutil.Tx) for the transaction at the -// specified index in the Block. The supplied index is 0 based. That is to -// say, the first transaction in the block is txNum 0. This is nearly -// equivalent to accessing the raw transaction (wire.MsgTx) from the -// underlying wire.MsgBlock, however the wrapped transaction has some helpful -// properties such as caching the hash so subsequent calls are more efficient. -func (b *Block) Tx(txNum int) (*Tx, error) { - // Ensure the requested transaction is in range. - numTx := uint64(len(b.msgBlock.Transactions)) - if txNum < 0 || uint64(txNum) >= numTx { - str := fmt.Sprintf("transaction index %d is out of range - max %d", - txNum, numTx-1) - return nil, OutOfRangeError(str) - } - - // Generate slice to hold all of the wrapped transactions if needed. - if len(b.transactions) == 0 { - b.transactions = make([]*Tx, numTx) - } - - // Return the wrapped transaction if it has already been generated. - if b.transactions[txNum] != nil { - return b.transactions[txNum], nil - } - - // Generate and cache the wrapped transaction and return it. - newTx := NewTx(b.msgBlock.Transactions[txNum]) - newTx.SetIndex(txNum) - b.transactions[txNum] = newTx - return newTx, nil -} - -// Transactions returns a slice of wrapped transactions (btcutil.Tx) for all -// transactions in the Block. This is nearly equivalent to accessing the raw -// transactions (wire.MsgTx) in the underlying wire.MsgBlock, however it -// instead provides easy access to wrapped versions (btcutil.Tx) of them. -func (b *Block) Transactions() []*Tx { - // Return transactions if they have ALL already been generated. This - // flag is necessary because the wrapped transactions are lazily - // generated in a sparse fashion. - if b.txnsGenerated { - return b.transactions - } - - // Generate slice to hold all of the wrapped transactions if needed. - if len(b.transactions) == 0 { - b.transactions = make([]*Tx, len(b.msgBlock.Transactions)) - } - - // Generate and cache the wrapped transactions for all that haven't - // already been done. - for i, tx := range b.transactions { - if tx == nil { - newTx := NewTx(b.msgBlock.Transactions[i]) - newTx.SetIndex(i) - b.transactions[i] = newTx - } - } - - b.txnsGenerated = true - return b.transactions -} - -// TxHash returns the hash for the requested transaction number in the Block. -// The supplied index is 0 based. That is to say, the first transaction in the -// block is txNum 0. This is equivalent to calling TxHash on the underlying -// wire.MsgTx, however it caches the result so subsequent calls are more -// efficient. -func (b *Block) TxHash(txNum int) (*chainhash.Hash, error) { - // Attempt to get a wrapped transaction for the specified index. It - // will be created lazily if needed or simply return the cached version - // if it has already been generated. - tx, err := b.Tx(txNum) - if err != nil { - return nil, err - } - - // Defer to the wrapped transaction which will return the cached hash if - // it has already been generated. - return tx.Hash(), nil -} - -// TxLoc returns the offsets and lengths of each transaction in a raw block. -// It is used to allow fast indexing into transactions within the raw byte -// stream. -func (b *Block) TxLoc() ([]wire.TxLoc, error) { - rawMsg, err := b.Bytes() - if err != nil { - return nil, err - } - rbuf := bytes.NewBuffer(rawMsg) - - var mblock wire.MsgBlock - txLocs, err := mblock.DeserializeTxLoc(rbuf) - if err != nil { - return nil, err - } - return txLocs, err -} - -// Height returns the saved height of the block in the block chain. This value -// will be BlockHeightUnknown if it hasn't already explicitly been set. -func (b *Block) Height() int32 { - return b.blockHeight -} - -// SetHeight sets the height of the block in the block chain. -func (b *Block) SetHeight(height int32) { - b.blockHeight = height -} - -// NewBlock returns a new instance of a bitcoin block given an underlying -// wire.MsgBlock. See Block. -func NewBlock(msgBlock *wire.MsgBlock) *Block { - return &Block{ - msgBlock: msgBlock, - blockHeight: BlockHeightUnknown, - } -} - -// NewBlockFromBytes returns a new instance of a bitcoin block given the -// serialized bytes. See Block. -func NewBlockFromBytes(serializedBlock []byte) (*Block, error) { - br := bytes.NewReader(serializedBlock) - b, err := NewBlockFromReader(br) - if err != nil { - return nil, err - } - b.serializedBlock = serializedBlock - return b, nil -} - -// NewBlockFromReader returns a new instance of a bitcoin block given a -// Reader to deserialize the block. See Block. -func NewBlockFromReader(r io.Reader) (*Block, error) { - // Deserialize the bytes into a MsgBlock. - var msgBlock wire.MsgBlock - err := msgBlock.Deserialize(r) - if err != nil { - return nil, err - } - - b := Block{ - msgBlock: &msgBlock, - blockHeight: BlockHeightUnknown, - } - return &b, nil -} - -// NewBlockFromBlockAndBytes returns a new instance of a bitcoin block given -// an underlying wire.MsgBlock and the serialized bytes for it. See Block. -func NewBlockFromBlockAndBytes(msgBlock *wire.MsgBlock, serializedBlock []byte) *Block { - return &Block{ - msgBlock: msgBlock, - serializedBlock: serializedBlock, - blockHeight: BlockHeightUnknown, - } -} diff --git a/block_test.go b/block_test.go deleted file mode 100644 index 4ad8f89ed..000000000 --- a/block_test.go +++ /dev/null @@ -1,556 +0,0 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil_test - -import ( - "bytes" - "io" - "reflect" - "testing" - "time" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" - "github.com/davecgh/go-spew/spew" -) - -// TestBlock tests the API for Block. -func TestBlock(t *testing.T) { - b := btcutil.NewBlock(&Block100000) - - // Ensure we get the same data back out. - if msgBlock := b.MsgBlock(); !reflect.DeepEqual(msgBlock, &Block100000) { - t.Errorf("MsgBlock: mismatched MsgBlock - got %v, want %v", - spew.Sdump(msgBlock), spew.Sdump(&Block100000)) - } - - // Ensure block height set and get work properly. - wantHeight := int32(100000) - b.SetHeight(wantHeight) - if gotHeight := b.Height(); gotHeight != wantHeight { - t.Errorf("Height: mismatched height - got %v, want %v", - gotHeight, wantHeight) - } - - // Hash for block 100,000. - wantHashStr := "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" - wantHash, err := chainhash.NewHashFromStr(wantHashStr) - if err != nil { - t.Errorf("NewHashFromStr: %v", err) - } - - // Request the hash multiple times to test generation and caching. - for i := 0; i < 2; i++ { - hash := b.Hash() - if !hash.IsEqual(wantHash) { - t.Errorf("Hash #%d mismatched hash - got %v, want %v", - i, hash, wantHash) - } - } - - // Hashes for the transactions in Block100000. - wantTxHashes := []string{ - "8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87", - "fff2525b8931402dd09222c50775608f75787bd2b87e56995a7bdd30f79702c4", - "6359f0868171b1d194cbee1af2f16ea598ae8fad666d9b012c8ed2b79a236ec4", - "e9a66845e05d5abc0ad04ec80f774a7e585c6e8db975962d069a522137b80c1d", - } - - // Create a new block to nuke all cached data. - b = btcutil.NewBlock(&Block100000) - - // Request hash for all transactions one at a time via Tx. - for i, txHash := range wantTxHashes { - wantHash, err := chainhash.NewHashFromStr(txHash) - if err != nil { - t.Errorf("NewHashFromStr: %v", err) - } - - // Request the hash multiple times to test generation and - // caching. - for j := 0; j < 2; j++ { - tx, err := b.Tx(i) - if err != nil { - t.Errorf("Tx #%d: %v", i, err) - continue - } - - hash := tx.Hash() - if !hash.IsEqual(wantHash) { - t.Errorf("Hash #%d mismatched hash - got %v, "+ - "want %v", j, hash, wantHash) - continue - } - } - } - - // Create a new block to nuke all cached data. - b = btcutil.NewBlock(&Block100000) - - // Request slice of all transactions multiple times to test generation - // and caching. - for i := 0; i < 2; i++ { - transactions := b.Transactions() - - // Ensure we get the expected number of transactions. - if len(transactions) != len(wantTxHashes) { - t.Errorf("Transactions #%d mismatched number of "+ - "transactions - got %d, want %d", i, - len(transactions), len(wantTxHashes)) - continue - } - - // Ensure all of the hashes match. - for j, tx := range transactions { - wantHash, err := chainhash.NewHashFromStr(wantTxHashes[j]) - if err != nil { - t.Errorf("NewHashFromStr: %v", err) - } - - hash := tx.Hash() - if !hash.IsEqual(wantHash) { - t.Errorf("Transactions #%d mismatched hashes "+ - "- got %v, want %v", j, hash, wantHash) - continue - } - } - } - - // Serialize the test block. - var block100000Buf bytes.Buffer - err = Block100000.Serialize(&block100000Buf) - if err != nil { - t.Errorf("Serialize: %v", err) - } - block100000Bytes := block100000Buf.Bytes() - - // Request serialized bytes multiple times to test generation and - // caching. - for i := 0; i < 2; i++ { - serializedBytes, err := b.Bytes() - if err != nil { - t.Errorf("Bytes: %v", err) - continue - } - if !bytes.Equal(serializedBytes, block100000Bytes) { - t.Errorf("Bytes #%d wrong bytes - got %v, want %v", i, - spew.Sdump(serializedBytes), - spew.Sdump(block100000Bytes)) - continue - } - } - - // Transaction offsets and length for the transaction in Block100000. - wantTxLocs := []wire.TxLoc{ - {TxStart: 81, TxLen: 144}, - {TxStart: 225, TxLen: 259}, - {TxStart: 484, TxLen: 257}, - {TxStart: 741, TxLen: 225}, - } - - // Ensure the transaction location information is accurate. - txLocs, err := b.TxLoc() - if err != nil { - t.Errorf("TxLoc: %v", err) - return - } - if !reflect.DeepEqual(txLocs, wantTxLocs) { - t.Errorf("TxLoc: mismatched transaction location information "+ - "- got %v, want %v", spew.Sdump(txLocs), - spew.Sdump(wantTxLocs)) - } -} - -// TestNewBlockFromBytes tests creation of a Block from serialized bytes. -func TestNewBlockFromBytes(t *testing.T) { - // Serialize the test block. - var block100000Buf bytes.Buffer - err := Block100000.Serialize(&block100000Buf) - if err != nil { - t.Errorf("Serialize: %v", err) - } - block100000Bytes := block100000Buf.Bytes() - - // Create a new block from the serialized bytes. - b, err := btcutil.NewBlockFromBytes(block100000Bytes) - if err != nil { - t.Errorf("NewBlockFromBytes: %v", err) - return - } - - // Ensure we get the same data back out. - serializedBytes, err := b.Bytes() - if err != nil { - t.Errorf("Bytes: %v", err) - return - } - if !bytes.Equal(serializedBytes, block100000Bytes) { - t.Errorf("Bytes: wrong bytes - got %v, want %v", - spew.Sdump(serializedBytes), - spew.Sdump(block100000Bytes)) - } - - // Ensure the generated MsgBlock is correct. - if msgBlock := b.MsgBlock(); !reflect.DeepEqual(msgBlock, &Block100000) { - t.Errorf("MsgBlock: mismatched MsgBlock - got %v, want %v", - spew.Sdump(msgBlock), spew.Sdump(&Block100000)) - } -} - -// TestNewBlockFromBlockAndBytes tests creation of a Block from a MsgBlock and -// raw bytes. -func TestNewBlockFromBlockAndBytes(t *testing.T) { - // Serialize the test block. - var block100000Buf bytes.Buffer - err := Block100000.Serialize(&block100000Buf) - if err != nil { - t.Errorf("Serialize: %v", err) - } - block100000Bytes := block100000Buf.Bytes() - - // Create a new block from the serialized bytes. - b := btcutil.NewBlockFromBlockAndBytes(&Block100000, block100000Bytes) - - // Ensure we get the same data back out. - serializedBytes, err := b.Bytes() - if err != nil { - t.Errorf("Bytes: %v", err) - return - } - if !bytes.Equal(serializedBytes, block100000Bytes) { - t.Errorf("Bytes: wrong bytes - got %v, want %v", - spew.Sdump(serializedBytes), - spew.Sdump(block100000Bytes)) - } - if msgBlock := b.MsgBlock(); !reflect.DeepEqual(msgBlock, &Block100000) { - t.Errorf("MsgBlock: mismatched MsgBlock - got %v, want %v", - spew.Sdump(msgBlock), spew.Sdump(&Block100000)) - } -} - -// TestBlockErrors tests the error paths for the Block API. -func TestBlockErrors(t *testing.T) { - // Ensure out of range errors are as expected. - wantErr := "transaction index -1 is out of range - max 3" - testErr := btcutil.OutOfRangeError(wantErr) - if testErr.Error() != wantErr { - t.Errorf("OutOfRangeError: wrong error - got %v, want %v", - testErr.Error(), wantErr) - } - - // Serialize the test block. - var block100000Buf bytes.Buffer - err := Block100000.Serialize(&block100000Buf) - if err != nil { - t.Errorf("Serialize: %v", err) - } - block100000Bytes := block100000Buf.Bytes() - - // Create a new block from the serialized bytes. - b, err := btcutil.NewBlockFromBytes(block100000Bytes) - if err != nil { - t.Errorf("NewBlockFromBytes: %v", err) - return - } - - // Truncate the block byte buffer to force errors. - shortBytes := block100000Bytes[:80] - _, err = btcutil.NewBlockFromBytes(shortBytes) - if err != io.EOF { - t.Errorf("NewBlockFromBytes: did not get expected error - "+ - "got %v, want %v", err, io.EOF) - } - - // Ensure TxHash returns expected error on invalid indices. - _, err = b.TxHash(-1) - if _, ok := err.(btcutil.OutOfRangeError); !ok { - t.Errorf("TxHash: wrong error - got: %v <%T>, "+ - "want: <%T>", err, err, btcutil.OutOfRangeError("")) - } - _, err = b.TxHash(len(Block100000.Transactions)) - if _, ok := err.(btcutil.OutOfRangeError); !ok { - t.Errorf("TxHash: wrong error - got: %v <%T>, "+ - "want: <%T>", err, err, btcutil.OutOfRangeError("")) - } - - // Ensure Tx returns expected error on invalid indices. - _, err = b.Tx(-1) - if _, ok := err.(btcutil.OutOfRangeError); !ok { - t.Errorf("Tx: wrong error - got: %v <%T>, "+ - "want: <%T>", err, err, btcutil.OutOfRangeError("")) - } - _, err = b.Tx(len(Block100000.Transactions)) - if _, ok := err.(btcutil.OutOfRangeError); !ok { - t.Errorf("Tx: wrong error - got: %v <%T>, "+ - "want: <%T>", err, err, btcutil.OutOfRangeError("")) - } - - // Ensure TxLoc returns expected error with short byte buffer. - // This makes use of the test package only function, SetBlockBytes, to - // inject a short byte buffer. - b.SetBlockBytes(shortBytes) - _, err = b.TxLoc() - if err != io.EOF { - t.Errorf("TxLoc: did not get expected error - "+ - "got %v, want %v", err, io.EOF) - } -} - -// Block100000 defines block 100,000 of the block chain. It is used to -// test Block operations. -var Block100000 = wire.MsgBlock{ - Header: wire.BlockHeader{ - Version: 1, - PrevBlock: chainhash.Hash([32]byte{ // Make go vet happy. - 0x50, 0x12, 0x01, 0x19, 0x17, 0x2a, 0x61, 0x04, - 0x21, 0xa6, 0xc3, 0x01, 0x1d, 0xd3, 0x30, 0xd9, - 0xdf, 0x07, 0xb6, 0x36, 0x16, 0xc2, 0xcc, 0x1f, - 0x1c, 0xd0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - }), // 000000000002d01c1fccc21636b607dfd930d31d01c3a62104612a1719011250 - MerkleRoot: chainhash.Hash([32]byte{ // Make go vet happy. - 0x66, 0x57, 0xa9, 0x25, 0x2a, 0xac, 0xd5, 0xc0, - 0xb2, 0x94, 0x09, 0x96, 0xec, 0xff, 0x95, 0x22, - 0x28, 0xc3, 0x06, 0x7c, 0xc3, 0x8d, 0x48, 0x85, - 0xef, 0xb5, 0xa4, 0xac, 0x42, 0x47, 0xe9, 0xf3, - }), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766 - Timestamp: time.Unix(1293623863, 0), // 2010-12-29 11:57:43 +0000 UTC - Bits: 0x1b04864c, // 453281356 - Nonce: 0x10572b0f, // 274148111 - }, - Transactions: []*wire.MsgTx{ - { - Version: 1, - TxIn: []*wire.TxIn{ - { - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash{}, - Index: 0xffffffff, - }, - SignatureScript: []byte{ - 0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02, - }, - Sequence: 0xffffffff, - Witness: [][]byte{ - {0x04, 0x31}, - {0x01, 0x43}, - }, - }, - }, - TxOut: []*wire.TxOut{ - { - Value: 0x12a05f200, // 5000000000 - PkScript: []byte{ - 0x41, // OP_DATA_65 - 0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25, - 0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73, - 0xdc, 0x44, 0x44, 0xac, 0xb8, 0x3c, 0x4e, 0xc7, - 0xa0, 0xe2, 0xf9, 0x9d, 0xd7, 0x45, 0x75, 0x16, - 0xc5, 0x81, 0x72, 0x42, 0xda, 0x79, 0x69, 0x24, - 0xca, 0x4e, 0x99, 0x94, 0x7d, 0x08, 0x7f, 0xed, - 0xf9, 0xce, 0x46, 0x7c, 0xb9, 0xf7, 0xc6, 0x28, - 0x70, 0x78, 0xf8, 0x01, 0xdf, 0x27, 0x6f, 0xdf, - 0x84, // 65-byte signature - 0xac, // OP_CHECKSIG - }, - }, - }, - LockTime: 0, - }, - { - Version: 1, - TxIn: []*wire.TxIn{ - { - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash([32]byte{ // Make go vet happy. - 0x03, 0x2e, 0x38, 0xe9, 0xc0, 0xa8, 0x4c, 0x60, - 0x46, 0xd6, 0x87, 0xd1, 0x05, 0x56, 0xdc, 0xac, - 0xc4, 0x1d, 0x27, 0x5e, 0xc5, 0x5f, 0xc0, 0x07, - 0x79, 0xac, 0x88, 0xfd, 0xf3, 0x57, 0xa1, 0x87, - }), // 87a157f3fd88ac7907c05fc55e271dc4acdc5605d187d646604ca8c0e9382e03 - Index: 0, - }, - SignatureScript: []byte{ - 0x49, // OP_DATA_73 - 0x30, 0x46, 0x02, 0x21, 0x00, 0xc3, 0x52, 0xd3, - 0xdd, 0x99, 0x3a, 0x98, 0x1b, 0xeb, 0xa4, 0xa6, - 0x3a, 0xd1, 0x5c, 0x20, 0x92, 0x75, 0xca, 0x94, - 0x70, 0xab, 0xfc, 0xd5, 0x7d, 0xa9, 0x3b, 0x58, - 0xe4, 0xeb, 0x5d, 0xce, 0x82, 0x02, 0x21, 0x00, - 0x84, 0x07, 0x92, 0xbc, 0x1f, 0x45, 0x60, 0x62, - 0x81, 0x9f, 0x15, 0xd3, 0x3e, 0xe7, 0x05, 0x5c, - 0xf7, 0xb5, 0xee, 0x1a, 0xf1, 0xeb, 0xcc, 0x60, - 0x28, 0xd9, 0xcd, 0xb1, 0xc3, 0xaf, 0x77, 0x48, - 0x01, // 73-byte signature - 0x41, // OP_DATA_65 - 0x04, 0xf4, 0x6d, 0xb5, 0xe9, 0xd6, 0x1a, 0x9d, - 0xc2, 0x7b, 0x8d, 0x64, 0xad, 0x23, 0xe7, 0x38, - 0x3a, 0x4e, 0x6c, 0xa1, 0x64, 0x59, 0x3c, 0x25, - 0x27, 0xc0, 0x38, 0xc0, 0x85, 0x7e, 0xb6, 0x7e, - 0xe8, 0xe8, 0x25, 0xdc, 0xa6, 0x50, 0x46, 0xb8, - 0x2c, 0x93, 0x31, 0x58, 0x6c, 0x82, 0xe0, 0xfd, - 0x1f, 0x63, 0x3f, 0x25, 0xf8, 0x7c, 0x16, 0x1b, - 0xc6, 0xf8, 0xa6, 0x30, 0x12, 0x1d, 0xf2, 0xb3, - 0xd3, // 65-byte pubkey - }, - Sequence: 0xffffffff, - }, - }, - TxOut: []*wire.TxOut{ - { - Value: 0x2123e300, // 556000000 - PkScript: []byte{ - 0x76, // OP_DUP - 0xa9, // OP_HASH160 - 0x14, // OP_DATA_20 - 0xc3, 0x98, 0xef, 0xa9, 0xc3, 0x92, 0xba, 0x60, - 0x13, 0xc5, 0xe0, 0x4e, 0xe7, 0x29, 0x75, 0x5e, - 0xf7, 0xf5, 0x8b, 0x32, - 0x88, // OP_EQUALVERIFY - 0xac, // OP_CHECKSIG - }, - }, - { - Value: 0x108e20f00, // 4444000000 - PkScript: []byte{ - 0x76, // OP_DUP - 0xa9, // OP_HASH160 - 0x14, // OP_DATA_20 - 0x94, 0x8c, 0x76, 0x5a, 0x69, 0x14, 0xd4, 0x3f, - 0x2a, 0x7a, 0xc1, 0x77, 0xda, 0x2c, 0x2f, 0x6b, - 0x52, 0xde, 0x3d, 0x7c, - 0x88, // OP_EQUALVERIFY - 0xac, // OP_CHECKSIG - }, - }, - }, - LockTime: 0, - }, - { - Version: 1, - TxIn: []*wire.TxIn{ - { - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash([32]byte{ // Make go vet happy. - 0xc3, 0x3e, 0xbf, 0xf2, 0xa7, 0x09, 0xf1, 0x3d, - 0x9f, 0x9a, 0x75, 0x69, 0xab, 0x16, 0xa3, 0x27, - 0x86, 0xaf, 0x7d, 0x7e, 0x2d, 0xe0, 0x92, 0x65, - 0xe4, 0x1c, 0x61, 0xd0, 0x78, 0x29, 0x4e, 0xcf, - }), // cf4e2978d0611ce46592e02d7e7daf8627a316ab69759a9f3df109a7f2bf3ec3 - Index: 1, - }, - SignatureScript: []byte{ - 0x47, // OP_DATA_71 - 0x30, 0x44, 0x02, 0x20, 0x03, 0x2d, 0x30, 0xdf, - 0x5e, 0xe6, 0xf5, 0x7f, 0xa4, 0x6c, 0xdd, 0xb5, - 0xeb, 0x8d, 0x0d, 0x9f, 0xe8, 0xde, 0x6b, 0x34, - 0x2d, 0x27, 0x94, 0x2a, 0xe9, 0x0a, 0x32, 0x31, - 0xe0, 0xba, 0x33, 0x3e, 0x02, 0x20, 0x3d, 0xee, - 0xe8, 0x06, 0x0f, 0xdc, 0x70, 0x23, 0x0a, 0x7f, - 0x5b, 0x4a, 0xd7, 0xd7, 0xbc, 0x3e, 0x62, 0x8c, - 0xbe, 0x21, 0x9a, 0x88, 0x6b, 0x84, 0x26, 0x9e, - 0xae, 0xb8, 0x1e, 0x26, 0xb4, 0xfe, 0x01, - 0x41, // OP_DATA_65 - 0x04, 0xae, 0x31, 0xc3, 0x1b, 0xf9, 0x12, 0x78, - 0xd9, 0x9b, 0x83, 0x77, 0xa3, 0x5b, 0xbc, 0xe5, - 0xb2, 0x7d, 0x9f, 0xff, 0x15, 0x45, 0x68, 0x39, - 0xe9, 0x19, 0x45, 0x3f, 0xc7, 0xb3, 0xf7, 0x21, - 0xf0, 0xba, 0x40, 0x3f, 0xf9, 0x6c, 0x9d, 0xee, - 0xb6, 0x80, 0xe5, 0xfd, 0x34, 0x1c, 0x0f, 0xc3, - 0xa7, 0xb9, 0x0d, 0xa4, 0x63, 0x1e, 0xe3, 0x95, - 0x60, 0x63, 0x9d, 0xb4, 0x62, 0xe9, 0xcb, 0x85, - 0x0f, // 65-byte pubkey - }, - Sequence: 0xffffffff, - }, - }, - TxOut: []*wire.TxOut{ - { - Value: 0xf4240, // 1000000 - PkScript: []byte{ - 0x76, // OP_DUP - 0xa9, // OP_HASH160 - 0x14, // OP_DATA_20 - 0xb0, 0xdc, 0xbf, 0x97, 0xea, 0xbf, 0x44, 0x04, - 0xe3, 0x1d, 0x95, 0x24, 0x77, 0xce, 0x82, 0x2d, - 0xad, 0xbe, 0x7e, 0x10, - 0x88, // OP_EQUALVERIFY - 0xac, // OP_CHECKSIG - }, - }, - { - Value: 0x11d260c0, // 299000000 - PkScript: []byte{ - 0x76, // OP_DUP - 0xa9, // OP_HASH160 - 0x14, // OP_DATA_20 - 0x6b, 0x12, 0x81, 0xee, 0xc2, 0x5a, 0xb4, 0xe1, - 0xe0, 0x79, 0x3f, 0xf4, 0xe0, 0x8a, 0xb1, 0xab, - 0xb3, 0x40, 0x9c, 0xd9, - 0x88, // OP_EQUALVERIFY - 0xac, // OP_CHECKSIG - }, - }, - }, - LockTime: 0, - }, - { - Version: 1, - TxIn: []*wire.TxIn{ - { - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash([32]byte{ // Make go vet happy. - 0x0b, 0x60, 0x72, 0xb3, 0x86, 0xd4, 0xa7, 0x73, - 0x23, 0x52, 0x37, 0xf6, 0x4c, 0x11, 0x26, 0xac, - 0x3b, 0x24, 0x0c, 0x84, 0xb9, 0x17, 0xa3, 0x90, - 0x9b, 0xa1, 0xc4, 0x3d, 0xed, 0x5f, 0x51, 0xf4, - }), // f4515fed3dc4a19b90a317b9840c243bac26114cf637522373a7d486b372600b - Index: 0, - }, - SignatureScript: []byte{ - 0x49, // OP_DATA_73 - 0x30, 0x46, 0x02, 0x21, 0x00, 0xbb, 0x1a, 0xd2, - 0x6d, 0xf9, 0x30, 0xa5, 0x1c, 0xce, 0x11, 0x0c, - 0xf4, 0x4f, 0x7a, 0x48, 0xc3, 0xc5, 0x61, 0xfd, - 0x97, 0x75, 0x00, 0xb1, 0xae, 0x5d, 0x6b, 0x6f, - 0xd1, 0x3d, 0x0b, 0x3f, 0x4a, 0x02, 0x21, 0x00, - 0xc5, 0xb4, 0x29, 0x51, 0xac, 0xed, 0xff, 0x14, - 0xab, 0xba, 0x27, 0x36, 0xfd, 0x57, 0x4b, 0xdb, - 0x46, 0x5f, 0x3e, 0x6f, 0x8d, 0xa1, 0x2e, 0x2c, - 0x53, 0x03, 0x95, 0x4a, 0xca, 0x7f, 0x78, 0xf3, - 0x01, // 73-byte signature - 0x41, // OP_DATA_65 - 0x04, 0xa7, 0x13, 0x5b, 0xfe, 0x82, 0x4c, 0x97, - 0xec, 0xc0, 0x1e, 0xc7, 0xd7, 0xe3, 0x36, 0x18, - 0x5c, 0x81, 0xe2, 0xaa, 0x2c, 0x41, 0xab, 0x17, - 0x54, 0x07, 0xc0, 0x94, 0x84, 0xce, 0x96, 0x94, - 0xb4, 0x49, 0x53, 0xfc, 0xb7, 0x51, 0x20, 0x65, - 0x64, 0xa9, 0xc2, 0x4d, 0xd0, 0x94, 0xd4, 0x2f, - 0xdb, 0xfd, 0xd5, 0xaa, 0xd3, 0xe0, 0x63, 0xce, - 0x6a, 0xf4, 0xcf, 0xaa, 0xea, 0x4e, 0xa1, 0x4f, - 0xbb, // 65-byte pubkey - }, - Sequence: 0xffffffff, - }, - }, - TxOut: []*wire.TxOut{ - { - Value: 0xf4240, // 1000000 - PkScript: []byte{ - 0x76, // OP_DUP - 0xa9, // OP_HASH160 - 0x14, // OP_DATA_20 - 0x39, 0xaa, 0x3d, 0x56, 0x9e, 0x06, 0xa1, 0xd7, - 0x92, 0x6d, 0xc4, 0xbe, 0x11, 0x93, 0xc9, 0x9b, - 0xf2, 0xeb, 0x9e, 0xe0, - 0x88, // OP_EQUALVERIFY - 0xac, // OP_CHECKSIG - }, - }, - }, - LockTime: 0, - }, - }, -} diff --git a/bloom/README.md b/bloom/README.md deleted file mode 100644 index a3177345e..000000000 --- a/bloom/README.md +++ /dev/null @@ -1,30 +0,0 @@ -bloom -===== - -[![Build Status](http://img.shields.io/travis/btcsuite/btcutil.svg)](https://travis-ci.org/btcsuite/btcutil) -[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) -[![GoDoc](http://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcutil/bloom) - -Package bloom provides an API for dealing with bitcoin-specific bloom filters. - -A comprehensive suite of tests is provided to ensure proper functionality. See -`test_coverage.txt` for the gocov coverage report. Alternatively, if you are -running a POSIX OS, you can run the `cov_report.sh` script for a real-time -report. - -## Installation and Updating - -```bash -$ go get -u github.com/btcsuite/btcutil/bloom -``` - -## Examples - -* [NewFilter Example](http://godoc.org/github.com/btcsuite/btcutil/bloom#example-NewFilter) - Demonstrates how to create a new bloom filter, add a transaction hash to it, - and check if the filter matches the transaction. - -## License - -Package bloom is licensed under the [copyfree](http://copyfree.org) ISC -License. diff --git a/bloom/cov_report.sh b/bloom/cov_report.sh deleted file mode 100644 index 307f05b76..000000000 --- a/bloom/cov_report.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -# This script uses gocov to generate a test coverage report. -# The gocov tool my be obtained with the following command: -# go get github.com/axw/gocov/gocov -# -# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. - -# Check for gocov. -type gocov >/dev/null 2>&1 -if [ $? -ne 0 ]; then - echo >&2 "This script requires the gocov tool." - echo >&2 "You may obtain it with the following command:" - echo >&2 "go get github.com/axw/gocov/gocov" - exit 1 -fi -gocov test | gocov report diff --git a/bloom/example_test.go b/bloom/example_test.go deleted file mode 100644 index f45ec2f34..000000000 --- a/bloom/example_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2014-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package bloom_test - -import ( - "fmt" - "math/rand" - "time" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil/bloom" -) - -// This example demonstrates how to create a new bloom filter, add a transaction -// hash to it, and check if the filter matches the transaction. -func ExampleNewFilter() { - rand.Seed(time.Now().UnixNano()) - tweak := rand.Uint32() - - // Create a new bloom filter intended to hold 10 elements with a 0.01% - // false positive rate and does not include any automatic update - // functionality when transactions are matched. - filter := bloom.NewFilter(10, tweak, 0.0001, wire.BloomUpdateNone) - - // Create a transaction hash and add it to the filter. This particular - // trasaction is the first transaction in block 310,000 of the main - // bitcoin block chain. - txHashStr := "fd611c56ca0d378cdcd16244b45c2ba9588da3adac367c4ef43e808b280b8a45" - txHash, err := chainhash.NewHashFromStr(txHashStr) - if err != nil { - fmt.Println(err) - return - } - filter.AddHash(txHash) - - // Show that the filter matches. - matches := filter.Matches(txHash[:]) - fmt.Println("Filter Matches?:", matches) - - // Output: - // Filter Matches?: true -} diff --git a/bloom/filter.go b/bloom/filter.go deleted file mode 100644 index b3379ecb4..000000000 --- a/bloom/filter.go +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright (c) 2014-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package bloom - -import ( - "encoding/binary" - "math" - "sync" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" -) - -// ln2Squared is simply the square of the natural log of 2. -const ln2Squared = math.Ln2 * math.Ln2 - -// minUint32 is a convenience function to return the minimum value of the two -// passed uint32 values. -func minUint32(a, b uint32) uint32 { - if a < b { - return a - } - return b -} - -// Filter defines a bitcoin bloom filter that provides easy manipulation of raw -// filter data. -type Filter struct { - mtx sync.Mutex - msgFilterLoad *wire.MsgFilterLoad -} - -// NewFilter creates a new bloom filter instance, mainly to be used by SPV -// clients. The tweak parameter is a random value added to the seed value. -// The false positive rate is the probability of a false positive where 1.0 is -// "match everything" and zero is unachievable. Thus, providing any false -// positive rates less than 0 or greater than 1 will be adjusted to the valid -// range. -// -// For more information on what values to use for both elements and fprate, -// see https://en.wikipedia.org/wiki/Bloom_filter. -func NewFilter(elements, tweak uint32, fprate float64, flags wire.BloomUpdateType) *Filter { - // Massage the false positive rate to sane values. - if fprate > 1.0 { - fprate = 1.0 - } - if fprate < 1e-9 { - fprate = 1e-9 - } - - // Calculate the size of the filter in bytes for the given number of - // elements and false positive rate. - // - // Equivalent to m = -(n*ln(p) / ln(2)^2), where m is in bits. - // Then clamp it to the maximum filter size and convert to bytes. - dataLen := uint32(-1 * float64(elements) * math.Log(fprate) / ln2Squared) - dataLen = minUint32(dataLen, wire.MaxFilterLoadFilterSize*8) / 8 - - // Calculate the number of hash functions based on the size of the - // filter calculated above and the number of elements. - // - // Equivalent to k = (m/n) * ln(2) - // Then clamp it to the maximum allowed hash funcs. - hashFuncs := uint32(float64(dataLen*8) / float64(elements) * math.Ln2) - hashFuncs = minUint32(hashFuncs, wire.MaxFilterLoadHashFuncs) - - data := make([]byte, dataLen) - msg := wire.NewMsgFilterLoad(data, hashFuncs, tweak, flags) - - return &Filter{ - msgFilterLoad: msg, - } -} - -// LoadFilter creates a new Filter instance with the given underlying -// wire.MsgFilterLoad. -func LoadFilter(filter *wire.MsgFilterLoad) *Filter { - return &Filter{ - msgFilterLoad: filter, - } -} - -// IsLoaded returns true if a filter is loaded, otherwise false. -// -// This function is safe for concurrent access. -func (bf *Filter) IsLoaded() bool { - bf.mtx.Lock() - loaded := bf.msgFilterLoad != nil - bf.mtx.Unlock() - return loaded -} - -// Reload loads a new filter replacing any existing filter. -// -// This function is safe for concurrent access. -func (bf *Filter) Reload(filter *wire.MsgFilterLoad) { - bf.mtx.Lock() - bf.msgFilterLoad = filter - bf.mtx.Unlock() -} - -// Unload unloads the bloom filter. -// -// This function is safe for concurrent access. -func (bf *Filter) Unload() { - bf.mtx.Lock() - bf.msgFilterLoad = nil - bf.mtx.Unlock() -} - -// hash returns the bit offset in the bloom filter which corresponds to the -// passed data for the given independent hash function number. -func (bf *Filter) hash(hashNum uint32, data []byte) uint32 { - // bitcoind: 0xfba4c795 chosen as it guarantees a reasonable bit - // difference between hashNum values. - // - // Note that << 3 is equivalent to multiplying by 8, but is faster. - // Thus the returned hash is brought into range of the number of bits - // the filter has and returned. - mm := MurmurHash3(hashNum*0xfba4c795+bf.msgFilterLoad.Tweak, data) - return mm % (uint32(len(bf.msgFilterLoad.Filter)) << 3) -} - -// matches returns true if the bloom filter might contain the passed data and -// false if it definitely does not. -// -// This function MUST be called with the filter lock held. -func (bf *Filter) matches(data []byte) bool { - if bf.msgFilterLoad == nil { - return false - } - - // The bloom filter does not contain the data if any of the bit offsets - // which result from hashing the data using each independent hash - // function are not set. The shifts and masks below are a faster - // equivalent of: - // arrayIndex := idx / 8 (idx >> 3) - // bitOffset := idx % 8 (idx & 7) - /// if filter[arrayIndex] & 1<>3]&(1<<(idx&7)) == 0 { - return false - } - } - return true -} - -// Matches returns true if the bloom filter might contain the passed data and -// false if it definitely does not. -// -// This function is safe for concurrent access. -func (bf *Filter) Matches(data []byte) bool { - bf.mtx.Lock() - match := bf.matches(data) - bf.mtx.Unlock() - return match -} - -// matchesOutPoint returns true if the bloom filter might contain the passed -// outpoint and false if it definitely does not. -// -// This function MUST be called with the filter lock held. -func (bf *Filter) matchesOutPoint(outpoint *wire.OutPoint) bool { - // Serialize - var buf [chainhash.HashSize + 4]byte - copy(buf[:], outpoint.Hash[:]) - binary.LittleEndian.PutUint32(buf[chainhash.HashSize:], outpoint.Index) - - return bf.matches(buf[:]) -} - -// MatchesOutPoint returns true if the bloom filter might contain the passed -// outpoint and false if it definitely does not. -// -// This function is safe for concurrent access. -func (bf *Filter) MatchesOutPoint(outpoint *wire.OutPoint) bool { - bf.mtx.Lock() - match := bf.matchesOutPoint(outpoint) - bf.mtx.Unlock() - return match -} - -// add adds the passed byte slice to the bloom filter. -// -// This function MUST be called with the filter lock held. -func (bf *Filter) add(data []byte) { - if bf.msgFilterLoad == nil { - return - } - - // Adding data to a bloom filter consists of setting all of the bit - // offsets which result from hashing the data using each independent - // hash function. The shifts and masks below are a faster equivalent - // of: - // arrayIndex := idx / 8 (idx >> 3) - // bitOffset := idx % 8 (idx & 7) - /// filter[arrayIndex] |= 1<>3] |= (1 << (7 & idx)) - } -} - -// Add adds the passed byte slice to the bloom filter. -// -// This function is safe for concurrent access. -func (bf *Filter) Add(data []byte) { - bf.mtx.Lock() - bf.add(data) - bf.mtx.Unlock() -} - -// AddHash adds the passed chainhash.Hash to the Filter. -// -// This function is safe for concurrent access. -func (bf *Filter) AddHash(hash *chainhash.Hash) { - bf.mtx.Lock() - bf.add(hash[:]) - bf.mtx.Unlock() -} - -// addOutPoint adds the passed transaction outpoint to the bloom filter. -// -// This function MUST be called with the filter lock held. -func (bf *Filter) addOutPoint(outpoint *wire.OutPoint) { - // Serialize - var buf [chainhash.HashSize + 4]byte - copy(buf[:], outpoint.Hash[:]) - binary.LittleEndian.PutUint32(buf[chainhash.HashSize:], outpoint.Index) - - bf.add(buf[:]) -} - -// AddOutPoint adds the passed transaction outpoint to the bloom filter. -// -// This function is safe for concurrent access. -func (bf *Filter) AddOutPoint(outpoint *wire.OutPoint) { - bf.mtx.Lock() - bf.addOutPoint(outpoint) - bf.mtx.Unlock() -} - -// maybeAddOutpoint potentially adds the passed outpoint to the bloom filter -// depending on the bloom update flags and the type of the passed public key -// script. -// -// This function MUST be called with the filter lock held. -func (bf *Filter) maybeAddOutpoint(pkScript []byte, outHash *chainhash.Hash, outIdx uint32) { - switch bf.msgFilterLoad.Flags { - case wire.BloomUpdateAll: - outpoint := wire.NewOutPoint(outHash, outIdx) - bf.addOutPoint(outpoint) - case wire.BloomUpdateP2PubkeyOnly: - class := txscript.GetScriptClass(pkScript) - if class == txscript.PubKeyTy || class == txscript.MultiSigTy { - outpoint := wire.NewOutPoint(outHash, outIdx) - bf.addOutPoint(outpoint) - } - } -} - -// matchTxAndUpdate returns true if the bloom filter matches data within the -// passed transaction, otherwise false is returned. If the filter does match -// the passed transaction, it will also update the filter depending on the bloom -// update flags set via the loaded filter if needed. -// -// This function MUST be called with the filter lock held. -func (bf *Filter) matchTxAndUpdate(tx *btcutil.Tx) bool { - // Check if the filter matches the hash of the transaction. - // This is useful for finding transactions when they appear in a block. - matched := bf.matches(tx.Hash()[:]) - - // Check if the filter matches any data elements in the public key - // scripts of any of the outputs. When it does, add the outpoint that - // matched so transactions which spend from the matched transaction are - // also included in the filter. This removes the burden of updating the - // filter for this scenario from the client. It is also more efficient - // on the network since it avoids the need for another filteradd message - // from the client and avoids some potential races that could otherwise - // occur. - for i, txOut := range tx.MsgTx().TxOut { - pushedData, err := txscript.PushedData(txOut.PkScript) - if err != nil { - continue - } - - for _, data := range pushedData { - if !bf.matches(data) { - continue - } - - matched = true - bf.maybeAddOutpoint(txOut.PkScript, tx.Hash(), uint32(i)) - break - } - } - - // Nothing more to do if a match has already been made. - if matched { - return true - } - - // At this point, the transaction and none of the data elements in the - // public key scripts of its outputs matched. - - // Check if the filter matches any outpoints this transaction spends or - // any data elements in the signature scripts of any of the inputs. - for _, txin := range tx.MsgTx().TxIn { - if bf.matchesOutPoint(&txin.PreviousOutPoint) { - return true - } - - pushedData, err := txscript.PushedData(txin.SignatureScript) - if err != nil { - continue - } - for _, data := range pushedData { - if bf.matches(data) { - return true - } - } - } - - return false -} - -// MatchTxAndUpdate returns true if the bloom filter matches data within the -// passed transaction, otherwise false is returned. If the filter does match -// the passed transaction, it will also update the filter depending on the bloom -// update flags set via the loaded filter if needed. -// -// This function is safe for concurrent access. -func (bf *Filter) MatchTxAndUpdate(tx *btcutil.Tx) bool { - bf.mtx.Lock() - match := bf.matchTxAndUpdate(tx) - bf.mtx.Unlock() - return match -} - -// MsgFilterLoad returns the underlying wire.MsgFilterLoad for the bloom -// filter. -// -// This function is safe for concurrent access. -func (bf *Filter) MsgFilterLoad() *wire.MsgFilterLoad { - bf.mtx.Lock() - msg := bf.msgFilterLoad - bf.mtx.Unlock() - return msg -} diff --git a/bloom/filter_test.go b/bloom/filter_test.go deleted file mode 100644 index 6589d04d2..000000000 --- a/bloom/filter_test.go +++ /dev/null @@ -1,660 +0,0 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package bloom_test - -import ( - "bytes" - "encoding/hex" - "testing" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/bloom" -) - -// TestFilterLarge ensures a maximum sized filter can be created. -func TestFilterLarge(t *testing.T) { - f := bloom.NewFilter(100000000, 0, 0.01, wire.BloomUpdateNone) - if len(f.MsgFilterLoad().Filter) > wire.MaxFilterLoadFilterSize { - t.Errorf("TestFilterLarge test failed: %d > %d", - len(f.MsgFilterLoad().Filter), wire.MaxFilterLoadFilterSize) - } -} - -// TestFilterLoad ensures loading and unloading of a filter pass. -func TestFilterLoad(t *testing.T) { - merkle := wire.MsgFilterLoad{} - - f := bloom.LoadFilter(&merkle) - if !f.IsLoaded() { - t.Errorf("TestFilterLoad IsLoaded test failed: want %v got %v", - true, !f.IsLoaded()) - return - } - f.Unload() - if f.IsLoaded() { - t.Errorf("TestFilterLoad IsLoaded test failed: want %v got %v", - f.IsLoaded(), false) - return - } -} - -// TestFilterInsert ensures inserting data into the filter causes that data -// to be matched and the resulting serialized MsgFilterLoad is the expected -// value. -func TestFilterInsert(t *testing.T) { - var tests = []struct { - hex string - insert bool - }{ - {"99108ad8ed9bb6274d3980bab5a85c048f0950c8", true}, - {"19108ad8ed9bb6274d3980bab5a85c048f0950c8", false}, - {"b5a2c786d9ef4658287ced5914b37a1b4aa32eee", true}, - {"b9300670b4c5366e95b2699e8b18bc75e5f729c5", true}, - } - - f := bloom.NewFilter(3, 0, 0.01, wire.BloomUpdateAll) - - for i, test := range tests { - data, err := hex.DecodeString(test.hex) - if err != nil { - t.Errorf("TestFilterInsert DecodeString failed: %v\n", err) - return - } - if test.insert { - f.Add(data) - } - - result := f.Matches(data) - if test.insert != result { - t.Errorf("TestFilterInsert Matches test #%d failure: got %v want %v\n", - i, result, test.insert) - return - } - } - - want, err := hex.DecodeString("03614e9b050000000000000001") - if err != nil { - t.Errorf("TestFilterInsert DecodeString failed: %v\n", err) - return - } - - got := bytes.NewBuffer(nil) - err = f.MsgFilterLoad().BtcEncode(got, wire.ProtocolVersion, wire.LatestEncoding) - if err != nil { - t.Errorf("TestFilterInsert BtcDecode failed: %v\n", err) - return - } - - if !bytes.Equal(got.Bytes(), want) { - t.Errorf("TestFilterInsert failure: got %v want %v\n", - got.Bytes(), want) - return - } -} - -// TestFilterFPRange checks that new filters made with out of range -// false positive targets result in either max or min false positive rates. -func TestFilterFPRange(t *testing.T) { - tests := []struct { - name string - hash string - want string - filter *bloom.Filter - }{ - { - name: "fprates > 1 should be clipped at 1", - hash: "02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041", - want: "00000000000000000001", - filter: bloom.NewFilter(1, 0, 20.9999999769, wire.BloomUpdateAll), - }, - { - name: "fprates less than 1e-9 should be clipped at min", - hash: "02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041", - want: "0566d97a91a91b0000000000000001", - filter: bloom.NewFilter(1, 0, 0, wire.BloomUpdateAll), - }, - { - name: "negative fprates should be clipped at min", - hash: "02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041", - want: "0566d97a91a91b0000000000000001", - filter: bloom.NewFilter(1, 0, -1, wire.BloomUpdateAll), - }, - } - - for _, test := range tests { - // Convert test input to appropriate types. - hash, err := chainhash.NewHashFromStr(test.hash) - if err != nil { - t.Errorf("NewHashFromStr unexpected error: %v", err) - continue - } - want, err := hex.DecodeString(test.want) - if err != nil { - t.Errorf("DecodeString unexpected error: %v\n", err) - continue - } - - // Add the test hash to the bloom filter and ensure the - // filter serializes to the expected bytes. - f := test.filter - f.AddHash(hash) - got := bytes.NewBuffer(nil) - err = f.MsgFilterLoad().BtcEncode(got, wire.ProtocolVersion, wire.LatestEncoding) - if err != nil { - t.Errorf("BtcDecode unexpected error: %v\n", err) - continue - } - if !bytes.Equal(got.Bytes(), want) { - t.Errorf("serialized filter mismatch: got %x want %x\n", - got.Bytes(), want) - continue - } - } -} - -// TestFilterInsert ensures inserting data into the filter with a tweak causes -// that data to be matched and the resulting serialized MsgFilterLoad is the -// expected value. -func TestFilterInsertWithTweak(t *testing.T) { - var tests = []struct { - hex string - insert bool - }{ - {"99108ad8ed9bb6274d3980bab5a85c048f0950c8", true}, - {"19108ad8ed9bb6274d3980bab5a85c048f0950c8", false}, - {"b5a2c786d9ef4658287ced5914b37a1b4aa32eee", true}, - {"b9300670b4c5366e95b2699e8b18bc75e5f729c5", true}, - } - - f := bloom.NewFilter(3, 2147483649, 0.01, wire.BloomUpdateAll) - - for i, test := range tests { - data, err := hex.DecodeString(test.hex) - if err != nil { - t.Errorf("TestFilterInsertWithTweak DecodeString failed: %v\n", err) - return - } - if test.insert { - f.Add(data) - } - - result := f.Matches(data) - if test.insert != result { - t.Errorf("TestFilterInsertWithTweak Matches test #%d failure: got %v want %v\n", - i, result, test.insert) - return - } - } - - want, err := hex.DecodeString("03ce4299050000000100008001") - if err != nil { - t.Errorf("TestFilterInsertWithTweak DecodeString failed: %v\n", err) - return - } - got := bytes.NewBuffer(nil) - err = f.MsgFilterLoad().BtcEncode(got, wire.ProtocolVersion, wire.LatestEncoding) - if err != nil { - t.Errorf("TestFilterInsertWithTweak BtcDecode failed: %v\n", err) - return - } - - if !bytes.Equal(got.Bytes(), want) { - t.Errorf("TestFilterInsertWithTweak failure: got %v want %v\n", - got.Bytes(), want) - return - } -} - -// TestFilterInsertKey ensures inserting public keys and addresses works as -// expected. -func TestFilterInsertKey(t *testing.T) { - secret := "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C" - - wif, err := btcutil.DecodeWIF(secret) - if err != nil { - t.Errorf("TestFilterInsertKey DecodeWIF failed: %v", err) - return - } - - f := bloom.NewFilter(2, 0, 0.001, wire.BloomUpdateAll) - f.Add(wif.SerializePubKey()) - f.Add(btcutil.Hash160(wif.SerializePubKey())) - - want, err := hex.DecodeString("038fc16b080000000000000001") - if err != nil { - t.Errorf("TestFilterInsertWithTweak DecodeString failed: %v\n", err) - return - } - got := bytes.NewBuffer(nil) - err = f.MsgFilterLoad().BtcEncode(got, wire.ProtocolVersion, wire.LatestEncoding) - if err != nil { - t.Errorf("TestFilterInsertWithTweak BtcDecode failed: %v\n", err) - return - } - - if !bytes.Equal(got.Bytes(), want) { - t.Errorf("TestFilterInsertWithTweak failure: got %v want %v\n", - got.Bytes(), want) - return - } -} - -func TestFilterBloomMatch(t *testing.T) { - str := "01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e" + - "88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7" + - "c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d2" + - "7d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee" + - "51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5e" + - "eef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c33" + - "9ffffffff021bff3d11000000001976a91404943fdd508053c75000106d3" + - "bc6e2754dbcff1988ac2f15de00000000001976a914a266436d296554760" + - "8b9e15d9032a7b9d64fa43188ac00000000" - strBytes, err := hex.DecodeString(str) - if err != nil { - t.Errorf("TestFilterBloomMatch DecodeString failure: %v", err) - return - } - tx, err := btcutil.NewTxFromBytes(strBytes) - if err != nil { - t.Errorf("TestFilterBloomMatch NewTxFromBytes failure: %v", err) - return - } - spendingTxBytes := []byte{0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, - 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, - 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, - 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, - 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, - 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, - 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, - 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, - 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, - 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, - 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, - 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, - 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, - 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, - 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, - 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, - 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, - 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, - 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, - 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, - 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, - 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, - 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, - 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, - 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, - 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, - 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, - 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00} - - spendingTx, err := btcutil.NewTxFromBytes(spendingTxBytes) - if err != nil { - t.Errorf("TestFilterBloomMatch NewTxFromBytes failure: %v", err) - return - } - - f := bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll) - inputStr := "b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b" - hash, err := chainhash.NewHashFromStr(inputStr) - if err != nil { - t.Errorf("TestFilterBloomMatch NewHashFromStr failed: %v\n", err) - return - } - f.AddHash(hash) - if !f.MatchTxAndUpdate(tx) { - t.Errorf("TestFilterBloomMatch didn't match hash %s", inputStr) - } - - f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll) - inputStr = "6bff7fcd4f8565ef406dd5d63d4ff94f318fe82027fd4dc451b04474019f74b4" - hashBytes, err := hex.DecodeString(inputStr) - if err != nil { - t.Errorf("TestFilterBloomMatch DecodeString failed: %v\n", err) - return - } - f.Add(hashBytes) - if !f.MatchTxAndUpdate(tx) { - t.Errorf("TestFilterBloomMatch didn't match hash %s", inputStr) - } - - f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll) - inputStr = "30450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065" + - "f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643" + - "ac4cb7cb3c462aced7f14711a01" - hashBytes, err = hex.DecodeString(inputStr) - if err != nil { - t.Errorf("TestFilterBloomMatch DecodeString failed: %v\n", err) - return - } - f.Add(hashBytes) - if !f.MatchTxAndUpdate(tx) { - t.Errorf("TestFilterBloomMatch didn't match input signature %s", inputStr) - } - - f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll) - inputStr = "046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95" + - "c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe" + - "76036c339" - hashBytes, err = hex.DecodeString(inputStr) - if err != nil { - t.Errorf("TestFilterBloomMatch DecodeString failed: %v\n", err) - return - } - f.Add(hashBytes) - if !f.MatchTxAndUpdate(tx) { - t.Errorf("TestFilterBloomMatch didn't match input pubkey %s", inputStr) - } - - f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll) - inputStr = "04943fdd508053c75000106d3bc6e2754dbcff19" - hashBytes, err = hex.DecodeString(inputStr) - if err != nil { - t.Errorf("TestFilterBloomMatch DecodeString failed: %v\n", err) - return - } - f.Add(hashBytes) - if !f.MatchTxAndUpdate(tx) { - t.Errorf("TestFilterBloomMatch didn't match output address %s", inputStr) - } - if !f.MatchTxAndUpdate(spendingTx) { - t.Errorf("TestFilterBloomMatch spendingTx didn't match output address %s", inputStr) - } - - f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll) - inputStr = "a266436d2965547608b9e15d9032a7b9d64fa431" - hashBytes, err = hex.DecodeString(inputStr) - if err != nil { - t.Errorf("TestFilterBloomMatch DecodeString failed: %v\n", err) - return - } - f.Add(hashBytes) - if !f.MatchTxAndUpdate(tx) { - t.Errorf("TestFilterBloomMatch didn't match output address %s", inputStr) - } - - f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll) - inputStr = "90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b" - hash, err = chainhash.NewHashFromStr(inputStr) - if err != nil { - t.Errorf("TestFilterBloomMatch NewHashFromStr failed: %v\n", err) - return - } - outpoint := wire.NewOutPoint(hash, 0) - f.AddOutPoint(outpoint) - if !f.MatchTxAndUpdate(tx) { - t.Errorf("TestFilterBloomMatch didn't match outpoint %s", inputStr) - } - - f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll) - inputStr = "00000009e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436" - hash, err = chainhash.NewHashFromStr(inputStr) - if err != nil { - t.Errorf("TestFilterBloomMatch NewHashFromStr failed: %v\n", err) - return - } - f.AddHash(hash) - if f.MatchTxAndUpdate(tx) { - t.Errorf("TestFilterBloomMatch matched hash %s", inputStr) - } - - f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll) - inputStr = "0000006d2965547608b9e15d9032a7b9d64fa431" - hashBytes, err = hex.DecodeString(inputStr) - if err != nil { - t.Errorf("TestFilterBloomMatch DecodeString failed: %v\n", err) - return - } - f.Add(hashBytes) - if f.MatchTxAndUpdate(tx) { - t.Errorf("TestFilterBloomMatch matched address %s", inputStr) - } - - f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll) - inputStr = "90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b" - hash, err = chainhash.NewHashFromStr(inputStr) - if err != nil { - t.Errorf("TestFilterBloomMatch NewHashFromStr failed: %v\n", err) - return - } - outpoint = wire.NewOutPoint(hash, 1) - f.AddOutPoint(outpoint) - if f.MatchTxAndUpdate(tx) { - t.Errorf("TestFilterBloomMatch matched outpoint %s", inputStr) - } - - f = bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll) - inputStr = "000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b" - hash, err = chainhash.NewHashFromStr(inputStr) - if err != nil { - t.Errorf("TestFilterBloomMatch NewHashFromStr failed: %v\n", err) - return - } - outpoint = wire.NewOutPoint(hash, 0) - f.AddOutPoint(outpoint) - if f.MatchTxAndUpdate(tx) { - t.Errorf("TestFilterBloomMatch matched outpoint %s", inputStr) - } -} - -func TestFilterInsertUpdateNone(t *testing.T) { - f := bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateNone) - - // Add the generation pubkey - inputStr := "04eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c" + - "876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a" + - "2252247d97a46a91" - inputBytes, err := hex.DecodeString(inputStr) - if err != nil { - t.Errorf("TestFilterInsertUpdateNone DecodeString failed: %v", err) - return - } - f.Add(inputBytes) - - // Add the output address for the 4th transaction - inputStr = "b6efd80d99179f4f4ff6f4dd0a007d018c385d21" - inputBytes, err = hex.DecodeString(inputStr) - if err != nil { - t.Errorf("TestFilterInsertUpdateNone DecodeString failed: %v", err) - return - } - f.Add(inputBytes) - - inputStr = "147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b" - hash, err := chainhash.NewHashFromStr(inputStr) - if err != nil { - t.Errorf("TestFilterInsertUpdateNone NewHashFromStr failed: %v", err) - return - } - outpoint := wire.NewOutPoint(hash, 0) - - if f.MatchesOutPoint(outpoint) { - t.Errorf("TestFilterInsertUpdateNone matched outpoint %s", inputStr) - return - } - - inputStr = "02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041" - hash, err = chainhash.NewHashFromStr(inputStr) - if err != nil { - t.Errorf("TestFilterInsertUpdateNone NewHashFromStr failed: %v", err) - return - } - outpoint = wire.NewOutPoint(hash, 0) - - if f.MatchesOutPoint(outpoint) { - t.Errorf("TestFilterInsertUpdateNone matched outpoint %s", inputStr) - return - } -} - -func TestFilterInsertP2PubKeyOnly(t *testing.T) { - blockStr := "0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc" + - "880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367" + - "117b3c30c1f8fdd0d9728776381b4d4c86041b554b85290701000000010" + - "00000000000000000000000000000000000000000000000000000000000" + - "0000ffffffff07044c86041b0136ffffffff0100f2052a0100000043410" + - "4eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2" + - "c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a22522" + - "47d97a46a91ac000000000100000001bcad20a6a29827d1424f08989255" + - "120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356" + - "e834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062e" + - "a10221009253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa" + - "608cd5bab753901ffffffff02008d380c010000001976a9142b4b8072ec" + - "bba129b6453c63e129e643207249ca88ac0065cd1d000000001976a9141" + - "b8dd13b994bcfc787b32aeadf58ccb3615cbd5488ac0000000001000000" + - "03fdacf9b3eb077412e7a968d2e4f11b9a9dee312d666187ed77ee7d26a" + - "f16cb0b000000008c493046022100ea1608e70911ca0de5af51ba57ad23" + - "b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8acc8634c6" + - "b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf49e" + - "29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245b" + - "d69fcad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585" + - "caffffffff309e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e7" + - "5429df397b5af83000000004948304502202bdb79c596a9ffc24e96f438" + - "6199aba386e9bc7b6071516e2b51dda942b3a1ed022100c53a857e76b72" + - "4fc14d45311eac5019650d415c3abb5428f3aae16d8e69bec2301ffffff" + - "ff2089e33491695080c9edc18a428f7d834db5b6d372df13ce2b1b0e0cb" + - "cb1e6c10000000049483045022100d4ce67c5896ee251c810ac1ff9cecc" + - "d328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d31f11" + - "87779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff010071" + - "4460030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8" + - "d88ac0000000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397" + - "cde8dd08138f4b72a00681743447000000008b48304502200c45de8c4f3" + - "e2c1821f2fc878cba97b1e6f8807d94930713aa1c86a67b9bf1e4022100" + - "8581abfef2e30f957815fc89978423746b2086375ca8ecf359c85c2a5b7" + - "c88ad01410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf64852" + - "61c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d" + - "3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d2253d88e0f2" + - "48e29b599c80bbcec344a83dda5f9aa72c000000008a473044022078124" + - "c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e93" + - "0220691d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346" + - "669507a3601410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6" + - "485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270e" + - "fb1d3ae37079b794a92d7ec95fffffffff878af0d93f5229a68166cf051" + - "fd372bb7a537232946e0a46f53636b4dafdaa4000000008c49304602210" + - "0c717d1714551663f69c3c5759bdbb3a0fcd3fab023abc0e522fe6440de" + - "35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7fe1c2e7b46f" + - "c37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561f5c0" + - "f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f" + - "4d87270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f8" + - "94aa0fd2d9e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493" + - "046022100e00847147cbf517bcc2f502f3ddc6d284358d102ed20d47a8a" + - "a788a62f0db780022100d17b2d6fa84dcaf1c95d88d7e7c30385aecf415" + - "588d749afd3ec81f6022cecd701410462bb73f76ca0994fcb8b4271e6fb" + - "7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018" + - "ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff0100c817a8" + - "040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c385d2188a" + - "c000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758d" + - "f616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34" + - "fdce11eeb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243" + - "bd399ff96b649a0fad05fa759d6a882f0af8c90cf7632c2840c29070aec" + - "20141045e58067e815c2f464c6a2a15f987758374203895710c2d452442" + - "e28496ff38ba8f5fd901dc20e29e88477167fe4fc299bf818fd0d9e1632" + - "d467b2a3d9503b1aaffffffff0280d7e636030000001976a914f34c3e10" + - "eb387efe872acb614c89e78bfca7815d88ac404b4c00000000001976a91" + - "4a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac00000000010000" + - "000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d850" + - "927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec" + - "2c1ac1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a25" + - "7b5c63ebd90f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e5" + - "21fa7946d12edbb1d1e95a15c34bd4398195e86433c92b431cd315f455f" + - "e30032ede69cad9d1e1ed6c3c4ec0dbfced53438c625462afb792dcb098" + - "544bffffffff0240420f00000000001976a9144676d1b820d63ec272f19" + - "00d59d43bc6463d96f888ac40420f00000000001976a914648d04341d00" + - "d7968b3405c034adc38d4d8fb9bd88ac00000000010000000248cc91750" + - "1ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3f10000" + - "00008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e" + - "280007b52b133021acd9acc02205e325d613e555f772802bf413d36ba80" + - "7892ed1a690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b" + - "5557b2c0b9df7b2b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c4" + - "7d7b173dbc9db8d37db0a33ae487982c59c6f8606e9d1791ffffffff41e" + - "d70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d0" + - "68000000008b4830450221008513ad65187b903aed1102d1d0c47688127" + - "658c51106753fed0151ce9c16b80902201432b9ebcb87bd04ceb2de6603" + - "5fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf80125bf50b" + - "e1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b0682" + - "0edca9ef982c35fda2d255afba340068c5035552368bc7200c1488fffff" + - "fff0100093d00000000001976a9148edb68822f1ad580b043c7b3df2e40" + - "0f8699eb4888ac00000000" - blockBytes, err := hex.DecodeString(blockStr) - if err != nil { - t.Errorf("TestFilterInsertP2PubKeyOnly DecodeString failed: %v", err) - return - } - block, err := btcutil.NewBlockFromBytes(blockBytes) - if err != nil { - t.Errorf("TestFilterInsertP2PubKeyOnly NewBlockFromBytes failed: %v", err) - return - } - - f := bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateP2PubkeyOnly) - - // Generation pubkey - inputStr := "04eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c" + - "876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a" + - "2252247d97a46a91" - inputBytes, err := hex.DecodeString(inputStr) - if err != nil { - t.Errorf("TestFilterInsertP2PubKeyOnly DecodeString failed: %v", err) - return - } - f.Add(inputBytes) - - // Output address of 4th transaction - inputStr = "b6efd80d99179f4f4ff6f4dd0a007d018c385d21" - inputBytes, err = hex.DecodeString(inputStr) - if err != nil { - t.Errorf("TestFilterInsertP2PubKeyOnly DecodeString failed: %v", err) - return - } - f.Add(inputBytes) - - // Ignore return value -- this is just used to update the filter. - _, _ = bloom.NewMerkleBlock(block, f) - - // We should match the generation pubkey - inputStr = "147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b" - hash, err := chainhash.NewHashFromStr(inputStr) - if err != nil { - t.Errorf("TestMerkleBlockP2PubKeyOnly NewHashFromStr failed: %v", err) - return - } - outpoint := wire.NewOutPoint(hash, 0) - if !f.MatchesOutPoint(outpoint) { - t.Errorf("TestMerkleBlockP2PubKeyOnly didn't match the generation "+ - "outpoint %s", inputStr) - return - } - - // We should not match the 4th transaction, which is not p2pk - inputStr = "02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041" - hash, err = chainhash.NewHashFromStr(inputStr) - if err != nil { - t.Errorf("TestMerkleBlockP2PubKeyOnly NewHashFromStr failed: %v", err) - return - } - outpoint = wire.NewOutPoint(hash, 0) - if f.MatchesOutPoint(outpoint) { - t.Errorf("TestMerkleBlockP2PubKeyOnly matched outpoint %s", inputStr) - return - } -} - -func TestFilterReload(t *testing.T) { - f := bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll) - - bFilter := bloom.LoadFilter(f.MsgFilterLoad()) - if bFilter.MsgFilterLoad() == nil { - t.Errorf("TestFilterReload LoadFilter test failed") - return - } - bFilter.Reload(nil) - - if bFilter.MsgFilterLoad() != nil { - t.Errorf("TestFilterReload Reload test failed") - } -} diff --git a/bloom/merkleblock.go b/bloom/merkleblock.go deleted file mode 100644 index b1da716d7..000000000 --- a/bloom/merkleblock.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package bloom - -import ( - "github.com/btcsuite/btcd/blockchain" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" -) - -// merkleBlock is used to house intermediate information needed to generate a -// wire.MsgMerkleBlock according to a filter. -type merkleBlock struct { - numTx uint32 - allHashes []*chainhash.Hash - finalHashes []*chainhash.Hash - matchedBits []byte - bits []byte -} - -// calcTreeWidth calculates and returns the the number of nodes (width) or a -// merkle tree at the given depth-first height. -func (m *merkleBlock) calcTreeWidth(height uint32) uint32 { - return (m.numTx + (1 << height) - 1) >> height -} - -// calcHash returns the hash for a sub-tree given a depth-first height and -// node position. -func (m *merkleBlock) calcHash(height, pos uint32) *chainhash.Hash { - if height == 0 { - return m.allHashes[pos] - } - - var right *chainhash.Hash - left := m.calcHash(height-1, pos*2) - if pos*2+1 < m.calcTreeWidth(height-1) { - right = m.calcHash(height-1, pos*2+1) - } else { - right = left - } - return blockchain.HashMerkleBranches(left, right) -} - -// traverseAndBuild builds a partial merkle tree using a recursive depth-first -// approach. As it calculates the hashes, it also saves whether or not each -// node is a parent node and a list of final hashes to be included in the -// merkle block. -func (m *merkleBlock) traverseAndBuild(height, pos uint32) { - // Determine whether this node is a parent of a matched node. - var isParent byte - for i := pos << height; i < (pos+1)< 1 { - height++ - } - - // Build the depth-first partial merkle tree. - mBlock.traverseAndBuild(height, 0) - - // Create and return the merkle block. - msgMerkleBlock := wire.MsgMerkleBlock{ - Header: block.MsgBlock().Header, - Transactions: mBlock.numTx, - Hashes: make([]*chainhash.Hash, 0, len(mBlock.finalHashes)), - Flags: make([]byte, (len(mBlock.bits)+7)/8), - } - for _, hash := range mBlock.finalHashes { - _ = msgMerkleBlock.AddTxHash(hash) - } - for i := uint32(0); i < uint32(len(mBlock.bits)); i++ { - msgMerkleBlock.Flags[i/8] |= mBlock.bits[i] << (i % 8) - } - return &msgMerkleBlock, matchedIndices -} diff --git a/bloom/merkleblock_test.go b/bloom/merkleblock_test.go deleted file mode 100644 index bd4a3dcb0..000000000 --- a/bloom/merkleblock_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package bloom_test - -import ( - "bytes" - "encoding/hex" - "testing" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/bloom" -) - -func TestMerkleBlock3(t *testing.T) { - blockStr := "0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b" + - "4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdc" + - "c96b2c3ff60abe184f196367291b4d4c86041b8fa45d630101000000010" + - "00000000000000000000000000000000000000000000000000000000000" + - "0000ffffffff08044c86041b020a02ffffffff0100f2052a01000000434" + - "104ecd3229b0571c3be876feaac0442a9f13c5a572742927af1dc623353" + - "ecf8c202225f64868137a18cdd85cbbb4c74fbccfd4f49639cf1bdc94a5" + - "672bb15ad5d4cac00000000" - blockBytes, err := hex.DecodeString(blockStr) - if err != nil { - t.Errorf("TestMerkleBlock3 DecodeString failed: %v", err) - return - } - blk, err := btcutil.NewBlockFromBytes(blockBytes) - if err != nil { - t.Errorf("TestMerkleBlock3 NewBlockFromBytes failed: %v", err) - return - } - - f := bloom.NewFilter(10, 0, 0.000001, wire.BloomUpdateAll) - - inputStr := "63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5" - hash, err := chainhash.NewHashFromStr(inputStr) - if err != nil { - t.Errorf("TestMerkleBlock3 NewHashFromStr failed: %v", err) - return - } - - f.AddHash(hash) - - mBlock, _ := bloom.NewMerkleBlock(blk, f) - - wantStr := "0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4" + - "b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc" + - "96b2c3ff60abe184f196367291b4d4c86041b8fa45d630100000001b50c" + - "c069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196" + - "30101" - want, err := hex.DecodeString(wantStr) - if err != nil { - t.Errorf("TestMerkleBlock3 DecodeString failed: %v", err) - return - } - - got := bytes.NewBuffer(nil) - err = mBlock.BtcEncode(got, wire.ProtocolVersion, wire.LatestEncoding) - if err != nil { - t.Errorf("TestMerkleBlock3 BtcEncode failed: %v", err) - return - } - - if !bytes.Equal(want, got.Bytes()) { - t.Errorf("TestMerkleBlock3 failed merkle block comparison: "+ - "got %v want %v", got.Bytes(), want) - return - } -} diff --git a/bloom/murmurhash3.go b/bloom/murmurhash3.go deleted file mode 100644 index 6bb562ecb..000000000 --- a/bloom/murmurhash3.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2013, 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package bloom - -import ( - "encoding/binary" -) - -// The following constants are used by the MurmurHash3 algorithm. -const ( - murmurC1 = 0xcc9e2d51 - murmurC2 = 0x1b873593 - murmurR1 = 15 - murmurR2 = 13 - murmurM = 5 - murmurN = 0xe6546b64 -) - -// MurmurHash3 implements a non-cryptographic hash function using the -// MurmurHash3 algorithm. This implementation yields a 32-bit hash value which -// is suitable for general hash-based lookups. The seed can be used to -// effectively randomize the hash function. This makes it ideal for use in -// bloom filters which need multiple independent hash functions. -func MurmurHash3(seed uint32, data []byte) uint32 { - dataLen := uint32(len(data)) - hash := seed - k := uint32(0) - numBlocks := dataLen / 4 - - // Calculate the hash in 4-byte chunks. - for i := uint32(0); i < numBlocks; i++ { - k = binary.LittleEndian.Uint32(data[i*4:]) - k *= murmurC1 - k = (k << murmurR1) | (k >> (32 - murmurR1)) - k *= murmurC2 - - hash ^= k - hash = (hash << murmurR2) | (hash >> (32 - murmurR2)) - hash = hash*murmurM + murmurN - } - - // Handle remaining bytes. - tailIdx := numBlocks * 4 - k = 0 - - switch dataLen & 3 { - case 3: - k ^= uint32(data[tailIdx+2]) << 16 - fallthrough - case 2: - k ^= uint32(data[tailIdx+1]) << 8 - fallthrough - case 1: - k ^= uint32(data[tailIdx]) - k *= murmurC1 - k = (k << murmurR1) | (k >> (32 - murmurR1)) - k *= murmurC2 - hash ^= k - } - - // Finalization. - hash ^= dataLen - hash ^= hash >> 16 - hash *= 0x85ebca6b - hash ^= hash >> 13 - hash *= 0xc2b2ae35 - hash ^= hash >> 16 - - return hash -} diff --git a/bloom/murmurhash3_test.go b/bloom/murmurhash3_test.go deleted file mode 100644 index a637b24fd..000000000 --- a/bloom/murmurhash3_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2013, 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package bloom_test - -import ( - "testing" - - "github.com/btcsuite/btcutil/bloom" -) - -// TestMurmurHash3 ensure the MurmurHash3 function produces the correct hash -// when given various seeds and data. -func TestMurmurHash3(t *testing.T) { - var tests = []struct { - seed uint32 - data []byte - out uint32 - }{ - {0x00000000, []byte{}, 0x00000000}, - {0xfba4c795, []byte{}, 0x6a396f08}, - {0xffffffff, []byte{}, 0x81f16f39}, - {0x00000000, []byte{0x00}, 0x514e28b7}, - {0xfba4c795, []byte{0x00}, 0xea3f0b17}, - {0x00000000, []byte{0xff}, 0xfd6cf10d}, - {0x00000000, []byte{0x00, 0x11}, 0x16c6b7ab}, - {0x00000000, []byte{0x00, 0x11, 0x22}, 0x8eb51c3d}, - {0x00000000, []byte{0x00, 0x11, 0x22, 0x33}, 0xb4471bf8}, - {0x00000000, []byte{0x00, 0x11, 0x22, 0x33, 0x44}, 0xe2301fa8}, - {0x00000000, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, 0xfc2e4a15}, - {0x00000000, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}, 0xb074502c}, - {0x00000000, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}, 0x8034d2a0}, - {0x00000000, []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, 0xb4698def}, - } - - for i, test := range tests { - result := bloom.MurmurHash3(test.seed, test.data) - if result != test.out { - t.Errorf("MurmurHash3 test #%d failed: got %v want %v\n", - i, result, test.out) - continue - } - } -} diff --git a/bloom/test_coverage.txt b/bloom/test_coverage.txt deleted file mode 100644 index e503f26fe..000000000 --- a/bloom/test_coverage.txt +++ /dev/null @@ -1,28 +0,0 @@ - -github.com/conformal/btcutil/bloom/murmurhash3.go MurmurHash3 100.00% (31/31) -github.com/conformal/btcutil/bloom/merkleblock.go NewMerkleBlock 100.00% (19/19) -github.com/conformal/btcutil/bloom/merkleblock.go merkleBlock.traverseAndBuild 100.00% (10/10) -github.com/conformal/btcutil/bloom/merkleblock.go merkleBlock.calcHash 100.00% (8/8) -github.com/conformal/btcutil/bloom/filter.go Filter.maybeAddOutpoint 100.00% (7/7) -github.com/conformal/btcutil/bloom/filter.go Filter.addOutPoint 100.00% (4/4) -github.com/conformal/btcutil/bloom/filter.go Filter.IsLoaded 100.00% (4/4) -github.com/conformal/btcutil/bloom/filter.go Filter.MsgFilterLoad 100.00% (4/4) -github.com/conformal/btcutil/bloom/filter.go Filter.matchesOutPoint 100.00% (4/4) -github.com/conformal/btcutil/bloom/filter.go Filter.MatchesOutPoint 100.00% (4/4) -github.com/conformal/btcutil/bloom/filter.go Filter.MatchTxAndUpdate 100.00% (4/4) -github.com/conformal/btcutil/bloom/filter.go Filter.Matches 100.00% (4/4) -github.com/conformal/btcutil/bloom/filter.go Filter.Add 100.00% (3/3) -github.com/conformal/btcutil/bloom/filter.go Filter.Reload 100.00% (3/3) -github.com/conformal/btcutil/bloom/filter.go Filter.Unload 100.00% (3/3) -github.com/conformal/btcutil/bloom/filter.go Filter.AddShaHash 100.00% (3/3) -github.com/conformal/btcutil/bloom/filter.go Filter.AddOutPoint 100.00% (3/3) -github.com/conformal/btcutil/bloom/filter.go minUint32 100.00% (3/3) -github.com/conformal/btcutil/bloom/filter.go Filter.hash 100.00% (2/2) -github.com/conformal/btcutil/bloom/merkleblock.go merkleBlock.calcTreeWidth 100.00% (1/1) -github.com/conformal/btcutil/bloom/filter.go LoadFilter 100.00% (1/1) -github.com/conformal/btcutil/bloom/filter.go Filter.matchTxAndUpdate 91.30% (21/23) -github.com/conformal/btcutil/bloom/filter.go Filter.matches 85.71% (6/7) -github.com/conformal/btcutil/bloom/filter.go NewFilter 81.82% (9/11) -github.com/conformal/btcutil/bloom/filter.go Filter.add 80.00% (4/5) -github.com/conformal/btcutil/bloom ---------------------------- 96.49% (165/171) - diff --git a/certgen.go b/certgen.go deleted file mode 100644 index 52e2d9cc9..000000000 --- a/certgen.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2013-2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil - -import ( - "bytes" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - _ "crypto/sha512" // Needed for RegisterHash in init - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "errors" - "fmt" - "math/big" - "net" - "os" - "time" -) - -// NewTLSCertPair returns a new PEM-encoded x.509 certificate pair -// based on a 521-bit ECDSA private key. The machine's local interface -// addresses and all variants of IPv4 and IPv6 localhost are included as -// valid IP addresses. -func NewTLSCertPair(organization string, validUntil time.Time, extraHosts []string) (cert, key []byte, err error) { - now := time.Now() - if validUntil.Before(now) { - return nil, nil, errors.New("validUntil would create an already-expired certificate") - } - - priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - if err != nil { - return nil, nil, err - } - - // end of ASN.1 time - endOfTime := time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC) - if validUntil.After(endOfTime) { - validUntil = endOfTime - } - - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - return nil, nil, fmt.Errorf("failed to generate serial number: %s", err) - } - - host, err := os.Hostname() - if err != nil { - return nil, nil, err - } - - ipAddresses := []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")} - dnsNames := []string{host} - if host != "localhost" { - dnsNames = append(dnsNames, "localhost") - } - - addIP := func(ipAddr net.IP) { - for _, ip := range ipAddresses { - if ip.Equal(ipAddr) { - return - } - } - ipAddresses = append(ipAddresses, ipAddr) - } - addHost := func(host string) { - for _, dnsName := range dnsNames { - if host == dnsName { - return - } - } - dnsNames = append(dnsNames, host) - } - - addrs, err := interfaceAddrs() - if err != nil { - return nil, nil, err - } - for _, a := range addrs { - ipAddr, _, err := net.ParseCIDR(a.String()) - if err == nil { - addIP(ipAddr) - } - } - - for _, hostStr := range extraHosts { - host, _, err := net.SplitHostPort(hostStr) - if err != nil { - host = hostStr - } - if ip := net.ParseIP(host); ip != nil { - addIP(ip) - } else { - addHost(host) - } - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - Organization: []string{organization}, - CommonName: host, - }, - NotBefore: now.Add(-time.Hour * 24), - NotAfter: validUntil, - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | - x509.KeyUsageCertSign, - IsCA: true, // so can sign self. - BasicConstraintsValid: true, - - DNSNames: dnsNames, - IPAddresses: ipAddresses, - } - - derBytes, err := x509.CreateCertificate(rand.Reader, &template, - &template, &priv.PublicKey, priv) - if err != nil { - return nil, nil, fmt.Errorf("failed to create certificate: %v", err) - } - - certBuf := &bytes.Buffer{} - err = pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - if err != nil { - return nil, nil, fmt.Errorf("failed to encode certificate: %v", err) - } - - keybytes, err := x509.MarshalECPrivateKey(priv) - if err != nil { - return nil, nil, fmt.Errorf("failed to marshal private key: %v", err) - } - - keyBuf := &bytes.Buffer{} - err = pem.Encode(keyBuf, &pem.Block{Type: "EC PRIVATE KEY", Bytes: keybytes}) - if err != nil { - return nil, nil, fmt.Errorf("failed to encode private key: %v", err) - } - - return certBuf.Bytes(), keyBuf.Bytes(), nil -} diff --git a/certgen_test.go b/certgen_test.go deleted file mode 100644 index f9e2c95ec..000000000 --- a/certgen_test.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2013-2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil_test - -import ( - "crypto/x509" - "encoding/pem" - "net" - "testing" - "time" - - "github.com/btcsuite/btcutil" - //"github.com/davecgh/go-spew/spew" -) - -// TestNewTLSCertPair ensures the NewTLSCertPair function works as expected. -func TestNewTLSCertPair(t *testing.T) { - // Certs don't support sub-second precision, so truncate it now to - // ensure the checks later don't fail due to nanosecond precision - // differences. - validUntil := time.Unix(time.Now().Add(10*365*24*time.Hour).Unix(), 0) - org := "test autogenerated cert" - extraHosts := []string{"testtlscert.bogus", "localhost", "127.0.0.1"} - cert, key, err := btcutil.NewTLSCertPair(org, validUntil, extraHosts) - if err != nil { - t.Fatalf("failed with unexpected error: %v", err) - } - - // Ensure the PEM-encoded cert that is returned can be decoded. - pemCert, _ := pem.Decode(cert) - if pemCert == nil { - t.Fatalf("pem.Decode was unable to decode the certificate") - } - - // Ensure the PEM-encoded key that is returned can be decoded. - pemKey, _ := pem.Decode(key) - if pemCert == nil { - t.Fatalf("pem.Decode was unable to decode the key") - } - - // Ensure the DER-encoded key bytes can be successfully parsed. - _, err = x509.ParseECPrivateKey(pemKey.Bytes) - if err != nil { - t.Fatalf("failed with unexpected error: %v", err) - } - - // Ensure the DER-encoded cert bytes can be successfully into an X.509 - // certificate. - x509Cert, err := x509.ParseCertificate(pemCert.Bytes) - if err != nil { - t.Fatalf("failed with unexpected error: %v", err) - } - - // Ensure the specified organization is correct. - x509Orgs := x509Cert.Subject.Organization - if len(x509Orgs) == 0 || x509Orgs[0] != org { - x509Org := "" - if len(x509Orgs) > 0 { - x509Org = x509Orgs[0] - } - t.Fatalf("generated cert organization field mismatch, got "+ - "'%v', want '%v'", x509Org, org) - } - - // Ensure the specified valid until value is correct. - if !x509Cert.NotAfter.Equal(validUntil) { - t.Fatalf("generated cert valid until field mismatch, got %v, "+ - "want %v", x509Cert.NotAfter, validUntil) - } - - // Ensure the specified extra hosts are present. - for _, host := range extraHosts { - if err := x509Cert.VerifyHostname(host); err != nil { - t.Fatalf("failed to verify extra host '%s'", host) - } - } - - // Ensure that the Common Name is also the first SAN DNS name. - cn := x509Cert.Subject.CommonName - san0 := x509Cert.DNSNames[0] - if cn != san0 { - t.Errorf("common name %s does not match first SAN %s", cn, san0) - } - - // Ensure there are no duplicate hosts or IPs. - hostCounts := make(map[string]int) - for _, host := range x509Cert.DNSNames { - hostCounts[host]++ - } - ipCounts := make(map[string]int) - for _, ip := range x509Cert.IPAddresses { - ipCounts[string(ip)]++ - } - for host, count := range hostCounts { - if count != 1 { - t.Errorf("host %s appears %d times in certificate", host, count) - } - } - for ipStr, count := range ipCounts { - if count != 1 { - t.Errorf("ip %s appears %d times in certificate", net.IP(ipStr), count) - } - } - - // Ensure the cert can be use for the intended purposes. - if !x509Cert.IsCA { - t.Fatal("generated cert is not a certificate authority") - } - if x509Cert.KeyUsage&x509.KeyUsageKeyEncipherment == 0 { - t.Fatal("generated cert can't be used for key encipherment") - } - if x509Cert.KeyUsage&x509.KeyUsageDigitalSignature == 0 { - t.Fatal("generated cert can't be used for digital signatures") - } - if x509Cert.KeyUsage&x509.KeyUsageCertSign == 0 { - t.Fatal("generated cert can't be used for signing other certs") - } - if !x509Cert.BasicConstraintsValid { - t.Fatal("generated cert does not have valid basic constraints") - } -} diff --git a/coinset/README.md b/coinset/README.md deleted file mode 100644 index fd05f8c8f..000000000 --- a/coinset/README.md +++ /dev/null @@ -1,71 +0,0 @@ -coinset -======= - -[![Build Status](http://img.shields.io/travis/btcsuite/btcutil.svg)](https://travis-ci.org/btcsuite/btcutil) -[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) -[![GoDoc](http://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcutil/coinset) - -Package coinset provides bitcoin-specific convenience functions for selecting -from and managing sets of unspent transaction outpoints (UTXOs). - -A comprehensive suite of tests is provided to ensure proper functionality. See -`test_coverage.txt` for the gocov coverage report. Alternatively, if you are -running a POSIX OS, you can run the `cov_report.sh` script for a real-time -report. - -## Installation and Updating - -```bash -$ go get -u github.com/btcsuite/btcutil/coinset -``` - -## Usage - -Each unspent transaction outpoint is represented by the Coin interface. An -example of a concrete type that implements Coin is coinset.SimpleCoin. - -The typical use case for this library is for creating raw bitcoin transactions -given a set of Coins that may be spent by the user, for example as below: - -```Go -var unspentCoins = []coinset.Coin{ ... } -``` - -When the user needs to spend a certain amount, they will need to select a -subset of these coins which contain at least that value. CoinSelector is -an interface that represents types that implement coin selection algos, -subject to various criteria. There are a few examples of CoinSelector's: - -- MinIndexCoinSelector - -- MinNumberCoinSelector - -- MaxValueAgeCoinSelector - -- MinPriorityCoinSelector - -For example, if the user wishes to maximize the probability that their -transaction is mined quickly, they could use the MaxValueAgeCoinSelector to -select high priority coins, then also attach a relatively high fee. - -```Go -selector := &coinset.MaxValueAgeCoinSelector{ - MaxInputs: 10, - MinAmountChange: 10000, -} -selectedCoins, err := selector.CoinSelect(targetAmount + bigFee, unspentCoins) -if err != nil { - return err -} -msgTx := coinset.NewMsgTxWithInputCoins(selectedCoins) -... - -``` - -The user can then create the msgTx.TxOut's as required, then sign the -transaction and transmit it to the network. - -## License - -Package coinset is licensed under the [copyfree](http://copyfree.org) ISC -License. diff --git a/coinset/coins.go b/coinset/coins.go deleted file mode 100644 index 177791829..000000000 --- a/coinset/coins.go +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package coinset - -import ( - "container/list" - "errors" - "sort" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" -) - -// Coin represents a spendable transaction outpoint -type Coin interface { - Hash() *chainhash.Hash - Index() uint32 - Value() btcutil.Amount - PkScript() []byte - NumConfs() int64 - ValueAge() int64 -} - -// Coins represents a set of Coins -type Coins interface { - Coins() []Coin -} - -// CoinSet is a utility struct for the modifications of a set of -// Coins that implements the Coins interface. To create a CoinSet, -// you must call NewCoinSet with nil for an empty set or a slice of -// coins as the initial contents. -// -// It is important to note that the all the Coins being added or removed -// from a CoinSet must have a constant ValueAge() during the use of -// the CoinSet, otherwise the cached values will be incorrect. -type CoinSet struct { - coinList *list.List - totalValue btcutil.Amount - totalValueAge int64 -} - -// Ensure that CoinSet is a Coins -var _ Coins = NewCoinSet(nil) - -// NewCoinSet creates a CoinSet containing the coins provided. -// To create an empty CoinSet, you may pass null as the coins input parameter. -func NewCoinSet(coins []Coin) *CoinSet { - newCoinSet := &CoinSet{ - coinList: list.New(), - totalValue: 0, - totalValueAge: 0, - } - for _, coin := range coins { - newCoinSet.PushCoin(coin) - } - return newCoinSet -} - -// Coins returns a new slice of the coins contained in the set. -func (cs *CoinSet) Coins() []Coin { - coins := make([]Coin, cs.coinList.Len()) - for i, e := 0, cs.coinList.Front(); e != nil; i, e = i+1, e.Next() { - coins[i] = e.Value.(Coin) - } - return coins -} - -// TotalValue returns the total value of the coins in the set. -func (cs *CoinSet) TotalValue() (value btcutil.Amount) { - return cs.totalValue -} - -// TotalValueAge returns the total value * number of confirmations -// of the coins in the set. -func (cs *CoinSet) TotalValueAge() (valueAge int64) { - return cs.totalValueAge -} - -// Num returns the number of coins in the set -func (cs *CoinSet) Num() int { - return cs.coinList.Len() -} - -// PushCoin adds a coin to the end of the list and updates -// the cached value amounts. -func (cs *CoinSet) PushCoin(c Coin) { - cs.coinList.PushBack(c) - cs.totalValue += c.Value() - cs.totalValueAge += c.ValueAge() -} - -// PopCoin removes the last coin on the list and returns it. -func (cs *CoinSet) PopCoin() Coin { - back := cs.coinList.Back() - if back == nil { - return nil - } - return cs.removeElement(back) -} - -// ShiftCoin removes the first coin on the list and returns it. -func (cs *CoinSet) ShiftCoin() Coin { - front := cs.coinList.Front() - if front == nil { - return nil - } - return cs.removeElement(front) -} - -// removeElement updates the cached value amounts in the CoinSet, -// removes the element from the list, then returns the Coin that -// was removed to the caller. -func (cs *CoinSet) removeElement(e *list.Element) Coin { - c := e.Value.(Coin) - cs.coinList.Remove(e) - cs.totalValue -= c.Value() - cs.totalValueAge -= c.ValueAge() - return c -} - -// NewMsgTxWithInputCoins takes the coins in the CoinSet and makes them -// the inputs to a new wire.MsgTx which is returned. -func NewMsgTxWithInputCoins(txVersion int32, inputCoins Coins) *wire.MsgTx { - msgTx := wire.NewMsgTx(txVersion) - coins := inputCoins.Coins() - msgTx.TxIn = make([]*wire.TxIn, len(coins)) - for i, coin := range coins { - msgTx.TxIn[i] = &wire.TxIn{ - PreviousOutPoint: wire.OutPoint{ - Hash: *coin.Hash(), - Index: coin.Index(), - }, - SignatureScript: nil, - Sequence: wire.MaxTxInSequenceNum, - } - } - return msgTx -} - -var ( - // ErrCoinsNoSelectionAvailable is returned when a CoinSelector believes there is no - // possible combination of coins which can meet the requirements provided to the selector. - ErrCoinsNoSelectionAvailable = errors.New("no coin selection possible") -) - -// satisfiesTargetValue checks that the totalValue is either exactly the targetValue -// or is greater than the targetValue by at least the minChange amount. -func satisfiesTargetValue(targetValue, minChange, totalValue btcutil.Amount) bool { - return (totalValue == targetValue || totalValue >= targetValue+minChange) -} - -// CoinSelector is an interface that wraps the CoinSelect method. -// -// CoinSelect will attempt to select a subset of the coins which has at -// least the targetValue amount. CoinSelect is not guaranteed to return a -// selection of coins even if the total value of coins given is greater -// than the target value. -// -// The exact choice of coins in the subset will be implementation specific. -// -// It is important to note that the Coins being used as inputs need to have -// a constant ValueAge() during the execution of CoinSelect. -type CoinSelector interface { - CoinSelect(targetValue btcutil.Amount, coins []Coin) (Coins, error) -} - -// MinIndexCoinSelector is a CoinSelector that attempts to construct a -// selection of coins whose total value is at least targetValue and prefers -// any number of lower indexes (as in the ordered array) over higher ones. -type MinIndexCoinSelector struct { - MaxInputs int - MinChangeAmount btcutil.Amount -} - -// CoinSelect will attempt to select coins using the algorithm described -// in the MinIndexCoinSelector struct. -func (s MinIndexCoinSelector) CoinSelect(targetValue btcutil.Amount, coins []Coin) (Coins, error) { - cs := NewCoinSet(nil) - for n := 0; n < len(coins) && n < s.MaxInputs; n++ { - cs.PushCoin(coins[n]) - if satisfiesTargetValue(targetValue, s.MinChangeAmount, cs.TotalValue()) { - return cs, nil - } - } - return nil, ErrCoinsNoSelectionAvailable -} - -// MinNumberCoinSelector is a CoinSelector that attempts to construct -// a selection of coins whose total value is at least targetValue -// that uses as few of the inputs as possible. -type MinNumberCoinSelector struct { - MaxInputs int - MinChangeAmount btcutil.Amount -} - -// CoinSelect will attempt to select coins using the algorithm described -// in the MinNumberCoinSelector struct. -func (s MinNumberCoinSelector) CoinSelect(targetValue btcutil.Amount, coins []Coin) (Coins, error) { - sortedCoins := make([]Coin, 0, len(coins)) - sortedCoins = append(sortedCoins, coins...) - sort.Sort(sort.Reverse(byAmount(sortedCoins))) - - return MinIndexCoinSelector(s).CoinSelect(targetValue, sortedCoins) -} - -// MaxValueAgeCoinSelector is a CoinSelector that attempts to construct -// a selection of coins whose total value is at least targetValue -// that has as much input value-age as possible. -// -// This would be useful in the case where you want to maximize -// likelihood of the inclusion of your transaction in the next mined -// block. -type MaxValueAgeCoinSelector struct { - MaxInputs int - MinChangeAmount btcutil.Amount -} - -// CoinSelect will attempt to select coins using the algorithm described -// in the MaxValueAgeCoinSelector struct. -func (s MaxValueAgeCoinSelector) CoinSelect(targetValue btcutil.Amount, coins []Coin) (Coins, error) { - sortedCoins := make([]Coin, 0, len(coins)) - sortedCoins = append(sortedCoins, coins...) - sort.Sort(sort.Reverse(byValueAge(sortedCoins))) - - return MinIndexCoinSelector(s).CoinSelect(targetValue, sortedCoins) -} - -// MinPriorityCoinSelector is a CoinSelector that attempts to construct -// a selection of coins whose total value is at least targetValue and -// whose average value-age per input is greater than MinAvgValueAgePerInput. -// If there is change, it must exceed MinChangeAmount to be a valid selection. -// -// When possible, MinPriorityCoinSelector will attempt to reduce the average -// input priority over the threshold, but no guarantees will be made as to -// minimality of the selection. The selection below is almost certainly -// suboptimal. -// -type MinPriorityCoinSelector struct { - MaxInputs int - MinChangeAmount btcutil.Amount - MinAvgValueAgePerInput int64 -} - -// CoinSelect will attempt to select coins using the algorithm described -// in the MinPriorityCoinSelector struct. -func (s MinPriorityCoinSelector) CoinSelect(targetValue btcutil.Amount, coins []Coin) (Coins, error) { - possibleCoins := make([]Coin, 0, len(coins)) - possibleCoins = append(possibleCoins, coins...) - - sort.Sort(byValueAge(possibleCoins)) - - // find the first coin with sufficient valueAge - cutoffIndex := -1 - for i := 0; i < len(possibleCoins); i++ { - if possibleCoins[i].ValueAge() >= s.MinAvgValueAgePerInput { - cutoffIndex = i - break - } - } - if cutoffIndex < 0 { - return nil, ErrCoinsNoSelectionAvailable - } - - // create sets of input coins that will obey minimum average valueAge - for i := cutoffIndex; i < len(possibleCoins); i++ { - possibleHighCoins := possibleCoins[cutoffIndex : i+1] - - // choose a set of high-enough valueAge coins - highSelect, err := (&MinNumberCoinSelector{ - MaxInputs: s.MaxInputs, - MinChangeAmount: s.MinChangeAmount, - }).CoinSelect(targetValue, possibleHighCoins) - - if err != nil { - // attempt to add available low priority to make a solution - - for numLow := 1; numLow <= cutoffIndex && numLow+(i-cutoffIndex) <= s.MaxInputs; numLow++ { - allHigh := NewCoinSet(possibleCoins[cutoffIndex : i+1]) - newTargetValue := targetValue - allHigh.TotalValue() - newMaxInputs := allHigh.Num() + numLow - if newMaxInputs > numLow { - newMaxInputs = numLow - } - newMinAvgValueAge := ((s.MinAvgValueAgePerInput * int64(allHigh.Num()+numLow)) - allHigh.TotalValueAge()) / int64(numLow) - - // find the minimum priority that can be added to set - lowSelect, err := (&MinPriorityCoinSelector{ - MaxInputs: newMaxInputs, - MinChangeAmount: s.MinChangeAmount, - MinAvgValueAgePerInput: newMinAvgValueAge, - }).CoinSelect(newTargetValue, possibleCoins[0:cutoffIndex]) - - if err != nil { - continue - } - - for _, coin := range lowSelect.Coins() { - allHigh.PushCoin(coin) - } - - return allHigh, nil - } - // oh well, couldn't fix, try to add more high priority to the set. - } else { - extendedCoins := NewCoinSet(highSelect.Coins()) - - // attempt to lower priority towards target with lowest ones first - for n := 0; n < cutoffIndex; n++ { - if extendedCoins.Num() >= s.MaxInputs { - break - } - if possibleCoins[n].ValueAge() == 0 { - continue - } - - extendedCoins.PushCoin(possibleCoins[n]) - if extendedCoins.TotalValueAge()/int64(extendedCoins.Num()) < s.MinAvgValueAgePerInput { - extendedCoins.PopCoin() - continue - } - } - return extendedCoins, nil - } - } - - return nil, ErrCoinsNoSelectionAvailable -} - -type byValueAge []Coin - -func (a byValueAge) Len() int { return len(a) } -func (a byValueAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byValueAge) Less(i, j int) bool { return a[i].ValueAge() < a[j].ValueAge() } - -type byAmount []Coin - -func (a byAmount) Len() int { return len(a) } -func (a byAmount) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byAmount) Less(i, j int) bool { return a[i].Value() < a[j].Value() } - -// SimpleCoin defines a concrete instance of Coin that is backed by a -// btcutil.Tx, a specific outpoint index, and the number of confirmations -// that transaction has had. -type SimpleCoin struct { - Tx *btcutil.Tx - TxIndex uint32 - TxNumConfs int64 -} - -// Ensure that SimpleCoin is a Coin -var _ Coin = &SimpleCoin{} - -// Hash returns the hash value of the transaction on which the Coin is an output -func (c *SimpleCoin) Hash() *chainhash.Hash { - return c.Tx.Hash() -} - -// Index returns the index of the output on the transaction which the Coin represents -func (c *SimpleCoin) Index() uint32 { - return c.TxIndex -} - -// txOut returns the TxOut of the transaction the Coin represents -func (c *SimpleCoin) txOut() *wire.TxOut { - return c.Tx.MsgTx().TxOut[c.TxIndex] -} - -// Value returns the value of the Coin -func (c *SimpleCoin) Value() btcutil.Amount { - return btcutil.Amount(c.txOut().Value) -} - -// PkScript returns the outpoint script of the Coin. -// -// This can be used to determine what type of script the Coin uses -// and extract standard addresses if possible using -// txscript.ExtractPkScriptAddrs for example. -func (c *SimpleCoin) PkScript() []byte { - return c.txOut().PkScript -} - -// NumConfs returns the number of confirmations that the transaction the Coin references -// has had. -func (c *SimpleCoin) NumConfs() int64 { - return c.TxNumConfs -} - -// ValueAge returns the product of the value and the number of confirmations. This is -// used as an input to calculate the priority of the transaction. -func (c *SimpleCoin) ValueAge() int64 { - return c.TxNumConfs * int64(c.Value()) -} diff --git a/coinset/coins_test.go b/coinset/coins_test.go deleted file mode 100644 index dc02d9f97..000000000 --- a/coinset/coins_test.go +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright (c) 2014-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package coinset_test - -import ( - "bytes" - "crypto/sha256" - "encoding/hex" - "fmt" - "testing" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/coinset" -) - -type TestCoin struct { - TxHash *chainhash.Hash - TxIndex uint32 - TxValue btcutil.Amount - TxNumConfs int64 -} - -func (c *TestCoin) Hash() *chainhash.Hash { return c.TxHash } -func (c *TestCoin) Index() uint32 { return c.TxIndex } -func (c *TestCoin) Value() btcutil.Amount { return c.TxValue } -func (c *TestCoin) PkScript() []byte { return nil } -func (c *TestCoin) NumConfs() int64 { return c.TxNumConfs } -func (c *TestCoin) ValueAge() int64 { return int64(c.TxValue) * c.TxNumConfs } - -func NewCoin(index int64, value btcutil.Amount, numConfs int64) coinset.Coin { - h := sha256.New() - _, _ = h.Write([]byte(fmt.Sprintf("%d", index))) - hash, _ := chainhash.NewHash(h.Sum(nil)) - c := &TestCoin{ - TxHash: hash, - TxIndex: 0, - TxValue: value, - TxNumConfs: numConfs, - } - return coinset.Coin(c) -} - -type coinSelectTest struct { - selector coinset.CoinSelector - inputCoins []coinset.Coin - targetValue btcutil.Amount - expectedCoins []coinset.Coin - expectedError error -} - -func testCoinSelector(tests []coinSelectTest, t *testing.T) { - for testIndex, test := range tests { - cs, err := test.selector.CoinSelect(test.targetValue, test.inputCoins) - if err != test.expectedError { - t.Errorf("[%d] expected a different error: got=%v, expected=%v", testIndex, err, test.expectedError) - continue - } - if test.expectedCoins != nil { - if cs == nil { - t.Errorf("[%d] expected non-nil coinset", testIndex) - continue - } - coins := cs.Coins() - if len(coins) != len(test.expectedCoins) { - t.Errorf("[%d] expected different number of coins: got=%d, expected=%d", testIndex, len(coins), len(test.expectedCoins)) - continue - } - for n := 0; n < len(test.expectedCoins); n++ { - if coins[n] != test.expectedCoins[n] { - t.Errorf("[%d] expected different coins at coin index %d: got=%#v, expected=%#v", testIndex, n, coins[n], test.expectedCoins[n]) - continue - } - } - coinSet := coinset.NewCoinSet(coins) - if coinSet.TotalValue() < test.targetValue { - t.Errorf("[%d] targetValue not satistifed", testIndex) - continue - } - } - } -} - -var coins = []coinset.Coin{ - NewCoin(1, 100000000, 1), - NewCoin(2, 10000000, 20), - NewCoin(3, 50000000, 0), - NewCoin(4, 25000000, 6), -} - -func TestCoinSet(t *testing.T) { - cs := coinset.NewCoinSet(nil) - if cs.PopCoin() != nil { - t.Error("Expected popCoin of empty to be nil") - } - if cs.ShiftCoin() != nil { - t.Error("Expected shiftCoin of empty to be nil") - } - - cs.PushCoin(coins[0]) - cs.PushCoin(coins[1]) - cs.PushCoin(coins[2]) - if cs.PopCoin() != coins[2] { - t.Error("Expected third coin") - } - if cs.ShiftCoin() != coins[0] { - t.Error("Expected first coin") - } - - mtx := coinset.NewMsgTxWithInputCoins(wire.TxVersion, cs) - if len(mtx.TxIn) != 1 { - t.Errorf("Expected only 1 TxIn, got %d", len(mtx.TxIn)) - } - op := mtx.TxIn[0].PreviousOutPoint - if !op.Hash.IsEqual(coins[1].Hash()) || op.Index != coins[1].Index() { - t.Errorf("Expected the second coin to be added as input to mtx") - } -} - -var minIndexSelectors = []coinset.MinIndexCoinSelector{ - {MaxInputs: 10, MinChangeAmount: 10000}, - {MaxInputs: 2, MinChangeAmount: 10000}, -} - -var minIndexTests = []coinSelectTest{ - {minIndexSelectors[0], coins, coins[0].Value() - minIndexSelectors[0].MinChangeAmount, []coinset.Coin{coins[0]}, nil}, - {minIndexSelectors[0], coins, coins[0].Value() - minIndexSelectors[0].MinChangeAmount + 1, []coinset.Coin{coins[0], coins[1]}, nil}, - {minIndexSelectors[0], coins, 100000000, []coinset.Coin{coins[0]}, nil}, - {minIndexSelectors[0], coins, 110000000, []coinset.Coin{coins[0], coins[1]}, nil}, - {minIndexSelectors[0], coins, 140000000, []coinset.Coin{coins[0], coins[1], coins[2]}, nil}, - {minIndexSelectors[0], coins, 200000000, nil, coinset.ErrCoinsNoSelectionAvailable}, - {minIndexSelectors[1], coins, 10000000, []coinset.Coin{coins[0]}, nil}, - {minIndexSelectors[1], coins, 110000000, []coinset.Coin{coins[0], coins[1]}, nil}, - {minIndexSelectors[1], coins, 140000000, nil, coinset.ErrCoinsNoSelectionAvailable}, -} - -func TestMinIndexSelector(t *testing.T) { - testCoinSelector(minIndexTests, t) -} - -var minNumberSelectors = []coinset.MinNumberCoinSelector{ - {MaxInputs: 10, MinChangeAmount: 10000}, - {MaxInputs: 2, MinChangeAmount: 10000}, -} - -var minNumberTests = []coinSelectTest{ - {minNumberSelectors[0], coins, coins[0].Value() - minNumberSelectors[0].MinChangeAmount, []coinset.Coin{coins[0]}, nil}, - {minNumberSelectors[0], coins, coins[0].Value() - minNumberSelectors[0].MinChangeAmount + 1, []coinset.Coin{coins[0], coins[2]}, nil}, - {minNumberSelectors[0], coins, 100000000, []coinset.Coin{coins[0]}, nil}, - {minNumberSelectors[0], coins, 110000000, []coinset.Coin{coins[0], coins[2]}, nil}, - {minNumberSelectors[0], coins, 160000000, []coinset.Coin{coins[0], coins[2], coins[3]}, nil}, - {minNumberSelectors[0], coins, 184990000, []coinset.Coin{coins[0], coins[2], coins[3], coins[1]}, nil}, - {minNumberSelectors[0], coins, 184990001, nil, coinset.ErrCoinsNoSelectionAvailable}, - {minNumberSelectors[0], coins, 200000000, nil, coinset.ErrCoinsNoSelectionAvailable}, - {minNumberSelectors[1], coins, 10000000, []coinset.Coin{coins[0]}, nil}, - {minNumberSelectors[1], coins, 110000000, []coinset.Coin{coins[0], coins[2]}, nil}, - {minNumberSelectors[1], coins, 140000000, []coinset.Coin{coins[0], coins[2]}, nil}, -} - -func TestMinNumberSelector(t *testing.T) { - testCoinSelector(minNumberTests, t) -} - -var maxValueAgeSelectors = []coinset.MaxValueAgeCoinSelector{ - {MaxInputs: 10, MinChangeAmount: 10000}, - {MaxInputs: 2, MinChangeAmount: 10000}, -} - -var maxValueAgeTests = []coinSelectTest{ - {maxValueAgeSelectors[0], coins, 100000, []coinset.Coin{coins[1]}, nil}, - {maxValueAgeSelectors[0], coins, 10000000, []coinset.Coin{coins[1]}, nil}, - {maxValueAgeSelectors[0], coins, 10000001, []coinset.Coin{coins[1], coins[3]}, nil}, - {maxValueAgeSelectors[0], coins, 35000000, []coinset.Coin{coins[1], coins[3]}, nil}, - {maxValueAgeSelectors[0], coins, 135000000, []coinset.Coin{coins[1], coins[3], coins[0]}, nil}, - {maxValueAgeSelectors[0], coins, 185000000, []coinset.Coin{coins[1], coins[3], coins[0], coins[2]}, nil}, - {maxValueAgeSelectors[0], coins, 200000000, nil, coinset.ErrCoinsNoSelectionAvailable}, - {maxValueAgeSelectors[1], coins, 40000000, nil, coinset.ErrCoinsNoSelectionAvailable}, - {maxValueAgeSelectors[1], coins, 35000000, []coinset.Coin{coins[1], coins[3]}, nil}, - {maxValueAgeSelectors[1], coins, 34990001, nil, coinset.ErrCoinsNoSelectionAvailable}, -} - -func TestMaxValueAgeSelector(t *testing.T) { - testCoinSelector(maxValueAgeTests, t) -} - -var minPrioritySelectors = []coinset.MinPriorityCoinSelector{ - {MaxInputs: 10, MinChangeAmount: 10000, MinAvgValueAgePerInput: 100000000}, - {MaxInputs: 02, MinChangeAmount: 10000, MinAvgValueAgePerInput: 200000000}, - {MaxInputs: 02, MinChangeAmount: 10000, MinAvgValueAgePerInput: 150000000}, - {MaxInputs: 03, MinChangeAmount: 10000, MinAvgValueAgePerInput: 150000000}, - {MaxInputs: 10, MinChangeAmount: 10000, MinAvgValueAgePerInput: 1000000000}, - {MaxInputs: 10, MinChangeAmount: 10000, MinAvgValueAgePerInput: 175000000}, - {MaxInputs: 02, MinChangeAmount: 10000, MinAvgValueAgePerInput: 125000000}, -} - -var connectedCoins = []coinset.Coin{coins[0], coins[1], coins[3]} - -var minPriorityTests = []coinSelectTest{ - {minPrioritySelectors[0], connectedCoins, 100000000, []coinset.Coin{coins[0]}, nil}, - {minPrioritySelectors[0], connectedCoins, 125000000, []coinset.Coin{coins[0], coins[3]}, nil}, - {minPrioritySelectors[0], connectedCoins, 135000000, []coinset.Coin{coins[0], coins[3], coins[1]}, nil}, - {minPrioritySelectors[0], connectedCoins, 140000000, nil, coinset.ErrCoinsNoSelectionAvailable}, - {minPrioritySelectors[1], connectedCoins, 100000000, nil, coinset.ErrCoinsNoSelectionAvailable}, - {minPrioritySelectors[1], connectedCoins, 10000000, []coinset.Coin{coins[1]}, nil}, - {minPrioritySelectors[1], connectedCoins, 100000000, nil, coinset.ErrCoinsNoSelectionAvailable}, - {minPrioritySelectors[2], connectedCoins, 11000000, []coinset.Coin{coins[3]}, nil}, - {minPrioritySelectors[2], connectedCoins, 25000001, []coinset.Coin{coins[3], coins[1]}, nil}, - {minPrioritySelectors[3], connectedCoins, 25000001, []coinset.Coin{coins[3], coins[1], coins[0]}, nil}, - {minPrioritySelectors[3], connectedCoins, 100000000, []coinset.Coin{coins[3], coins[1], coins[0]}, nil}, - {minPrioritySelectors[3], []coinset.Coin{coins[1], coins[2]}, 10000000, []coinset.Coin{coins[1]}, nil}, - {minPrioritySelectors[4], connectedCoins, 1, nil, coinset.ErrCoinsNoSelectionAvailable}, - {minPrioritySelectors[5], connectedCoins, 20000000, []coinset.Coin{coins[1], coins[3]}, nil}, - {minPrioritySelectors[6], connectedCoins, 25000000, []coinset.Coin{coins[3], coins[0]}, nil}, -} - -func TestMinPrioritySelector(t *testing.T) { - testCoinSelector(minPriorityTests, t) -} - -var ( - // should be two outpoints, with 1st one having 0.035BTC value. - testSimpleCoinNumConfs = int64(1) - testSimpleCoinTxHash = "9b5965c86de51d5dc824e179a05cf232db78c80ae86ca9d7cb2a655b5e19c1e2" - testSimpleCoinTxHex = "0100000001a214a110f79e4abe073865ea5b3745c6e82c913bad44be70652804a5e4003b0a010000008c493046022100edd18a69664efa57264be207100c203e6cade1888cbb88a0ad748548256bb2f0022100f1027dc2e6c7f248d78af1dd90027b5b7d8ec563bb62aa85d4e74d6376f3868c0141048f3757b65ed301abd1b0e8942d1ab5b50594d3314cff0299f300c696376a0a9bf72e74710a8af7a5372d4af4bb519e2701a094ef48c8e48e3b65b28502452dceffffffff02e0673500000000001976a914686dd149a79b4a559d561fbc396d3e3c6628b98d88ace86ef102000000001976a914ac3f995655e81b875b38b64351d6f896ddbfc68588ac00000000" - testSimpleCoinTxValue0 = btcutil.Amount(3500000) - testSimpleCoinTxValueAge0 = int64(testSimpleCoinTxValue0) * testSimpleCoinNumConfs - testSimpleCoinTxPkScript0Hex = "76a914686dd149a79b4a559d561fbc396d3e3c6628b98d88ac" - testSimpleCoinTxPkScript0Bytes, _ = hex.DecodeString(testSimpleCoinTxPkScript0Hex) - testSimpleCoinTxBytes, _ = hex.DecodeString(testSimpleCoinTxHex) - testSimpleCoinTx, _ = btcutil.NewTxFromBytes(testSimpleCoinTxBytes) - testSimpleCoin = &coinset.SimpleCoin{ - Tx: testSimpleCoinTx, - TxIndex: 0, - TxNumConfs: testSimpleCoinNumConfs, - } -) - -func TestSimpleCoin(t *testing.T) { - if testSimpleCoin.Hash().String() != testSimpleCoinTxHash { - t.Error("Different value for tx hash than expected") - } - if testSimpleCoin.Index() != 0 { - t.Error("Different value for index of outpoint than expected") - } - if testSimpleCoin.Value() != testSimpleCoinTxValue0 { - t.Error("Different value of coin value than expected") - } - if !bytes.Equal(testSimpleCoin.PkScript(), testSimpleCoinTxPkScript0Bytes) { - t.Error("Different value of coin pkScript than expected") - } - if testSimpleCoin.NumConfs() != 1 { - t.Error("Differet value of num confs than expected") - } - if testSimpleCoin.ValueAge() != testSimpleCoinTxValueAge0 { - t.Error("Different value of coin value * age than expected") - } -} diff --git a/coinset/cov_report.sh b/coinset/cov_report.sh deleted file mode 100644 index 307f05b76..000000000 --- a/coinset/cov_report.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -# This script uses gocov to generate a test coverage report. -# The gocov tool my be obtained with the following command: -# go get github.com/axw/gocov/gocov -# -# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. - -# Check for gocov. -type gocov >/dev/null 2>&1 -if [ $? -ne 0 ]; then - echo >&2 "This script requires the gocov tool." - echo >&2 "You may obtain it with the following command:" - echo >&2 "go get github.com/axw/gocov/gocov" - exit 1 -fi -gocov test | gocov report diff --git a/coinset/test_coverage.txt b/coinset/test_coverage.txt deleted file mode 100644 index 0e3e15d9f..000000000 --- a/coinset/test_coverage.txt +++ /dev/null @@ -1,31 +0,0 @@ - -github.com/conformal/btcutil/coinset/coins.go MinPriorityCoinSelector.CoinSelect 100.00% (39/39) -github.com/conformal/btcutil/coinset/coins.go NewMsgTxWithInputCoins 100.00% (6/6) -github.com/conformal/btcutil/coinset/coins.go MinIndexCoinSelector.CoinSelect 100.00% (6/6) -github.com/conformal/btcutil/coinset/coins.go CoinSet.removeElement 100.00% (5/5) -github.com/conformal/btcutil/coinset/coins.go NewCoinSet 100.00% (4/4) -github.com/conformal/btcutil/coinset/coins.go CoinSet.Coins 100.00% (4/4) -github.com/conformal/btcutil/coinset/coins.go CoinSet.PopCoin 100.00% (4/4) -github.com/conformal/btcutil/coinset/coins.go CoinSet.ShiftCoin 100.00% (4/4) -github.com/conformal/btcutil/coinset/coins.go MinNumberCoinSelector.CoinSelect 100.00% (4/4) -github.com/conformal/btcutil/coinset/coins.go MaxValueAgeCoinSelector.CoinSelect 100.00% (4/4) -github.com/conformal/btcutil/coinset/coins.go CoinSet.PushCoin 100.00% (3/3) -github.com/conformal/btcutil/coinset/coins.go CoinSet.TotalValueAge 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go SimpleCoin.NumConfs 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go SimpleCoin.ValueAge 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go CoinSet.TotalValue 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go byValueAge.Len 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go byValueAge.Swap 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go byValueAge.Less 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go byAmount.Len 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go byAmount.Swap 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go byAmount.Less 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go SimpleCoin.Hash 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go SimpleCoin.Index 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go SimpleCoin.txOut 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go SimpleCoin.Value 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go SimpleCoin.PkScript 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go CoinSet.Num 100.00% (1/1) -github.com/conformal/btcutil/coinset/coins.go satisfiesTargetValue 100.00% (1/1) -github.com/conformal/btcutil/coinset ---------------------------------- 100.00% (100/100) - diff --git a/const.go b/const.go deleted file mode 100644 index c73946056..000000000 --- a/const.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2013-2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil - -const ( - // SatoshiPerBitcent is the number of satoshi in one bitcoin cent. - SatoshiPerBitcent = 1e6 - - // SatoshiPerBitcoin is the number of satoshi in one bitcoin (1 BTC). - SatoshiPerBitcoin = 1e8 - - // MaxSatoshi is the maximum transaction amount allowed in satoshi. - MaxSatoshi = 21e6 * SatoshiPerBitcoin -) diff --git a/cov_report.sh b/cov_report.sh deleted file mode 100644 index 307f05b76..000000000 --- a/cov_report.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -# This script uses gocov to generate a test coverage report. -# The gocov tool my be obtained with the following command: -# go get github.com/axw/gocov/gocov -# -# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. - -# Check for gocov. -type gocov >/dev/null 2>&1 -if [ $? -ne 0 ]; then - echo >&2 "This script requires the gocov tool." - echo >&2 "You may obtain it with the following command:" - echo >&2 "go get github.com/axw/gocov/gocov" - exit 1 -fi -gocov test | gocov report diff --git a/doc.go b/doc.go deleted file mode 100644 index 36cda1c78..000000000 --- a/doc.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2013-2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -/* -Package btcutil provides bitcoin-specific convenience functions and types. - -Block Overview - -A Block defines a bitcoin block that provides easier and more efficient -manipulation of raw wire protocol blocks. It also memoizes hashes for the -block and its transactions on their first access so subsequent accesses don't -have to repeat the relatively expensive hashing operations. - -Tx Overview - -A Tx defines a bitcoin transaction that provides more efficient manipulation of -raw wire protocol transactions. It memoizes the hash for the transaction on its -first access so subsequent accesses don't have to repeat the relatively -expensive hashing operations. - -Address Overview - -The Address interface provides an abstraction for a Bitcoin address. While the -most common type is a pay-to-pubkey-hash, Bitcoin already supports others and -may well support more in the future. This package currently provides -implementations for the pay-to-pubkey, pay-to-pubkey-hash, and -pay-to-script-hash address types. - -To decode/encode an address: - - // NOTE: The default network is only used for address types which do not - // already contain that information. At this time, that is only - // pay-to-pubkey addresses. - addrString := "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962" + - "e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d57" + - "8a4c702b6bf11d5f" - defaultNet := &chaincfg.MainNetParams - addr, err := btcutil.DecodeAddress(addrString, defaultNet) - if err != nil { - fmt.Println(err) - return - } - fmt.Println(addr.EncodeAddress()) -*/ -package btcutil diff --git a/example_test.go b/example_test.go deleted file mode 100644 index 73ec9f839..000000000 --- a/example_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package btcutil_test - -import ( - "fmt" - "math" - - "github.com/btcsuite/btcutil" -) - -func ExampleAmount() { - - a := btcutil.Amount(0) - fmt.Println("Zero Satoshi:", a) - - a = btcutil.Amount(1e8) - fmt.Println("100,000,000 Satoshis:", a) - - a = btcutil.Amount(1e5) - fmt.Println("100,000 Satoshis:", a) - // Output: - // Zero Satoshi: 0 BTC - // 100,000,000 Satoshis: 1 BTC - // 100,000 Satoshis: 0.001 BTC -} - -func ExampleNewAmount() { - amountOne, err := btcutil.NewAmount(1) - if err != nil { - fmt.Println(err) - return - } - fmt.Println(amountOne) //Output 1 - - amountFraction, err := btcutil.NewAmount(0.01234567) - if err != nil { - fmt.Println(err) - return - } - fmt.Println(amountFraction) //Output 2 - - amountZero, err := btcutil.NewAmount(0) - if err != nil { - fmt.Println(err) - return - } - fmt.Println(amountZero) //Output 3 - - amountNaN, err := btcutil.NewAmount(math.NaN()) - if err != nil { - fmt.Println(err) - return - } - fmt.Println(amountNaN) //Output 4 - - // Output: 1 BTC - // 0.01234567 BTC - // 0 BTC - // invalid bitcoin amount -} - -func ExampleAmount_unitConversions() { - amount := btcutil.Amount(44433322211100) - - fmt.Println("Satoshi to kBTC:", amount.Format(btcutil.AmountKiloBTC)) - fmt.Println("Satoshi to BTC:", amount) - fmt.Println("Satoshi to MilliBTC:", amount.Format(btcutil.AmountMilliBTC)) - fmt.Println("Satoshi to MicroBTC:", amount.Format(btcutil.AmountMicroBTC)) - fmt.Println("Satoshi to Satoshi:", amount.Format(btcutil.AmountSatoshi)) - - // Output: - // Satoshi to kBTC: 444.333222111 kBTC - // Satoshi to BTC: 444333.222111 BTC - // Satoshi to MilliBTC: 444333222.111 mBTC - // Satoshi to MicroBTC: 444333222111 μBTC - // Satoshi to Satoshi: 44433322211100 Satoshi -} diff --git a/gcs/README.md b/gcs/README.md deleted file mode 100644 index e663a0e0f..000000000 --- a/gcs/README.md +++ /dev/null @@ -1,24 +0,0 @@ -gcs -========== - -[![Build Status](http://img.shields.io/travis/btcsuite/btcutil.svg)] -(https://travis-ci.org/btcsuite/btcutil) [![ISC License] -(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) -[![GoDoc](https://godoc.org/github.com/btcsuite/btcutil/gcs?status.png)] -(http://godoc.org/github.com/btcsuite/btcutil/gcs) - -Package gcs provides an API for building and using a Golomb-coded set filter -similar to that described [here](http://giovanni.bajo.it/post/47119962313/golomb-coded-sets-smaller-than-bloom-filters). - -A comprehensive suite of tests is provided to ensure proper functionality. - -## Installation and Updating - -```bash -$ go get -u github.com/btcsuite/btcutil/gcs -``` - -## License - -Package gcs is licensed under the [copyfree](http://copyfree.org) ISC -License. diff --git a/gcs/builder/builder.go b/gcs/builder/builder.go deleted file mode 100644 index 8b45d7842..000000000 --- a/gcs/builder/builder.go +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright (c) 2017 The btcsuite developers -// Copyright (c) 2017 The Lightning Network Developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package builder - -import ( - "crypto/rand" - "fmt" - "math" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil/gcs" -) - -const ( - // DefaultP is the default collision probability (2^-19) - DefaultP = 19 - - // DefaultM is the default value used for the hash range. - DefaultM uint64 = 784931 -) - -// GCSBuilder is a utility class that makes building GCS filters convenient. -type GCSBuilder struct { - p uint8 - - m uint64 - - key [gcs.KeySize]byte - - // data is a set of entries represented as strings. This is done to - // deduplicate items as they are added. - data map[string]struct{} - err error -} - -// RandomKey is a utility function that returns a cryptographically random -// [gcs.KeySize]byte usable as a key for a GCS filter. -func RandomKey() ([gcs.KeySize]byte, error) { - var key [gcs.KeySize]byte - - // Read a byte slice from rand.Reader. - randKey := make([]byte, gcs.KeySize) - _, err := rand.Read(randKey) - - // This shouldn't happen unless the user is on a system that doesn't - // have a system CSPRNG. OK to panic in this case. - if err != nil { - return key, err - } - - // Copy the byte slice to a [gcs.KeySize]byte array and return it. - copy(key[:], randKey) - return key, nil -} - -// DeriveKey is a utility function that derives a key from a chainhash.Hash by -// truncating the bytes of the hash to the appopriate key size. -func DeriveKey(keyHash *chainhash.Hash) [gcs.KeySize]byte { - var key [gcs.KeySize]byte - copy(key[:], keyHash.CloneBytes()) - return key -} - -// Key retrieves the key with which the builder will build a filter. This is -// useful if the builder is created with a random initial key. -func (b *GCSBuilder) Key() ([gcs.KeySize]byte, error) { - // Do nothing if the builder's errored out. - if b.err != nil { - return [gcs.KeySize]byte{}, b.err - } - - return b.key, nil -} - -// SetKey sets the key with which the builder will build a filter to the passed -// [gcs.KeySize]byte. -func (b *GCSBuilder) SetKey(key [gcs.KeySize]byte) *GCSBuilder { - // Do nothing if the builder's already errored out. - if b.err != nil { - return b - } - - copy(b.key[:], key[:]) - return b -} - -// SetKeyFromHash sets the key with which the builder will build a filter to a -// key derived from the passed chainhash.Hash using DeriveKey(). -func (b *GCSBuilder) SetKeyFromHash(keyHash *chainhash.Hash) *GCSBuilder { - // Do nothing if the builder's already errored out. - if b.err != nil { - return b - } - - return b.SetKey(DeriveKey(keyHash)) -} - -// SetP sets the filter's probability after calling Builder(). -func (b *GCSBuilder) SetP(p uint8) *GCSBuilder { - // Do nothing if the builder's already errored out. - if b.err != nil { - return b - } - - // Basic sanity check. - if p > 32 { - b.err = gcs.ErrPTooBig - return b - } - - b.p = p - return b -} - -// SetM sets the filter's modulous value after calling Builder(). -func (b *GCSBuilder) SetM(m uint64) *GCSBuilder { - // Do nothing if the builder's already errored out. - if b.err != nil { - return b - } - - // Basic sanity check. - if m > uint64(math.MaxUint32) { - b.err = gcs.ErrPTooBig - return b - } - - b.m = m - return b -} - -// Preallocate sets the estimated filter size after calling Builder() to reduce -// the probability of memory reallocations. If the builder has already had data -// added to it, Preallocate has no effect. -func (b *GCSBuilder) Preallocate(n uint32) *GCSBuilder { - // Do nothing if the builder's already errored out. - if b.err != nil { - return b - } - - if b.data == nil { - b.data = make(map[string]struct{}, n) - } - - return b -} - -// AddEntry adds a []byte to the list of entries to be included in the GCS -// filter when it's built. -func (b *GCSBuilder) AddEntry(data []byte) *GCSBuilder { - // Do nothing if the builder's already errored out. - if b.err != nil { - return b - } - - b.data[string(data)] = struct{}{} - return b -} - -// AddEntries adds all the []byte entries in a [][]byte to the list of entries -// to be included in the GCS filter when it's built. -func (b *GCSBuilder) AddEntries(data [][]byte) *GCSBuilder { - // Do nothing if the builder's already errored out. - if b.err != nil { - return b - } - - for _, entry := range data { - b.AddEntry(entry) - } - return b -} - -// AddHash adds a chainhash.Hash to the list of entries to be included in the -// GCS filter when it's built. -func (b *GCSBuilder) AddHash(hash *chainhash.Hash) *GCSBuilder { - // Do nothing if the builder's already errored out. - if b.err != nil { - return b - } - - return b.AddEntry(hash.CloneBytes()) -} - -// AddWitness adds each item of the passed filter stack to the filter, and then -// adds each item as a script. -func (b *GCSBuilder) AddWitness(witness wire.TxWitness) *GCSBuilder { - // Do nothing if the builder's already errored out. - if b.err != nil { - return b - } - - return b.AddEntries(witness) -} - -// Build returns a function which builds a GCS filter with the given parameters -// and data. -func (b *GCSBuilder) Build() (*gcs.Filter, error) { - // Do nothing if the builder's already errored out. - if b.err != nil { - return nil, b.err - } - - // We'll ensure that all the parmaters we need to actually build the - // filter properly are set. - if b.p == 0 { - return nil, fmt.Errorf("p value is not set, cannot build") - } - if b.m == 0 { - return nil, fmt.Errorf("m value is not set, cannot build") - } - - dataSlice := make([][]byte, 0, len(b.data)) - for item := range b.data { - dataSlice = append(dataSlice, []byte(item)) - } - - return gcs.BuildGCSFilter(b.p, b.m, b.key, dataSlice) -} - -// WithKeyPNM creates a GCSBuilder with specified key and the passed -// probability, modulus and estimated filter size. -func WithKeyPNM(key [gcs.KeySize]byte, p uint8, n uint32, m uint64) *GCSBuilder { - b := GCSBuilder{} - return b.SetKey(key).SetP(p).SetM(m).Preallocate(n) -} - -// WithKeyPM creates a GCSBuilder with specified key and the passed -// probability. Estimated filter size is set to zero, which means more -// reallocations are done when building the filter. -func WithKeyPM(key [gcs.KeySize]byte, p uint8, m uint64) *GCSBuilder { - return WithKeyPNM(key, p, 0, m) -} - -// WithKey creates a GCSBuilder with specified key. Probability is set to 19 -// (2^-19 collision probability). Estimated filter size is set to zero, which -// means more reallocations are done when building the filter. -func WithKey(key [gcs.KeySize]byte) *GCSBuilder { - return WithKeyPNM(key, DefaultP, 0, DefaultM) -} - -// WithKeyHashPNM creates a GCSBuilder with key derived from the specified -// chainhash.Hash and the passed probability and estimated filter size. -func WithKeyHashPNM(keyHash *chainhash.Hash, p uint8, n uint32, - m uint64) *GCSBuilder { - - return WithKeyPNM(DeriveKey(keyHash), p, n, m) -} - -// WithKeyHashPM creates a GCSBuilder with key derived from the specified -// chainhash.Hash and the passed probability. Estimated filter size is set to -// zero, which means more reallocations are done when building the filter. -func WithKeyHashPM(keyHash *chainhash.Hash, p uint8, m uint64) *GCSBuilder { - return WithKeyHashPNM(keyHash, p, 0, m) -} - -// WithKeyHash creates a GCSBuilder with key derived from the specified -// chainhash.Hash. Probability is set to 20 (2^-20 collision probability). -// Estimated filter size is set to zero, which means more reallocations are -// done when building the filter. -func WithKeyHash(keyHash *chainhash.Hash) *GCSBuilder { - return WithKeyHashPNM(keyHash, DefaultP, 0, DefaultM) -} - -// WithRandomKeyPNM creates a GCSBuilder with a cryptographically random key and -// the passed probability and estimated filter size. -func WithRandomKeyPNM(p uint8, n uint32, m uint64) *GCSBuilder { - key, err := RandomKey() - if err != nil { - b := GCSBuilder{err: err} - return &b - } - return WithKeyPNM(key, p, n, m) -} - -// WithRandomKeyPM creates a GCSBuilder with a cryptographically random key and -// the passed probability. Estimated filter size is set to zero, which means -// more reallocations are done when building the filter. -func WithRandomKeyPM(p uint8, m uint64) *GCSBuilder { - return WithRandomKeyPNM(p, 0, m) -} - -// WithRandomKey creates a GCSBuilder with a cryptographically random key. -// Probability is set to 20 (2^-20 collision probability). Estimated filter -// size is set to zero, which means more reallocations are done when -// building the filter. -func WithRandomKey() *GCSBuilder { - return WithRandomKeyPNM(DefaultP, 0, DefaultM) -} - -// BuildBasicFilter builds a basic GCS filter from a block. A basic GCS filter -// will contain all the previous output scripts spent by inputs within a block, -// as well as the data pushes within all the outputs created within a block. -func BuildBasicFilter(block *wire.MsgBlock, prevOutScripts [][]byte) (*gcs.Filter, error) { - blockHash := block.BlockHash() - b := WithKeyHash(&blockHash) - - // If the filter had an issue with the specified key, then we force it - // to bubble up here by calling the Key() function. - _, err := b.Key() - if err != nil { - return nil, err - } - - // In order to build a basic filter, we'll range over the entire block, - // adding each whole script itself. - for _, tx := range block.Transactions { - // For each output in a transaction, we'll add each of the - // individual data pushes within the script. - for _, txOut := range tx.TxOut { - if len(txOut.PkScript) == 0 { - continue - } - - // In order to allow the filters to later be committed - // to within an OP_RETURN output, we ignore all - // OP_RETURNs to avoid a circular dependency. - if txOut.PkScript[0] == txscript.OP_RETURN { - continue - } - - b.AddEntry(txOut.PkScript) - } - } - - // In the second pass, we'll also add all the prevOutScripts - // individually as elements. - for _, prevScript := range prevOutScripts { - if len(prevScript) == 0 { - continue - } - - b.AddEntry(prevScript) - } - - return b.Build() -} - -// GetFilterHash returns the double-SHA256 of the filter. -func GetFilterHash(filter *gcs.Filter) (chainhash.Hash, error) { - filterData, err := filter.NBytes() - if err != nil { - return chainhash.Hash{}, err - } - - return chainhash.DoubleHashH(filterData), nil -} - -// MakeHeaderForFilter makes a filter chain header for a filter, given the -// filter and the previous filter chain header. -func MakeHeaderForFilter(filter *gcs.Filter, prevHeader chainhash.Hash) (chainhash.Hash, error) { - filterTip := make([]byte, 2*chainhash.HashSize) - filterHash, err := GetFilterHash(filter) - if err != nil { - return chainhash.Hash{}, err - } - - // In the buffer we created above we'll compute hash || prevHash as an - // intermediate value. - copy(filterTip, filterHash[:]) - copy(filterTip[chainhash.HashSize:], prevHeader[:]) - - // The final filter hash is the double-sha256 of the hash computed - // above. - return chainhash.DoubleHashH(filterTip), nil -} diff --git a/gcs/builder/builder_test.go b/gcs/builder/builder_test.go deleted file mode 100644 index de04421f4..000000000 --- a/gcs/builder/builder_test.go +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright (c) 2017 The btcsuite developers -// Copyright (c) 2017 The Lightning Network Developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package builder_test - -import ( - "encoding/hex" - "testing" - - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/gcs" - "github.com/btcsuite/btcutil/gcs/builder" -) - -var ( - // List of values for building a filter - contents = [][]byte{ - []byte("Alex"), - []byte("Bob"), - []byte("Charlie"), - []byte("Dick"), - []byte("Ed"), - []byte("Frank"), - []byte("George"), - []byte("Harry"), - []byte("Ilya"), - []byte("John"), - []byte("Kevin"), - []byte("Larry"), - []byte("Michael"), - []byte("Nate"), - []byte("Owen"), - []byte("Paul"), - []byte("Quentin"), - } - - testKey = [16]byte{0x4c, 0xb1, 0xab, 0x12, 0x57, 0x62, 0x1e, 0x41, - 0x3b, 0x8b, 0x0e, 0x26, 0x64, 0x8d, 0x4a, 0x15} - - testHash = "000000000000000000496d7ff9bd2c96154a8d64260e8b3b411e625712abb14c" - - testAddr = "3Nxwenay9Z8Lc9JBiywExpnEFiLp6Afp8v" - - witness = [][]byte{ - {0x4c, 0xb1, 0xab, 0x12, 0x57, 0x62, 0x1e, 0x41, - 0x3b, 0x8b, 0x0e, 0x26, 0x64, 0x8d, 0x4a, 0x15, - 0x3b, 0x8b, 0x0e, 0x26, 0x64, 0x8d, 0x4a, 0x15, - 0x3b, 0x8b, 0x0e, 0x26, 0x64, 0x8d, 0x4a, 0x15}, - - {0xdd, 0xa3, 0x5a, 0x14, 0x88, 0xfb, 0x97, 0xb6, - 0xeb, 0x3f, 0xe6, 0xe9, 0xef, 0x2a, 0x25, 0x81, - 0x4e, 0x39, 0x6f, 0xb5, 0xdc, 0x29, 0x5f, 0xe9, - 0x94, 0xb9, 0x67, 0x89, 0xb2, 0x1a, 0x03, 0x98, - 0x94, 0xb9, 0x67, 0x89, 0xb2, 0x1a, 0x03, 0x98, - 0x94, 0xb9, 0x67, 0x89, 0xb2, 0x1a, 0x03, 0x98}, - } -) - -// TestUseBlockHash tests using a block hash as a filter key. -func TestUseBlockHash(t *testing.T) { - // Block hash #448710, pretty high difficulty. - hash, err := chainhash.NewHashFromStr(testHash) - if err != nil { - t.Fatalf("Hash from string failed: %s", err.Error()) - } - - // wire.OutPoint - outPoint := wire.OutPoint{ - Hash: *hash, - Index: 4321, - } - - // btcutil.Address - addr, err := btcutil.DecodeAddress(testAddr, &chaincfg.MainNetParams) - if err != nil { - t.Fatalf("Address decode failed: %s", err.Error()) - } - addrBytes, err := txscript.PayToAddrScript(addr) - if err != nil { - t.Fatalf("Address script build failed: %s", err.Error()) - } - - // Create a GCSBuilder with a key hash and check that the key is derived - // correctly, then test it. - b := builder.WithKeyHash(hash) - key, err := b.Key() - if err != nil { - t.Fatalf("Builder instantiation with key hash failed: %s", - err.Error()) - } - if key != testKey { - t.Fatalf("Key not derived correctly from key hash:\n%s\n%s", - hex.EncodeToString(key[:]), - hex.EncodeToString(testKey[:])) - } - BuilderTest(b, hash, builder.DefaultP, outPoint, addrBytes, witness, t) - - // Create a GCSBuilder with a key hash and non-default P and test it. - b = builder.WithKeyHashPM(hash, 30, 90) - BuilderTest(b, hash, 30, outPoint, addrBytes, witness, t) - - // Create a GCSBuilder with a random key, set the key from a hash - // manually, check that the key is correct, and test it. - b = builder.WithRandomKey() - b.SetKeyFromHash(hash) - key, err = b.Key() - if err != nil { - t.Fatalf("Builder instantiation with known key failed: %s", - err.Error()) - } - if key != testKey { - t.Fatalf("Key not copied correctly from known key:\n%s\n%s", - hex.EncodeToString(key[:]), - hex.EncodeToString(testKey[:])) - } - BuilderTest(b, hash, builder.DefaultP, outPoint, addrBytes, witness, t) - - // Create a GCSBuilder with a random key and test it. - b = builder.WithRandomKey() - key1, err := b.Key() - if err != nil { - t.Fatalf("Builder instantiation with random key failed: %s", - err.Error()) - } - t.Logf("Random Key 1: %s", hex.EncodeToString(key1[:])) - BuilderTest(b, hash, builder.DefaultP, outPoint, addrBytes, witness, t) - - // Create a GCSBuilder with a random key and non-default P and test it. - b = builder.WithRandomKeyPM(30, 90) - key2, err := b.Key() - if err != nil { - t.Fatalf("Builder instantiation with random key failed: %s", - err.Error()) - } - t.Logf("Random Key 2: %s", hex.EncodeToString(key2[:])) - if key2 == key1 { - t.Fatalf("Random keys are the same!") - } - BuilderTest(b, hash, 30, outPoint, addrBytes, witness, t) - - // Create a GCSBuilder with a known key and test it. - b = builder.WithKey(testKey) - key, err = b.Key() - if err != nil { - t.Fatalf("Builder instantiation with known key failed: %s", - err.Error()) - } - if key != testKey { - t.Fatalf("Key not copied correctly from known key:\n%s\n%s", - hex.EncodeToString(key[:]), - hex.EncodeToString(testKey[:])) - } - BuilderTest(b, hash, builder.DefaultP, outPoint, addrBytes, witness, t) - - // Create a GCSBuilder with a known key and non-default P and test it. - b = builder.WithKeyPM(testKey, 30, 90) - key, err = b.Key() - if err != nil { - t.Fatalf("Builder instantiation with known key failed: %s", - err.Error()) - } - if key != testKey { - t.Fatalf("Key not copied correctly from known key:\n%s\n%s", - hex.EncodeToString(key[:]), - hex.EncodeToString(testKey[:])) - } - BuilderTest(b, hash, 30, outPoint, addrBytes, witness, t) - - // Create a GCSBuilder with a known key and too-high P and ensure error - // works throughout all functions that use it. - b = builder.WithRandomKeyPM(33, 99).SetKeyFromHash(hash).SetKey(testKey) - b.SetP(30).AddEntry(hash.CloneBytes()).AddEntries(contents). - AddHash(hash).AddEntry(addrBytes) - _, err = b.Key() - if err != gcs.ErrPTooBig { - t.Fatalf("No error on P too big!") - } - _, err = b.Build() - if err != gcs.ErrPTooBig { - t.Fatalf("No error on P too big!") - } -} - -func BuilderTest(b *builder.GCSBuilder, hash *chainhash.Hash, p uint8, - outPoint wire.OutPoint, addrBytes []byte, witness wire.TxWitness, - t *testing.T) { - - key, err := b.Key() - if err != nil { - t.Fatalf("Builder instantiation with key hash failed: %s", - err.Error()) - } - - // Build a filter and test matches. - b.AddEntries(contents) - f, err := b.Build() - if err != nil { - t.Fatalf("Filter build failed: %s", err.Error()) - } - if f.P() != p { - t.Fatalf("Filter built with wrong probability") - } - match, err := f.Match(key, []byte("Nate")) - if err != nil { - t.Fatalf("Filter match failed: %s", err) - } - if !match { - t.Fatal("Filter didn't match when it should have!") - } - match, err = f.Match(key, []byte("weks")) - if err != nil { - t.Fatalf("Filter match failed: %s", err) - } - if match { - t.Logf("False positive match, should be 1 in 2**%d!", - builder.DefaultP) - } - - // Add a hash, build a filter, and test matches - b.AddHash(hash) - f, err = b.Build() - if err != nil { - t.Fatalf("Filter build failed: %s", err.Error()) - } - match, err = f.Match(key, hash.CloneBytes()) - if err != nil { - t.Fatalf("Filter match failed: %s", err) - } - if !match { - t.Fatal("Filter didn't match when it should have!") - } - - // Add a script, build a filter, and test matches - b.AddEntry(addrBytes) - f, err = b.Build() - if err != nil { - t.Fatalf("Filter build failed: %s", err.Error()) - } - match, err = f.MatchAny(key, [][]byte{addrBytes}) - if err != nil { - t.Fatalf("Filter match any failed: %s", err) - } - if !match { - t.Fatal("Filter didn't match when it should have!") - } - - // Add a routine witness stack, build a filter, and test that it - // matches. - b.AddWitness(witness) - f, err = b.Build() - if err != nil { - t.Fatalf("Filter build failed: %s", err.Error()) - } - match, err = f.MatchAny(key, witness) - if err != nil { - t.Fatalf("Filter match any failed: %s", err) - } - if !match { - t.Fatal("Filter didn't match when it should have!") - } - - // Check that adding duplicate items does not increase filter size. - originalSize := f.N() - b.AddEntry(addrBytes) - b.AddWitness(witness) - f, err = b.Build() - if err != nil { - t.Fatalf("Filter build failed: %s", err.Error()) - } - if f.N() != originalSize { - t.Fatal("Filter size increased with duplicate items") - } -} diff --git a/gcs/doc.go b/gcs/doc.go deleted file mode 100644 index 780fd7663..000000000 --- a/gcs/doc.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2016-2017 The btcsuite developers -// Copyright (c) 2016-2017 The Lightning Network Developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -/* -Package gcs provides an API for building and using a Golomb-coded set filter. - -Golomb-Coded Set - -A Golomb-coded set is a probabilistic data structure used similarly to a Bloom -filter. A filter uses constant-size overhead plus on average n+2 bits per -item added to the filter, where 2^-n is the desired false positive (collision) -probability. - -GCS use in Bitcoin - -GCS filters are a proposed mechanism for storing and transmitting per-block -filters in Bitcoin. The usage is intended to be the inverse of Bloom filters: -a full node would send an SPV node the GCS filter for a block, which the SPV -node would check against its list of relevant items. The suggested collision -probability for Bitcoin use is 2^-20. -*/ -package gcs diff --git a/gcs/gcs.go b/gcs/gcs.go deleted file mode 100644 index fbffb0613..000000000 --- a/gcs/gcs.go +++ /dev/null @@ -1,541 +0,0 @@ -// Copyright (c) 2016-2017 The btcsuite developers -// Copyright (c) 2016-2017 The Lightning Network Developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package gcs - -import ( - "bytes" - "fmt" - "io" - "sort" - - "github.com/aead/siphash" - "github.com/btcsuite/btcd/wire" - "github.com/kkdai/bstream" -) - -// Inspired by https://github.com/rasky/gcs - -var ( - // ErrNTooBig signifies that the filter can't handle N items. - ErrNTooBig = fmt.Errorf("N is too big to fit in uint32") - - // ErrPTooBig signifies that the filter can't handle `1/2**P` - // collision probability. - ErrPTooBig = fmt.Errorf("P is too big to fit in uint32") -) - -const ( - // KeySize is the size of the byte array required for key material for - // the SipHash keyed hash function. - KeySize = 16 - - // varIntProtoVer is the protocol version to use for serializing N as a - // VarInt. - varIntProtoVer uint32 = 0 -) - -// fastReduction calculates a mapping that's more ore less equivalent to: x mod -// N. However, instead of using a mod operation, which using a non-power of two -// will lead to slowness on many processors due to unnecessary division, we -// instead use a "multiply-and-shift" trick which eliminates all divisions, -// described in: -// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ -// -// * v * N >> log_2(N) -// -// In our case, using 64-bit integers, log_2 is 64. As most processors don't -// support 128-bit arithmetic natively, we'll be super portable and unfold the -// operation into several operations with 64-bit arithmetic. As inputs, we the -// number to reduce, and our modulus N divided into its high 32-bits and lower -// 32-bits. -func fastReduction(v, nHi, nLo uint64) uint64 { - // First, we'll spit the item we need to reduce into its higher and - // lower bits. - vhi := v >> 32 - vlo := uint64(uint32(v)) - - // Then, we distribute multiplication over each part. - vnphi := vhi * nHi - vnpmid := vhi * nLo - npvmid := nHi * vlo - vnplo := vlo * nLo - - // We calculate the carry bit. - carry := (uint64(uint32(vnpmid)) + uint64(uint32(npvmid)) + - (vnplo >> 32)) >> 32 - - // Last, we add the high bits, the middle bits, and the carry. - v = vnphi + (vnpmid >> 32) + (npvmid >> 32) + carry - - return v -} - -// Filter describes an immutable filter that can be built from a set of data -// elements, serialized, deserialized, and queried in a thread-safe manner. The -// serialized form is compressed as a Golomb Coded Set (GCS), but does not -// include N or P to allow the user to encode the metadata separately if -// necessary. The hash function used is SipHash, a keyed function; the key used -// in building the filter is required in order to match filter values and is -// not included in the serialized form. -type Filter struct { - n uint32 - p uint8 - modulusNP uint64 - - filterData []byte -} - -// BuildGCSFilter builds a new GCS filter with the collision probability of -// `1/(2**P)`, key `key`, and including every `[]byte` in `data` as a member of -// the set. -func BuildGCSFilter(P uint8, M uint64, key [KeySize]byte, data [][]byte) (*Filter, error) { // nolint:gocritic - // Some initial parameter checks: make sure we have data from which to - // build the filter, and make sure our parameters will fit the hash - // function we're using. - if uint64(len(data)) >= (1 << 32) { - return nil, ErrNTooBig - } - if P > 32 { - return nil, ErrPTooBig - } - - // Create the filter object and insert metadata. - f := Filter{ - n: uint32(len(data)), - p: P, - } - - // First we'll compute the value of m, which is the modulus we use - // within our finite field. We want to compute: mScalar * 2^P. We use - // math.Round in order to round the value up, rather than down. - f.modulusNP = uint64(f.n) * M - - // Shortcut if the filter is empty. - if f.n == 0 { - return &f, nil - } - - // Build the filter. - values := make([]uint64, 0, len(data)) - b := bstream.NewBStreamWriter(0) - - // Insert the hash (fast-ranged over a space of N*P) of each data - // element into a slice and sort the slice. This can be greatly - // optimized with native 128-bit multiplication, but we're going to be - // fully portable for now. - // - // First, we cache the high and low bits of modulusNP for the - // multiplication of 2 64-bit integers into a 128-bit integer. - nphi := f.modulusNP >> 32 - nplo := uint64(uint32(f.modulusNP)) - for _, d := range data { - // For each datum, we assign the initial hash to a uint64. - v := siphash.Sum64(d, &key) - - v = fastReduction(v, nphi, nplo) - values = append(values, v) - } - sort.Slice(values, func(i, j int) bool { return values[i] < values[j] }) - - // Write the sorted list of values into the filter bitstream, - // compressing it using Golomb coding. - var value, lastValue, remainder uint64 - for _, v := range values { - // Calculate the difference between this value and the last, - // modulo P. - remainder = (v - lastValue) & ((uint64(1) << f.p) - 1) - - // Calculate the difference between this value and the last, - // divided by P. - value = (v - lastValue - remainder) >> f.p - lastValue = v - - // Write the P multiple into the bitstream in unary; the - // average should be around 1 (2 bits - 0b10). - for value > 0 { - b.WriteBit(true) - value-- - } - b.WriteBit(false) - - // Write the remainder as a big-endian integer with enough bits - // to represent the appropriate collision probability. - b.WriteBits(remainder, int(f.p)) - } - - // Copy the bitstream into the filter object and return the object. - f.filterData = b.Bytes() - - return &f, nil -} - -// FromBytes deserializes a GCS filter from a known N, P, and serialized filter -// as returned by Bytes(). -func FromBytes(N uint32, P uint8, M uint64, d []byte) (*Filter, error) { // nolint:gocritic - // Basic sanity check. - if P > 32 { - return nil, ErrPTooBig - } - - // Create the filter object and insert metadata. - f := &Filter{ - n: N, - p: P, - } - - // First we'll compute the value of m, which is the modulus we use - // within our finite field. We want to compute: mScalar * 2^P. We use - // math.Round in order to round the value up, rather than down. - f.modulusNP = uint64(f.n) * M - - // Copy the filter. - f.filterData = make([]byte, len(d)) - copy(f.filterData, d) - - return f, nil -} - -// FromNBytes deserializes a GCS filter from a known P, and serialized N and -// filter as returned by NBytes(). -func FromNBytes(P uint8, M uint64, d []byte) (*Filter, error) { // nolint:gocritic - buffer := bytes.NewBuffer(d) - N, err := wire.ReadVarInt(buffer, varIntProtoVer) - if err != nil { - return nil, err - } - if N >= (1 << 32) { - return nil, ErrNTooBig - } - return FromBytes(uint32(N), P, M, buffer.Bytes()) -} - -// Bytes returns the serialized format of the GCS filter, which does not -// include N or P (returned by separate methods) or the key used by SipHash. -func (f *Filter) Bytes() ([]byte, error) { - filterData := make([]byte, len(f.filterData)) - copy(filterData, f.filterData) - return filterData, nil -} - -// NBytes returns the serialized format of the GCS filter with N, which does -// not include P (returned by a separate method) or the key used by SipHash. -func (f *Filter) NBytes() ([]byte, error) { - var buffer bytes.Buffer - buffer.Grow(wire.VarIntSerializeSize(uint64(f.n)) + len(f.filterData)) - - err := wire.WriteVarInt(&buffer, varIntProtoVer, uint64(f.n)) - if err != nil { - return nil, err - } - - _, err = buffer.Write(f.filterData) - if err != nil { - return nil, err - } - - return buffer.Bytes(), nil -} - -// PBytes returns the serialized format of the GCS filter with P, which does -// not include N (returned by a separate method) or the key used by SipHash. -func (f *Filter) PBytes() ([]byte, error) { - filterData := make([]byte, len(f.filterData)+1) - filterData[0] = f.p - copy(filterData[1:], f.filterData) - return filterData, nil -} - -// NPBytes returns the serialized format of the GCS filter with N and P, which -// does not include the key used by SipHash. -func (f *Filter) NPBytes() ([]byte, error) { - var buffer bytes.Buffer - buffer.Grow(wire.VarIntSerializeSize(uint64(f.n)) + 1 + len(f.filterData)) - - err := wire.WriteVarInt(&buffer, varIntProtoVer, uint64(f.n)) - if err != nil { - return nil, err - } - - err = buffer.WriteByte(f.p) - if err != nil { - return nil, err - } - - _, err = buffer.Write(f.filterData) - if err != nil { - return nil, err - } - - return buffer.Bytes(), nil -} - -// P returns the filter's collision probability as a negative power of 2 (that -// is, a collision probability of `1/2**20` is represented as 20). -func (f *Filter) P() uint8 { - return f.p -} - -// N returns the size of the data set used to build the filter. -func (f *Filter) N() uint32 { - return f.n -} - -// Match checks whether a []byte value is likely (within collision probability) -// to be a member of the set represented by the filter. -func (f *Filter) Match(key [KeySize]byte, data []byte) (bool, error) { - // Create a filter bitstream. - filterData, err := f.Bytes() - if err != nil { - return false, err - } - - b := bstream.NewBStreamReader(filterData) - - // We take the high and low bits of modulusNP for the multiplication - // of 2 64-bit integers into a 128-bit integer. - nphi := f.modulusNP >> 32 - nplo := uint64(uint32(f.modulusNP)) - - // Then we hash our search term with the same parameters as the filter. - term := siphash.Sum64(data, &key) - term = fastReduction(term, nphi, nplo) - - // Go through the search filter and look for the desired value. - var value uint64 - for i := uint32(0); i < f.N(); i++ { - // Read the difference between previous and new value from - // bitstream. - delta, err := f.readFullUint64(b) - if err != nil { - if err == io.EOF { - return false, nil - } - return false, err - } - - // Add the delta to the previous value. - value += delta - switch { - - // The current value matches our query term, success. - case value == term: - return true, nil - - // The current value is greater than our query term, thus no - // future decoded value can match because the values - // monotonically increase. - case value > term: - return false, nil - } - } - - // All values were decoded and none produced a successful match. This - // indicates that the items in the filter were all smaller than our - // target. - return false, nil -} - -// MatchAny returns checks whether any []byte value is likely (within collision -// probability) to be a member of the set represented by the filter faster than -// calling Match() for each value individually. -func (f *Filter) MatchAny(key [KeySize]byte, data [][]byte) (bool, error) { - // TODO(conner): add real heuristics to query optimization - switch { - - case len(data) >= int(f.N()/2): - return f.HashMatchAny(key, data) - - default: - return f.ZipMatchAny(key, data) - } -} - -// ZipMatchAny returns checks whether any []byte value is likely (within -// collision probability) to be a member of the set represented by the filter -// faster than calling Match() for each value individually. -// -// NOTE: This method should outperform HashMatchAny when the number of query -// entries is smaller than the number of filter entries. -func (f *Filter) ZipMatchAny(key [KeySize]byte, data [][]byte) (bool, error) { - // Basic anity check. - if len(data) == 0 { - return false, nil - } - - // Create a filter bitstream. - filterData, err := f.Bytes() - if err != nil { - return false, err - } - - b := bstream.NewBStreamReader(filterData) - - // Create an uncompressed filter of the search values. - values := make([]uint64, 0, len(data)) - - // First, we cache the high and low bits of modulusNP for the - // multiplication of 2 64-bit integers into a 128-bit integer. - nphi := f.modulusNP >> 32 - nplo := uint64(uint32(f.modulusNP)) - for _, d := range data { - // For each datum, we assign the initial hash to a uint64. - v := siphash.Sum64(d, &key) - - // We'll then reduce the value down to the range of our - // modulus. - v = fastReduction(v, nphi, nplo) - values = append(values, v) - } - sort.Slice(values, func(i, j int) bool { return values[i] < values[j] }) - - querySize := len(values) - - // Zip down the filters, comparing values until we either run out of - // values to compare in one of the filters or we reach a matching - // value. - var ( - value uint64 - queryIndex int - ) -out: - for i := uint32(0); i < f.N(); i++ { - // Advance filter we're searching or return false if we're at - // the end because nothing matched. - delta, err := f.readFullUint64(b) - if err != nil { - if err == io.EOF { - return false, nil - } - return false, err - } - value += delta - - for { - switch { - - // All query items have been exhausted and we haven't - // had a match, therefore there are no matches. - case queryIndex == querySize: - return false, nil - - // The current item in the query matches the decoded - // value, success. - case values[queryIndex] == value: - return true, nil - - // The current item in the query is greater than the - // current decoded value, continue to decode the next - // delta and try again. - case values[queryIndex] > value: - continue out - } - - queryIndex++ - } - } - - // All items in the filter were decoded and none produced a successful - // match. - return false, nil -} - -// HashMatchAny returns checks whether any []byte value is likely (within -// collision probability) to be a member of the set represented by the filter -// faster than calling Match() for each value individually. -// -// NOTE: This method should outperform MatchAny if the number of query entries -// approaches the number of filter entries, len(data) >= f.N(). -func (f *Filter) HashMatchAny(key [KeySize]byte, data [][]byte) (bool, error) { - // Basic sanity check. - if len(data) == 0 { - return false, nil - } - - // Create a filter bitstream. - filterData, err := f.Bytes() - if err != nil { - return false, err - } - - b := bstream.NewBStreamReader(filterData) - - var ( - values = make(map[uint32]struct{}, f.N()) - lastValue uint64 - ) - - // First, decompress the filter and construct an index of the keys - // contained within the filter. Index construction terminates after all - // values have been read from the bitstream. - for { - // Read the next diff value from the filter, add it to the - // last value, and set the new value in the index. - value, err := f.readFullUint64(b) - if err == nil { - lastValue += value - values[uint32(lastValue)] = struct{}{} - continue - } else if err == io.EOF { - break - } - - return false, err - } - - // We cache the high and low bits of modulusNP for the multiplication of - // 2 64-bit integers into a 128-bit integer. - nphi := f.modulusNP >> 32 - nplo := uint64(uint32(f.modulusNP)) - - // Finally, run through the provided data items, querying the index to - // determine if the filter contains any elements of interest. - for _, d := range data { - // For each datum, we assign the initial hash to - // a uint64. - v := siphash.Sum64(d, &key) - - // We'll then reduce the value down to the range - // of our modulus. - v = fastReduction(v, nphi, nplo) - - if _, ok := values[uint32(v)]; !ok { - continue - } - - return true, nil - } - - return false, nil -} - -// readFullUint64 reads a value represented by the sum of a unary multiple of -// the filter's P modulus (`2**P`) and a big-endian P-bit remainder. -func (f *Filter) readFullUint64(b *bstream.BStream) (uint64, error) { - var quotient uint64 - - // Count the 1s until we reach a 0. - c, err := b.ReadBit() - if err != nil { - return 0, err - } - for c { - quotient++ - c, err = b.ReadBit() - if err != nil { - return 0, err - } - } - - // Read P bits. - remainder, err := b.ReadBits(int(f.p)) - if err != nil { - return 0, err - } - - // Add the multiple and the remainder. - v := (quotient << f.p) + remainder - return v, nil -} diff --git a/gcs/gcs_test.go b/gcs/gcs_test.go deleted file mode 100644 index bb4d550b7..000000000 --- a/gcs/gcs_test.go +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright (c) 2016-2017 The btcsuite developers -// Copyright (c) 2016-2017 The Lightning Network Developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package gcs_test - -import ( - "bytes" - "encoding/binary" - "math/rand" - "testing" - - "github.com/btcsuite/btcutil/gcs" -) - -var ( - // No need to allocate an err variable in every test - err error - - // Collision probability for the tests (1/2**19) - P = uint8(19) - - // Modulus value for the tests. - M uint64 = 784931 - - // Filters are conserved between tests but we must define with an - // interface which functions we're testing because the gcsFilter type - // isn't exported - filter, filter2, filter3 *gcs.Filter - - // We need to use the same key for building and querying the filters - key [gcs.KeySize]byte - - // List of values for building a filter - contents = [][]byte{ - []byte("Alex"), - []byte("Bob"), - []byte("Charlie"), - []byte("Dick"), - []byte("Ed"), - []byte("Frank"), - []byte("George"), - []byte("Harry"), - []byte("Ilya"), - []byte("John"), - []byte("Kevin"), - []byte("Larry"), - []byte("Michael"), - []byte("Nate"), - []byte("Owen"), - []byte("Paul"), - []byte("Quentin"), - } - - // List of values for querying a filter using MatchAny() - contents2 = [][]byte{ - []byte("Alice"), - []byte("Betty"), - []byte("Charmaine"), - []byte("Donna"), - []byte("Edith"), - []byte("Faina"), - []byte("Georgia"), - []byte("Hannah"), - []byte("Ilsbeth"), - []byte("Jennifer"), - []byte("Kayla"), - []byte("Lena"), - []byte("Michelle"), - []byte("Natalie"), - []byte("Ophelia"), - []byte("Peggy"), - []byte("Queenie"), - } -) - -// TestGCSFilterBuild builds a test filter with a randomized key. For Bitcoin -// use, deterministic filter generation is desired. Therefore, a key that's -// derived deterministically would be required. -func TestGCSFilterBuild(t *testing.T) { - for i := 0; i < gcs.KeySize; i += 4 { - binary.BigEndian.PutUint32(key[i:], rand.Uint32()) - } - filter, err = gcs.BuildGCSFilter(P, M, key, contents) - if err != nil { - t.Fatalf("Filter build failed: %s", err.Error()) - } -} - -// TestGCSMatchZeroHash ensures that Match and MatchAny properly match an item -// if it's hash after the reduction is zero. This is accomplished by brute -// forcing a specific target whose hash is zero given a certain (P, M, key, -// len(elements)) combination. In this case, P and M are the default, key was -// chosen randomly, and len(elements) is 13. The target 4-byte value of 16060032 -// is the first such 32-bit value, thus we use the number 0-11 as the other -// elements in the filter since we know they won't collide. We test both the -// positive and negative cases, when the zero hash item is in the filter and -// when it is excluded. In the negative case, the 32-bit value of 12 is added to -// the filter instead of the target. -func TestGCSMatchZeroHash(t *testing.T) { - t.Run("include zero", func(t *testing.T) { - testGCSMatchZeroHash(t, true) - }) - t.Run("exclude zero", func(t *testing.T) { - testGCSMatchZeroHash(t, false) - }) -} - -func testGCSMatchZeroHash(t *testing.T, includeZeroHash bool) { - key := [gcs.KeySize]byte{ - 0x25, 0x28, 0x0d, 0x25, 0x26, 0xe1, 0xd3, 0xc7, - 0xa5, 0x71, 0x85, 0x34, 0x92, 0xa5, 0x7e, 0x68, - } - - // Construct the target data to match, whose hash is zero after applying - // the reduction with the parameters in the test. - target := make([]byte, 4) - binary.BigEndian.PutUint32(target, 16060032) - - // Construct the set of 13 items including the target, using the 32-bit - // values of 0 through 11 as the first 12 items. We known none of these - // hash to zero since the brute force ended well beyond them. - elements := make([][]byte, 0, 13) - for i := 0; i < 12; i++ { - data := make([]byte, 4) - binary.BigEndian.PutUint32(data, uint32(i)) - elements = append(elements, data) - } - - // If the filter should include the zero hash element, add the target - // which we know hashes to zero. Otherwise add 32-bit value of 12 which - // we know does not hash to zero. - if includeZeroHash { - elements = append(elements, target) - } else { - data := make([]byte, 4) - binary.BigEndian.PutUint32(data, 12) - elements = append(elements, data) - } - - filter, err := gcs.BuildGCSFilter(P, M, key, elements) - if err != nil { - t.Fatalf("unable to build filter: %v", err) - } - - match, err := filter.Match(key, target) - if err != nil { - t.Fatalf("unable to match: %v", err) - } - - // We should only get a match iff the target was included. - if match != includeZeroHash { - t.Fatalf("expected match from Match: %t, got %t", - includeZeroHash, match) - } - - match, err = filter.MatchAny(key, [][]byte{target}) - if err != nil { - t.Fatalf("unable to match any: %v", err) - } - - // We should only get a match iff the target was included. - if match != includeZeroHash { - t.Fatalf("expected match from MatchAny: %t, got %t", - includeZeroHash, match) - } -} - -// TestGCSFilterCopy deserializes and serializes a filter to create a copy. -func TestGCSFilterCopy(t *testing.T) { - serialized2, err := filter.Bytes() - if err != nil { - t.Fatalf("Filter Bytes() failed: %v", err) - } - filter2, err = gcs.FromBytes(filter.N(), P, M, serialized2) - if err != nil { - t.Fatalf("Filter copy failed: %s", err.Error()) - } - serialized3, err := filter.NBytes() - if err != nil { - t.Fatalf("Filter NBytes() failed: %v", err) - } - filter3, err = gcs.FromNBytes(filter.P(), M, serialized3) - if err != nil { - t.Fatalf("Filter copy failed: %s", err.Error()) - } -} - -// TestGCSFilterMetadata checks that the filter metadata is built and copied -// correctly. -func TestGCSFilterMetadata(t *testing.T) { - if filter.P() != P { - t.Fatal("P not correctly stored in filter metadata") - } - if filter.N() != uint32(len(contents)) { - t.Fatal("N not correctly stored in filter metadata") - } - if filter.P() != filter2.P() { - t.Fatal("P doesn't match between copied filters") - } - if filter.P() != filter3.P() { - t.Fatal("P doesn't match between copied filters") - } - if filter.N() != filter2.N() { - t.Fatal("N doesn't match between copied filters") - } - if filter.N() != filter3.N() { - t.Fatal("N doesn't match between copied filters") - } - serialized, err := filter.Bytes() - if err != nil { - t.Fatalf("Filter Bytes() failed: %v", err) - } - serialized2, err := filter2.Bytes() - if err != nil { - t.Fatalf("Filter Bytes() failed: %v", err) - } - if !bytes.Equal(serialized, serialized2) { - t.Fatal("Bytes don't match between copied filters") - } - serialized3, err := filter3.Bytes() - if err != nil { - t.Fatalf("Filter Bytes() failed: %v", err) - } - if !bytes.Equal(serialized, serialized3) { - t.Fatal("Bytes don't match between copied filters") - } - serialized4, err := filter3.Bytes() - if err != nil { - t.Fatalf("Filter Bytes() failed: %v", err) - } - if !bytes.Equal(serialized, serialized4) { - t.Fatal("Bytes don't match between copied filters") - } -} - -// TestGCSFilterMatch checks that both the built and copied filters match -// correctly, logging any false positives without failing on them. -func TestGCSFilterMatch(t *testing.T) { - match, err := filter.Match(key, []byte("Nate")) - if err != nil { - t.Fatalf("Filter match failed: %s", err.Error()) - } - if !match { - t.Fatal("Filter didn't match when it should have!") - } - match, err = filter2.Match(key, []byte("Nate")) - if err != nil { - t.Fatalf("Filter match failed: %s", err.Error()) - } - if !match { - t.Fatal("Filter didn't match when it should have!") - } - match, err = filter.Match(key, []byte("Quentin")) - if err != nil { - t.Fatalf("Filter match failed: %s", err.Error()) - } - if !match { - t.Fatal("Filter didn't match when it should have!") - } - match, err = filter2.Match(key, []byte("Quentin")) - if err != nil { - t.Fatalf("Filter match failed: %s", err.Error()) - } - if !match { - t.Fatal("Filter didn't match when it should have!") - } - match, err = filter.Match(key, []byte("Nates")) - if err != nil { - t.Fatalf("Filter match failed: %s", err.Error()) - } - if match { - t.Logf("False positive match, should be 1 in 2**%d!", P) - } - match, err = filter2.Match(key, []byte("Nates")) - if err != nil { - t.Fatalf("Filter match failed: %s", err.Error()) - } - if match { - t.Logf("False positive match, should be 1 in 2**%d!", P) - } - match, err = filter.Match(key, []byte("Quentins")) - if err != nil { - t.Fatalf("Filter match failed: %s", err.Error()) - } - if match { - t.Logf("False positive match, should be 1 in 2**%d!", P) - } - match, err = filter2.Match(key, []byte("Quentins")) - if err != nil { - t.Fatalf("Filter match failed: %s", err.Error()) - } - if match { - t.Logf("False positive match, should be 1 in 2**%d!", P) - } -} - -// AnyMatcher is the function signature of our matching algorithms. -type AnyMatcher func(key [gcs.KeySize]byte, data [][]byte) (bool, error) - -// TestGCSFilterMatchAnySuite checks that all of our matching algorithms -// properly match a list correctly when using built or copied filters, logging -// any false positives without failing on them. -func TestGCSFilterMatchAnySuite(t *testing.T) { - funcs := []struct { - name string - matchAny func(*gcs.Filter) AnyMatcher - }{ - { - "default", - func(f *gcs.Filter) AnyMatcher { - return f.MatchAny - }, - }, - { - "hash", - func(f *gcs.Filter) AnyMatcher { - return f.HashMatchAny - }, - }, - { - "zip", - func(f *gcs.Filter) AnyMatcher { - return f.ZipMatchAny - }, - }, - } - - for _, test := range funcs { - test := test - - t.Run(test.name, func(t *testing.T) { - contentsCopy := make([][]byte, len(contents2)) - copy(contentsCopy, contents2) - - match, err := test.matchAny(filter)(key, contentsCopy) - if err != nil { - t.Fatalf("Filter match any failed: %s", err.Error()) - } - if match { - t.Logf("False positive match, should be 1 in 2**%d!", P) - } - match, err = test.matchAny(filter2)(key, contentsCopy) - if err != nil { - t.Fatalf("Filter match any failed: %s", err.Error()) - } - if match { - t.Logf("False positive match, should be 1 in 2**%d!", P) - } - contentsCopy = append(contentsCopy, []byte("Nate")) - match, err = test.matchAny(filter)(key, contentsCopy) - if err != nil { - t.Fatalf("Filter match any failed: %s", err.Error()) - } - if !match { - t.Fatal("Filter didn't match any when it should have!") - } - match, err = test.matchAny(filter2)(key, contentsCopy) - if err != nil { - t.Fatalf("Filter match any failed: %s", err.Error()) - } - if !match { - t.Fatal("Filter didn't match any when it should have!") - } - }) - } -} diff --git a/gcs/gcsbench_test.go b/gcs/gcsbench_test.go deleted file mode 100644 index ffb2599d1..000000000 --- a/gcs/gcsbench_test.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright (c) 2016-2017 The btcsuite developers -// Copyright (c) 2016-2017 The Lightning Network Developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package gcs_test - -import ( - "encoding/binary" - "math/rand" - "testing" - - "github.com/btcsuite/btcutil/gcs" -) - -func genRandFilterElements(numElements uint) ([][]byte, error) { - testContents := make([][]byte, numElements) - for i := range testContents { - randElem := make([]byte, 32) - if _, err := rand.Read(randElem); err != nil { - return nil, err - } - testContents[i] = randElem - } - - return testContents, nil -} - -var ( - generatedFilter *gcs.Filter -) - -// BenchmarkGCSFilterBuild benchmarks building a filter. -func BenchmarkGCSFilterBuild50000(b *testing.B) { - var testKey [gcs.KeySize]byte - for i := 0; i < gcs.KeySize; i += 4 { - binary.BigEndian.PutUint32(testKey[i:], rand.Uint32()) - } - - randFilterElems, genErr := genRandFilterElements(50000) - if err != nil { - b.Fatalf("unable to generate random item: %v", genErr) - } - - b.ReportAllocs() - b.ResetTimer() - - var localFilter *gcs.Filter - for i := 0; i < b.N; i++ { - localFilter, err = gcs.BuildGCSFilter( - P, M, key, randFilterElems, - ) - if err != nil { - b.Fatalf("unable to generate filter: %v", err) - } - } - generatedFilter = localFilter -} - -// BenchmarkGCSFilterBuild benchmarks building a filter. -func BenchmarkGCSFilterBuild100000(b *testing.B) { - var testKey [gcs.KeySize]byte - for i := 0; i < gcs.KeySize; i += 4 { - binary.BigEndian.PutUint32(testKey[i:], rand.Uint32()) - } - - randFilterElems, genErr := genRandFilterElements(100000) - if err != nil { - b.Fatalf("unable to generate random item: %v", genErr) - } - - b.ReportAllocs() - b.ResetTimer() - - var localFilter *gcs.Filter - for i := 0; i < b.N; i++ { - localFilter, err = gcs.BuildGCSFilter( - P, M, key, randFilterElems, - ) - if err != nil { - b.Fatalf("unable to generate filter: %v", err) - } - } - generatedFilter = localFilter -} - -var ( - match bool -) - -// BenchmarkGCSFilterMatch benchmarks querying a filter for a single value. -func BenchmarkGCSFilterMatch(b *testing.B) { - filter, err := gcs.BuildGCSFilter(P, M, key, contents) - if err != nil { - b.Fatalf("Failed to build filter") - } - - b.ReportAllocs() - b.ResetTimer() - - var localMatch bool - for i := 0; i < b.N; i++ { - _, err = filter.Match(key, []byte("Nate")) - if err != nil { - b.Fatalf("unable to match filter: %v", err) - } - - localMatch, err = filter.Match(key, []byte("Nates")) - if err != nil { - b.Fatalf("unable to match filter: %v", err) - } - } - match = localMatch -} - -var ( - randElems100, _ = genRandFilterElements(100) - randElems1000, _ = genRandFilterElements(1000) - randElems10000, _ = genRandFilterElements(10000) - randElems100000, _ = genRandFilterElements(100000) - randElems1000000, _ = genRandFilterElements(1000000) - randElems10000000, _ = genRandFilterElements(10000000) - - filterElems1000, _ = genRandFilterElements(1000) - filter1000, _ = gcs.BuildGCSFilter(P, M, key, filterElems1000) - filterElems5000, _ = genRandFilterElements(5000) - filter5000, _ = gcs.BuildGCSFilter(P, M, key, filterElems5000) - filterElems10000, _ = genRandFilterElements(10000) - filter10000, _ = gcs.BuildGCSFilter(P, M, key, filterElems10000) -) - -// matchAnyBenchmarks contains combinations of random filters and queries used -// to measure performance of various MatchAny implementations. -var matchAnyBenchmarks = []struct { - name string - query [][]byte - filter *gcs.Filter -}{ - {"q100-f1K", randElems100, filter1000}, - {"q1K-f1K", randElems1000, filter1000}, - {"q10K-f1K", randElems10000, filter1000}, - {"q100K-f1K", randElems100000, filter1000}, - {"q1M-f1K", randElems1000000, filter1000}, - {"q10M-f1K", randElems10000000, filter1000}, - - {"q100-f5K", randElems100, filter5000}, - {"q1K-f5K", randElems1000, filter5000}, - {"q10K-f5K", randElems10000, filter5000}, - {"q100K-f5K", randElems100000, filter5000}, - {"q1M-f5K", randElems1000000, filter5000}, - {"q10M-f5K", randElems10000000, filter5000}, - - {"q100-f10K", randElems100, filter10000}, - {"q1K-f10K", randElems1000, filter10000}, - {"q10K-f10K", randElems10000, filter10000}, - {"q100K-f10K", randElems100000, filter10000}, - {"q1M-f10K", randElems1000000, filter10000}, - {"q10M-f10K", randElems10000000, filter10000}, -} - -// BenchmarkGCSFilterMatchAny benchmarks the sort-and-zip MatchAny impl. -func BenchmarkGCSFilterZipMatchAny(b *testing.B) { - for _, test := range matchAnyBenchmarks { - test := test - - b.Run(test.name, func(b *testing.B) { - b.ReportAllocs() - - var ( - localMatch bool - err error - ) - - for i := 0; i < b.N; i++ { - localMatch, err = test.filter.ZipMatchAny( - key, test.query, - ) - if err != nil { - b.Fatalf("unable to match filter: %v", err) - } - } - match = localMatch - }) - } -} - -// BenchmarkGCSFilterMatchAny benchmarks the hash-join MatchAny impl. -func BenchmarkGCSFilterHashMatchAny(b *testing.B) { - for _, test := range matchAnyBenchmarks { - test := test - - b.Run(test.name, func(b *testing.B) { - b.ReportAllocs() - - var ( - localMatch bool - err error - ) - - for i := 0; i < b.N; i++ { - localMatch, err = test.filter.HashMatchAny( - key, test.query, - ) - if err != nil { - b.Fatalf("unable to match filter: %v", err) - } - } - match = localMatch - }) - } -} - -// BenchmarkGCSFilterMatchAny benchmarks the hybrid MatchAny impl. -func BenchmarkGCSFilterMatchAny(b *testing.B) { - for _, test := range matchAnyBenchmarks { - test := test - - b.Run(test.name, func(b *testing.B) { - b.ReportAllocs() - - var ( - localMatch bool - err error - ) - - for i := 0; i < b.N; i++ { - localMatch, err = test.filter.MatchAny( - key, test.query, - ) - if err != nil { - b.Fatalf("unable to match filter: %v", err) - } - } - match = localMatch - }) - } -} diff --git a/go.mod b/go.mod deleted file mode 100644 index 11583b4b8..000000000 --- a/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module github.com/btcsuite/btcutil - -go 1.14 - -require ( - github.com/aead/siphash v1.0.1 - github.com/btcsuite/btcd v0.20.1-beta - github.com/davecgh/go-spew v1.1.0 - github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 - golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 99468a100..000000000 --- a/go.sum +++ /dev/null @@ -1,55 +0,0 @@ -github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd h1:qdGvebPBDuYDPGi1WCPjy1tGyMpmDK8IEapSsszn7HE= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723 h1:ZA/jbKoGcVAnER6pCHPEkGdZOV7U1oLUedErBHCUMs0= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d h1:2+ZP7EfsZV7Vvmx3TIqSlSzATMkTAKqM14YGFPoSKjI= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/hash160.go b/hash160.go deleted file mode 100644 index fa26e09bf..000000000 --- a/hash160.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2013-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil - -import ( - "crypto/sha256" - "hash" - - "golang.org/x/crypto/ripemd160" -) - -// Calculate the hash of hasher over buf. -func calcHash(buf []byte, hasher hash.Hash) []byte { - _, _ = hasher.Write(buf) - return hasher.Sum(nil) -} - -// Hash160 calculates the hash ripemd160(sha256(b)). -func Hash160(buf []byte) []byte { - return calcHash(calcHash(buf, sha256.New()), ripemd160.New()) -} diff --git a/hdkeychain/README.md b/hdkeychain/README.md deleted file mode 100644 index f9c15d897..000000000 --- a/hdkeychain/README.md +++ /dev/null @@ -1,59 +0,0 @@ -hdkeychain -========== - -[![Build Status](http://img.shields.io/travis/btcsuite/btcutil.svg)](https://travis-ci.org/btcsuite/btcutil) -[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) -[![GoDoc](http://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcutil/hdkeychain) - -Package hdkeychain provides an API for bitcoin hierarchical deterministic -extended keys (BIP0032). - -A comprehensive suite of tests is provided to ensure proper functionality. See -`test_coverage.txt` for the gocov coverage report. Alternatively, if you are -running a POSIX OS, you can run the `cov_report.sh` script for a real-time -report. - -## Feature Overview - -- Full BIP0032 implementation -- Single type for private and public extended keys -- Convenient cryptograpically secure seed generation -- Simple creation of master nodes -- Support for multi-layer derivation -- Easy serialization and deserialization for both private and public extended - keys -- Support for custom networks by registering them with chaincfg -- Obtaining the underlying EC pubkeys, EC privkeys, and associated bitcoin - addresses ties in seamlessly with existing btcec and btcutil types which - provide powerful tools for working with them to do things like sign - transations and generate payment scripts -- Uses the btcec package which is highly optimized for secp256k1 -- Code examples including: - - Generating a cryptographically secure random seed and deriving a - master node from it - - Default HD wallet layout as described by BIP0032 - - Audits use case as described by BIP0032 -- Comprehensive test coverage including the BIP0032 test vectors -- Benchmarks - -## Installation and Updating - -```bash -$ go get -u github.com/btcsuite/btcutil/hdkeychain -``` - -## Examples - -* [NewMaster Example](http://godoc.org/github.com/btcsuite/btcutil/hdkeychain#example-NewMaster) - Demonstrates how to generate a cryptographically random seed then use it to - create a new master node (extended key). -* [Default Wallet Layout Example](http://godoc.org/github.com/btcsuite/btcutil/hdkeychain#example-package--DefaultWalletLayout) - Demonstrates the default hierarchical deterministic wallet layout as described - in BIP0032. -* [Audits Use Case Example](http://godoc.org/github.com/btcsuite/btcutil/hdkeychain#example-package--Audits) - Demonstrates the audits use case in BIP0032. - -## License - -Package hdkeychain is licensed under the [copyfree](http://copyfree.org) ISC -License. diff --git a/hdkeychain/bench_test.go b/hdkeychain/bench_test.go deleted file mode 100644 index 7c86ca931..000000000 --- a/hdkeychain/bench_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package hdkeychain_test - -import ( - "testing" - - "github.com/btcsuite/btcutil/hdkeychain" -) - -// bip0032MasterPriv1 is the master private extended key from the first set of -// test vectors in BIP0032. -const bip0032MasterPriv1 = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbP" + - "y6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" - -// BenchmarkDeriveHardened benchmarks how long it takes to derive a hardened -// child from a master private extended key. -func BenchmarkDeriveHardened(b *testing.B) { - b.StopTimer() - masterKey, err := hdkeychain.NewKeyFromString(bip0032MasterPriv1) - if err != nil { - b.Errorf("Failed to decode master seed: %v", err) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - _, _ = masterKey.Derive(hdkeychain.HardenedKeyStart) - } -} - -// BenchmarkDeriveNormal benchmarks how long it takes to derive a normal -// (non-hardened) child from a master private extended key. -func BenchmarkDeriveNormal(b *testing.B) { - b.StopTimer() - masterKey, err := hdkeychain.NewKeyFromString(bip0032MasterPriv1) - if err != nil { - b.Errorf("Failed to decode master seed: %v", err) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - _, _ = masterKey.Derive(0) - } -} - -// BenchmarkPrivToPub benchmarks how long it takes to convert a private extended -// key to a public extended key. -func BenchmarkPrivToPub(b *testing.B) { - b.StopTimer() - masterKey, err := hdkeychain.NewKeyFromString(bip0032MasterPriv1) - if err != nil { - b.Errorf("Failed to decode master seed: %v", err) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - _, _ = masterKey.Neuter() - } -} - -// BenchmarkDeserialize benchmarks how long it takes to deserialize a private -// extended key. -func BenchmarkDeserialize(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _ = hdkeychain.NewKeyFromString(bip0032MasterPriv1) - } -} - -// BenchmarkSerialize benchmarks how long it takes to serialize a private -// extended key. -func BenchmarkSerialize(b *testing.B) { - b.StopTimer() - masterKey, err := hdkeychain.NewKeyFromString(bip0032MasterPriv1) - if err != nil { - b.Errorf("Failed to decode master seed: %v", err) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - _ = masterKey.String() - } -} diff --git a/hdkeychain/cov_report.sh b/hdkeychain/cov_report.sh deleted file mode 100644 index 307f05b76..000000000 --- a/hdkeychain/cov_report.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -# This script uses gocov to generate a test coverage report. -# The gocov tool my be obtained with the following command: -# go get github.com/axw/gocov/gocov -# -# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. - -# Check for gocov. -type gocov >/dev/null 2>&1 -if [ $? -ne 0 ]; then - echo >&2 "This script requires the gocov tool." - echo >&2 "You may obtain it with the following command:" - echo >&2 "go get github.com/axw/gocov/gocov" - exit 1 -fi -gocov test | gocov report diff --git a/hdkeychain/doc.go b/hdkeychain/doc.go deleted file mode 100644 index dcf74f6b5..000000000 --- a/hdkeychain/doc.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -/* -Package hdkeychain provides an API for bitcoin hierarchical deterministic -extended keys (BIP0032). - -Overview - -The ability to implement hierarchical deterministic wallets depends on the -ability to create and derive hierarchical deterministic extended keys. - -At a high level, this package provides support for those hierarchical -deterministic extended keys by providing an ExtendedKey type and supporting -functions. Each extended key can either be a private or public extended key -which itself is capable of deriving a child extended key. - -Determining the Extended Key Type - -Whether an extended key is a private or public extended key can be determined -with the IsPrivate function. - -Transaction Signing Keys and Payment Addresses - -In order to create and sign transactions, or provide others with addresses to -send funds to, the underlying key and address material must be accessible. This -package provides the ECPubKey, ECPrivKey, and Address functions for this -purpose. - -The Master Node - -As previously mentioned, the extended keys are hierarchical meaning they are -used to form a tree. The root of that tree is called the master node and this -package provides the NewMaster function to create it from a cryptographically -random seed. The GenerateSeed function is provided as a convenient way to -create a random seed for use with the NewMaster function. - -Deriving Children - -Once you have created a tree root (or have deserialized an extended key as -discussed later), the child extended keys can be derived by using the Derive -function. The Derive function supports deriving both normal (non-hardened) and -hardened child extended keys. In order to derive a hardened extended key, use -the HardenedKeyStart constant + the hardened key number as the index to the -Derive function. This provides the ability to cascade the keys into a tree and -hence generate the hierarchical deterministic key chains. - -Normal vs Hardened Derived Extended Keys - -A private extended key can be used to derive both hardened and non-hardened -(normal) child private and public extended keys. A public extended key can only -be used to derive non-hardened child public extended keys. As enumerated in -BIP0032 "knowledge of the extended public key plus any non-hardened private key -descending from it is equivalent to knowing the extended private key (and thus -every private and public key descending from it). This means that extended -public keys must be treated more carefully than regular public keys. It is also -the reason for the existence of hardened keys, and why they are used for the -account level in the tree. This way, a leak of an account-specific (or below) -private key never risks compromising the master or other accounts." - -Neutering a Private Extended Key - -A private extended key can be converted to a new instance of the corresponding -public extended key with the Neuter function. The original extended key is not -modified. A public extended key is still capable of deriving non-hardened child -public extended keys. - -Serializing and Deserializing Extended Keys - -Extended keys are serialized and deserialized with the String and -NewKeyFromString functions. The serialized key is a Base58-encoded string which -looks like the following: - public key: xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw - private key: xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7 - -Network - -Extended keys are much like normal Bitcoin addresses in that they have version -bytes which tie them to a specific network. The SetNet and IsForNet functions -are provided to set and determinine which network an extended key is associated -with. -*/ -package hdkeychain diff --git a/hdkeychain/example_test.go b/hdkeychain/example_test.go deleted file mode 100644 index e7f462d0d..000000000 --- a/hdkeychain/example_test.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package hdkeychain_test - -import ( - "fmt" - - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil/hdkeychain" -) - -// This example demonstrates how to generate a cryptographically random seed -// then use it to create a new master node (extended key). -func ExampleNewMaster() { - // Generate a random seed at the recommended length. - seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen) - if err != nil { - fmt.Println(err) - return - } - - // Generate a new master node using the seed. - key, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) - if err != nil { - fmt.Println(err) - return - } - - // Show that the generated master node extended key is private. - fmt.Println("Private Extended Key?:", key.IsPrivate()) - - // Output: - // Private Extended Key?: true -} - -// This example demonstrates the default hierarchical deterministic wallet -// layout as described in BIP0032. -func Example_defaultWalletLayout() { - // The default wallet layout described in BIP0032 is: - // - // Each account is composed of two keypair chains: an internal and an - // external one. The external keychain is used to generate new public - // addresses, while the internal keychain is used for all other - // operations (change addresses, generation addresses, ..., anything - // that doesn't need to be communicated). - // - // * m/iH/0/k - // corresponds to the k'th keypair of the external chain of account - // number i of the HDW derived from master m. - // * m/iH/1/k - // corresponds to the k'th keypair of the internal chain of account - // number i of the HDW derived from master m. - - // Ordinarily this would either be read from some encrypted source - // and be decrypted or generated as the NewMaster example shows, but - // for the purposes of this example, the private extended key for the - // master node is being hard coded here. - master := "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jP" + - "PqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" - - // Start by getting an extended key instance for the master node. - // This gives the path: - // m - masterKey, err := hdkeychain.NewKeyFromString(master) - if err != nil { - fmt.Println(err) - return - } - - // Derive the extended key for account 0. This gives the path: - // m/0H - acct0, err := masterKey.Derive(hdkeychain.HardenedKeyStart + 0) - if err != nil { - fmt.Println(err) - return - } - - // Derive the extended key for the account 0 external chain. This - // gives the path: - // m/0H/0 - acct0Ext, err := acct0.Derive(0) - if err != nil { - fmt.Println(err) - return - } - - // Derive the extended key for the account 0 internal chain. This gives - // the path: - // m/0H/1 - acct0Int, err := acct0.Derive(1) - if err != nil { - fmt.Println(err) - return - } - - // At this point, acct0Ext and acct0Int are ready to derive the keys for - // the external and internal wallet chains. - - // Derive the 10th extended key for the account 0 external chain. This - // gives the path: - // m/0H/0/10 - acct0Ext10, err := acct0Ext.Derive(10) - if err != nil { - fmt.Println(err) - return - } - - // Derive the 1st extended key for the account 0 internal chain. This - // gives the path: - // m/0H/1/0 - acct0Int0, err := acct0Int.Derive(0) - if err != nil { - fmt.Println(err) - return - } - - // Get and show the address associated with the extended keys for the - // main bitcoin network. - acct0ExtAddr, err := acct0Ext10.Address(&chaincfg.MainNetParams) - if err != nil { - fmt.Println(err) - return - } - acct0IntAddr, err := acct0Int0.Address(&chaincfg.MainNetParams) - if err != nil { - fmt.Println(err) - return - } - fmt.Println("Account 0 External Address 10:", acct0ExtAddr) - fmt.Println("Account 0 Internal Address 0:", acct0IntAddr) - - // Output: - // Account 0 External Address 10: 1HVccubUT8iKTapMJ5AnNA4sLRN27xzQ4F - // Account 0 Internal Address 0: 1J5rebbkQaunJTUoNVREDbeB49DqMNFFXk -} - -// This example demonstrates the audits use case in BIP0032. -func Example_audits() { - // The audits use case described in BIP0032 is: - // - // In case an auditor needs full access to the list of incoming and - // outgoing payments, one can share all account public extended keys. - // This will allow the auditor to see all transactions from and to the - // wallet, in all accounts, but not a single secret key. - // - // * N(m/*) - // corresponds to the neutered master extended key (also called - // the master public extended key) - - // Ordinarily this would either be read from some encrypted source - // and be decrypted or generated as the NewMaster example shows, but - // for the purposes of this example, the private extended key for the - // master node is being hard coded here. - master := "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jP" + - "PqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" - - // Start by getting an extended key instance for the master node. - // This gives the path: - // m - masterKey, err := hdkeychain.NewKeyFromString(master) - if err != nil { - fmt.Println(err) - return - } - - // Neuter the master key to generate a master public extended key. This - // gives the path: - // N(m/*) - masterPubKey, err := masterKey.Neuter() - if err != nil { - fmt.Println(err) - return - } - - // Share the master public extended key with the auditor. - fmt.Println("Audit key N(m/*):", masterPubKey) - - // Output: - // Audit key N(m/*): xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8 -} diff --git a/hdkeychain/extendedkey.go b/hdkeychain/extendedkey.go deleted file mode 100644 index 929fe9968..000000000 --- a/hdkeychain/extendedkey.go +++ /dev/null @@ -1,706 +0,0 @@ -// Copyright (c) 2014-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package hdkeychain - -// References: -// [BIP32]: BIP0032 - Hierarchical Deterministic Wallets -// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki - -import ( - "bytes" - "crypto/hmac" - "crypto/rand" - "crypto/sha512" - "encoding/binary" - "errors" - "fmt" - "math/big" - - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/base58" -) - -const ( - // RecommendedSeedLen is the recommended length in bytes for a seed - // to a master node. - RecommendedSeedLen = 32 // 256 bits - - // HardenedKeyStart is the index at which a hardened key starts. Each - // extended key has 2^31 normal child keys and 2^31 hardened child keys. - // Thus the range for normal child keys is [0, 2^31 - 1] and the range - // for hardened child keys is [2^31, 2^32 - 1]. - HardenedKeyStart = 0x80000000 // 2^31 - - // MinSeedBytes is the minimum number of bytes allowed for a seed to - // a master node. - MinSeedBytes = 16 // 128 bits - - // MaxSeedBytes is the maximum number of bytes allowed for a seed to - // a master node. - MaxSeedBytes = 64 // 512 bits - - // serializedKeyLen is the length of a serialized public or private - // extended key. It consists of 4 bytes version, 1 byte depth, 4 bytes - // fingerprint, 4 bytes child number, 32 bytes chain code, and 33 bytes - // public/private key data. - serializedKeyLen = 4 + 1 + 4 + 4 + 32 + 33 // 78 bytes - - // maxUint8 is the max positive integer which can be serialized in a uint8 - maxUint8 = 1<<8 - 1 -) - -var ( - // ErrDeriveHardFromPublic describes an error in which the caller - // attempted to derive a hardened extended key from a public key. - ErrDeriveHardFromPublic = errors.New("cannot derive a hardened key " + - "from a public key") - - // ErrDeriveBeyondMaxDepth describes an error in which the caller - // has attempted to derive more than 255 keys from a root key. - ErrDeriveBeyondMaxDepth = errors.New("cannot derive a key with more than " + - "255 indices in its path") - - // ErrNotPrivExtKey describes an error in which the caller attempted - // to extract a private key from a public extended key. - ErrNotPrivExtKey = errors.New("unable to create private keys from a " + - "public extended key") - - // ErrInvalidChild describes an error in which the child at a specific - // index is invalid due to the derived key falling outside of the valid - // range for secp256k1 private keys. This error indicates the caller - // should simply ignore the invalid child extended key at this index and - // increment to the next index. - ErrInvalidChild = errors.New("the extended key at this index is invalid") - - // ErrUnusableSeed describes an error in which the provided seed is not - // usable due to the derived key falling outside of the valid range for - // secp256k1 private keys. This error indicates the caller must choose - // another seed. - ErrUnusableSeed = errors.New("unusable seed") - - // ErrInvalidSeedLen describes an error in which the provided seed or - // seed length is not in the allowed range. - ErrInvalidSeedLen = fmt.Errorf("seed length must be between %d and %d "+ - "bits", MinSeedBytes*8, MaxSeedBytes*8) - - // ErrBadChecksum describes an error in which the checksum encoded with - // a serialized extended key does not match the calculated value. - ErrBadChecksum = errors.New("bad extended key checksum") - - // ErrInvalidKeyLen describes an error in which the provided serialized - // key is not the expected length. - ErrInvalidKeyLen = errors.New("the provided serialized extended key " + - "length is invalid") -) - -// masterKey is the master key used along with a random seed used to generate -// the master node in the hierarchical tree. -var masterKey = []byte("Bitcoin seed") - -// ExtendedKey houses all the information needed to support a hierarchical -// deterministic extended key. See the package overview documentation for -// more details on how to use extended keys. -type ExtendedKey struct { - key []byte // This will be the pubkey for extended pub keys - pubKey []byte // This will only be set for extended priv keys - chainCode []byte - depth uint8 - parentFP []byte - childNum uint32 - version []byte - isPrivate bool -} - -// NewExtendedKey returns a new instance of an extended key with the given -// fields. No error checking is performed here as it's only intended to be a -// convenience method used to create a populated struct. This function should -// only be used by applications that need to create custom ExtendedKeys. All -// other applications should just use NewMaster, Derive, or Neuter. -func NewExtendedKey(version, key, chainCode, parentFP []byte, depth uint8, - childNum uint32, isPrivate bool) *ExtendedKey { - - // NOTE: The pubKey field is intentionally left nil so it is only - // computed and memoized as required. - return &ExtendedKey{ - key: key, - chainCode: chainCode, - depth: depth, - parentFP: parentFP, - childNum: childNum, - version: version, - isPrivate: isPrivate, - } -} - -// pubKeyBytes returns bytes for the serialized compressed public key associated -// with this extended key in an efficient manner including memoization as -// necessary. -// -// When the extended key is already a public key, the key is simply returned as -// is since it's already in the correct form. However, when the extended key is -// a private key, the public key will be calculated and memoized so future -// accesses can simply return the cached result. -func (k *ExtendedKey) pubKeyBytes() []byte { - // Just return the key if it's already an extended public key. - if !k.isPrivate { - return k.key - } - - // This is a private extended key, so calculate and memoize the public - // key if needed. - if len(k.pubKey) == 0 { - pkx, pky := btcec.S256().ScalarBaseMult(k.key) - pubKey := btcec.PublicKey{Curve: btcec.S256(), X: pkx, Y: pky} - k.pubKey = pubKey.SerializeCompressed() - } - - return k.pubKey -} - -// IsPrivate returns whether or not the extended key is a private extended key. -// -// A private extended key can be used to derive both hardened and non-hardened -// child private and public extended keys. A public extended key can only be -// used to derive non-hardened child public extended keys. -func (k *ExtendedKey) IsPrivate() bool { - return k.isPrivate -} - -// Depth returns the current derivation level with respect to the root. -// -// The root key has depth zero, and the field has a maximum of 255 due to -// how depth is serialized. -func (k *ExtendedKey) Depth() uint8 { - return k.depth -} - -// Version returns the extended key's hardened derivation version. This can be -// used to identify the extended key's type. -func (k *ExtendedKey) Version() []byte { - return k.version -} - -// ParentFingerprint returns a fingerprint of the parent extended key from which -// this one was derived. -func (k *ExtendedKey) ParentFingerprint() uint32 { - return binary.BigEndian.Uint32(k.parentFP) -} - -// ChainCode returns the chain code part of this extended key. -// -// It is identical for both public and private extended keys. -func (k *ExtendedKey) ChainCode() []byte { - return append([]byte{}, k.chainCode...) -} - -// Derive returns a derived child extended key at the given index. -// -// IMPORTANT: if you were previously using the Child method, this method is incompatible. -// The Child method had a BIP-32 standard compatibility issue. You have to check whether -// any hardened derivations in your derivation path are affected by this issue, via the -// IsAffectedByIssue172 method and migrate the wallet if so. This method does conform -// to the standard. If you need the old behavior, use DeriveNonStandard. -// -// When this extended key is a private extended key (as determined by the IsPrivate -// function), a private extended key will be derived. Otherwise, the derived -// extended key will be also be a public extended key. -// -// When the index is greater to or equal than the HardenedKeyStart constant, the -// derived extended key will be a hardened extended key. It is only possible to -// derive a hardened extended key from a private extended key. Consequently, -// this function will return ErrDeriveHardFromPublic if a hardened child -// extended key is requested from a public extended key. -// -// A hardened extended key is useful since, as previously mentioned, it requires -// a parent private extended key to derive. In other words, normal child -// extended public keys can be derived from a parent public extended key (no -// knowledge of the parent private key) whereas hardened extended keys may not -// be. -// -// NOTE: There is an extremely small chance (< 1 in 2^127) the specific child -// index does not derive to a usable child. The ErrInvalidChild error will be -// returned if this should occur, and the caller is expected to ignore the -// invalid child and simply increment to the next index. -func (k *ExtendedKey) Derive(i uint32) (*ExtendedKey, error) { - // Prevent derivation of children beyond the max allowed depth. - if k.depth == maxUint8 { - return nil, ErrDeriveBeyondMaxDepth - } - - // There are four scenarios that could happen here: - // 1) Private extended key -> Hardened child private extended key - // 2) Private extended key -> Non-hardened child private extended key - // 3) Public extended key -> Non-hardened child public extended key - // 4) Public extended key -> Hardened child public extended key (INVALID!) - - // Case #4 is invalid, so error out early. - // A hardened child extended key may not be created from a public - // extended key. - isChildHardened := i >= HardenedKeyStart - if !k.isPrivate && isChildHardened { - return nil, ErrDeriveHardFromPublic - } - - // The data used to derive the child key depends on whether or not the - // child is hardened per [BIP32]. - // - // For hardened children: - // 0x00 || ser256(parentKey) || ser32(i) - // - // For normal children: - // serP(parentPubKey) || ser32(i) - keyLen := 33 - data := make([]byte, keyLen+4) - if isChildHardened { - // Case #1. - // When the child is a hardened child, the key is known to be a - // private key due to the above early return. Pad it with a - // leading zero as required by [BIP32] for deriving the child. - // Additionally, right align it if it's shorter than 32 bytes. - offset := 33 - len(k.key) - copy(data[offset:], k.key) - } else { - // Case #2 or #3. - // This is either a public or private extended key, but in - // either case, the data which is used to derive the child key - // starts with the secp256k1 compressed public key bytes. - copy(data, k.pubKeyBytes()) - } - binary.BigEndian.PutUint32(data[keyLen:], i) - - // Take the HMAC-SHA512 of the current key's chain code and the derived - // data: - // I = HMAC-SHA512(Key = chainCode, Data = data) - hmac512 := hmac.New(sha512.New, k.chainCode) - _, _ = hmac512.Write(data) - ilr := hmac512.Sum(nil) - - // Split "I" into two 32-byte sequences Il and Ir where: - // Il = intermediate key used to derive the child - // Ir = child chain code - il := ilr[:len(ilr)/2] - childChainCode := ilr[len(ilr)/2:] - - // Both derived public or private keys rely on treating the left 32-byte - // sequence calculated above (Il) as a 256-bit integer that must be - // within the valid range for a secp256k1 private key. There is a small - // chance (< 1 in 2^127) this condition will not hold, and in that case, - // a child extended key can't be created for this index and the caller - // should simply increment to the next index. - ilNum := new(big.Int).SetBytes(il) - if ilNum.Cmp(btcec.S256().N) >= 0 || ilNum.Sign() == 0 { - return nil, ErrInvalidChild - } - - // The algorithm used to derive the child key depends on whether or not - // a private or public child is being derived. - // - // For private children: - // childKey = parse256(Il) + parentKey - // - // For public children: - // childKey = serP(point(parse256(Il)) + parentKey) - var isPrivate bool - var childKey []byte - if k.isPrivate { - // Case #1 or #2. - // Add the parent private key to the intermediate private key to - // derive the final child key. - // - // childKey = parse256(Il) + parenKey - keyNum := new(big.Int).SetBytes(k.key) - ilNum.Add(ilNum, keyNum) - ilNum.Mod(ilNum, btcec.S256().N) - childKey = ilNum.Bytes() - isPrivate = true - } else { - // Case #3. - // Calculate the corresponding intermediate public key for - // intermediate private key. - ilx, ily := btcec.S256().ScalarBaseMult(il) - if ilx.Sign() == 0 || ily.Sign() == 0 { - return nil, ErrInvalidChild - } - - // Convert the serialized compressed parent public key into X - // and Y coordinates so it can be added to the intermediate - // public key. - pubKey, err := btcec.ParsePubKey(k.key, btcec.S256()) - if err != nil { - return nil, err - } - - // Add the intermediate public key to the parent public key to - // derive the final child key. - // - // childKey = serP(point(parse256(Il)) + parentKey) - childX, childY := btcec.S256().Add(ilx, ily, pubKey.X, pubKey.Y) - pk := btcec.PublicKey{Curve: btcec.S256(), X: childX, Y: childY} - childKey = pk.SerializeCompressed() - } - - // The fingerprint of the parent for the derived child is the first 4 - // bytes of the RIPEMD160(SHA256(parentPubKey)). - parentFP := btcutil.Hash160(k.pubKeyBytes())[:4] - return NewExtendedKey(k.version, childKey, childChainCode, parentFP, - k.depth+1, i, isPrivate), nil -} - -// Returns true if this key was affected by the BIP-32 issue in the Child -// method (since renamed to DeriveNonStandard). -func (k *ExtendedKey) IsAffectedByIssue172() bool { - return len(k.key) < 32 -} - -// Deprecated: This is a non-standard derivation that is affected by issue #172. -// 1-of-256 hardened derivations will be wrong. See note in the Derive method -// and IsAffectedByIssue172. -func (k *ExtendedKey) DeriveNonStandard(i uint32) (*ExtendedKey, error) { - if k.depth == maxUint8 { - return nil, ErrDeriveBeyondMaxDepth - } - - isChildHardened := i >= HardenedKeyStart - if !k.isPrivate && isChildHardened { - return nil, ErrDeriveHardFromPublic - } - - keyLen := 33 - data := make([]byte, keyLen+4) - if isChildHardened { - copy(data[1:], k.key) - } else { - copy(data, k.pubKeyBytes()) - } - binary.BigEndian.PutUint32(data[keyLen:], i) - - hmac512 := hmac.New(sha512.New, k.chainCode) - _, _ = hmac512.Write(data) - ilr := hmac512.Sum(nil) - - il := ilr[:len(ilr)/2] - childChainCode := ilr[len(ilr)/2:] - - ilNum := new(big.Int).SetBytes(il) - if ilNum.Cmp(btcec.S256().N) >= 0 || ilNum.Sign() == 0 { - return nil, ErrInvalidChild - } - - var isPrivate bool - var childKey []byte - if k.isPrivate { - keyNum := new(big.Int).SetBytes(k.key) - ilNum.Add(ilNum, keyNum) - ilNum.Mod(ilNum, btcec.S256().N) - childKey = ilNum.Bytes() - isPrivate = true - } else { - ilx, ily := btcec.S256().ScalarBaseMult(il) - if ilx.Sign() == 0 || ily.Sign() == 0 { - return nil, ErrInvalidChild - } - - pubKey, err := btcec.ParsePubKey(k.key, btcec.S256()) - if err != nil { - return nil, err - } - - childX, childY := btcec.S256().Add(ilx, ily, pubKey.X, pubKey.Y) - pk := btcec.PublicKey{Curve: btcec.S256(), X: childX, Y: childY} - childKey = pk.SerializeCompressed() - } - - parentFP := btcutil.Hash160(k.pubKeyBytes())[:4] - return NewExtendedKey(k.version, childKey, childChainCode, parentFP, - k.depth+1, i, isPrivate), nil -} - -// ChildNum returns the index at which the child extended key was derived. -// -// Extended keys with ChildNum value between 0 and 2^31-1 are normal child -// keys, and those with a value between 2^31 and 2^32-1 are hardened keys. -func (k *ExtendedKey) ChildIndex() uint32 { - return k.childNum -} - -// Neuter returns a new extended public key from this extended private key. The -// same extended key will be returned unaltered if it is already an extended -// public key. -// -// As the name implies, an extended public key does not have access to the -// private key, so it is not capable of signing transactions or deriving -// child extended private keys. However, it is capable of deriving further -// child extended public keys. -func (k *ExtendedKey) Neuter() (*ExtendedKey, error) { - // Already an extended public key. - if !k.isPrivate { - return k, nil - } - - // Get the associated public extended key version bytes. - version, err := chaincfg.HDPrivateKeyToPublicKeyID(k.version) - if err != nil { - return nil, err - } - - // Convert it to an extended public key. The key for the new extended - // key will simply be the pubkey of the current extended private key. - // - // This is the function N((k,c)) -> (K, c) from [BIP32]. - return NewExtendedKey(version, k.pubKeyBytes(), k.chainCode, k.parentFP, - k.depth, k.childNum, false), nil -} - -// CloneWithVersion returns a new extended key cloned from this extended key, -// but using the provided HD version bytes. The version must be a private HD -// key ID for an extended private key, and a public HD key ID for an extended -// public key. -// -// This method creates a new copy and therefore does not mutate the original -// extended key instance. -// -// Unlike Neuter(), this does NOT convert an extended private key to an -// extended public key. It is particularly useful for converting between -// standard BIP0032 extended keys (serializable to xprv/xpub) and keys based -// on the SLIP132 standard (serializable to yprv/ypub, zprv/zpub, etc.). -// -// References: -// [SLIP132]: SLIP-0132 - Registered HD version bytes for BIP-0032 -// https://github.com/satoshilabs/slips/blob/master/slip-0132.md -func (k *ExtendedKey) CloneWithVersion(version []byte) (*ExtendedKey, error) { - if len(version) != 4 { - // TODO: The semantically correct error to return here is - // ErrInvalidHDKeyID (introduced in btcsuite/btcd#1617). Update the - // error type once available in a stable btcd / chaincfg release. - return nil, chaincfg.ErrUnknownHDKeyID - } - - // Initialize a new extended key instance with the same fields as the - // current extended private/public key and the provided HD version bytes. - return NewExtendedKey(version, k.key, k.chainCode, k.parentFP, - k.depth, k.childNum, k.isPrivate), nil -} - -// ECPubKey converts the extended key to a btcec public key and returns it. -func (k *ExtendedKey) ECPubKey() (*btcec.PublicKey, error) { - return btcec.ParsePubKey(k.pubKeyBytes(), btcec.S256()) -} - -// ECPrivKey converts the extended key to a btcec private key and returns it. -// As you might imagine this is only possible if the extended key is a private -// extended key (as determined by the IsPrivate function). The ErrNotPrivExtKey -// error will be returned if this function is called on a public extended key. -func (k *ExtendedKey) ECPrivKey() (*btcec.PrivateKey, error) { - if !k.isPrivate { - return nil, ErrNotPrivExtKey - } - - privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), k.key) - return privKey, nil -} - -// Address converts the extended key to a standard bitcoin pay-to-pubkey-hash -// address for the passed network. -func (k *ExtendedKey) Address(net *chaincfg.Params) (*btcutil.AddressPubKeyHash, error) { - pkHash := btcutil.Hash160(k.pubKeyBytes()) - return btcutil.NewAddressPubKeyHash(pkHash, net) -} - -// paddedAppend appends the src byte slice to dst, returning the new slice. -// If the length of the source is smaller than the passed size, leading zero -// bytes are appended to the dst slice before appending src. -func paddedAppend(size uint, dst, src []byte) []byte { - for i := 0; i < int(size)-len(src); i++ { - dst = append(dst, 0) - } - return append(dst, src...) -} - -// String returns the extended key as a human-readable base58-encoded string. -func (k *ExtendedKey) String() string { - if len(k.key) == 0 { - return "zeroed extended key" - } - - var childNumBytes [4]byte - binary.BigEndian.PutUint32(childNumBytes[:], k.childNum) - - // The serialized format is: - // version (4) || depth (1) || parent fingerprint (4)) || - // child num (4) || chain code (32) || key data (33) || checksum (4) - serializedBytes := make([]byte, 0, serializedKeyLen+4) - serializedBytes = append(serializedBytes, k.version...) - serializedBytes = append(serializedBytes, k.depth) - serializedBytes = append(serializedBytes, k.parentFP...) - serializedBytes = append(serializedBytes, childNumBytes[:]...) - serializedBytes = append(serializedBytes, k.chainCode...) - if k.isPrivate { - serializedBytes = append(serializedBytes, 0x00) - serializedBytes = paddedAppend(32, serializedBytes, k.key) - } else { - serializedBytes = append(serializedBytes, k.pubKeyBytes()...) - } - - checkSum := chainhash.DoubleHashB(serializedBytes)[:4] - serializedBytes = append(serializedBytes, checkSum...) - return base58.Encode(serializedBytes) -} - -// IsForNet returns whether or not the extended key is associated with the -// passed bitcoin network. -func (k *ExtendedKey) IsForNet(net *chaincfg.Params) bool { - return bytes.Equal(k.version, net.HDPrivateKeyID[:]) || - bytes.Equal(k.version, net.HDPublicKeyID[:]) -} - -// SetNet associates the extended key, and any child keys yet to be derived from -// it, with the passed network. -func (k *ExtendedKey) SetNet(net *chaincfg.Params) { - if k.isPrivate { - k.version = net.HDPrivateKeyID[:] - } else { - k.version = net.HDPublicKeyID[:] - } -} - -// zero sets all bytes in the passed slice to zero. This is used to -// explicitly clear private key material from memory. -func zero(b []byte) { - lenb := len(b) - for i := 0; i < lenb; i++ { - b[i] = 0 - } -} - -// Zero manually clears all fields and bytes in the extended key. This can be -// used to explicitly clear key material from memory for enhanced security -// against memory scraping. This function only clears this particular key and -// not any children that have already been derived. -func (k *ExtendedKey) Zero() { - zero(k.key) - zero(k.pubKey) - zero(k.chainCode) - zero(k.parentFP) - k.version = nil - k.key = nil - k.depth = 0 - k.childNum = 0 - k.isPrivate = false -} - -// NewMaster creates a new master node for use in creating a hierarchical -// deterministic key chain. The seed must be between 128 and 512 bits and -// should be generated by a cryptographically secure random generation source. -// -// NOTE: There is an extremely small chance (< 1 in 2^127) the provided seed -// will derive to an unusable secret key. The ErrUnusable error will be -// returned if this should occur, so the caller must check for it and generate a -// new seed accordingly. -func NewMaster(seed []byte, net *chaincfg.Params) (*ExtendedKey, error) { - // Per [BIP32], the seed must be in range [MinSeedBytes, MaxSeedBytes]. - if len(seed) < MinSeedBytes || len(seed) > MaxSeedBytes { - return nil, ErrInvalidSeedLen - } - - // First take the HMAC-SHA512 of the master key and the seed data: - // I = HMAC-SHA512(Key = "Bitcoin seed", Data = S) - hmac512 := hmac.New(sha512.New, masterKey) - _, _ = hmac512.Write(seed) - lr := hmac512.Sum(nil) - - // Split "I" into two 32-byte sequences Il and Ir where: - // Il = master secret key - // Ir = master chain code - secretKey := lr[:len(lr)/2] - chainCode := lr[len(lr)/2:] - - // Ensure the key in usable. - secretKeyNum := new(big.Int).SetBytes(secretKey) - if secretKeyNum.Cmp(btcec.S256().N) >= 0 || secretKeyNum.Sign() == 0 { - return nil, ErrUnusableSeed - } - - parentFP := []byte{0x00, 0x00, 0x00, 0x00} - return NewExtendedKey(net.HDPrivateKeyID[:], secretKey, chainCode, - parentFP, 0, 0, true), nil -} - -// NewKeyFromString returns a new extended key instance from a base58-encoded -// extended key. -func NewKeyFromString(key string) (*ExtendedKey, error) { - // The base58-decoded extended key must consist of a serialized payload - // plus an additional 4 bytes for the checksum. - decoded := base58.Decode(key) - if len(decoded) != serializedKeyLen+4 { - return nil, ErrInvalidKeyLen - } - - // The serialized format is: - // version (4) || depth (1) || parent fingerprint (4)) || - // child num (4) || chain code (32) || key data (33) || checksum (4) - - // Split the payload and checksum up and ensure the checksum matches. - payload := decoded[:len(decoded)-4] - checkSum := decoded[len(decoded)-4:] - expectedCheckSum := chainhash.DoubleHashB(payload)[:4] - if !bytes.Equal(checkSum, expectedCheckSum) { - return nil, ErrBadChecksum - } - - // Deserialize each of the payload fields. - version := payload[:4] - depth := payload[4:5][0] - parentFP := payload[5:9] - childNum := binary.BigEndian.Uint32(payload[9:13]) - chainCode := payload[13:45] - keyData := payload[45:78] - - // The key data is a private key if it starts with 0x00. Serialized - // compressed pubkeys either start with 0x02 or 0x03. - isPrivate := keyData[0] == 0x00 - if isPrivate { - // Ensure the private key is valid. It must be within the range - // of the order of the secp256k1 curve and not be 0. - keyData = keyData[1:] - keyNum := new(big.Int).SetBytes(keyData) - if keyNum.Cmp(btcec.S256().N) >= 0 || keyNum.Sign() == 0 { - return nil, ErrUnusableSeed - } - } else { - // Ensure the public key parses correctly and is actually on the - // secp256k1 curve. - _, err := btcec.ParsePubKey(keyData, btcec.S256()) - if err != nil { - return nil, err - } - } - - return NewExtendedKey(version, keyData, chainCode, parentFP, depth, - childNum, isPrivate), nil -} - -// GenerateSeed returns a cryptographically secure random seed that can be used -// as the input for the NewMaster function to generate a new master node. -// -// The length is in bytes and it must be between 16 and 64 (128 to 512 bits). -// The recommended length is 32 (256 bits) as defined by the RecommendedSeedLen -// constant. -func GenerateSeed(length uint8) ([]byte, error) { - // Per [BIP32], the seed must be in range [MinSeedBytes, MaxSeedBytes]. - if length < MinSeedBytes || length > MaxSeedBytes { - return nil, ErrInvalidSeedLen - } - - buf := make([]byte, length) - _, err := rand.Read(buf) - if err != nil { - return nil, err - } - - return buf, nil -} diff --git a/hdkeychain/extendedkey_test.go b/hdkeychain/extendedkey_test.go deleted file mode 100644 index dd6deb514..000000000 --- a/hdkeychain/extendedkey_test.go +++ /dev/null @@ -1,1209 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package hdkeychain - -// References: -// [BIP32]: BIP0032 - Hierarchical Deterministic Wallets -// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki - -import ( - "bytes" - "encoding/binary" - "encoding/hex" - "errors" - "math" - "reflect" - "testing" - - "github.com/btcsuite/btcd/chaincfg" -) - -// TestBIP0032Vectors tests the vectors provided by [BIP32] to ensure the -// derivation works as intended. -func TestBIP0032Vectors(t *testing.T) { - // The master seeds for each of the two test vectors in [BIP32]. - testVec1MasterHex := "000102030405060708090a0b0c0d0e0f" - testVec2MasterHex := "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" - testVec3MasterHex := "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be" - hkStart := uint32(0x80000000) - - tests := []struct { - name string - master string - path []uint32 - wantPub string - wantPriv string - net *chaincfg.Params - }{ - // Test vector 1 - { - name: "test vector 1 chain m", - master: testVec1MasterHex, - path: []uint32{}, - wantPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", - wantPriv: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - net: &chaincfg.MainNetParams, - }, - { - name: "test vector 1 chain m/0H", - master: testVec1MasterHex, - path: []uint32{hkStart}, - wantPub: "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", - wantPriv: "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", - net: &chaincfg.MainNetParams, - }, - { - name: "test vector 1 chain m/0H/1", - master: testVec1MasterHex, - path: []uint32{hkStart, 1}, - wantPub: "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ", - wantPriv: "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", - net: &chaincfg.MainNetParams, - }, - { - name: "test vector 1 chain m/0H/1/2H", - master: testVec1MasterHex, - path: []uint32{hkStart, 1, hkStart + 2}, - wantPub: "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5", - wantPriv: "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", - net: &chaincfg.MainNetParams, - }, - { - name: "test vector 1 chain m/0H/1/2H/2", - master: testVec1MasterHex, - path: []uint32{hkStart, 1, hkStart + 2, 2}, - wantPub: "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV", - wantPriv: "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", - net: &chaincfg.MainNetParams, - }, - { - name: "test vector 1 chain m/0H/1/2H/2/1000000000", - master: testVec1MasterHex, - path: []uint32{hkStart, 1, hkStart + 2, 2, 1000000000}, - wantPub: "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy", - wantPriv: "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", - net: &chaincfg.MainNetParams, - }, - - // Test vector 2 - { - name: "test vector 2 chain m", - master: testVec2MasterHex, - path: []uint32{}, - wantPub: "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", - wantPriv: "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", - net: &chaincfg.MainNetParams, - }, - { - name: "test vector 2 chain m/0", - master: testVec2MasterHex, - path: []uint32{0}, - wantPub: "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", - wantPriv: "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", - net: &chaincfg.MainNetParams, - }, - { - name: "test vector 2 chain m/0/2147483647H", - master: testVec2MasterHex, - path: []uint32{0, hkStart + 2147483647}, - wantPub: "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a", - wantPriv: "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", - net: &chaincfg.MainNetParams, - }, - { - name: "test vector 2 chain m/0/2147483647H/1", - master: testVec2MasterHex, - path: []uint32{0, hkStart + 2147483647, 1}, - wantPub: "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon", - wantPriv: "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", - net: &chaincfg.MainNetParams, - }, - { - name: "test vector 2 chain m/0/2147483647H/1/2147483646H", - master: testVec2MasterHex, - path: []uint32{0, hkStart + 2147483647, 1, hkStart + 2147483646}, - wantPub: "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL", - wantPriv: "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", - net: &chaincfg.MainNetParams, - }, - { - name: "test vector 2 chain m/0/2147483647H/1/2147483646H/2", - master: testVec2MasterHex, - path: []uint32{0, hkStart + 2147483647, 1, hkStart + 2147483646, 2}, - wantPub: "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", - wantPriv: "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", - net: &chaincfg.MainNetParams, - }, - - // Test vector 3 - { - name: "test vector 3 chain m", - master: testVec3MasterHex, - path: []uint32{}, - wantPub: "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13", - wantPriv: "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6", - net: &chaincfg.MainNetParams, - }, - { - name: "test vector 3 chain m/0H", - master: testVec3MasterHex, - path: []uint32{hkStart}, - wantPub: "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y", - wantPriv: "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L", - net: &chaincfg.MainNetParams, - }, - - // Test vector 1 - Testnet - { - name: "test vector 1 chain m - testnet", - master: testVec1MasterHex, - path: []uint32{}, - wantPub: "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp", - wantPriv: "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m", - net: &chaincfg.TestNet3Params, - }, - { - name: "test vector 1 chain m/0H - testnet", - master: testVec1MasterHex, - path: []uint32{hkStart}, - wantPub: "tpubD8eQVK4Kdxg3gHrF62jGP7dKVCoYiEB8dFSpuTawkL5YxTus5j5pf83vaKnii4bc6v2NVEy81P2gYrJczYne3QNNwMTS53p5uzDyHvnw2jm", - wantPriv: "tprv8bxNLu25VazNnppTCP4fyhyCvBHcYtzE3wr3cwYeL4HA7yf6TLGEUdS4QC1vLT63TkjRssqJe4CvGNEC8DzW5AoPUw56D1Ayg6HY4oy8QZ9", - net: &chaincfg.TestNet3Params, - }, - { - name: "test vector 1 chain m/0H/1 - testnet", - master: testVec1MasterHex, - path: []uint32{hkStart, 1}, - wantPub: "tpubDApXh6cD2fZ7WjtgpHd8yrWyYaneiFuRZa7fVjMkgxsmC1QzoXW8cgx9zQFJ81Jx4deRGfRE7yXA9A3STsxXj4CKEZJHYgpMYikkas9DBTP", - wantPriv: "tprv8e8VYgZxtHsSdGrtvdxYaSrryZGiYviWzGWtDDKTGh5NMXAEB8gYSCLHpFCywNs5uqV7ghRjimALQJkRFZnUrLHpzi2pGkwqLtbubgWuQ8q", - net: &chaincfg.TestNet3Params, - }, - { - name: "test vector 1 chain m/0H/1/2H - testnet", - master: testVec1MasterHex, - path: []uint32{hkStart, 1, hkStart + 2}, - wantPub: "tpubDDRojdS4jYQXNugn4t2WLrZ7mjfAyoVQu7MLk4eurqFCbrc7cHLZX8W5YRS8ZskGR9k9t3PqVv68bVBjAyW4nWM9pTGRddt3GQftg6MVQsm", - wantPriv: "tprv8gjmbDPpbAirVSezBEMuwSu1Ci9EpUJWKokZTYccSZSomNMLytWyLdtDNHRbucNaRJWWHANf9AzEdWVAqahfyRjVMKbNRhBmxAM8EJr7R15", - net: &chaincfg.TestNet3Params, - }, - { - name: "test vector 1 chain m/0H/1/2H/2 - testnet", - master: testVec1MasterHex, - path: []uint32{hkStart, 1, hkStart + 2, 2}, - wantPub: "tpubDFfCa4Z1v25WTPAVm9EbEMiRrYwucPocLbEe12BPBGooxxEUg42vihy1DkRWyftztTsL23snYezF9uXjGGwGW6pQjEpcTpmsH6ajpf4CVPn", - wantPriv: "tprv8iyAReWmmePqZv8hsVZzpx4KHXRyT4chmHdriW95m11R8Tyi3fDLYDM93bq4NGn1V6eCu5cE3zSQ6hPd31F2ApKXkZgTyn1V78pHjkq1V2v", - net: &chaincfg.TestNet3Params, - }, - { - name: "test vector 1 chain m/0H/1/2H/2/1000000000 - testnet", - master: testVec1MasterHex, - path: []uint32{hkStart, 1, hkStart + 2, 2, 1000000000}, - wantPub: "tpubDHNy3kAG39ThyiwwsgoKY4iRenXDRtce8qdCFJZXPMCJg5dsCUHayp84raLTpvyiNA9sXPob5rgqkKvkN8S7MMyXbnEhGJMW64Cf4vFAoaF", - wantPriv: "tprv8kgvuL81tmn36Fv9z38j8f4K5m1HGZRjZY2QxnXDy5PuqbP6a5TzoKWCgTcGHBu66W3TgSbAu2yX6sPza5FkHmy564Sh6gmCPUNeUt4yj2x", - net: &chaincfg.TestNet3Params, - }, - } - -tests: - for i, test := range tests { - masterSeed, err := hex.DecodeString(test.master) - if err != nil { - t.Errorf("DecodeString #%d (%s): unexpected error: %v", - i, test.name, err) - continue - } - - extKey, err := NewMaster(masterSeed, test.net) - if err != nil { - t.Errorf("NewMaster #%d (%s): unexpected error when "+ - "creating new master key: %v", i, test.name, - err) - continue - } - - for _, childNum := range test.path { - var err error - extKey, err = extKey.Derive(childNum) - if err != nil { - t.Errorf("err: %v", err) - continue tests - } - } - - if extKey.Depth() != uint8(len(test.path)) { - t.Errorf("Depth of key %d should match fixture path: %v", - extKey.Depth(), len(test.path)) - continue - } - - privStr := extKey.String() - if privStr != test.wantPriv { - t.Errorf("Serialize #%d (%s): mismatched serialized "+ - "private extended key -- got: %s, want: %s", i, - test.name, privStr, test.wantPriv) - continue - } - - pubKey, err := extKey.Neuter() - if err != nil { - t.Errorf("Neuter #%d (%s): unexpected error: %v ", i, - test.name, err) - continue - } - - // Neutering a second time should have no effect. - pubKey, err = pubKey.Neuter() - if err != nil { - t.Errorf("Neuter #%d (%s): unexpected error: %v", i, - test.name, err) - return - } - - pubStr := pubKey.String() - if pubStr != test.wantPub { - t.Errorf("Neuter #%d (%s): mismatched serialized "+ - "public extended key -- got: %s, want: %s", i, - test.name, pubStr, test.wantPub) - continue - } - } -} - -// TestPrivateDerivation tests several vectors which derive private keys from -// other private keys works as intended. -func TestPrivateDerivation(t *testing.T) { - // The private extended keys for test vectors in [BIP32]. - testVec1MasterPrivKey := "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" - testVec2MasterPrivKey := "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U" - - tests := []struct { - name string - master string - path []uint32 - wantPriv string - }{ - // Test vector 1 - { - name: "test vector 1 chain m", - master: testVec1MasterPrivKey, - path: []uint32{}, - wantPriv: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - }, - { - name: "test vector 1 chain m/0", - master: testVec1MasterPrivKey, - path: []uint32{0}, - wantPriv: "xprv9uHRZZhbkedL37eZEnyrNsQPFZYRAvjy5rt6M1nbEkLSo378x1CQQLo2xxBvREwiK6kqf7GRNvsNEchwibzXaV6i5GcsgyjBeRguXhKsi4R", - }, - { - name: "test vector 1 chain m/0/1", - master: testVec1MasterPrivKey, - path: []uint32{0, 1}, - wantPriv: "xprv9ww7sMFLzJMzy7bV1qs7nGBxgKYrgcm3HcJvGb4yvNhT9vxXC7eX7WVULzCfxucFEn2TsVvJw25hH9d4mchywguGQCZvRgsiRaTY1HCqN8G", - }, - { - name: "test vector 1 chain m/0/1/2", - master: testVec1MasterPrivKey, - path: []uint32{0, 1, 2}, - wantPriv: "xprv9xrdP7iD2L1YZCgR9AecDgpDMZSTzP5KCfUykGXgjBxLgp1VFHsEeL3conzGAkbc1MigG1o8YqmfEA2jtkPdf4vwMaGJC2YSDbBTPAjfRUi", - }, - { - name: "test vector 1 chain m/0/1/2/2", - master: testVec1MasterPrivKey, - path: []uint32{0, 1, 2, 2}, - wantPriv: "xprvA2J8Hq4eiP7xCEBP7gzRJGJnd9CHTkEU6eTNMrZ6YR7H5boik8daFtDZxmJDfdMSKHwroCfAfsBKWWidRfBQjpegy6kzXSkQGGoMdWKz5Xh", - }, - { - name: "test vector 1 chain m/0/1/2/2/1000000000", - master: testVec1MasterPrivKey, - path: []uint32{0, 1, 2, 2, 1000000000}, - wantPriv: "xprvA3XhazxncJqJsQcG85Gg61qwPQKiobAnWjuPpjKhExprZjfse6nErRwTMwGe6uGWXPSykZSTiYb2TXAm7Qhwj8KgRd2XaD21Styu6h6AwFz", - }, - - // Test vector 2 - { - name: "test vector 2 chain m", - master: testVec2MasterPrivKey, - path: []uint32{}, - wantPriv: "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", - }, - { - name: "test vector 2 chain m/0", - master: testVec2MasterPrivKey, - path: []uint32{0}, - wantPriv: "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", - }, - { - name: "test vector 2 chain m/0/2147483647", - master: testVec2MasterPrivKey, - path: []uint32{0, 2147483647}, - wantPriv: "xprv9wSp6B7cXJWXZRpDbxkFg3ry2fuSyUfvboJ5Yi6YNw7i1bXmq9QwQ7EwMpeG4cK2pnMqEx1cLYD7cSGSCtruGSXC6ZSVDHugMsZgbuY62m6", - }, - { - name: "test vector 2 chain m/0/2147483647/1", - master: testVec2MasterPrivKey, - path: []uint32{0, 2147483647, 1}, - wantPriv: "xprv9ysS5br6UbWCRCJcggvpUNMyhVWgD7NypY9gsVTMYmuRtZg8izyYC5Ey4T931WgWbfJwRDwfVFqV3b29gqHDbuEpGcbzf16pdomk54NXkSm", - }, - { - name: "test vector 2 chain m/0/2147483647/1/2147483646", - master: testVec2MasterPrivKey, - path: []uint32{0, 2147483647, 1, 2147483646}, - wantPriv: "xprvA2LfeWWwRCxh4iqigcDMnUf2E3nVUFkntc93nmUYBtb9rpSPYWa8MY3x9ZHSLZkg4G84UefrDruVK3FhMLSJsGtBx883iddHNuH1LNpRrEp", - }, - { - name: "test vector 2 chain m/0/2147483647/1/2147483646/2", - master: testVec2MasterPrivKey, - path: []uint32{0, 2147483647, 1, 2147483646, 2}, - wantPriv: "xprvA48ALo8BDjcRET68R5RsPzF3H7WeyYYtHcyUeLRGBPHXu6CJSGjwW7dWoeUWTEzT7LG3qk6Eg6x2ZoqD8gtyEFZecpAyvchksfLyg3Zbqam", - }, - - // Custom tests to trigger specific conditions. - { - // Seed 000000000000000000000000000000da. - name: "Derived privkey with zero high byte m/0", - master: "xprv9s21ZrQH143K4FR6rNeqEK4EBhRgLjWLWhA3pw8iqgAKk82ypz58PXbrzU19opYcxw8JDJQF4id55PwTsN1Zv8Xt6SKvbr2KNU5y8jN8djz", - path: []uint32{0}, - wantPriv: "xprv9uC5JqtViMmgcAMUxcsBCBFA7oYCNs4bozPbyvLfddjHou4rMiGEHipz94xNaPb1e4f18TRoPXfiXx4C3cDAcADqxCSRSSWLvMBRWPctSN9", - }, - } - -tests: - for i, test := range tests { - extKey, err := NewKeyFromString(test.master) - if err != nil { - t.Errorf("NewKeyFromString #%d (%s): unexpected error "+ - "creating extended key: %v", i, test.name, - err) - continue - } - - for _, childNum := range test.path { - var err error - extKey, err = extKey.Derive(childNum) - if err != nil { - t.Errorf("err: %v", err) - continue tests - } - } - - privStr := extKey.String() - if privStr != test.wantPriv { - t.Errorf("Derive #%d (%s): mismatched serialized "+ - "private extended key -- got: %s, want: %s", i, - test.name, privStr, test.wantPriv) - continue - } - } -} - -// TestPublicDerivation tests several vectors which derive public keys from -// other public keys works as intended. -func TestPublicDerivation(t *testing.T) { - // The public extended keys for test vectors in [BIP32]. - testVec1MasterPubKey := "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" - testVec2MasterPubKey := "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB" - - tests := []struct { - name string - master string - path []uint32 - wantPub string - }{ - // Test vector 1 - { - name: "test vector 1 chain m", - master: testVec1MasterPubKey, - path: []uint32{}, - wantPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", - }, - { - name: "test vector 1 chain m/0", - master: testVec1MasterPubKey, - path: []uint32{0}, - wantPub: "xpub68Gmy5EVb2BdFbj2LpWrk1M7obNuaPTpT5oh9QCCo5sRfqSHVYWex97WpDZzszdzHzxXDAzPLVSwybe4uPYkSk4G3gnrPqqkV9RyNzAcNJ1", - }, - { - name: "test vector 1 chain m/0/1", - master: testVec1MasterPubKey, - path: []uint32{0, 1}, - wantPub: "xpub6AvUGrnEpfvJBbfx7sQ89Q8hEMPM65UteqEX4yUbUiES2jHfjexmfJoxCGSwFMZiPBaKQT1RiKWrKfuDV4vpgVs4Xn8PpPTR2i79rwHd4Zr", - }, - { - name: "test vector 1 chain m/0/1/2", - master: testVec1MasterPubKey, - path: []uint32{0, 1, 2}, - wantPub: "xpub6BqyndF6rhZqmgktFCBcapkwubGxPqoAZtQaYewJHXVKZcLdnqBVC8N6f6FSHWUghjuTLeubWyQWfJdk2G3tGgvgj3qngo4vLTnnSjAZckv", - }, - { - name: "test vector 1 chain m/0/1/2/2", - master: testVec1MasterPubKey, - path: []uint32{0, 1, 2, 2}, - wantPub: "xpub6FHUhLbYYkgFQiFrDiXRfQFXBB2msCxKTsNyAExi6keFxQ8sHfwpogY3p3s1ePSpUqLNYks5T6a3JqpCGszt4kxbyq7tUoFP5c8KWyiDtPp", - }, - { - name: "test vector 1 chain m/0/1/2/2/1000000000", - master: testVec1MasterPubKey, - path: []uint32{0, 1, 2, 2, 1000000000}, - wantPub: "xpub6GX3zWVgSgPc5tgjE6ogT9nfwSADD3tdsxpzd7jJoJMqSY12Be6VQEFwDCp6wAQoZsH2iq5nNocHEaVDxBcobPrkZCjYW3QUmoDYzMFBDu9", - }, - - // Test vector 2 - { - name: "test vector 2 chain m", - master: testVec2MasterPubKey, - path: []uint32{}, - wantPub: "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", - }, - { - name: "test vector 2 chain m/0", - master: testVec2MasterPubKey, - path: []uint32{0}, - wantPub: "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", - }, - { - name: "test vector 2 chain m/0/2147483647", - master: testVec2MasterPubKey, - path: []uint32{0, 2147483647}, - wantPub: "xpub6ASAVgeWMg4pmutghzHG3BohahjwNwPmy2DgM6W9wGegtPrvNgjBwuZRD7hSDFhYfunq8vDgwG4ah1gVzZysgp3UsKz7VNjCnSUJJ5T4fdD", - }, - { - name: "test vector 2 chain m/0/2147483647/1", - master: testVec2MasterPubKey, - path: []uint32{0, 2147483647, 1}, - wantPub: "xpub6CrnV7NzJy4VdgP5niTpqWJiFXMAca6qBm5Hfsry77SQmN1HGYHnjsZSujoHzdxf7ZNK5UVrmDXFPiEW2ecwHGWMFGUxPC9ARipss9rXd4b", - }, - { - name: "test vector 2 chain m/0/2147483647/1/2147483646", - master: testVec2MasterPubKey, - path: []uint32{0, 2147483647, 1, 2147483646}, - wantPub: "xpub6FL2423qFaWzHCvBndkN9cbkn5cysiUeFq4eb9t9kE88jcmY63tNuLNRzpHPdAM4dUpLhZ7aUm2cJ5zF7KYonf4jAPfRqTMTRBNkQL3Tfta", - }, - { - name: "test vector 2 chain m/0/2147483647/1/2147483646/2", - master: testVec2MasterPubKey, - path: []uint32{0, 2147483647, 1, 2147483646, 2}, - wantPub: "xpub6H7WkJf547AiSwAbX6xsm8Bmq9M9P1Gjequ5SipsjipWmtXSyp4C3uwzewedGEgAMsDy4jEvNTWtxLyqqHY9C12gaBmgUdk2CGmwachwnWK", - }, - } - -tests: - for i, test := range tests { - extKey, err := NewKeyFromString(test.master) - if err != nil { - t.Errorf("NewKeyFromString #%d (%s): unexpected error "+ - "creating extended key: %v", i, test.name, - err) - continue - } - - for _, childNum := range test.path { - var err error - extKey, err = extKey.Derive(childNum) - if err != nil { - t.Errorf("err: %v", err) - continue tests - } - } - - pubStr := extKey.String() - if pubStr != test.wantPub { - t.Errorf("Derive #%d (%s): mismatched serialized "+ - "public extended key -- got: %s, want: %s", i, - test.name, pubStr, test.wantPub) - continue - } - } -} - -// TestGenerateSeed ensures the GenerateSeed function works as intended. -func TestGenerateSeed(t *testing.T) { - wantErr := errors.New("seed length must be between 128 and 512 bits") - - tests := []struct { - name string - length uint8 - err error - }{ - // Test various valid lengths. - {name: "16 bytes", length: 16}, - {name: "17 bytes", length: 17}, - {name: "20 bytes", length: 20}, - {name: "32 bytes", length: 32}, - {name: "64 bytes", length: 64}, - - // Test invalid lengths. - {name: "15 bytes", length: 15, err: wantErr}, - {name: "65 bytes", length: 65, err: wantErr}, - } - - for i, test := range tests { - seed, err := GenerateSeed(test.length) - if !reflect.DeepEqual(err, test.err) { - t.Errorf("GenerateSeed #%d (%s): unexpected error -- "+ - "want %v, got %v", i, test.name, test.err, err) - continue - } - - if test.err == nil && len(seed) != int(test.length) { - t.Errorf("GenerateSeed #%d (%s): length mismatch -- "+ - "got %d, want %d", i, test.name, len(seed), - test.length) - continue - } - } -} - -// TestExtendedKeyAPI ensures the API on the ExtendedKey type works as intended. -func TestExtendedKeyAPI(t *testing.T) { - tests := []struct { - name string - extKey string - isPrivate bool - parentFP uint32 - chainCode []byte - childNum uint32 - privKey string - privKeyErr error - pubKey string - address string - }{ - { - name: "test vector 1 master node private", - extKey: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - isPrivate: true, - parentFP: 0, - chainCode: []byte{135, 61, 255, 129, 192, 47, 82, 86, 35, 253, 31, 229, 22, 126, 172, 58, 85, 160, 73, 222, 61, 49, 75, 180, 46, 226, 39, 255, 237, 55, 213, 8}, - childNum: 0, - privKey: "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", - pubKey: "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2", - address: "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma", - }, - { - name: "test vector 1 chain m/0H/1/2H public", - extKey: "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5", - isPrivate: false, - parentFP: 3203769081, - chainCode: []byte{4, 70, 107, 156, 200, 225, 97, 233, 102, 64, 156, 165, 41, 134, 197, 132, 240, 126, 157, 200, 31, 115, 93, 182, 131, 195, 255, 110, 199, 177, 80, 63}, - childNum: 2147483650, - privKeyErr: ErrNotPrivExtKey, - pubKey: "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2", - address: "1NjxqbA9aZWnh17q1UW3rB4EPu79wDXj7x", - }, - } - - for i, test := range tests { - key, err := NewKeyFromString(test.extKey) - if err != nil { - t.Errorf("NewKeyFromString #%d (%s): unexpected "+ - "error: %v", i, test.name, err) - continue - } - - if key.IsPrivate() != test.isPrivate { - t.Errorf("IsPrivate #%d (%s): mismatched key type -- "+ - "want private %v, got private %v", i, test.name, - test.isPrivate, key.IsPrivate()) - continue - } - - parentFP := key.ParentFingerprint() - if parentFP != test.parentFP { - t.Errorf("ParentFingerprint #%d (%s): mismatched "+ - "parent fingerprint -- want %d, got %d", i, - test.name, test.parentFP, parentFP) - continue - } - - chainCode := key.ChainCode() - if !bytes.Equal(chainCode, test.chainCode) { - t.Errorf("ChainCode #%d (%s): want %v, got %v", i, - test.name, test.chainCode, chainCode) - continue - } - - childIndex := key.ChildIndex() - if childIndex != test.childNum { - t.Errorf("ChildIndex #%d (%s): want %d, got %d", i, - test.name, test.childNum, childIndex) - continue - } - - serializedKey := key.String() - if serializedKey != test.extKey { - t.Errorf("String #%d (%s): mismatched serialized key "+ - "-- want %s, got %s", i, test.name, test.extKey, - serializedKey) - continue - } - - privKey, err := key.ECPrivKey() - if !reflect.DeepEqual(err, test.privKeyErr) { - t.Errorf("ECPrivKey #%d (%s): mismatched error: want "+ - "%v, got %v", i, test.name, test.privKeyErr, err) - continue - } - if test.privKeyErr == nil { - privKeyStr := hex.EncodeToString(privKey.Serialize()) - if privKeyStr != test.privKey { - t.Errorf("ECPrivKey #%d (%s): mismatched "+ - "private key -- want %s, got %s", i, - test.name, test.privKey, privKeyStr) - continue - } - } - - pubKey, err := key.ECPubKey() - if err != nil { - t.Errorf("ECPubKey #%d (%s): unexpected error: %v", i, - test.name, err) - continue - } - pubKeyStr := hex.EncodeToString(pubKey.SerializeCompressed()) - if pubKeyStr != test.pubKey { - t.Errorf("ECPubKey #%d (%s): mismatched public key -- "+ - "want %s, got %s", i, test.name, test.pubKey, - pubKeyStr) - continue - } - - addr, err := key.Address(&chaincfg.MainNetParams) - if err != nil { - t.Errorf("Address #%d (%s): unexpected error: %v", i, - test.name, err) - continue - } - if addr.EncodeAddress() != test.address { - t.Errorf("Address #%d (%s): mismatched address -- want "+ - "%s, got %s", i, test.name, test.address, - addr.EncodeAddress()) - continue - } - } -} - -// TestNet ensures the network related APIs work as intended. -func TestNet(t *testing.T) { - tests := []struct { - name string - key string - origNet *chaincfg.Params - newNet *chaincfg.Params - newPriv string - newPub string - isPrivate bool - }{ - // Private extended keys. - { - name: "mainnet -> simnet", - key: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - origNet: &chaincfg.MainNetParams, - newNet: &chaincfg.SimNetParams, - newPriv: "sprv8Erh3X3hFeKunvVdAGQQtambRPapECWiTDtvsTGdyrhzhbYgnSZajRRWbihzvq4AM4ivm6uso31VfKaukwJJUs3GYihXP8ebhMb3F2AHu3P", - newPub: "spub4Tr3T2ab61tD1Qa6GHwRFiiKyRRJdfEZpSpXfqgFYCEyaPsqKysqHDjzSzMJSiUEGbcsG3w2SLMoTqn44B8x6u3MLRRkYfACTUBnHK79THk", - isPrivate: true, - }, - { - name: "simnet -> mainnet", - key: "sprv8Erh3X3hFeKunvVdAGQQtambRPapECWiTDtvsTGdyrhzhbYgnSZajRRWbihzvq4AM4ivm6uso31VfKaukwJJUs3GYihXP8ebhMb3F2AHu3P", - origNet: &chaincfg.SimNetParams, - newNet: &chaincfg.MainNetParams, - newPriv: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - newPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", - isPrivate: true, - }, - { - name: "mainnet -> regtest", - key: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - origNet: &chaincfg.MainNetParams, - newNet: &chaincfg.RegressionNetParams, - newPriv: "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m", - newPub: "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp", - isPrivate: true, - }, - { - name: "regtest -> mainnet", - key: "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m", - origNet: &chaincfg.RegressionNetParams, - newNet: &chaincfg.MainNetParams, - newPriv: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - newPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", - isPrivate: true, - }, - - // Public extended keys. - { - name: "mainnet -> simnet", - key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", - origNet: &chaincfg.MainNetParams, - newNet: &chaincfg.SimNetParams, - newPub: "spub4Tr3T2ab61tD1Qa6GHwRFiiKyRRJdfEZpSpXfqgFYCEyaPsqKysqHDjzSzMJSiUEGbcsG3w2SLMoTqn44B8x6u3MLRRkYfACTUBnHK79THk", - isPrivate: false, - }, - { - name: "simnet -> mainnet", - key: "spub4Tr3T2ab61tD1Qa6GHwRFiiKyRRJdfEZpSpXfqgFYCEyaPsqKysqHDjzSzMJSiUEGbcsG3w2SLMoTqn44B8x6u3MLRRkYfACTUBnHK79THk", - origNet: &chaincfg.SimNetParams, - newNet: &chaincfg.MainNetParams, - newPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", - isPrivate: false, - }, - { - name: "mainnet -> regtest", - key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", - origNet: &chaincfg.MainNetParams, - newNet: &chaincfg.RegressionNetParams, - newPub: "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp", - isPrivate: false, - }, - { - name: "regtest -> mainnet", - key: "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp", - origNet: &chaincfg.RegressionNetParams, - newNet: &chaincfg.MainNetParams, - newPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", - isPrivate: false, - }, - } - - for i, test := range tests { - extKey, err := NewKeyFromString(test.key) - if err != nil { - t.Errorf("NewKeyFromString #%d (%s): unexpected error "+ - "creating extended key: %v", i, test.name, - err) - continue - } - - if !extKey.IsForNet(test.origNet) { - t.Errorf("IsForNet #%d (%s): key is not for expected "+ - "network %v", i, test.name, test.origNet.Name) - continue - } - - extKey.SetNet(test.newNet) - if !extKey.IsForNet(test.newNet) { - t.Errorf("SetNet/IsForNet #%d (%s): key is not for "+ - "expected network %v", i, test.name, - test.newNet.Name) - continue - } - - if test.isPrivate { - privStr := extKey.String() - if privStr != test.newPriv { - t.Errorf("Serialize #%d (%s): mismatched serialized "+ - "private extended key -- got: %s, want: %s", i, - test.name, privStr, test.newPriv) - continue - } - - extKey, err = extKey.Neuter() - if err != nil { - t.Errorf("Neuter #%d (%s): unexpected error: %v ", i, - test.name, err) - continue - } - } - - pubStr := extKey.String() - if pubStr != test.newPub { - t.Errorf("Neuter #%d (%s): mismatched serialized "+ - "public extended key -- got: %s, want: %s", i, - test.name, pubStr, test.newPub) - continue - } - } -} - -// TestErrors performs some negative tests for various invalid cases to ensure -// the errors are handled properly. -func TestErrors(t *testing.T) { - // Should get an error when seed has too few bytes. - net := &chaincfg.MainNetParams - _, err := NewMaster(bytes.Repeat([]byte{0x00}, 15), net) - if err != ErrInvalidSeedLen { - t.Fatalf("NewMaster: mismatched error -- got: %v, want: %v", - err, ErrInvalidSeedLen) - } - - // Should get an error when seed has too many bytes. - _, err = NewMaster(bytes.Repeat([]byte{0x00}, 65), net) - if err != ErrInvalidSeedLen { - t.Fatalf("NewMaster: mismatched error -- got: %v, want: %v", - err, ErrInvalidSeedLen) - } - - // Generate a new key and neuter it to a public extended key. - seed, err := GenerateSeed(RecommendedSeedLen) - if err != nil { - t.Fatalf("GenerateSeed: unexpected error: %v", err) - } - extKey, err := NewMaster(seed, net) - if err != nil { - t.Fatalf("NewMaster: unexpected error: %v", err) - } - pubKey, err := extKey.Neuter() - if err != nil { - t.Fatalf("Neuter: unexpected error: %v", err) - } - - // Deriving a hardened child extended key should fail from a public key. - _, err = pubKey.Derive(HardenedKeyStart) - if err != ErrDeriveHardFromPublic { - t.Fatalf("Derive: mismatched error -- got: %v, want: %v", - err, ErrDeriveHardFromPublic) - } - - // NewKeyFromString failure tests. - tests := []struct { - name string - key string - err error - neuter bool - neuterErr error - }{ - { - name: "invalid key length", - key: "xpub1234", - err: ErrInvalidKeyLen, - }, - { - name: "bad checksum", - key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EBygr15", - err: ErrBadChecksum, - }, - { - name: "pubkey not on curve", - key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ1hr9Rwbk95YadvBkQXxzHBSngB8ndpW6QH7zhhsXZ2jHyZqPjk", - err: errors.New("invalid square root"), - }, - { - name: "unsupported version", - key: "xbad4LfUL9eKmA66w2GJdVMqhvDmYGJpTGjWRAtjHqoUY17sGaymoMV9Cm3ocn9Ud6Hh2vLFVC7KSKCRVVrqc6dsEdsTjRV1WUmkK85YEUujAPX", - err: nil, - neuter: true, - neuterErr: chaincfg.ErrUnknownHDKeyID, - }, - } - - for i, test := range tests { - extKey, err := NewKeyFromString(test.key) - if !reflect.DeepEqual(err, test.err) { - t.Errorf("NewKeyFromString #%d (%s): mismatched error "+ - "-- got: %v, want: %v", i, test.name, err, - test.err) - continue - } - - if test.neuter { - _, err := extKey.Neuter() - if !reflect.DeepEqual(err, test.neuterErr) { - t.Errorf("Neuter #%d (%s): mismatched error "+ - "-- got: %v, want: %v", i, test.name, - err, test.neuterErr) - continue - } - } - } -} - -// TestZero ensures that zeroing an extended key works as intended. -func TestZero(t *testing.T) { - tests := []struct { - name string - master string - extKey string - net *chaincfg.Params - }{ - // Test vector 1 - { - name: "test vector 1 chain m", - master: "000102030405060708090a0b0c0d0e0f", - extKey: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - net: &chaincfg.MainNetParams, - }, - - // Test vector 2 - { - name: "test vector 2 chain m", - master: "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", - extKey: "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", - net: &chaincfg.MainNetParams, - }, - } - - // Use a closure to test that a key is zeroed since the tests create - // keys in different ways and need to test the same things multiple - // times. - testZeroed := func(i int, testName string, key *ExtendedKey) bool { - // Zeroing a key should result in it no longer being private - if key.IsPrivate() { - t.Errorf("IsPrivate #%d (%s): mismatched key type -- "+ - "want private %v, got private %v", i, testName, - false, key.IsPrivate()) - return false - } - - parentFP := key.ParentFingerprint() - if parentFP != 0 { - t.Errorf("ParentFingerprint #%d (%s): mismatched "+ - "parent fingerprint -- want %d, got %d", i, - testName, 0, parentFP) - return false - } - - wantKey := "zeroed extended key" - serializedKey := key.String() - if serializedKey != wantKey { - t.Errorf("String #%d (%s): mismatched serialized key "+ - "-- want %s, got %s", i, testName, wantKey, - serializedKey) - return false - } - - wantErr := ErrNotPrivExtKey - _, err := key.ECPrivKey() - if !reflect.DeepEqual(err, wantErr) { - t.Errorf("ECPrivKey #%d (%s): mismatched error: want "+ - "%v, got %v", i, testName, wantErr, err) - return false - } - - wantErr = errors.New("pubkey string is empty") - _, err = key.ECPubKey() - if !reflect.DeepEqual(err, wantErr) { - t.Errorf("ECPubKey #%d (%s): mismatched error: want "+ - "%v, got %v", i, testName, wantErr, err) - return false - } - - wantAddr := "1HT7xU2Ngenf7D4yocz2SAcnNLW7rK8d4E" - addr, err := key.Address(&chaincfg.MainNetParams) - if err != nil { - t.Errorf("Address #%d (%s): unexpected error: %v", i, - testName, err) - return false - } - if addr.EncodeAddress() != wantAddr { - t.Errorf("Address #%d (%s): mismatched address -- want "+ - "%s, got %s", i, testName, wantAddr, - addr.EncodeAddress()) - return false - } - - return true - } - - for i, test := range tests { - // Create new key from seed and get the neutered version. - masterSeed, err := hex.DecodeString(test.master) - if err != nil { - t.Errorf("DecodeString #%d (%s): unexpected error: %v", - i, test.name, err) - continue - } - key, err := NewMaster(masterSeed, test.net) - if err != nil { - t.Errorf("NewMaster #%d (%s): unexpected error when "+ - "creating new master key: %v", i, test.name, - err) - continue - } - neuteredKey, err := key.Neuter() - if err != nil { - t.Errorf("Neuter #%d (%s): unexpected error: %v", i, - test.name, err) - continue - } - - // Ensure both non-neutered and neutered keys are zeroed - // properly. - key.Zero() - if !testZeroed(i, test.name+" from seed not neutered", key) { - continue - } - neuteredKey.Zero() - if !testZeroed(i, test.name+" from seed neutered", key) { - continue - } - - // Deserialize key and get the neutered version. - key, err = NewKeyFromString(test.extKey) - if err != nil { - t.Errorf("NewKeyFromString #%d (%s): unexpected "+ - "error: %v", i, test.name, err) - continue - } - neuteredKey, err = key.Neuter() - if err != nil { - t.Errorf("Neuter #%d (%s): unexpected error: %v", i, - test.name, err) - continue - } - - // Ensure both non-neutered and neutered keys are zeroed - // properly. - key.Zero() - if !testZeroed(i, test.name+" deserialized not neutered", key) { - continue - } - neuteredKey.Zero() - if !testZeroed(i, test.name+" deserialized neutered", key) { - continue - } - } -} - -// TestMaximumDepth ensures that attempting to retrieve a child key when already -// at the maximum depth is not allowed. The serialization of a BIP32 key uses -// uint8 to encode the depth. This implicitly bounds the depth of the tree to -// 255 derivations. Here we test that an error is returned after 'max uint8'. -func TestMaximumDepth(t *testing.T) { - net := &chaincfg.MainNetParams - extKey, err := NewMaster([]byte(`abcd1234abcd1234abcd1234abcd1234`), net) - if err != nil { - t.Fatalf("NewMaster: unexpected error: %v", err) - } - - for i := uint8(0); i < math.MaxUint8; i++ { - if extKey.Depth() != i { - t.Fatalf("extendedkey depth %d should match expected value %d", - extKey.Depth(), i) - } - newKey, err := extKey.Derive(1) - if err != nil { - t.Fatalf("Derive: unexpected error: %v", err) - } - extKey = newKey - } - - noKey, err := extKey.Derive(1) - if err != ErrDeriveBeyondMaxDepth { - t.Fatalf("Derive: mismatched error: want %v, got %v", - ErrDeriveBeyondMaxDepth, err) - } - if noKey != nil { - t.Fatal("Derive: deriving 256th key should not succeed") - } -} - -// TestCloneWithVersion ensures proper conversion between standard and SLIP132 -// extended keys. -// -// The following tool was used for generating the tests: -// https://jlopp.github.io/xpub-converter -func TestCloneWithVersion(t *testing.T) { - tests := []struct { - name string - key string - version []byte - want string - wantErr error - }{ - { - name: "test xpub to zpub", - key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", - version: []byte{0x04, 0xb2, 0x47, 0x46}, - want: "zpub6jftahH18ngZxUuv6oSniLNrBCSSE1B4EEU59bwTCEt8x6aS6b2mdfLxbS4QS53g85SWWP6wexqeer516433gYpZQoJie2tcMYdJ1SYYYAL", - }, - { - name: "test zpub to xpub", - key: "zpub6jftahH18ngZxUuv6oSniLNrBCSSE1B4EEU59bwTCEt8x6aS6b2mdfLxbS4QS53g85SWWP6wexqeer516433gYpZQoJie2tcMYdJ1SYYYAL", - version: []byte{0x04, 0x88, 0xb2, 0x1e}, - want: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", - }, - { - name: "test xprv to zprv", - key: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - version: []byte{0x04, 0xb2, 0x43, 0x0c}, - want: "zprvAWgYBBk7JR8GjzqSzmunMCS7dAbwpYTCs1YUMDXqduMA5JFHZ3iX5s2UkAR6vBdcCYYa1S5o1fVLrKsrnpCQ4WpUd6aVUWP1bS2Yy5DoaKv", - }, - { - name: "test zprv to xprv", - key: "zprvAWgYBBk7JR8GjzqSzmunMCS7dAbwpYTCs1YUMDXqduMA5JFHZ3iX5s2UkAR6vBdcCYYa1S5o1fVLrKsrnpCQ4WpUd6aVUWP1bS2Yy5DoaKv", - version: []byte{0x04, 0x88, 0xad, 0xe4}, - want: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - }, - { - name: "test invalid key id", - key: "zprvAWgYBBk7JR8GjzqSzmunMCS7dAbwpYTCs1YUMDXqduMA5JFHZ3iX5s2UkAR6vBdcCYYa1S5o1fVLrKsrnpCQ4WpUd6aVUWP1bS2Yy5DoaKv", - version: []byte{0x4B, 0x1D}, - wantErr: chaincfg.ErrUnknownHDKeyID, - }, - } - - for i, test := range tests { - extKey, err := NewKeyFromString(test.key) - if err != nil { - panic(err) // This is never expected to fail. - } - - got, err := extKey.CloneWithVersion(test.version) - if !reflect.DeepEqual(err, test.wantErr) { - t.Errorf("CloneWithVersion #%d (%s): unexpected error -- "+ - "want %v, got %v", i, test.name, test.wantErr, err) - continue - } - - if test.wantErr == nil { - if k := got.String(); k != test.want { - t.Errorf("CloneWithVersion #%d (%s): "+ - "got %s, want %s", i, test.name, k, test.want) - continue - } - } - } -} - -// TestLeadingZero ensures that deriving children from keys with a leading zero byte is done according -// to the BIP-32 standard and that the legacy method generates a backwards-compatible result. -func TestLeadingZero(t *testing.T) { - // The 400th seed results in a m/0' public key with a leading zero, allowing us to test - // the desired behavior. - ii := 399 - seed := make([]byte, 32) - binary.BigEndian.PutUint32(seed[28:], uint32(ii)) - masterKey, err := NewMaster(seed, &chaincfg.MainNetParams) - if err != nil { - t.Fatalf("hdkeychain.NewMaster failed: %v", err) - } - child0, err := masterKey.Derive(0 + HardenedKeyStart) - if err != nil { - t.Fatalf("masterKey.Derive failed: %v", err) - } - if !child0.IsAffectedByIssue172() { - t.Fatal("expected child0 to be affected by issue 172") - } - child1, err := child0.Derive(0 + HardenedKeyStart) - if err != nil { - t.Fatalf("child0.Derive failed: %v", err) - } - if child1.IsAffectedByIssue172() { - t.Fatal("did not expect child1 to be affected by issue 172") - } - - child1nonstandard, err := child0.DeriveNonStandard(0 + HardenedKeyStart) - if err != nil { - t.Fatalf("child0.DeriveNonStandard failed: %v", err) - } - - // This is the correct result based on BIP32 - if hex.EncodeToString(child1.key) != "a9b6b30a5b90b56ed48728c73af1d8a7ef1e9cc372ec21afcc1d9bdf269b0988" { - t.Error("incorrect standard BIP32 derivation") - } - - if hex.EncodeToString(child1nonstandard.key) != "ea46d8f58eb863a2d371a938396af8b0babe85c01920f59a8044412e70e837ee" { - t.Error("incorrect btcutil backwards compatible BIP32-like derivation") - } - - if !child0.IsAffectedByIssue172() { - t.Error("child 0 should be affected by issue 172") - } - - if child1.IsAffectedByIssue172() { - t.Error("child 1 should not be affected by issue 172") - } -} diff --git a/hdkeychain/test_coverage.txt b/hdkeychain/test_coverage.txt deleted file mode 100644 index c0bc7ef03..000000000 --- a/hdkeychain/test_coverage.txt +++ /dev/null @@ -1,20 +0,0 @@ - -github.com/conformal/btcutil/hdkeychain/extendedkey.go ExtendedKey.String 100.00% (18/18) -github.com/conformal/btcutil/hdkeychain/extendedkey.go ExtendedKey.Zero 100.00% (9/9) -github.com/conformal/btcutil/hdkeychain/extendedkey.go ExtendedKey.pubKeyBytes 100.00% (7/7) -github.com/conformal/btcutil/hdkeychain/extendedkey.go ExtendedKey.Neuter 100.00% (6/6) -github.com/conformal/btcutil/hdkeychain/extendedkey.go ExtendedKey.ECPrivKey 100.00% (4/4) -github.com/conformal/btcutil/hdkeychain/extendedkey.go zero 100.00% (3/3) -github.com/conformal/btcutil/hdkeychain/extendedkey.go ExtendedKey.SetNet 100.00% (3/3) -github.com/conformal/btcutil/hdkeychain/extendedkey.go ExtendedKey.Address 100.00% (2/2) -github.com/conformal/btcutil/hdkeychain/extendedkey.go newExtendedKey 100.00% (1/1) -github.com/conformal/btcutil/hdkeychain/extendedkey.go ExtendedKey.IsPrivate 100.00% (1/1) -github.com/conformal/btcutil/hdkeychain/extendedkey.go ExtendedKey.ParentFingerprint 100.00% (1/1) -github.com/conformal/btcutil/hdkeychain/extendedkey.go ExtendedKey.ECPubKey 100.00% (1/1) -github.com/conformal/btcutil/hdkeychain/extendedkey.go ExtendedKey.IsForNet 100.00% (1/1) -github.com/conformal/btcutil/hdkeychain/extendedkey.go NewKeyFromString 95.83% (23/24) -github.com/conformal/btcutil/hdkeychain/extendedkey.go ExtendedKey.Child 91.67% (33/36) -github.com/conformal/btcutil/hdkeychain/extendedkey.go NewMaster 91.67% (11/12) -github.com/conformal/btcutil/hdkeychain/extendedkey.go GenerateSeed 85.71% (6/7) -github.com/conformal/btcutil/hdkeychain ----------------------------- 95.59% (130/136) - diff --git a/internal_test.go b/internal_test.go deleted file mode 100644 index ec2daa008..000000000 --- a/internal_test.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2013-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -/* -This test file is part of the btcutil package rather than than the -btcutil_test package so it can bridge access to the internals to properly test -cases which are either not possible or can't reliably be tested via the public -interface. The functions are only exported while the tests are being run. -*/ - -package btcutil - -import ( - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcutil/base58" - "github.com/btcsuite/btcutil/bech32" - "golang.org/x/crypto/ripemd160" -) - -// SetBlockBytes sets the internal serialized block byte buffer to the passed -// buffer. It is used to inject errors and is only available to the test -// package. -func (b *Block) SetBlockBytes(buf []byte) { - b.serializedBlock = buf -} - -// TstAppDataDir makes the internal appDataDir function available to the test -// package. -func TstAppDataDir(goos, appName string, roaming bool) string { - return appDataDir(goos, appName, roaming) -} - -// TstAddressPubKeyHash makes an AddressPubKeyHash, setting the -// unexported fields with the parameters hash and netID. -func TstAddressPubKeyHash(hash [ripemd160.Size]byte, - netID byte) *AddressPubKeyHash { - - return &AddressPubKeyHash{ - hash: hash, - netID: netID, - } -} - -// TstAddressScriptHash makes an AddressScriptHash, setting the -// unexported fields with the parameters hash and netID. -func TstAddressScriptHash(hash [ripemd160.Size]byte, - netID byte) *AddressScriptHash { - - return &AddressScriptHash{ - hash: hash, - netID: netID, - } -} - -// TstAddressWitnessPubKeyHash creates an AddressWitnessPubKeyHash, initiating -// the fields as given. -func TstAddressWitnessPubKeyHash(version byte, program [20]byte, - hrp string) *AddressWitnessPubKeyHash { - - return &AddressWitnessPubKeyHash{ - AddressSegWit{ - hrp: hrp, - witnessVersion: version, - witnessProgram: program[:], - }, - } -} - -// TstAddressWitnessScriptHash creates an AddressWitnessScriptHash, initiating -// the fields as given. -func TstAddressWitnessScriptHash(version byte, program [32]byte, - hrp string) *AddressWitnessScriptHash { - - return &AddressWitnessScriptHash{ - AddressSegWit{ - hrp: hrp, - witnessVersion: version, - witnessProgram: program[:], - }, - } -} - -// TstAddressTaproot creates an AddressTaproot, initiating the fields as given. -func TstAddressTaproot(version byte, program [32]byte, - hrp string) *AddressTaproot { - - return &AddressTaproot{ - AddressSegWit{ - hrp: hrp, - witnessVersion: version, - witnessProgram: program[:], - }, - } -} - -// TstAddressPubKey makes an AddressPubKey, setting the unexported fields with -// the parameters. -func TstAddressPubKey(serializedPubKey []byte, pubKeyFormat PubKeyFormat, - netID byte) *AddressPubKey { - - pubKey, _ := btcec.ParsePubKey(serializedPubKey, btcec.S256()) - return &AddressPubKey{ - pubKeyFormat: pubKeyFormat, - pubKey: pubKey, - pubKeyHashID: netID, - } -} - -// TstAddressSAddr returns the expected script address bytes for -// P2PKH and P2SH bitcoin addresses. -func TstAddressSAddr(addr string) []byte { - decoded := base58.Decode(addr) - return decoded[1 : 1+ripemd160.Size] -} - -// TstAddressSegwitSAddr returns the expected witness program bytes for -// bech32 encoded P2WPKH and P2WSH bitcoin addresses. -func TstAddressSegwitSAddr(addr string) []byte { - _, data, err := bech32.Decode(addr) - if err != nil { - return []byte{} - } - - // First byte is version, rest is base 32 encoded data. - data, err = bech32.ConvertBits(data[1:], 5, 8, false) - if err != nil { - return []byte{} - } - return data -} - -// TstAddressTaprootSAddr returns the expected witness program bytes for a -// bech32m encoded P2TR bitcoin address. -func TstAddressTaprootSAddr(addr string) []byte { - _, data, err := bech32.Decode(addr) - if err != nil { - return []byte{} - } - - // First byte is version, rest is base 32 encoded data. - data, err = bech32.ConvertBits(data[1:], 5, 8, false) - if err != nil { - return []byte{} - } - return data -} diff --git a/net.go b/net.go deleted file mode 100644 index bf11733c6..000000000 --- a/net.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2013-2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -// +build !appengine - -package btcutil - -import ( - "net" -) - -// interfaceAddrs returns a list of the system's network interface addresses. -// It is wrapped here so that we can substitute it for other functions when -// building for systems that do not allow access to net.InterfaceAddrs(). -func interfaceAddrs() ([]net.Addr, error) { - return net.InterfaceAddrs() -} diff --git a/net_noop.go b/net_noop.go deleted file mode 100644 index b0b7c2e40..000000000 --- a/net_noop.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2013-2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -// +build appengine - -package btcutil - -import ( - "net" -) - -// interfaceAddrs returns a list of the system's network interface addresses. -// It is wrapped here so that we can substitute it for a no-op function that -// returns an empty slice of net.Addr when building for systems that do not -// allow access to net.InterfaceAddrs(). -func interfaceAddrs() ([]net.Addr, error) { - return []net.Addr{}, nil -} diff --git a/psbt/bip32.go b/psbt/bip32.go deleted file mode 100644 index 9fbfc73b6..000000000 --- a/psbt/bip32.go +++ /dev/null @@ -1,77 +0,0 @@ -package psbt - -import ( - "bytes" - "encoding/binary" -) - -// Bip32Derivation encapsulates the data for the input and output -// Bip32Derivation key-value fields. -// -// TODO(roasbeef): use hdkeychain here instead? -type Bip32Derivation struct { - // PubKey is the raw pubkey serialized in compressed format. - PubKey []byte - - // MasterKeyFingerprint is the finger print of the master pubkey. - MasterKeyFingerprint uint32 - - // Bip32Path is the BIP 32 path with child index as a distinct integer. - Bip32Path []uint32 -} - -// checkValid ensures that the PubKey in the Bip32Derivation struct is valid. -func (pb *Bip32Derivation) checkValid() bool { - return validatePubkey(pb.PubKey) -} - -// Bip32Sorter implements sort.Interface for the Bip32Derivation struct. -type Bip32Sorter []*Bip32Derivation - -func (s Bip32Sorter) Len() int { return len(s) } - -func (s Bip32Sorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -func (s Bip32Sorter) Less(i, j int) bool { - return bytes.Compare(s[i].PubKey, s[j].PubKey) < 0 -} - -// readBip32Derivation deserializes a byte slice containing chunks of 4 byte -// little endian encodings of uint32 values, the first of which is the -// masterkeyfingerprint and the remainder of which are the derivation path. -func readBip32Derivation(path []byte) (uint32, []uint32, error) { - - if len(path)%4 != 0 || len(path)/4-1 < 1 { - return 0, nil, ErrInvalidPsbtFormat - } - - masterKeyInt := binary.LittleEndian.Uint32(path[:4]) - - var paths []uint32 - for i := 4; i < len(path); i += 4 { - paths = append(paths, binary.LittleEndian.Uint32(path[i:i+4])) - } - - return masterKeyInt, paths, nil -} - -// SerializeBIP32Derivation takes a master key fingerprint as defined in BIP32, -// along with a path specified as a list of uint32 values, and returns a -// bytestring specifying the derivation in the format required by BIP174: // -// master key fingerprint (4) || child index (4) || child index (4) || .... -func SerializeBIP32Derivation(masterKeyFingerprint uint32, - bip32Path []uint32) []byte { - - var masterKeyBytes [4]byte - binary.LittleEndian.PutUint32(masterKeyBytes[:], masterKeyFingerprint) - - derivationPath := make([]byte, 0, 4+4*len(bip32Path)) - derivationPath = append(derivationPath, masterKeyBytes[:]...) - for _, path := range bip32Path { - var pathbytes [4]byte - binary.LittleEndian.PutUint32(pathbytes[:], path) - derivationPath = append(derivationPath, pathbytes[:]...) - } - - return derivationPath -} diff --git a/psbt/creator.go b/psbt/creator.go deleted file mode 100644 index a5f832e0d..000000000 --- a/psbt/creator.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2018 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package psbt - -import ( - "github.com/btcsuite/btcd/wire" -) - -// MinTxVersion is the lowest transaction version that we'll permit. -const MinTxVersion = 1 - -// New on provision of an input and output 'skeleton' for the transaction, a -// new partially populated PBST packet. The populated packet will include the -// unsigned transaction, and the set of known inputs and outputs contained -// within the unsigned transaction. The values of nLockTime, nSequence (per -// input) and transaction version (must be 1 of 2) must be specified here. Note -// that the default nSequence value is wire.MaxTxInSequenceNum. Referencing -// the PSBT BIP, this function serves the roles of teh Creator. -func New(inputs []*wire.OutPoint, - outputs []*wire.TxOut, version int32, nLockTime uint32, - nSequences []uint32) (*Packet, error) { - - // Create the new struct; the input and output lists will be empty, the - // unsignedTx object must be constructed and serialized, and that - // serialization should be entered as the only entry for the - // globalKVPairs list. - // - // Ensure that the version of the transaction is greater then our - // minimum allowed transaction version. There must be one sequence - // number per input. - if version < MinTxVersion || len(nSequences) != len(inputs) { - return nil, ErrInvalidPsbtFormat - } - - unsignedTx := wire.NewMsgTx(version) - unsignedTx.LockTime = nLockTime - for i, in := range inputs { - unsignedTx.AddTxIn(&wire.TxIn{ - PreviousOutPoint: *in, - Sequence: nSequences[i], - }) - } - for _, out := range outputs { - unsignedTx.AddTxOut(out) - } - - // The input and output lists are empty, but there is a list of those - // two lists, and each one must be of length matching the unsigned - // transaction; the unknown list can be nil. - pInputs := make([]PInput, len(unsignedTx.TxIn)) - pOutputs := make([]POutput, len(unsignedTx.TxOut)) - - // This new Psbt is "raw" and contains no key-value fields, so sanity - // checking with c.Cpsbt.SanityCheck() is not required. - return &Packet{ - UnsignedTx: unsignedTx, - Inputs: pInputs, - Outputs: pOutputs, - Unknowns: nil, - }, nil -} diff --git a/psbt/extractor.go b/psbt/extractor.go deleted file mode 100644 index dc7f10fdd..000000000 --- a/psbt/extractor.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2018 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package psbt - -// The Extractor requires provision of a single PSBT -// in which all necessary signatures are encoded, and -// uses it to construct a fully valid network serialized -// transaction. - -import ( - "bytes" - - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" -) - -// Extract takes a finalized psbt.Packet and outputs a finalized transaction -// instance. Note that if the PSBT is in-complete, then an error -// ErrIncompletePSBT will be returned. As the extracted transaction has been -// fully finalized, it will be ready for network broadcast once returned. -func Extract(p *Packet) (*wire.MsgTx, error) { - // If the packet isn't complete, then we'll return an error as it - // doesn't have all the required witness data. - if !p.IsComplete() { - return nil, ErrIncompletePSBT - } - - // First, we'll make a copy of the underlying unsigned transaction (the - // initial template) so we don't mutate it during our activates below. - finalTx := p.UnsignedTx.Copy() - - // For each input, we'll now populate any relevant witness and - // sigScript data. - for i, tin := range finalTx.TxIn { - // We'll grab the corresponding internal packet input which - // matches this materialized transaction input and emplace that - // final sigScript (if present). - pInput := p.Inputs[i] - if pInput.FinalScriptSig != nil { - tin.SignatureScript = pInput.FinalScriptSig - } - - // Similarly, if there's a final witness, then we'll also need - // to extract that as well, parsing the lower-level transaction - // encoding. - if pInput.FinalScriptWitness != nil { - // In order to set the witness, need to re-deserialize - // the field as encoded within the PSBT packet. For - // each input, the witness is encoded as a stack with - // one or more items. - witnessReader := bytes.NewReader( - pInput.FinalScriptWitness, - ) - - // First we extract the number of witness elements - // encoded in the above witnessReader. - witCount, err := wire.ReadVarInt(witnessReader, 0) - if err != nil { - return nil, err - } - - // Now that we know how may inputs we'll need, we'll - // construct a packing slice, then read out each input - // (with a varint prefix) from the witnessReader. - tin.Witness = make(wire.TxWitness, witCount) - for j := uint64(0); j < witCount; j++ { - wit, err := wire.ReadVarBytes( - witnessReader, 0, txscript.MaxScriptSize, "witness", - ) - if err != nil { - return nil, err - } - tin.Witness[j] = wit - } - } - } - - return finalTx, nil -} diff --git a/psbt/finalizer.go b/psbt/finalizer.go deleted file mode 100644 index 0bf9dbb56..000000000 --- a/psbt/finalizer.go +++ /dev/null @@ -1,462 +0,0 @@ -// Copyright (c) 2018 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package psbt - -// The Finalizer requires provision of a single PSBT input -// in which all necessary signatures are encoded, and -// uses it to construct valid final sigScript and scriptWitness -// fields. -// NOTE that p2sh (legacy) and p2wsh currently support only -// multisig and no other custom script. - -import ( - "github.com/btcsuite/btcd/txscript" -) - -// isFinalized considers this input finalized if it contains at least one of -// the FinalScriptSig or FinalScriptWitness are filled (which only occurs in a -// successful call to Finalize*). -func isFinalized(p *Packet, inIndex int) bool { - input := p.Inputs[inIndex] - return input.FinalScriptSig != nil || input.FinalScriptWitness != nil -} - -// isFinalizableWitnessInput returns true if the target input is a witness UTXO -// that can be finalized. -func isFinalizableWitnessInput(pInput *PInput) bool { - pkScript := pInput.WitnessUtxo.PkScript - - switch { - // If this is a native witness output, then we require both - // the witness script, but not a redeem script. - case txscript.IsWitnessProgram(pkScript): - if txscript.IsPayToWitnessScriptHash(pkScript) { - if pInput.WitnessScript == nil || - pInput.RedeemScript != nil { - return false - } - } else { - // A P2WKH output on the other hand doesn't need - // neither a witnessScript or redeemScript. - if pInput.WitnessScript != nil || - pInput.RedeemScript != nil { - return false - } - } - - // For nested P2SH inputs, we verify that a witness script is known. - case txscript.IsPayToScriptHash(pkScript): - if pInput.RedeemScript == nil { - return false - } - - // If this is a nested P2SH input, then it must also have a - // witness script, while we don't need one for P2WKH. - if txscript.IsPayToWitnessScriptHash(pInput.RedeemScript) { - if pInput.WitnessScript == nil { - return false - } - } else if txscript.IsPayToWitnessPubKeyHash(pInput.RedeemScript) { - if pInput.WitnessScript != nil { - return false - } - } else { - // unrecognized type - return false - } - - // If this isn't a nested nested P2SH output or a native witness - // output, then we can't finalize this input as we don't understand it. - default: - return false - } - - return true -} - -// isFinalizableLegacyInput returns true of the passed input a legacy input -// (non-witness) that can be finalized. -func isFinalizableLegacyInput(p *Packet, pInput *PInput, inIndex int) bool { - // If the input has a witness, then it's invalid. - if pInput.WitnessScript != nil { - return false - } - - // Otherwise, we'll verify that we only have a RedeemScript if the prev - // output script is P2SH. - outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index - if txscript.IsPayToScriptHash(pInput.NonWitnessUtxo.TxOut[outIndex].PkScript) { - if pInput.RedeemScript == nil { - return false - } - } else { - if pInput.RedeemScript != nil { - return false - } - } - - return true -} - -// isFinalizable checks whether the structure of the entry for the input of the -// psbt.Packet at index inIndex contains sufficient information to finalize -// this input. -func isFinalizable(p *Packet, inIndex int) bool { - pInput := p.Inputs[inIndex] - - // The input cannot be finalized without any signatures - if pInput.PartialSigs == nil { - return false - } - - // For an input to be finalized, we'll one of two possible top-level - // UTXOs present. Each UTXO type has a distinct set of requirements to - // be considered finalized. - switch { - - // A witness input must be either native P2WSH or nested P2SH with all - // relevant sigScript or witness data populated. - case pInput.WitnessUtxo != nil: - if !isFinalizableWitnessInput(&pInput) { - return false - } - - case pInput.NonWitnessUtxo != nil: - if !isFinalizableLegacyInput(p, &pInput, inIndex) { - return false - } - - // If neither a known UTXO type isn't present at all, then we'll - // return false as we need one of them. - default: - return false - } - - return true -} - -// MaybeFinalize attempts to finalize the input at index inIndex in the PSBT p, -// returning true with no error if it succeeds, OR if the input has already -// been finalized. -func MaybeFinalize(p *Packet, inIndex int) (bool, error) { - if isFinalized(p, inIndex) { - return true, nil - } - - if !isFinalizable(p, inIndex) { - return false, ErrNotFinalizable - } - - if err := Finalize(p, inIndex); err != nil { - return false, err - } - - return true, nil -} - -// MaybeFinalizeAll attempts to finalize all inputs of the psbt.Packet that are -// not already finalized, and returns an error if it fails to do so. -func MaybeFinalizeAll(p *Packet) error { - - for i := range p.UnsignedTx.TxIn { - success, err := MaybeFinalize(p, i) - if err != nil || !success { - return err - } - } - - return nil -} - -// Finalize assumes that the provided psbt.Packet struct has all partial -// signatures and redeem scripts/witness scripts already prepared for the -// specified input, and so removes all temporary data and replaces them with -// completed sigScript and witness fields, which are stored in key-types 07 and -// 08. The witness/non-witness utxo fields in the inputs (key-types 00 and 01) -// are left intact as they may be needed for validation (?). If there is any -// invalid or incomplete data, an error is returned. -func Finalize(p *Packet, inIndex int) error { - pInput := p.Inputs[inIndex] - - // Depending on the UTXO type, we either attempt to finalize it as a - // witness or legacy UTXO. - switch { - case pInput.WitnessUtxo != nil: - if err := finalizeWitnessInput(p, inIndex); err != nil { - return err - } - - case pInput.NonWitnessUtxo != nil: - if err := finalizeNonWitnessInput(p, inIndex); err != nil { - return err - } - - default: - return ErrInvalidPsbtFormat - } - - // Before returning we sanity check the PSBT to ensure we don't extract - // an invalid transaction or produce an invalid intermediate state. - if err := p.SanityCheck(); err != nil { - return err - } - - return nil -} - -// checkFinalScriptSigWitness checks whether a given input in the psbt.Packet -// struct already has the fields 07 (FinalInScriptSig) or 08 (FinalInWitness). -// If so, it returns true. It does not modify the Psbt. -func checkFinalScriptSigWitness(p *Packet, inIndex int) bool { - pInput := p.Inputs[inIndex] - - if pInput.FinalScriptSig != nil { - return true - } - - if pInput.FinalScriptWitness != nil { - return true - } - - return false -} - -// finalizeNonWitnessInput attempts to create a PsbtInFinalScriptSig field for -// the input at index inIndex, and removes all other fields except for the UTXO -// field, for an input of type non-witness, or returns an error. -func finalizeNonWitnessInput(p *Packet, inIndex int) error { - // If this input has already been finalized, then we'll return an error - // as we can't proceed. - if checkFinalScriptSigWitness(p, inIndex) { - return ErrInputAlreadyFinalized - } - - // Our goal here is to construct a sigScript given the pubkey, - // signature (keytype 02), of which there might be multiple, and the - // redeem script field (keytype 04) if present (note, it is not present - // for p2pkh type inputs). - var sigScript []byte - - pInput := p.Inputs[inIndex] - containsRedeemScript := pInput.RedeemScript != nil - - var ( - pubKeys [][]byte - sigs [][]byte - ) - for _, ps := range pInput.PartialSigs { - pubKeys = append(pubKeys, ps.PubKey) - - sigOK := checkSigHashFlags(ps.Signature, &pInput) - if !sigOK { - return ErrInvalidSigHashFlags - } - - sigs = append(sigs, ps.Signature) - } - - // We have failed to identify at least 1 (sig, pub) pair in the PSBT, - // which indicates it was not ready to be finalized. As a result, we - // can't proceed. - if len(sigs) < 1 || len(pubKeys) < 1 { - return ErrNotFinalizable - } - - // If this input doesn't need a redeem script (P2PKH), then we'll - // construct a simple sigScript that's just the signature then the - // pubkey (OP_CHECKSIG). - var err error - if !containsRedeemScript { - // At this point, we should only have a single signature and - // pubkey. - if len(sigs) != 1 || len(pubKeys) != 1 { - return ErrNotFinalizable - } - - // In this case, our sigScript is just: . - builder := txscript.NewScriptBuilder() - builder.AddData(sigs[0]).AddData(pubKeys[0]) - sigScript, err = builder.Script() - if err != nil { - return err - } - } else { - // This is assumed p2sh multisig Given redeemScript and pubKeys - // we can decide in what order signatures must be appended. - orderedSigs, err := extractKeyOrderFromScript( - pInput.RedeemScript, pubKeys, sigs, - ) - if err != nil { - return err - } - - // At this point, we assume that this is a mult-sig input, so - // we construct our sigScript which looks something like this - // (mind the extra element for the extra multi-sig pop): - // * - // - // TODO(waxwing): the below is specific to the multisig case. - builder := txscript.NewScriptBuilder() - builder.AddOp(txscript.OP_FALSE) - for _, os := range orderedSigs { - builder.AddData(os) - } - builder.AddData(pInput.RedeemScript) - sigScript, err = builder.Script() - if err != nil { - return err - } - } - - // At this point, a sigScript has been constructed. Remove all fields - // other than non-witness utxo (00) and finaliscriptsig (07) - newInput := NewPsbtInput(pInput.NonWitnessUtxo, nil) - newInput.FinalScriptSig = sigScript - - // Overwrite the entry in the input list at the correct index. Note - // that this removes all the other entries in the list for this input - // index. - p.Inputs[inIndex] = *newInput - - return nil -} - -// finalizeWitnessInput attempts to create PsbtInFinalScriptSig field and -// PsbtInFinalScriptWitness field for input at index inIndex, and removes all -// other fields except for the utxo field, for an input of type witness, or -// returns an error. -func finalizeWitnessInput(p *Packet, inIndex int) error { - // If this input has already been finalized, then we'll return an error - // as we can't proceed. - if checkFinalScriptSigWitness(p, inIndex) { - return ErrInputAlreadyFinalized - } - - // Depending on the actual output type, we'll either populate a - // serializedWitness or a witness as well asa sigScript. - var ( - sigScript []byte - serializedWitness []byte - ) - - pInput := p.Inputs[inIndex] - - // First we'll validate and collect the pubkey+sig pairs from the set - // of partial signatures. - var ( - pubKeys [][]byte - sigs [][]byte - ) - for _, ps := range pInput.PartialSigs { - pubKeys = append(pubKeys, ps.PubKey) - - sigOK := checkSigHashFlags(ps.Signature, &pInput) - if !sigOK { - return ErrInvalidSigHashFlags - - } - - sigs = append(sigs, ps.Signature) - } - - // If at this point, we don't have any pubkey+sig pairs, then we bail - // as we can't proceed. - if len(sigs) == 0 || len(pubKeys) == 0 { - return ErrNotFinalizable - } - - containsRedeemScript := pInput.RedeemScript != nil - cointainsWitnessScript := pInput.WitnessScript != nil - - // If there's no redeem script, then we assume that this is native - // segwit input. - var err error - if !containsRedeemScript { - // If we have only a sigley pubkey+sig pair, and no witness - // script, then we assume this is a P2WKH input. - if len(pubKeys) == 1 && len(sigs) == 1 && - !cointainsWitnessScript { - - serializedWitness, err = writePKHWitness( - sigs[0], pubKeys[0], - ) - if err != nil { - return err - } - } else { - // Otherwise, we must have a witnessScript field, so - // we'll generate a valid multi-sig witness. - // - // NOTE: We tacitly assume multisig. - // - // TODO(roasbeef): need to add custom finalize for - // non-multisig P2WSH outputs (HTLCs, delay outputs, - // etc). - if !cointainsWitnessScript { - return ErrNotFinalizable - } - - serializedWitness, err = getMultisigScriptWitness( - pInput.WitnessScript, pubKeys, sigs, - ) - if err != nil { - return err - } - } - } else { - // Otherwise, we assume that this is a p2wsh multi-sig output, - // which is nested in a p2sh, or a p2wkh nested in a p2sh. - // - // In this case, we'll take the redeem script (the witness - // program in this case), and push it on the stack within the - // sigScript. - builder := txscript.NewScriptBuilder() - builder.AddData(pInput.RedeemScript) - sigScript, err = builder.Script() - if err != nil { - return err - } - - // If don't have a witness script, then we assume this is a - // nested p2wkh output. - if !cointainsWitnessScript { - // Assumed p2sh-p2wkh Here the witness is just (sig, - // pub) as for p2pkh case - if len(sigs) != 1 || len(pubKeys) != 1 { - return ErrNotFinalizable - } - - serializedWitness, err = writePKHWitness(sigs[0], pubKeys[0]) - if err != nil { - return err - } - - } else { - // Otherwise, we assume that this is a p2wsh multi-sig, - // so we generate the proper witness. - serializedWitness, err = getMultisigScriptWitness( - pInput.WitnessScript, pubKeys, sigs, - ) - if err != nil { - return err - } - } - } - - // At this point, a witness has been constructed, and a sigScript (if - // nested; else it's []). Remove all fields other than witness utxo - // (01) and finalscriptsig (07), finalscriptwitness (08). - newInput := NewPsbtInput(nil, pInput.WitnessUtxo) - if len(sigScript) > 0 { - newInput.FinalScriptSig = sigScript - } - - newInput.FinalScriptWitness = serializedWitness - - // Finally, we overwrite the entry in the input list at the correct - // index. - p.Inputs[inIndex] = *newInput - return nil -} diff --git a/psbt/go.mod b/psbt/go.mod deleted file mode 100644 index d47c286d7..000000000 --- a/psbt/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module github.com/btcsuite/btcutil/psbt - -go 1.13 - -require ( - github.com/btcsuite/btcd v0.20.1-beta - github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d - github.com/davecgh/go-spew v1.1.1 -) diff --git a/psbt/go.sum b/psbt/go.sum deleted file mode 100644 index 96107a51f..000000000 --- a/psbt/go.sum +++ /dev/null @@ -1,58 +0,0 @@ -github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v0.0.0-20191219182022-e17c9730c422 h1:EqnrgSSg0SFWRlEZLExgjtuUR/IPnuQ6qw6nwRda4Uk= -github.com/btcsuite/btcutil v0.0.0-20191219182022-e17c9730c422/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd h1:qdGvebPBDuYDPGi1WCPjy1tGyMpmDK8IEapSsszn7HE= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723 h1:ZA/jbKoGcVAnER6pCHPEkGdZOV7U1oLUedErBHCUMs0= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89 h1:12K8AlpT0/6QUXSfV0yi4Q0jkbq8NDtIKFtF61AoqV0= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44 h1:9lP3x0pW80sDI6t1UMSLA4to18W7R7imwAI/sWS9S8Q= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/psbt/partial_input.go b/psbt/partial_input.go deleted file mode 100644 index 4783c74d3..000000000 --- a/psbt/partial_input.go +++ /dev/null @@ -1,361 +0,0 @@ -package psbt - -import ( - "bytes" - "encoding/binary" - "io" - "sort" - - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" -) - -// PInput is a struct encapsulating all the data that can be attached to any -// specific input of the PSBT. -type PInput struct { - NonWitnessUtxo *wire.MsgTx - WitnessUtxo *wire.TxOut - PartialSigs []*PartialSig - SighashType txscript.SigHashType - RedeemScript []byte - WitnessScript []byte - Bip32Derivation []*Bip32Derivation - FinalScriptSig []byte - FinalScriptWitness []byte - Unknowns []*Unknown -} - -// NewPsbtInput creates an instance of PsbtInput given either a nonWitnessUtxo -// or a witnessUtxo. -// -// NOTE: Only one of the two arguments should be specified, with the other -// being `nil`; otherwise the created PsbtInput object will fail IsSane() -// checks and will not be usable. -func NewPsbtInput(nonWitnessUtxo *wire.MsgTx, - witnessUtxo *wire.TxOut) *PInput { - - return &PInput{ - NonWitnessUtxo: nonWitnessUtxo, - WitnessUtxo: witnessUtxo, - PartialSigs: []*PartialSig{}, - SighashType: 0, - RedeemScript: nil, - WitnessScript: nil, - Bip32Derivation: []*Bip32Derivation{}, - FinalScriptSig: nil, - FinalScriptWitness: nil, - Unknowns: nil, - } -} - -// IsSane returns true only if there are no conflicting values in the Psbt -// PInput. For segwit v0 no checks are currently implemented. -func (pi *PInput) IsSane() bool { - - // TODO(guggero): Implement sanity checks for segwit v1. For segwit v0 - // it is unsafe to only rely on the witness UTXO so we don't check that - // only one is set anymore. - // See https://github.com/bitcoin/bitcoin/pull/19215. - - return true -} - -// deserialize attempts to deserialize a new PInput from the passed io.Reader. -func (pi *PInput) deserialize(r io.Reader) error { - for { - keyint, keydata, err := getKey(r) - if err != nil { - return err - } - if keyint == -1 { - // Reached separator byte - break - } - value, err := wire.ReadVarBytes( - r, 0, MaxPsbtValueLength, "PSBT value", - ) - if err != nil { - return err - } - - switch InputType(keyint) { - - case NonWitnessUtxoType: - if pi.NonWitnessUtxo != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - tx := wire.NewMsgTx(2) - - err := tx.Deserialize(bytes.NewReader(value)) - if err != nil { - return err - } - pi.NonWitnessUtxo = tx - - case WitnessUtxoType: - if pi.WitnessUtxo != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - txout, err := readTxOut(value) - if err != nil { - return err - } - pi.WitnessUtxo = txout - - case PartialSigType: - newPartialSig := PartialSig{ - PubKey: keydata, - Signature: value, - } - - if !newPartialSig.checkValid() { - return ErrInvalidPsbtFormat - } - - // Duplicate keys are not allowed - for _, x := range pi.PartialSigs { - if bytes.Equal(x.PubKey, newPartialSig.PubKey) { - return ErrDuplicateKey - } - } - - pi.PartialSigs = append(pi.PartialSigs, &newPartialSig) - - case SighashType: - if pi.SighashType != 0 { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - - // Bounds check on value here since the sighash type must be a - // 32-bit unsigned integer. - if len(value) != 4 { - return ErrInvalidKeydata - } - - shtype := txscript.SigHashType( - binary.LittleEndian.Uint32(value), - ) - pi.SighashType = shtype - - case RedeemScriptInputType: - if pi.RedeemScript != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - pi.RedeemScript = value - - case WitnessScriptInputType: - if pi.WitnessScript != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - pi.WitnessScript = value - - case Bip32DerivationInputType: - if !validatePubkey(keydata) { - return ErrInvalidPsbtFormat - } - master, derivationPath, err := readBip32Derivation(value) - if err != nil { - return err - } - - // Duplicate keys are not allowed - for _, x := range pi.Bip32Derivation { - if bytes.Equal(x.PubKey, keydata) { - return ErrDuplicateKey - } - } - - pi.Bip32Derivation = append( - pi.Bip32Derivation, - &Bip32Derivation{ - PubKey: keydata, - MasterKeyFingerprint: master, - Bip32Path: derivationPath, - }, - ) - - case FinalScriptSigType: - if pi.FinalScriptSig != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - - pi.FinalScriptSig = value - - case FinalScriptWitnessType: - if pi.FinalScriptWitness != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - - pi.FinalScriptWitness = value - - default: - // A fall through case for any proprietary types. - keyintanddata := []byte{byte(keyint)} - keyintanddata = append(keyintanddata, keydata...) - newUnknown := &Unknown{ - Key: keyintanddata, - Value: value, - } - - // Duplicate key+keydata are not allowed - for _, x := range pi.Unknowns { - if bytes.Equal(x.Key, newUnknown.Key) && - bytes.Equal(x.Value, newUnknown.Value) { - return ErrDuplicateKey - } - } - - pi.Unknowns = append(pi.Unknowns, newUnknown) - } - } - - return nil -} - -// serialize attempts to serialize the target PInput into the passed io.Writer. -func (pi *PInput) serialize(w io.Writer) error { - - if !pi.IsSane() { - return ErrInvalidPsbtFormat - } - - if pi.NonWitnessUtxo != nil { - var buf bytes.Buffer - err := pi.NonWitnessUtxo.Serialize(&buf) - if err != nil { - return err - } - - err = serializeKVPairWithType( - w, uint8(NonWitnessUtxoType), nil, buf.Bytes(), - ) - if err != nil { - return err - } - } - if pi.WitnessUtxo != nil { - var buf bytes.Buffer - err := wire.WriteTxOut(&buf, 0, 0, pi.WitnessUtxo) - if err != nil { - return err - } - - err = serializeKVPairWithType( - w, uint8(WitnessUtxoType), nil, buf.Bytes(), - ) - if err != nil { - return err - } - } - - if pi.FinalScriptSig == nil && pi.FinalScriptWitness == nil { - sort.Sort(PartialSigSorter(pi.PartialSigs)) - for _, ps := range pi.PartialSigs { - err := serializeKVPairWithType( - w, uint8(PartialSigType), ps.PubKey, - ps.Signature, - ) - if err != nil { - return err - } - } - - if pi.SighashType != 0 { - var shtBytes [4]byte - binary.LittleEndian.PutUint32( - shtBytes[:], uint32(pi.SighashType), - ) - - err := serializeKVPairWithType( - w, uint8(SighashType), nil, shtBytes[:], - ) - if err != nil { - return err - } - } - - if pi.RedeemScript != nil { - err := serializeKVPairWithType( - w, uint8(RedeemScriptInputType), nil, - pi.RedeemScript, - ) - if err != nil { - return err - } - } - - if pi.WitnessScript != nil { - err := serializeKVPairWithType( - w, uint8(WitnessScriptInputType), nil, - pi.WitnessScript, - ) - if err != nil { - return err - } - } - - sort.Sort(Bip32Sorter(pi.Bip32Derivation)) - for _, kd := range pi.Bip32Derivation { - err := serializeKVPairWithType( - w, - uint8(Bip32DerivationInputType), kd.PubKey, - SerializeBIP32Derivation( - kd.MasterKeyFingerprint, kd.Bip32Path, - ), - ) - if err != nil { - return err - } - } - } - - if pi.FinalScriptSig != nil { - err := serializeKVPairWithType( - w, uint8(FinalScriptSigType), nil, pi.FinalScriptSig, - ) - if err != nil { - return err - } - } - - if pi.FinalScriptWitness != nil { - err := serializeKVPairWithType( - w, uint8(FinalScriptWitnessType), nil, pi.FinalScriptWitness, - ) - if err != nil { - return err - } - } - - // Unknown is a special case; we don't have a key type, only a key and - // a value field - for _, kv := range pi.Unknowns { - err := serializeKVpair(w, kv.Key, kv.Value) - if err != nil { - return err - } - } - - return nil -} diff --git a/psbt/partial_output.go b/psbt/partial_output.go deleted file mode 100644 index 64d1bd4ca..000000000 --- a/psbt/partial_output.go +++ /dev/null @@ -1,139 +0,0 @@ -package psbt - -import ( - "bytes" - "io" - "sort" - - "github.com/btcsuite/btcd/wire" -) - -// POutput is a struct encapsulating all the data that can be attached -// to any specific output of the PSBT. -type POutput struct { - RedeemScript []byte - WitnessScript []byte - Bip32Derivation []*Bip32Derivation -} - -// NewPsbtOutput creates an instance of PsbtOutput; the three parameters -// redeemScript, witnessScript and Bip32Derivation are all allowed to be -// `nil`. -func NewPsbtOutput(redeemScript []byte, witnessScript []byte, - bip32Derivation []*Bip32Derivation) *POutput { - return &POutput{ - RedeemScript: redeemScript, - WitnessScript: witnessScript, - Bip32Derivation: bip32Derivation, - } -} - -// deserialize attempts to recode a new POutput from the passed io.Reader. -func (po *POutput) deserialize(r io.Reader) error { - for { - keyint, keydata, err := getKey(r) - if err != nil { - return err - } - if keyint == -1 { - // Reached separator byte - break - } - - value, err := wire.ReadVarBytes( - r, 0, MaxPsbtValueLength, "PSBT value", - ) - if err != nil { - return err - } - - switch OutputType(keyint) { - - case RedeemScriptOutputType: - if po.RedeemScript != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - po.RedeemScript = value - - case WitnessScriptOutputType: - if po.WitnessScript != nil { - return ErrDuplicateKey - } - if keydata != nil { - return ErrInvalidKeydata - } - po.WitnessScript = value - - case Bip32DerivationOutputType: - if !validatePubkey(keydata) { - return ErrInvalidKeydata - } - master, derivationPath, err := readBip32Derivation(value) - if err != nil { - return err - } - - // Duplicate keys are not allowed - for _, x := range po.Bip32Derivation { - if bytes.Equal(x.PubKey, keydata) { - return ErrDuplicateKey - } - } - - po.Bip32Derivation = append(po.Bip32Derivation, - &Bip32Derivation{ - PubKey: keydata, - MasterKeyFingerprint: master, - Bip32Path: derivationPath, - }, - ) - - default: - // Unknown type is allowed for inputs but not outputs. - return ErrInvalidPsbtFormat - } - } - - return nil -} - -// serialize attempts to write out the target POutput into the passed -// io.Writer. -func (po *POutput) serialize(w io.Writer) error { - if po.RedeemScript != nil { - err := serializeKVPairWithType( - w, uint8(RedeemScriptOutputType), nil, po.RedeemScript, - ) - if err != nil { - return err - } - } - if po.WitnessScript != nil { - err := serializeKVPairWithType( - w, uint8(WitnessScriptOutputType), nil, po.WitnessScript, - ) - if err != nil { - return err - } - } - - sort.Sort(Bip32Sorter(po.Bip32Derivation)) - for _, kd := range po.Bip32Derivation { - err := serializeKVPairWithType(w, - uint8(Bip32DerivationOutputType), - kd.PubKey, - SerializeBIP32Derivation( - kd.MasterKeyFingerprint, - kd.Bip32Path, - ), - ) - if err != nil { - return err - } - } - - return nil -} diff --git a/psbt/partialsig.go b/psbt/partialsig.go deleted file mode 100644 index e11bb80de..000000000 --- a/psbt/partialsig.go +++ /dev/null @@ -1,52 +0,0 @@ -package psbt - -import ( - "bytes" - - "github.com/btcsuite/btcd/btcec" -) - -// PartialSig encapsulate a (BTC public key, ECDSA signature) -// pair, note that the fields are stored as byte slices, not -// btcec.PublicKey or btcec.Signature (because manipulations will -// be with the former not the latter, here); compliance with consensus -// serialization is enforced with .checkValid() -type PartialSig struct { - PubKey []byte - Signature []byte -} - -// PartialSigSorter implements sort.Interface for PartialSig. -type PartialSigSorter []*PartialSig - -func (s PartialSigSorter) Len() int { return len(s) } - -func (s PartialSigSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -func (s PartialSigSorter) Less(i, j int) bool { - return bytes.Compare(s[i].PubKey, s[j].PubKey) < 0 -} - -// validatePubkey checks if pubKey is *any* valid pubKey serialization in a -// Bitcoin context (compressed/uncomp. OK). -func validatePubkey(pubKey []byte) bool { - _, err := btcec.ParsePubKey(pubKey, btcec.S256()) - return err == nil -} - -// validateSignature checks that the passed byte slice is a valid DER-encoded -// ECDSA signature, including the sighash flag. It does *not* of course -// validate the signature against any message or public key. -func validateSignature(sig []byte) bool { - _, err := btcec.ParseDERSignature(sig, btcec.S256()) - return err == nil -} - -// checkValid checks that both the pbukey and sig are valid. See the methods -// (PartialSig, validatePubkey, validateSignature) for more details. -// -// TODO(waxwing): update for Schnorr will be needed here if/when that -// activates. -func (ps *PartialSig) checkValid() bool { - return validatePubkey(ps.PubKey) && validateSignature(ps.Signature) -} diff --git a/psbt/psbt.go b/psbt/psbt.go deleted file mode 100644 index 73126c3eb..000000000 --- a/psbt/psbt.go +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright (c) 2018 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -// Package psbt is an implementation of Partially Signed Bitcoin -// Transactions (PSBT). The format is defined in BIP 174: -// https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki -package psbt - -import ( - "bytes" - "encoding/base64" - "errors" - - "io" - - "github.com/btcsuite/btcd/wire" -) - -// psbtMagicLength is the length of the magic bytes used to signal the start of -// a serialized PSBT packet. -const psbtMagicLength = 5 - -var ( - // psbtMagic is the separator - psbtMagic = [psbtMagicLength]byte{0x70, - 0x73, 0x62, 0x74, 0xff, // = "psbt" + 0xff sep - } -) - -// MaxPsbtValueLength is the size of the largest transaction serialization -// that could be passed in a NonWitnessUtxo field. This is definitely -//less than 4M. -const MaxPsbtValueLength = 4000000 - -// MaxPsbtKeyLength is the length of the largest key that we'll successfully -// deserialize from the wire. Anything more will return ErrInvalidKeydata. -const MaxPsbtKeyLength = 10000 - -var ( - - // ErrInvalidPsbtFormat is a generic error for any situation in which a - // provided Psbt serialization does not conform to the rules of BIP174. - ErrInvalidPsbtFormat = errors.New("Invalid PSBT serialization format") - - // ErrDuplicateKey indicates that a passed Psbt serialization is invalid - // due to having the same key repeated in the same key-value pair. - ErrDuplicateKey = errors.New("Invalid Psbt due to duplicate key") - - // ErrInvalidKeydata indicates that a key-value pair in the PSBT - // serialization contains data in the key which is not valid. - ErrInvalidKeydata = errors.New("Invalid key data") - - // ErrInvalidMagicBytes indicates that a passed Psbt serialization is invalid - // due to having incorrect magic bytes. - ErrInvalidMagicBytes = errors.New("Invalid Psbt due to incorrect magic bytes") - - // ErrInvalidRawTxSigned indicates that the raw serialized transaction in the - // global section of the passed Psbt serialization is invalid because it - // contains scriptSigs/witnesses (i.e. is fully or partially signed), which - // is not allowed by BIP174. - ErrInvalidRawTxSigned = errors.New("Invalid Psbt, raw transaction must " + - "be unsigned.") - - // ErrInvalidPrevOutNonWitnessTransaction indicates that the transaction - // hash (i.e. SHA256^2) of the fully serialized previous transaction - // provided in the NonWitnessUtxo key-value field doesn't match the prevout - // hash in the UnsignedTx field in the PSBT itself. - ErrInvalidPrevOutNonWitnessTransaction = errors.New("Prevout hash does " + - "not match the provided non-witness utxo serialization") - - // ErrInvalidSignatureForInput indicates that the signature the user is - // trying to append to the PSBT is invalid, either because it does - // not correspond to the previous transaction hash, or redeem script, - // or witness script. - // NOTE this does not include ECDSA signature checking. - ErrInvalidSignatureForInput = errors.New("Signature does not correspond " + - "to this input") - - // ErrInputAlreadyFinalized indicates that the PSBT passed to a Finalizer - // already contains the finalized scriptSig or witness. - ErrInputAlreadyFinalized = errors.New("Cannot finalize PSBT, finalized " + - "scriptSig or scriptWitnes already exists") - - // ErrIncompletePSBT indicates that the Extractor object - // was unable to successfully extract the passed Psbt struct because - // it is not complete - ErrIncompletePSBT = errors.New("PSBT cannot be extracted as it is " + - "incomplete") - - // ErrNotFinalizable indicates that the PSBT struct does not have - // sufficient data (e.g. signatures) for finalization - ErrNotFinalizable = errors.New("PSBT is not finalizable") - - // ErrInvalidSigHashFlags indicates that a signature added to the PSBT - // uses Sighash flags that are not in accordance with the requirement - // according to the entry in PsbtInSighashType, or otherwise not the - // default value (SIGHASH_ALL) - ErrInvalidSigHashFlags = errors.New("Invalid Sighash Flags") - - // ErrUnsupportedScriptType indicates that the redeem script or - // scriptwitness given is not supported by this codebase, or is otherwise - // not valid. - ErrUnsupportedScriptType = errors.New("Unsupported script type") -) - -// Unknown is a struct encapsulating a key-value pair for which the key type is -// unknown by this package; these fields are allowed in both the 'Global' and -// the 'Input' section of a PSBT. -type Unknown struct { - Key []byte - Value []byte -} - -// Packet is the actual psbt repreesntation. It is a is a set of 1 + N + M -// key-value pair lists, 1 global, defining the unsigned transaction structure -// with N inputs and M outputs. These key-value pairs can contain scripts, -// signatures, key derivations and other transaction-defining data. -type Packet struct { - // UnsignedTx is the decoded unsigned transaction for this PSBT. - UnsignedTx *wire.MsgTx // Deserialization of unsigned tx - - // Inputs contains all the information needed to properly sign this - // target input within the above transaction. - Inputs []PInput - - // Outputs contains all information required to spend any outputs - // produced by this PSBT. - Outputs []POutput - - // Unknowns are the set of custom types (global only) within this PSBT. - Unknowns []Unknown -} - -// validateUnsignedTx returns true if the transaction is unsigned. Note that -// more basic sanity requirements, such as the presence of inputs and outputs, -// is implicitly checked in the call to MsgTx.Deserialize(). -func validateUnsignedTX(tx *wire.MsgTx) bool { - for _, tin := range tx.TxIn { - if len(tin.SignatureScript) != 0 || len(tin.Witness) != 0 { - return false - } - } - - return true -} - -// NewFromUnsignedTx creates a new Psbt struct, without any signatures (i.e. -// only the global section is non-empty) using the passed unsigned transaction. -func NewFromUnsignedTx(tx *wire.MsgTx) (*Packet, error) { - - if !validateUnsignedTX(tx) { - return nil, ErrInvalidRawTxSigned - } - - inSlice := make([]PInput, len(tx.TxIn)) - outSlice := make([]POutput, len(tx.TxOut)) - unknownSlice := make([]Unknown, 0) - - retPsbt := Packet{ - UnsignedTx: tx, - Inputs: inSlice, - Outputs: outSlice, - Unknowns: unknownSlice, - } - - return &retPsbt, nil -} - -// NewFromRawBytes returns a new instance of a Packet struct created by reading -// from a byte slice. If the format is invalid, an error is returned. If the -// argument b64 is true, the passed byte slice is decoded from base64 encoding -// before processing. -// -// NOTE: To create a Packet from one's own data, rather than reading in a -// serialization from a counterparty, one should use a psbt.New. -func NewFromRawBytes(r io.Reader, b64 bool) (*Packet, error) { - - // If the PSBT is encoded in bas64, then we'll create a new wrapper - // reader that'll allow us to incrementally decode the contents of the - // io.Reader. - if b64 { - based64EncodedReader := r - r = base64.NewDecoder(base64.StdEncoding, based64EncodedReader) - } - - // The Packet struct does not store the fixed magic bytes, but they - // must be present or the serialization must be explicitly rejected. - var magic [5]byte - if _, err := io.ReadFull(r, magic[:]); err != nil { - return nil, err - } - if magic != psbtMagic { - return nil, ErrInvalidMagicBytes - } - - // Next we parse the GLOBAL section. There is currently only 1 known - // key type, UnsignedTx. We insist this exists first; unknowns are - // allowed, but only after. - keyint, keydata, err := getKey(r) - if err != nil { - return nil, err - } - if GlobalType(keyint) != UnsignedTxType || keydata != nil { - return nil, ErrInvalidPsbtFormat - } - - // Now that we've verified the global type is present, we'll decode it - // into a proper unsigned transaction, and validate it. - value, err := wire.ReadVarBytes( - r, 0, MaxPsbtValueLength, "PSBT value", - ) - if err != nil { - return nil, err - } - msgTx := wire.NewMsgTx(2) - err = msgTx.Deserialize(bytes.NewReader(value)) - if err != nil { - // If there are no inputs in this yet incomplete transaction, - // the wire package still incorrectly assumes it's encoded in - // the witness format. We can fix this by just trying the non- - // witness encoding too. If that also fails, it's probably an - // invalid transaction. - msgTx = wire.NewMsgTx(2) - err2 := msgTx.DeserializeNoWitness(bytes.NewReader(value)) - - // If the second attempt also failed, something else is wrong - // and it probably makes more sense to return the original - // error instead of the error from the workaround. - if err2 != nil { - return nil, err - } - } - if !validateUnsignedTX(msgTx) { - return nil, ErrInvalidRawTxSigned - } - - // Next we parse any unknowns that may be present, making sure that we - // break at the separator. - var unknownSlice []Unknown - for { - keyint, keydata, err := getKey(r) - if err != nil { - return nil, ErrInvalidPsbtFormat - } - if keyint == -1 { - break - } - - value, err := wire.ReadVarBytes( - r, 0, MaxPsbtValueLength, "PSBT value", - ) - if err != nil { - return nil, err - } - - keyintanddata := []byte{byte(keyint)} - keyintanddata = append(keyintanddata, keydata...) - - newUnknown := Unknown{ - Key: keyintanddata, - Value: value, - } - unknownSlice = append(unknownSlice, newUnknown) - } - - // Next we parse the INPUT section. - inSlice := make([]PInput, len(msgTx.TxIn)) - for i := range msgTx.TxIn { - input := PInput{} - err = input.deserialize(r) - if err != nil { - return nil, err - } - - inSlice[i] = input - } - - // Next we parse the OUTPUT section. - outSlice := make([]POutput, len(msgTx.TxOut)) - for i := range msgTx.TxOut { - output := POutput{} - err = output.deserialize(r) - if err != nil { - return nil, err - } - - outSlice[i] = output - } - - // Populate the new Packet object - newPsbt := Packet{ - UnsignedTx: msgTx, - Inputs: inSlice, - Outputs: outSlice, - Unknowns: unknownSlice, - } - - // Extended sanity checking is applied here to make sure the - // externally-passed Packet follows all the rules. - if err = newPsbt.SanityCheck(); err != nil { - return nil, err - } - - return &newPsbt, nil -} - -// Serialize creates a binary serialization of the referenced Packet struct -// with lexicographical ordering (by key) of the subsections. -func (p *Packet) Serialize(w io.Writer) error { - - // First we write out the precise set of magic bytes that identify a - // valid PSBT transaction. - if _, err := w.Write(psbtMagic[:]); err != nil { - return err - } - - // Next we prep to write out the unsigned transaction by first - // serializing it into an intermediate buffer. - serializedTx := bytes.NewBuffer( - make([]byte, 0, p.UnsignedTx.SerializeSize()), - ) - if err := p.UnsignedTx.Serialize(serializedTx); err != nil { - return err - } - - // Now that we have the serialized transaction, we'll write it out to - // the proper global type. - err := serializeKVPairWithType( - w, uint8(UnsignedTxType), nil, serializedTx.Bytes(), - ) - if err != nil { - return err - } - - // With that our global section is done, so we'll write out the - // separator. - separator := []byte{0x00} - if _, err := w.Write(separator); err != nil { - return err - } - - for _, pInput := range p.Inputs { - err := pInput.serialize(w) - if err != nil { - return err - } - - if _, err := w.Write(separator); err != nil { - return err - } - } - - for _, pOutput := range p.Outputs { - err := pOutput.serialize(w) - if err != nil { - return err - } - - if _, err := w.Write(separator); err != nil { - return err - } - } - - return nil -} - -// B64Encode returns the base64 encoding of the serialization of -// the current PSBT, or an error if the encoding fails. -func (p *Packet) B64Encode() (string, error) { - var b bytes.Buffer - if err := p.Serialize(&b); err != nil { - return "", err - } - - return base64.StdEncoding.EncodeToString(b.Bytes()), nil -} - -// IsComplete returns true only if all of the inputs are -// finalized; this is particularly important in that it decides -// whether the final extraction to a network serialized signed -// transaction will be possible. -func (p *Packet) IsComplete() bool { - for i := 0; i < len(p.UnsignedTx.TxIn); i++ { - if !isFinalized(p, i) { - return false - } - } - return true -} - -// SanityCheck checks conditions on a PSBT to ensure that it obeys the -// rules of BIP174, and returns true if so, false if not. -func (p *Packet) SanityCheck() error { - - if !validateUnsignedTX(p.UnsignedTx) { - return ErrInvalidRawTxSigned - } - - for _, tin := range p.Inputs { - if !tin.IsSane() { - return ErrInvalidPsbtFormat - } - } - - return nil -} diff --git a/psbt/psbt_test.go b/psbt/psbt_test.go deleted file mode 100644 index c366766ab..000000000 --- a/psbt/psbt_test.go +++ /dev/null @@ -1,1448 +0,0 @@ -// Copyright (c) 2018 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package psbt - -import ( - "bytes" - "encoding/base64" - "encoding/binary" - "encoding/hex" - "testing" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" - "github.com/davecgh/go-spew/spew" -) - -// Test vectors from: -// // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#test-vectors - -// createPsbtFromSignedTx is a utility function to create a PSBT from an -// already-signed transaction, so we can test reconstructing, signing and -// extracting it. Returned are: an unsigned transaction serialization, a list -// of scriptSigs, one per input, and a list of witnesses, one per input. -func createPsbtFromSignedTx(serializedSignedTx []byte) ( - *Packet, [][]byte, []wire.TxWitness, error) { - - tx := wire.NewMsgTx(2) - err := tx.Deserialize(bytes.NewReader(serializedSignedTx)) - if err != nil { - return nil, nil, nil, err - } - scriptSigs := make([][]byte, 0, len(tx.TxIn)) - witnesses := make([]wire.TxWitness, 0, len(tx.TxIn)) - tx2 := tx.Copy() - - // Blank out signature info in inputs - for i, tin := range tx2.TxIn { - tin.SignatureScript = nil - scriptSigs = append(scriptSigs, tx.TxIn[i].SignatureScript) - tin.Witness = nil - witnesses = append(witnesses, tx.TxIn[i].Witness) - - } - - // Outputs always contain: (value, scriptPubkey) so don't need - // amending. Now tx2 is tx with all signing data stripped out - unsignedPsbt, err := NewFromUnsignedTx(tx2) - if err != nil { - return nil, nil, nil, err - } - return unsignedPsbt, scriptSigs, witnesses, nil -} - -// These are all valid PSBTs -var validPsbtHex = map[int]string{ - 0: "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000", - 1: "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000", - 2: "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001030401000000000000", - 3: "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000100df0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e13000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000", - 4: "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", - 5: "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0000", -} - -// These are all invalid PSBTs for the indicated -// reasons. -var invalidPsbtHex = map[int]string{ - // wire format, not PSBT format - 0: "0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300", - // missing outputs - 1: "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000", - // Filled in scriptSig in unsigned tx - 2: "70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000", - // No unsigned tx - 3: "70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000", - // Duplicate keys in an input - 4: "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000", - // Invalid global transaction typed key - 5: "70736274ff020001550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", - // Invalid input witness utxo typed key - 6: "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac000000000002010020955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", - // Invalid pubkey length for input partial signature typed key - 7: "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87210203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", - // Invalid redeemscript typed key - 8: "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01020400220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", - // Invalid witness script typed key - 9: "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d568102050047522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", - // Invalid bip32 typed key - 10: "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae210603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd10b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", - // Invalid non-witness utxo typed key - 11: "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f0000000000020000bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", - // Invalid final scriptsig typed key - 12: "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000020700da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", - // Invalid final script witness typed key - 13: "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903020800da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", - // Invalid pubkey in output BIP32 derivation paths typed key - 14: "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00210203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58710d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", - // Invalid input sighash type typed key - 15: "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0203000100000000010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00", - // Invalid output redeemscript typed key - 16: "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0002000016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00", - // Invalid output witnessScript typed key - 17: "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c00010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a6521010025512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00", - // Additional cases outside the existing test vectors. - // Invalid duplicate PartialSig - 18: "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", - // Invalid duplicate BIP32 derivation (different derivs, same key) - 19: "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba670000008000000080050000800000", -} - -// This tests that valid PSBT serializations can be parsed -// into Psbt structs. -func TestReadValidPsbtAndReserialize(t *testing.T) { - for _, v := range validPsbtHex { - PsbtBytes, err := hex.DecodeString(v) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - - testPsbt, err := NewFromRawBytes( - bytes.NewReader(PsbtBytes), false, - ) - if err != nil { - t.Fatalf("unable to parse psbt: %v", err) - } - - t.Logf("Successfully parsed test, got transaction: %v", - spew.Sdump(testPsbt.UnsignedTx)) - - var b bytes.Buffer - err = testPsbt.Serialize(&b) - if err != nil { - t.Fatalf("Unable to serialize created Psbt: %v", err) - } - - raw := b.Bytes() - if !bytes.Equal(raw, PsbtBytes) { - t.Fatalf("Serialized PSBT didn't match: %v", - hex.EncodeToString(raw)) - } - } -} - -func TestReadInvalidPsbt(t *testing.T) { - for _, v := range invalidPsbtHex { - PsbtBytes, err := hex.DecodeString(v) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - - _, err = NewFromRawBytes(bytes.NewReader(PsbtBytes), false) - if err == nil { - t.Fatalf("Incorrectly validated psbt: %v", - hex.EncodeToString(PsbtBytes)) - } - - t.Logf("Correctly got error: %v", err) - } -} - -func TestSanityCheck(t *testing.T) { - // TODO(guggero): Remove when checks for segwit v1 are implemented. - t.Skip("Skipping PSBT sanity checks for segwit v0.") - - // Test strategy: - // 1. Create an invalid PSBT from a serialization - // Then ensure that the sanity check fails. - // 2. Create a valid PSBT from a serialization - // Then create an updater, add a witness utxo to a non-witness - // utxo. - // Then ensure that the sanity check fails. - // Then add a witnessScript field to a non-witness utxo. - // Then ensure that the sanity check fails. - - // index 1 contains a psbt with two inputs, first non-witness, - // second witness. - psbtraw1, err := hex.DecodeString(validPsbtHex[1]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - psbt1, err := NewFromRawBytes(bytes.NewReader(psbtraw1), false) - if err != nil { - t.Fatalf("Unable to create Psbt struct: %v", err) - } - - // Add a non-witness utxo field to input2 using raw insertion function, - // so that it becomes invalid, then NewUpdater should fail. - nonWitnessUtxoRaw, err := hex.DecodeString( - CUTestHexData["NonWitnessUtxo"], - ) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - nonWitnessUtxo := wire.NewMsgTx(2) - err = nonWitnessUtxo.Deserialize(bytes.NewReader(nonWitnessUtxoRaw)) - if err != nil { - t.Fatalf("Unable to deserialize: %v", err) - } - inputs1 := &psbt1.Inputs[1] - inputs1.NonWitnessUtxo = nonWitnessUtxo - - // The PSBT is now in an inconsistent state; Updater creation should - // fail. - updater, err := NewUpdater(psbt1) - if err == nil { - t.Fatalf("Failed to identify invalid PSBT state ( " + - "witness, non-witness fields)") - } - - // Overwrite back with the correct psbt - psbtraw1, err = hex.DecodeString(validPsbtHex[1]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - psbt1, err = NewFromRawBytes(bytes.NewReader(psbtraw1), false) - updater, err = NewUpdater(psbt1) - if err != nil { - t.Fatalf("Unable to create Updater: %v", err) - } - - // Create a fake non-witness utxo field to overlap with - // the existing witness input at index 1. - tx := wire.NewMsgTx(2) - err = tx.Deserialize(bytes.NewReader(nonWitnessUtxoRaw)) - if err != nil { - t.Fatalf("Error deserializing transaction: %v", err) - } - err = updater.AddInNonWitnessUtxo(tx, 1) - if err == nil { - t.Fatalf("Incorrectly accepted Psbt with conflicting witness " + - "and non-witness utxo entries in the same input.") - } - - // Now we try again; this time we try to add a witnessScript - // key-value pair to an input which is non-witness, which should - // also be rejected. - psbt2, err := NewFromRawBytes( - bytes.NewReader(psbtraw1), false, - ) - if err != nil { - t.Fatalf("Unable to create Psbt struct: %v", err) - } - updater2, err := NewUpdater(psbt2) - if err != nil { - t.Fatalf("Got error creating updater2: %v", err) - } - witnessScript, err := hex.DecodeString( - CUTestHexData["Input2WitnessScript"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - err = updater2.AddInWitnessScript(witnessScript, 0) - if err == nil { - t.Fatalf("Incorrectly accepted adding witness script field " + - "to non-witness utxo") - } -} - -// Data for creation and updating tests -// =============================================================================== -var CUTestHexData = map[string]string{ - "scriptPubkey1": "0014d85c2b71d0060b09c9886aeb815e50991dda124d", - "scriptPubkey2": "001400aea9a2e5f0f876a588df5546e8742d1d87008f", - "txid1": "75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858", - "txid2": "1dea7cd05979072a3578cab271c02244ea8a090bbb46aa680a65ecd027048d83", - "COPsbtHex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000000000000000000", - "NonWitnessUtxo": "0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000", - "WitnessUtxo": "00c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887", - // After adding witnessutxo and nonwitness utxo to inputs: - "UOPsbtHex": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887000000", - "Input1RedeemScript": "5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae", - "Input2RedeemScript": "00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903", - "Input2WitnessScript": "522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae", - // After adding redeemscripts and witness scripts to inputs: - "UOPsbtHex2": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88701042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae000000", - // After adding bip32 derivations to inputs and outputs: - "UOPsbtHex3": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88701042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", - //After adding sighash types to inputs - "UOPsbtHex4": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", -} - -// Just one example sanity check of B64 construction; after sighash appending above -var CUTestB64Data = map[string]string{ - "UOPsbtB644": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", -} - -var CUTestAmountData = map[string]int64{ - "amount1": 149990000, - "amount2": 100000000, - "amount3": 200000000, -} - -var CUTestIndexData = map[string]uint32{ - "index1": 0, - "index2": 1, -} - -var CUMasterKeyFingerPrint = "d90c6a4f" - -var CUTestPathData = map[string][]uint32{ - "dpath1": {0 + 0x80000000, 0 + 0x80000000, 0 + 0x80000000}, - "dpath2": {0 + 0x80000000, 0 + 0x80000000, 1 + 0x80000000}, - "dpath3": {0 + 0x80000000, 0 + 0x80000000, 2 + 0x80000000}, - "dpath4": {0 + 0x80000000, 0 + 0x80000000, 3 + 0x80000000}, - "dpath5": {0 + 0x80000000, 0 + 0x80000000, 4 + 0x80000000}, - "dpath6": {0 + 0x80000000, 0 + 0x80000000, 5 + 0x80000000}, -} - -var CUTestPubkeyData = map[string]string{ - "pub1": "029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f", - "pub2": "02dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7", - "pub3": "03089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc", - "pub4": "023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73", - "pub5": "03a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58771", - "pub6": "027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b50051096", -} - -// =============================================================================== - -func TestPsbtCreator(t *testing.T) { - spkOut1, err := hex.DecodeString(CUTestHexData["scriptPubkey1"]) - if err != nil { - t.Fatalf("Error: %v", err) - } - spkOut2, err := hex.DecodeString(CUTestHexData["scriptPubkey2"]) - if err != nil { - t.Fatalf("Error: %v", err) - } - out1 := wire.NewTxOut(CUTestAmountData["amount1"], spkOut1) - out2 := wire.NewTxOut(CUTestAmountData["amount2"], spkOut2) - outputs := []*wire.TxOut{out1, out2} - hash1, err := chainhash.NewHashFromStr(CUTestHexData["txid1"]) - if err != nil { - t.Fatalf("Error: %v", err) - } - prevOut1 := wire.NewOutPoint(hash1, uint32(0)) - hash2, err := chainhash.NewHashFromStr(CUTestHexData["txid2"]) - if err != nil { - t.Fatalf("Error: %v", err) - } - prevOut2 := wire.NewOutPoint(hash2, uint32(1)) - inputs := []*wire.OutPoint{prevOut1, prevOut2} - - // Check creation fails with invalid sequences: - nSequences := []uint32{wire.MaxTxInSequenceNum} - _, err = New(inputs, outputs, int32(3), uint32(0), nSequences) - if err == nil { - t.Fatalf("Did not error when creating transaction with " + - "invalid nSequences") - } - nSequences = append(nSequences, wire.MaxTxInSequenceNum) - - // Check creation fails with invalid version - _, err = New(inputs, outputs, int32(0), uint32(0), nSequences) - if err == nil { - t.Fatalf("Did not error when creating transaction with " + - "invalid version (3)") - } - - // Use valid data to create: - cPsbt, err := New(inputs, outputs, int32(2), uint32(0), nSequences) - var b bytes.Buffer - err = cPsbt.Serialize(&b) - if err != nil { - t.Fatalf("Unable to serialize created Psbt: %v", err) - } - if CUTestHexData["COPsbtHex"] != hex.EncodeToString(b.Bytes()) { - t.Fatalf("Failed to create expected psbt, instead got: %v", - hex.EncodeToString(b.Bytes())) - } - - // Now simulate passing the created PSBT to an Updater - updater, err := NewUpdater(cPsbt) - if err != nil { - t.Fatalf("Unable to create Updater object") - } - tx := wire.NewMsgTx(2) - nonWitnessUtxoHex, err := hex.DecodeString( - CUTestHexData["NonWitnessUtxo"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - err = tx.Deserialize(bytes.NewReader(nonWitnessUtxoHex)) - if err != nil { - t.Fatalf("Error deserializing transaction: %v", err) - } - witnessUtxoHex, err := hex.DecodeString( - CUTestHexData["WitnessUtxo"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - txout := wire.TxOut{Value: CUTestAmountData["amount3"], - PkScript: witnessUtxoHex[9:]} - err = updater.AddInNonWitnessUtxo(tx, 0) - if err != nil { - t.Fatalf("Unable to add NonWitness Utxo to inputs: %v", err) - } - err = updater.AddInWitnessUtxo(&txout, 1) - if err != nil { - t.Fatalf("Unable to add Witness Utxo to inputs: %v", err) - } - - b.Reset() - - err = updater.Upsbt.Serialize(&b) - if err != nil { - t.Fatalf("Unable to serialize updated Psbt: %v", err) - } - if CUTestHexData["UOPsbtHex"] != hex.EncodeToString(b.Bytes()) { - t.Fatal("Failed to create valid updated PSBT after utxos") - } - input1RedeemScript, err := hex.DecodeString(CUTestHexData["Input1RedeemScript"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - err = updater.AddInRedeemScript(input1RedeemScript, 0) - if err != nil { - t.Fatalf("Unable to add redeem script: %v", err) - } - input2RedeemScript, err := hex.DecodeString(CUTestHexData["Input2RedeemScript"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - err = updater.AddInRedeemScript(input2RedeemScript, 1) - if err != nil { - t.Fatalf("Unable to add redeem script: %v", err) - } - input2WitnessScript, err := hex.DecodeString(CUTestHexData["Input2WitnessScript"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - err = updater.AddInWitnessScript(input2WitnessScript, 1) - if err != nil { - t.Fatalf("Unable to add witness script: %v", err) - } - - b.Reset() - err = updater.Upsbt.Serialize(&b) - if err != nil { - t.Fatalf("Unable to serialize updated Psbt: %v", err) - } - if CUTestHexData["UOPsbtHex2"] != hex.EncodeToString(b.Bytes()) { - t.Fatal("Failed to create valid updated PSBT after redeem scripts") - } - masterKey, err := hex.DecodeString(CUMasterKeyFingerPrint) - masterKeyInt := binary.LittleEndian.Uint32(masterKey) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - input1Path1 := CUTestPathData["dpath1"] - input1Path2 := CUTestPathData["dpath2"] - input1Key1, err := hex.DecodeString(CUTestPubkeyData["pub1"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - input1Key2, err := hex.DecodeString(CUTestPubkeyData["pub2"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - err = updater.AddInBip32Derivation(masterKeyInt, input1Path1, input1Key1, 0) - if err != nil { - t.Fatal("Failed to add first key derivation for input 1") - } - err = updater.AddInBip32Derivation(masterKeyInt, input1Path2, input1Key2, 0) - if err != nil { - t.Fatal("Failed to add second key derivation for input 1") - } - input2Path1 := CUTestPathData["dpath3"] - input2Path2 := CUTestPathData["dpath4"] - input2Key1, err := hex.DecodeString(CUTestPubkeyData["pub3"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - input2Key2, err := hex.DecodeString(CUTestPubkeyData["pub4"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - - // check invalid pubkeys are not accepted - borkedInput2Key1 := append([]byte{0xff}, input2Key1...) - err = updater.AddInBip32Derivation(masterKeyInt, input2Path1, - borkedInput2Key1, 1) - if err == nil { - t.Fatalf("Expected invalid pubkey, got: %v", err) - } - - err = updater.AddInBip32Derivation(masterKeyInt, input2Path1, input2Key1, 1) - if err != nil { - t.Fatal("Failed to add first key derivation for input 2") - } - err = updater.AddInBip32Derivation(masterKeyInt, input2Path2, input2Key2, 1) - if err != nil { - t.Fatal("Failed to add second key derivation for input 2") - } - output1Key1, err := hex.DecodeString(CUTestPubkeyData["pub5"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - output1Path := CUTestPathData["dpath5"] - - // check invalid pubkeys are not accepted - borkedOutput1Key1 := append([]byte{0xab}, output1Key1[:13]...) - err = updater.AddOutBip32Derivation(masterKeyInt, output1Path, - borkedOutput1Key1, 0) - if err == nil { - t.Fatalf("Expected invalid pubkey, got: %v", err) - } - - err = updater.AddOutBip32Derivation(masterKeyInt, output1Path, output1Key1, 0) - if err != nil { - t.Fatal("Failed to add key to first output") - } - output2Key1, err := hex.DecodeString(CUTestPubkeyData["pub6"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - output2Path := CUTestPathData["dpath6"] - err = updater.AddOutBip32Derivation(masterKeyInt, output2Path, output2Key1, 1) - if err != nil { - t.Fatal("Failed to add key to second output") - } - - b.Reset() - err = updater.Upsbt.Serialize(&b) - if err != nil { - t.Fatalf("Unable to serialize updated Psbt: %v", err) - } - if CUTestHexData["UOPsbtHex3"] != hex.EncodeToString(b.Bytes()) { - t.Fatal("Failed to create valid updated PSBT after BIP32 derivations") - } - err = updater.AddInSighashType(txscript.SigHashType(1), 0) - if err != nil { - t.Fatal("Failed to add sighash type to first input") - } - err = updater.AddInSighashType(txscript.SigHashType(1), 1) - if err != nil { - t.Fatal("Failed to add sighash type to second input") - } - - b.Reset() - err = updater.Upsbt.Serialize(&b) - if err != nil { - t.Fatalf("Unable to serialize updated Psbt: %v", err) - } - if CUTestHexData["UOPsbtHex4"] != hex.EncodeToString(b.Bytes()) { - t.Fatal("Failed to create valid updated PSBT after sighash types") - } - b644, err := updater.Upsbt.B64Encode() - if err != nil { - t.Fatalf("Unable to B64Encode updated Psbt: %v", err) - } - if b644 != CUTestB64Data["UOPsbtB644"] { - t.Fatalf("Failed to base64 encode updated PSBT after sighash "+ - "types: %v", b644) - } -} - -// Signing test data taken from -// https://github.com/achow101/bitcoin/blob/020628e3a4e88e36647eaf92bac4b3552796ac6a/test/functional/data/rpc_psbt.json -var signerPsbtData = map[string]string{ - "signer1Privkey1": "cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr", - "signer1Privkey2": "cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d", - "signer1PsbtB64": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAQMEAQAAAAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAAQMEAQAAAAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", - "signer1Result": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", - "signer2Privkey1": "cT7J9YpCwY3AVRFSjN6ukeEeWY6mhpbJPxRaDaP5QTdygQRxP9Au", - "signer2Privkey2": "cNBc3SWUip9PPm1GjRoLEJT6T41iNzCYtD7qro84FMnM5zEqeJsE", - "signer2Psbt": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f000000800000008001000080010304010000000001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88701042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f0000008000000080020000800103040100000000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", - "signer2Result": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8872202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", -} - -func TestPsbtSigner(t *testing.T) { - psbt1, err := NewFromRawBytes( - bytes.NewReader([]byte(signerPsbtData["signer1PsbtB64"])), - true, - ) - if err != nil { - t.Fatalf("Failed to parse PSBT: %v", err) - } - psbtUpdater1 := Updater{ - Upsbt: psbt1, - } - sig1, err := hex.DecodeString("3044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01") - pub1, err := hex.DecodeString("029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f") - res, err := psbtUpdater1.Sign(0, sig1, pub1, nil, nil) - if err != nil || res != 0 { - t.Fatalf("Error from adding signatures: %v %v", err, res) - } - sig2, err := hex.DecodeString("3044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01") - pub2, err := hex.DecodeString("03089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc") - res, err = psbtUpdater1.Sign(1, sig2, pub2, nil, nil) - if err != nil || res != 0 { - t.Fatalf("Error from adding signatures: %v %v", err, res) - } - signer1Result, err := hex.DecodeString(signerPsbtData["signer1Result"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - - var b bytes.Buffer - err = psbtUpdater1.Upsbt.Serialize(&b) - if err != nil { - t.Fatalf("Unable to serialize updated Psbt: %v", err) - } - if !bytes.Equal(b.Bytes(), signer1Result) { - t.Fatalf("Failed to add signatures correctly") - } -} - -// Finalizer-extractor test - -var finalizerPsbtData = map[string]string{ - "finalizeb64": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", - "finalize": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f012202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", - "resultb64": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==", - "result": "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", - "network": "0200000000010258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7500000000da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752aeffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d01000000232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00000000", - "twoOfThree": "70736274ff01005e01000000019a5fdb3c36f2168ea34a031857863c63bb776fd8a8a9149efd7341dfaf81c9970000000000ffffffff01e013a8040000000022002001c3a65ccfa5b39e31e6bafa504446200b9c88c58b4f21eb7e18412aff154e3f000000000001012bc817a80400000000220020114c9ab91ea00eb3e81a7aa4d0d8f1bc6bd8761f8f00dbccb38060dc2b9fdd5522020242ecd19afda551d58f496c17e3f51df4488089df4caafac3285ed3b9c590f6a847304402207c6ab50f421c59621323460aaf0f731a1b90ca76eddc635aed40e4d2fc86f97e02201b3f8fe931f1f94fde249e2b5b4dbfaff2f9df66dd97c6b518ffa746a4390bd1012202039f0acfe5a292aafc5331f18f6360a3cc53d645ebf0cc7f0509630b22b5d9f547473044022075329343e01033ebe5a22ea6eecf6361feca58752716bdc2260d7f449360a0810220299740ed32f694acc5f99d80c988bb270a030f63947f775382daf4669b272da0010103040100000001056952210242ecd19afda551d58f496c17e3f51df4488089df4caafac3285ed3b9c590f6a821035a654524d301dd0265c2370225a6837298b8ca2099085568cc61a8491287b63921039f0acfe5a292aafc5331f18f6360a3cc53d645ebf0cc7f0509630b22b5d9f54753ae22060242ecd19afda551d58f496c17e3f51df4488089df4caafac3285ed3b9c590f6a818d5f7375b2c000080000000800000008000000000010000002206035a654524d301dd0265c2370225a6837298b8ca2099085568cc61a8491287b63918e2314cf32c000080000000800000008000000000010000002206039f0acfe5a292aafc5331f18f6360a3cc53d645ebf0cc7f0509630b22b5d9f54718e524a1ce2c000080000000800000008000000000010000000000", -} - -func TestFinalize2of3(t *testing.T) { - b, err := hex.DecodeString(finalizerPsbtData["twoOfThree"]) - if err != nil { - t.Fatalf("Error decoding hex: %v", err) - } - p, err := NewFromRawBytes(bytes.NewReader(b), false) - if p.IsComplete() { - t.Fatalf("Psbt is complete") - } - err = MaybeFinalizeAll(p) - if err != nil { - t.Fatalf("Error in MaybeFinalizeAll: %v", err) - } - if !p.IsComplete() { - t.Fatalf("Psbt is not complete") - } -} - -func TestPsbtExtractor(t *testing.T) { - rawToFinalize, err := base64.StdEncoding.DecodeString( - finalizerPsbtData["finalizeb64"], - ) - if err != nil { - t.Fatalf("Error decoding b64: %v", err) - } - - psbt1, err := NewFromRawBytes( - bytes.NewReader(rawToFinalize), false, - ) - if err != nil { - t.Fatalf("Failed to parse PSBT: %v", err) - } - - for i := range psbt1.Inputs { - err = Finalize(psbt1, i) - if err != nil { - t.Fatalf("Error from finalizing PSBT: %v", err) - } - } - - finalizer1Result, err := base64.StdEncoding.DecodeString( - finalizerPsbtData["resultb64"], - ) - if err != nil { - t.Fatalf("Unable to decode b64: %v", err) - } - finalToNetworkExpected, err := hex.DecodeString(finalizerPsbtData["network"]) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - tx, err := Extract(psbt1) - if err != nil { - t.Fatalf("Failed to extract: %v", err) - } - var resultToNetwork bytes.Buffer - if err := tx.Serialize(&resultToNetwork); err != nil { - t.Fatalf("unable to serialize: %v", err) - } - - var b bytes.Buffer - err = psbt1.Serialize(&b) - if err != nil { - t.Fatalf("Unable to serialize updated Psbt: %v", err) - } - if !bytes.Equal(b.Bytes(), finalizer1Result) { - t.Fatalf("Failed to finalize transaction: expected %x, "+ - "got %x", finalizer1Result, b.Bytes()) - } - if !bytes.Equal(finalToNetworkExpected, resultToNetwork.Bytes()) { - t.Fatalf("Failed to network serialize transaction: %x", b.Bytes()) - } -} - -func TestImportFromCore1(t *testing.T) { - // This example #1 was created manually using Bitcoin Core 0.17 regtest. - // It contains two inputs, one p2wkh and one p2pkh (non-witness). - // We take the created PSBT as input, then add the fields for each input - // separately, then finalize and extract, and compare with the network - // serialized tx output from Core. - imported := "cHNidP8BAJwCAAAAAjaoF6eKeGsPiDQxxqqhFDfHWjBtZzRqmaZmvyCVWZ5JAQAAAAD/////RhypNiFfnQSMNpo0SGsgIvDOyMQFAYEHZXD5jp4kCrUAAAAAAP////8CgCcSjAAAAAAXqRQFWy8ScSkkhlGMwfOnx15YwRzApofwX5MDAAAAABepFAt4TyLfGnL9QY6GLYHbpSQj+QclhwAAAAAAAAAAAA==" - psbt1, err := NewFromRawBytes(bytes.NewReader([]byte(imported)), true) - if err != nil { - t.Fatalf("Failed to parse PSBT: %v", err) - } - - // update with the first input's utxo (witness) and the second input's utxo - // (non-witness) - fundingTxInput1Hex := "02000000014f2cbac7d7691fafca30313097d79be9e78aa6670752fcb1fc15508e77586efb000000004847304402201b5568d7cab977ae0892840b779d84e36d62e42fd93b95e648aaebeacd2577d602201d2ebda2b0cddfa0c1a71d3cbcb602e7c9c860a41ed8b4d18d40c92ccbe92aed01feffffff028c636f91000000001600147447b6d7e6193499565779c8eb5184fcfdfee6ef00879303000000001600149e88f2828a074ebf64af23c2168d1816258311d72d010000" - fundingTxInput2Hex := "020000000001012f03f70c673d83d65da0e8d0db3867b3e7d7bfbd34fd6be65892042e57576eb00000000000feffffff028027128c000000001976a91485780899b61a5506f342bd67a2f635181f50c8b788acb8032c040000000017a914e2e3d32d42d6f043cab39708a6073301df5039db8702473044022047ae396fd8aba8f67482ad16e315fe680db585c1ac6422ffb18dacd9cf5bac350220321176fd6157ef51d9eae9230b0b5bd7dd29bb6247a879189e6aaa8091f3020201210368081f7ff37dfadbed407eba17b232f959e41e6ac78741192c805ebf80d487852f010000" - fundingTxInput1Bytes, err := hex.DecodeString(fundingTxInput1Hex) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - txFund1 := wire.NewMsgTx(2) - err = txFund1.Deserialize(bytes.NewReader(fundingTxInput1Bytes)) - if err != nil { - t.Fatalf("Error deserializing transaction: %v", err) - } - // First input is witness, take correct output: - txFund1Out := txFund1.TxOut[1] - - fundingTxInput2Bytes, err := hex.DecodeString(fundingTxInput2Hex) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - txFund2 := wire.NewMsgTx(2) - err = txFund2.Deserialize(bytes.NewReader(fundingTxInput2Bytes)) - if err != nil { - t.Fatalf("Error deserializing transaction: %v", err) - } - psbtupdater1 := Updater{Upsbt: psbt1} - psbtupdater1.AddInWitnessUtxo(txFund1Out, 0) - err = psbtupdater1.AddInNonWitnessUtxo(txFund2, 1) - if err != nil { - t.Fatalf("Error inserting non-witness utxo: %v", err) - } - - // Signing was done with Core; we manually insert the relevant input - // entries here. - sig1Hex := "304402200da03ac9890f5d724c42c83c2a62844c08425a274f1a5bca50dcde4126eb20dd02205278897b65cb8e390a0868c9582133c7157b2ad3e81c1c70d8fbd65f51a5658b01" - sig1, err := hex.DecodeString(sig1Hex) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - pub1Hex := "024d6b24f372dd4551277c8df4ecc0655101e11c22894c8e05a3468409c865a72c" - pub1, err := hex.DecodeString(pub1Hex) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - - // Check that invalid pubkeys are not accepted. - pubInvalid := append(pub1, 0x00) - - res, err := psbtupdater1.Sign(0, sig1, pubInvalid, nil, nil) - if err == nil { - t.Fatalf("Incorrectly accepted invalid pubkey: %v", - pubInvalid) - } - - res, err = psbtupdater1.Sign(0, sig1, pub1, nil, nil) - if err != nil || res != 0 { - t.Fatalf("Error from adding signatures: %v %v", err, res) - } - - sig2Hex := "3044022014eb9c4858f71c9f280bc68402aa742a5187f54c56c8eb07c902eb1eb5804e5502203d66656de8386b9b044346d5605f5ae2b200328fb30476f6ac993fc0dbb0455901" - sig2, err := hex.DecodeString(sig2Hex) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - pub2Hex := "03b4c79acdf4e7d978bef4019c421e4c6c67044ed49d27322dc90e808d8080e862" - pub2, err := hex.DecodeString(pub2Hex) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - - // =============================================================== - // Before adding the signature, we'll make a new PSBT with - // modifications to the input data and check it fails sanity checks. - - // First an invalid tx: - psbtBorkedInput2, _ := NewFromRawBytes(bytes.NewReader([]byte(imported)), true) - borkedUpdater, err := NewUpdater(psbtBorkedInput2) - if err != nil { - t.Fatalf("NewUpdater failed while trying to create borked "+ - "version: %v", err) - } - borkedUpdater.AddInWitnessUtxo(txFund1Out, 0) - - res, err = borkedUpdater.Sign(0, sig2, pub2, nil, nil) - if err != ErrInvalidSignatureForInput { - t.Fatalf("AddPartialSig succeeded, but should have failed "+ - "due to mismatch between pubkey and prevOut; err was: %v", err) - } - - // Next, a valid tx serialization, but not the right one - wrongTxBytes, err := hex.DecodeString("020000000001012d1d7b17356d0ad8232a5817d2d2fa5cd97d803c0ed03e013e97b65f4f1e5e7501000000171600147848cfb25bb163c7c63732615980a25eddbadc7bfeffffff022a8227630000000017a91472128ae6b6a1b74e499bedb5efb1cb09c9a6713287107240000000000017a91485f81cb970d854e2513ebf5c5b5d09e4509f4af3870247304402201c09aa8bcd18753ef01d8712a55eea5a0f69b6c4cc2944ac942264ff0662c91402201fc1390bf8b0023dd12ae78d7ec181124e106de57bc8f00812ae92bd024d3045012103ba077fc011aa59393bfe17cf491b3a02a9c4d39df122b2148322da0ec23508f459430800") - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - wrongTx := wire.NewMsgTx(2) - err = wrongTx.Deserialize(bytes.NewReader(wrongTxBytes)) - if err != nil { - t.Fatalf("Error deserializing transaction: %v", err) - } - psbtBorkedInput2.Inputs[1] = *NewPsbtInput(wrongTx, nil) - res, err = borkedUpdater.Sign(1, sig2, pub2, nil, nil) - if err != ErrInvalidSignatureForInput { - t.Fatalf("Error should have been invalid sig for input, was: %v", err) - } - // ====================================================== - - res, err = psbtupdater1.Sign(1, sig2, pub2, nil, nil) - if err != nil || res != 0 { - t.Fatalf("Failed to add signature to second input: %v %v", err, res) - } - - // Neither input (p2pkh and p2wkh) require redeem script nor witness script, - // so there are no more fields to add; we are ready to finalize. - err = Finalize(psbt1, 0) - if err != nil { - t.Fatalf("Failed to finalize the first input, %v", err) - } - if psbt1.IsComplete() { - t.Fatalf("PSBT was complete but has not been fully finalized") - } - err = Finalize(psbt1, 1) - if err != nil { - t.Fatalf("Failed to finalize second input, %v", err) - } - - tx, err := Extract(psbt1) - if err != nil { - t.Fatalf("unable to extract tx: %v", err) - } - var networkSerializedTx bytes.Buffer - if err := tx.Serialize(&networkSerializedTx); err != nil { - t.Fatalf("unable to encode tx: %v", err) - } - - expectedTx := "0200000000010236a817a78a786b0f883431c6aaa11437c75a306d67346a99a666bf2095599e490100000000ffffffff461ca936215f9d048c369a34486b2022f0cec8c4050181076570f98e9e240ab5000000006a473044022014eb9c4858f71c9f280bc68402aa742a5187f54c56c8eb07c902eb1eb5804e5502203d66656de8386b9b044346d5605f5ae2b200328fb30476f6ac993fc0dbb04559012103b4c79acdf4e7d978bef4019c421e4c6c67044ed49d27322dc90e808d8080e862ffffffff028027128c0000000017a914055b2f1271292486518cc1f3a7c75e58c11cc0a687f05f93030000000017a9140b784f22df1a72fd418e862d81dba52423f90725870247304402200da03ac9890f5d724c42c83c2a62844c08425a274f1a5bca50dcde4126eb20dd02205278897b65cb8e390a0868c9582133c7157b2ad3e81c1c70d8fbd65f51a5658b0121024d6b24f372dd4551277c8df4ecc0655101e11c22894c8e05a3468409c865a72c0000000000" - expectedTxBytes, err := hex.DecodeString(expectedTx) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - if !bytes.Equal(expectedTxBytes, networkSerializedTx.Bytes()) { - t.Fatalf("The produced network transaction did not match the expected: %x \n %x \n", - networkSerializedTx.Bytes(), expectedTxBytes) - } - -} - -func TestImportFromCore2(t *testing.T) { - // This example #2 was created manually using Bitcoin Core 0.17 regtest. - // It contains two inputs, one p2sh-p2wkh and one fake utxo. - // The PSBT has been created with walletcreatepsbt and then partial-signed - // on the real input with walletprocessbst in Core. - // We first check that the updating here, using the Core created signature, - // redeem script and signature for the p2sh-p2wkh input, creates the - // same partial-signed intermediate transaction as Core did after - // walletprocesspsbt. - // We then attach a fake - // input of type p2sh-p2wsh, attach its witnessUtxo, redeemscript and - // witnessscript fields, and then finalize the whole transaction. Unlike - // the previous example, we cannot here compare with a Core produced - // network serialized final transaction, because of the fake input. - imported := "cHNidP8BAJsCAAAAAkxTQ+rig5QNnUS5nMc+Pccow4IcOJeQRcNNw+7p5ZA5AQAAAAD/////qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqoNAAAAAP////8CAIYOcAAAAAAWABQ1l7nn13RubTwqRQU2BnVV5WlXBWAxMbUAAAAAF6kUkiuXUjfWFgTp6nl/gf9+8zIWR6KHAAAAAAAAAAAA" - psbt1, err := NewFromRawBytes(bytes.NewReader([]byte(imported)), true) - if err != nil { - t.Fatalf("Failed to parse PSBT: %v", err) - } - - // update with the first input's utxo, taken from its funding - // transaction - fundingTxInput1Hex := "02000000017b260536a3c17aee49c41a9b36fdf01a418e0c04df06fbabcb0d4f590b95d175000000006a473044022074a5a13159b6c12d77881c9501aa5c18616fb76c1809fc4d55f18a2e63159a6702200d1aa72be6056a41808898d24da93c0c0192cad65b7c2cc86e00b3e0fbbd57f601210212cc429d61fde565d0c2271a3e4fdb063cb49ae2257fa71460be753ceb56d175feffffff02bc060d8f0000000017a9140b56c31b5dc5a5a22c45a7850e707ad602d94a3087008352840000000017a9149f3679d67a9a486238764f618a93b82a7d999103879a000000" - fundingTxInput1Bytes, err := hex.DecodeString(fundingTxInput1Hex) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - txFund1 := wire.NewMsgTx(2) - err = txFund1.Deserialize(bytes.NewReader(fundingTxInput1Bytes)) - if err != nil { - t.Fatalf("Error deserializing transaction: %v", err) - } - // First input is witness, take correct output: - txFund1Out := txFund1.TxOut[1] - - psbtupdater1 := Updater{Upsbt: psbt1} - psbtupdater1.AddInWitnessUtxo(txFund1Out, 0) - - // This input is p2sh-p2wkh, so it requires a redeemscript but not - // a witness script. The redeemscript is the witness program. - redeemScript, err := hex.DecodeString("00147aed39420a8b7ab98a83791327ccb70819d1fbe2") - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - psbtupdater1.AddInRedeemScript(redeemScript, 0) - - // Signing for the first input was done with Core; we manually insert the - // relevant input entries here. - sig1Hex := "30440220546d182d00e45ef659c329dce6197dc19e0abc795e2c9279873f5a887998b273022044143113fc3475d04fc8d5113e0bbcb42d80514a9f1a2247e9b2a7878e20d44901" - sig1, err := hex.DecodeString(sig1Hex) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - pub1Hex := "02bb3ce35af26f4c826eab3e5fc263ef56871b26686a8a995599b7ee6576613104" - pub1, err := hex.DecodeString(pub1Hex) - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - - res, err := psbtupdater1.Sign(0, sig1, pub1, nil, nil) - if err != nil || res != 0 { - t.Fatalf("Unable to add partial signature: %v %v", err, res) - } - - // Since this input is now finalizable, we do so: - err = Finalize(psbt1, 0) - if err != nil { - t.Fatalf("Failed to finalize the first input: %v", err) - } - if psbt1.IsComplete() { - t.Fatalf("PSBT was complete but has not been fully finalized") - } - - // Core also adds the OutRedeemScript field for the output it knows about. - // Note that usually we would not of course re-create, but rather start - // from the half-signed version; so this is needed only for a sanity check - // that we can recreate the half-signed. - output2RedeemScript, err := hex.DecodeString("0014e0846bd17848ab40ca1f56b655c6fa31667880cc") - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - psbtupdater1.AddOutRedeemScript(output2RedeemScript, 1) - // The main function of the test is to compare the thus-generated - // partially (not completely) signed transaction with that generated and - // encoded by Core. - expectedPsbtPartialB64 := "cHNidP8BAJsCAAAAAkxTQ+rig5QNnUS5nMc+Pccow4IcOJeQRcNNw+7p5ZA5AQAAAAD/////qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqoNAAAAAP////8CAIYOcAAAAAAWABQ1l7nn13RubTwqRQU2BnVV5WlXBWAxMbUAAAAAF6kUkiuXUjfWFgTp6nl/gf9+8zIWR6KHAAAAAAABASAAg1KEAAAAABepFJ82edZ6mkhiOHZPYYqTuCp9mZEDhwEHFxYAFHrtOUIKi3q5ioN5EyfMtwgZ0fviAQhrAkcwRAIgVG0YLQDkXvZZwync5hl9wZ4KvHleLJJ5hz9aiHmYsnMCIEQUMRP8NHXQT8jVET4LvLQtgFFKnxoiR+myp4eOINRJASECuzzjWvJvTIJuqz5fwmPvVocbJmhqiplVmbfuZXZhMQQAAAABABYAFOCEa9F4SKtAyh9WtlXG+jFmeIDMAA==" - generatedPsbtPartialB64, err := psbt1.B64Encode() - if err != nil { - t.Fatalf("Unable to B64Encode Psbt: %v", err) - } - if expectedPsbtPartialB64 != generatedPsbtPartialB64 { - t.Fatalf("Partial did not match expected: %v", generatedPsbtPartialB64) - } - - // We now simulate adding the signing data for the second (fake) input, - // and check that we can finalize and extract. This input is p2sh-p2wsh. - // the second input is fake, we're going to make it witness type, - // so create a TxOut struct that fits - fakeTxOutSerialized, err := hex.DecodeString("00c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887") - if err != nil { - t.Fatalf("Failed to decode hex: %v", err) - } - fakevalSerialized := binary.LittleEndian.Uint64(fakeTxOutSerialized[:8]) - fakeScriptPubKey := fakeTxOutSerialized[9:] - txFund2Out := wire.NewTxOut(int64(fakevalSerialized), fakeScriptPubKey) - psbt2, err := NewFromRawBytes(bytes.NewReader([]byte(expectedPsbtPartialB64)), true) - if err != nil { - t.Fatalf("Failed to load partial PSBT: %v", err) - } - psbtupdater2, err := NewUpdater(psbt2) - if err != nil { - t.Fatalf("Failed to create updater: %v", err) - } - psbtupdater2.AddInWitnessUtxo(txFund2Out, 1) - // Add redeemScript, which is the witnessscript/program: - redeemScript, err = hex.DecodeString("00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903") - if err != nil { - t.Fatalf("Failed to decode hex: %v", err) - } - err = psbtupdater2.AddInRedeemScript(redeemScript, 1) - if err != nil { - t.Fatalf("Failed to add redeemscript to second input: %v", err) - } - // Add witnessScript, which here is multisig: - witnessScript, err := hex.DecodeString("522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae") - if err != nil { - t.Fatalf("Failed to decode hex: %v", err) - } - // To test multisig checks, add a nonsense version of the multisig script - witnessScriptNonsense, err := hex.DecodeString("52ffff") - if err != nil { - t.Fatalf("Failed to decode hex: %v", err) - } - err = psbtupdater2.AddInWitnessScript(witnessScript, 1) - if err != nil { - t.Fatalf("Failed to add witnessscript to second input: %v", err) - } - // Construct the two partial signatures to be added - sig21, err := hex.DecodeString("3044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01") - if err != nil { - t.Fatalf("Failed to decode hex: %v", err) - } - pub21, err := hex.DecodeString("03089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc") - if err != nil { - t.Fatalf("Failed to decode hex: %v", err) - } - sig22, err := hex.DecodeString("3044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d201") - if err != nil { - t.Fatalf("Failed to decode hex: %v", err) - } - pub22, err := hex.DecodeString("023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73") - if err != nil { - t.Fatalf("Failed to decode hex: %v", err) - } - res, err = psbtupdater2.Sign(1, sig21, pub21, nil, nil) - - // Check that the finalization procedure fails here due to not - // meeting the multisig policy - success, err := MaybeFinalize(psbt2, 1) - if success { - t.Fatalf("Incorrectly succeeded in finalizing without sigs") - } - if err != ErrUnsupportedScriptType { - t.Fatalf("Got unexpected error type: %v", err) - } - - res, err = psbtupdater2.Sign(1, sig22, pub22, nil, nil) - - // Check that the finalization procedure also fails with a nonsense - // script - err = psbtupdater2.AddInWitnessScript(witnessScriptNonsense, 1) - if err != nil { - t.Fatalf("Failed to add witnessscript to second input: %v", err) - } - success, err = MaybeFinalize(psbt2, 1) - if success { - t.Fatalf("Incorrectly succeeded in finalizing with invalid msigscript") - } - if err != ErrUnsupportedScriptType { - t.Fatalf("Got unexpected error type: %v", err) - } - - // Restore the correct witnessScript to complete correctly - err = psbtupdater2.AddInWitnessScript(witnessScript, 1) - if err != nil { - t.Fatalf("Failed to add witnessscript to second input: %v", err) - } - - success, err = MaybeFinalize(psbt2, 1) - if !success { - if err != nil { - t.Fatalf("Failed to finalize second input: %v", err) - } else { - t.Fatalf("Input was not finalizable") - } - } - - // Add a (fake) witnessOut descriptor field to one of the outputs, - // for coverage purposes (we aren't currently using this field) - psbtupdater2.AddOutWitnessScript([]byte{0xff, 0xff, 0xff}, 0) - - // Sanity check; we should not have lost the additional output entry - // provided by Core initially - uoutput1 := psbtupdater2.Upsbt.Outputs[1] - if uoutput1.RedeemScript == nil { - t.Fatalf("PSBT should contain outredeemscript entry, but it does not.") - } - // Nor should we have lost our fake witnessscript output entry - uoutput2 := psbtupdater2.Upsbt.Outputs[0] - if uoutput2.WitnessScript == nil { - t.Fatalf("PSBT should contain outwitnessscript but it does not.") - } - var tx bytes.Buffer - networkSerializedTx, err := Extract(psbt2) - if err != nil { - t.Fatalf("unable to extract tx: %v", err) - } - if err := networkSerializedTx.Serialize(&tx); err != nil { - t.Fatalf("unable to encode tx: %v", err) - } - expectedSerializedTx, err := hex.DecodeString("020000000001024c5343eae283940d9d44b99cc73e3dc728c3821c38979045c34dc3eee9e5903901000000171600147aed39420a8b7ab98a83791327ccb70819d1fbe2ffffffffaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0d000000232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903ffffffff0200860e70000000001600143597b9e7d7746e6d3c2a450536067555e5695705603131b50000000017a914922b975237d61604e9ea797f81ff7ef3321647a287024730440220546d182d00e45ef659c329dce6197dc19e0abc795e2c9279873f5a887998b273022044143113fc3475d04fc8d5113e0bbcb42d80514a9f1a2247e9b2a7878e20d449012102bb3ce35af26f4c826eab3e5fc263ef56871b26686a8a995599b7ee65766131040400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00000000") - if err != nil { - t.Fatalf("Failed to decode hex: %v", err) - } - if !bytes.Equal(expectedSerializedTx, tx.Bytes()) { - t.Fatalf("Failed to create correct network serialized "+ - "transaction: expected %x, got %x", - expectedSerializedTx, tx.Bytes()) - } -} - -func TestMaybeFinalizeAll(t *testing.T) { - // The following data is from a 3rd transaction from Core, - // using 3 inputs, all p2wkh. - imported := "cHNidP8BAKQCAAAAAzJyXH13IqBFvvZ7y1VSgUgkMvMoPgP5CfFNqsjQexKQAQAAAAD/////fMdLydu5bsoiHN9cFSaBL0Qnq2KLSKx0RA4b938CAgQAAAAAAP/////yKNgfsDAHr/zFz8R9k8EFI26allfg9DdE8Gzj6tGlegEAAAAA/////wHw9E0OAAAAABYAFDnPCRduiEWmmSc1j30SJ8k9u7PHAAAAAAAAAAAA" - psbt1, err := NewFromRawBytes(bytes.NewReader([]byte(imported)), true) - if err != nil { - t.Fatalf("Failed to parse PSBT: %v", err) - } - - // update with the first input's utxo, taken from its funding - // transaction - fundingTxInput1, err := hex.DecodeString("020000000001017b260536a3c17aee49c41a9b36fdf01a418e0c04df06fbabcb0d4f590b95d1750100000017160014af82cd4409241b1de892726324bd780e3b5cd8aafeffffff02a85f9800000000001600149d21f8b306ddfd4dd035080689e88b4c3471e3cc801d2c0400000000160014d97ccd3dfb60820d7d33d862371ca5a73039bd560247304402201a1d2fdb5a7190b7fa59907769f0fc9c91fd3b34f6424acf5868a8ac21ec287102200a59b9d076ecf98c88f2196ed2be0aafff4966ead754041182fff5f92115a783012103604ffd31dc71db2e32c20f09eafe6353cd7515d3648aff829bb4879b553e30629a000000") - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - fundingTxInput2, err := hex.DecodeString("020000000001019c27b886e420fcadb077706b0933efa8bb53e3a250c3ec45cfdba5e05e233f360100000000feffffff0200b4c404000000001600140853f50c7d2d5d2af326a75efdbc83b62551e89afce31c0d000000001600142d6936c082c35607ec3bdb334a932d928150b75802473044022000d962f5e5e6425f9de21da7ac65b4fd8af8f6bfbd33c7ba022827c73866b477022034c59935c1ea10b5ba335d93f55a200c2588ec6058b8c7aedd10d5cbc4654f99012102c30e9f0cd98f6a805464d6b8a326b5679b6c3262934341855ee0436eaedfd2869a000000") - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - fundingTxInput3, err := hex.DecodeString("02000000012bf4331bb95df4eadb14f7a28db3fecdc5e87f08c29c2332b66338dd606699f60000000048473044022075ed43f508528da47673550a785702e9a93eca84a11faea91c4e9c66fcab3c9e022054a37610bd40b12263a5933188f062b718e007f290cecde2b6e41da3e1ebbddf01feffffff020c99a8240100000016001483bd916985726094d6d1c5b969722da580b5966a804a5d05000000001600140a2ee13a6696d75006af5e8a026ea49316087dae9a000000") - if err != nil { - t.Fatalf("Unable to decode hex: %v", err) - } - - psbtupdater1 := Updater{Upsbt: psbt1} - tx := wire.NewMsgTx(2) - err = tx.Deserialize(bytes.NewReader(fundingTxInput1)) - if err != nil { - t.Fatalf("Error deserializing transaction: %v", err) - } - txFund1Out := tx.TxOut[1] - psbtupdater1.AddInWitnessUtxo(txFund1Out, 0) - - tx = wire.NewMsgTx(2) - err = tx.Deserialize(bytes.NewReader(fundingTxInput2)) - if err != nil { - t.Fatalf("Error deserializing transaction: %v", err) - } - txFund2Out := tx.TxOut[0] - psbtupdater1.AddInWitnessUtxo(txFund2Out, 1) - - tx = wire.NewMsgTx(2) - err = tx.Deserialize(bytes.NewReader(fundingTxInput3)) - if err != nil { - t.Fatalf("Error deserializing transaction: %v", err) - } - txFund3Out := tx.TxOut[1] - psbtupdater1.AddInWitnessUtxo(txFund3Out, 2) - - // To be ready for finalization, we need to have partial signature - // fields for each input - sig1, _ := hex.DecodeString("30440220027605ee8015970baf02a72652967a543e1b29a6882d799738ed1baee508822702203818a2f1b9770c46a473f47ad7ae90bcc129a5d047f00fae354c80197a7cf50601") - pub1, _ := hex.DecodeString("03235fc1f9dc8bbf6fa3df35dfeb0dd486f2d488f139579885eb684510f004f6c1") - sig2, _ := hex.DecodeString("304402206f5aea4621696610de48736b95a89b1d3a434a4e536d9aae65e039c477cf4c7202203b27a18b0f63be7d3bbf5be1bc2306a7ec8c2da12c2820ff07b73c7f3f1d4d7301") - pub2, _ := hex.DecodeString("022011b496f0603a268b55a781c7be0c3849f605f09cb2e917ed44288b8144a752") - sig3, _ := hex.DecodeString("3044022036dbc6f8f85a856e7803cbbcf0a97b7a74806fc592e92d7c06826f911610b98e0220111d43c4b20f756581791334d9c5cbb1a9c07558f28404cabf01c782897ad50501") - pub3, _ := hex.DecodeString("0381772a80c69e275e20d7f014555b13031e9cacf1c54a44a67ab2bc7eba64f227") - res, err := psbtupdater1.Sign(0, sig1, pub1, nil, nil) - if err != nil || res != 0 { - t.Fatalf("Failed to add partial signature for input 0: %v %v", err, res) - } - res, err = psbtupdater1.Sign(1, sig2, pub2, nil, nil) - if err != nil || res != 0 { - t.Fatalf("Failed to add partial signature for input 1: %v %v", err, res) - } - - // Not ready for finalize all, check it fails: - err = MaybeFinalizeAll(psbt1) - if err != ErrNotFinalizable { - t.Fatalf("Expected finalization failure, got: %v", err) - } - - res, err = psbtupdater1.Sign(2, sig3, pub3, nil, nil) - - // Since this input is now finalizable and is p2wkh only, we can do - // all at once: - err = MaybeFinalizeAll(psbt1) - if err != nil { - t.Fatalf("Failed to finalize PSBT: %v", err) - } - if !psbt1.IsComplete() { - t.Fatalf("PSBT was finalized but not marked complete") - } - -} - -func TestFromUnsigned(t *testing.T) { - serTx, err := hex.DecodeString("00000000000101e165f072311e71825b47a4797221d7ae56d4b40b7707c540049aee43302448a40000000000feffffff0212f1126a0000000017a9143e836801b2b15aa193449d815c62d6c4b6227c898780778e060000000017a914ba4bdb0b07d67bc60f59c1f4fe54170565254974870000000000") - if err != nil { - t.Fatalf("Error: %v", err) - } - tx := wire.NewMsgTx(2) - err = tx.Deserialize(bytes.NewReader(serTx)) - if err != nil { - t.Fatalf("Error: %v", err) - } - psbt1, err := NewFromUnsignedTx(tx) - if err != nil { - t.Fatalf("Error: %v", err) - } - encoded, err := psbt1.B64Encode() - if err != nil { - t.Fatalf("Unable to B64Encode Psbt: %v", err) - } - - // Compare with output of Core: - fromCoreB64 := "cHNidP8BAHMAAAAAAeFl8HIxHnGCW0ekeXIh165W1LQLdwfFQASa7kMwJEikAAAAAAD+////AhLxEmoAAAAAF6kUPoNoAbKxWqGTRJ2BXGLWxLYifImHgHeOBgAAAAAXqRS6S9sLB9Z7xg9ZwfT+VBcFZSVJdIcAAAAAAAAAAA==" - if encoded != fromCoreB64 { - t.Fatalf("Got incorrect b64: %v", encoded) - } - _, err = NewFromRawBytes(bytes.NewReader([]byte(fromCoreB64)), true) - if err != nil { - t.Fatalf("Error: %v", err) - } -} - -func TestNonWitnessToWitness(t *testing.T) { - // We'll start with a PSBT produced by Core for which - // the first input is signed and we'll provided the signatures for - // the other three inputs; they are p2sh-p2wkh, p2wkh and legacy - // respectively. - // In each case we'll *first* attach the NonWitnessUtxo field, - // and then call sign; in the first two but not the third case, the - // NonWitnessUtxo will automatically be replaced with the WitnessUtxo. - // Finally we'll check that the fully finalized PSBT produced matches - // the one produced by Core for the same keys. - - psbt1B64 := "cHNidP8BAM4CAAAABHtBMXY+SX95xidmWJP67CTQ02FPUpbNhIxNplAdlvk+AQAAAAD/////G2mt4bX7+sVi1jdbuBa5Q/xsJdgzFCgdHHSZq3ewK6YAAAAAAP/////NrbZb7GzfAg4kOqFWAIbXabq4cAvtVGv+eecIIv1KggEAAAAA/////73s9ifprgErlaONH1rgpNs3l6+t+mz2XGTHsTVWCem/AQAAAAD/////AfAmclMAAAAAF6kUQwsEC5nzbdY5meON2ZQ2thmeFgOHAAAAAAABASAAZc0dAAAAABepFPAv3VTMu5+4WN+/HIji6kG9RpzKhwEHFxYAFLN3PqXSyIHWKqm4ah5m9erc/3OoAQhrAkcwRAIgH7kzGO2iskfCvX0dgkDuzfqJ7tAu7KUZOeykTkJ1SYkCIBv4QRZK1hLz45D0gs+Lz93OE4s37lkPVE+SlXZtazWEASEC3jaf19MMferBn0Bn5lxXJGOqoqmfSvnHclQvB5gJ3nEAAAAAAQAWABTB+Qcq6iqdSvvc6959kd7XHrhYFgA=" - nwutxo1ser, _ := hex.DecodeString("02000000017f7baa6b7377541c4aca372d2dce8e1098ba44aa8379b7ea87644ef27e08ec240000000048473044022072e3b94c33cb5128518cd3903cc0ca19e8c234ac6d462e01ae2bb1da7768ed7d0220167d7ad89f6e1bbb3b866ae6fc2f67b5e7d51eb4f33f7bfe3f4b2673856b815001feffffff0200c2eb0b0000000017a9142dd25c78db2e2e09376eab9cb342e1b03005abe487e4ab953e0000000017a914120b8ca3fb4c7f852e30d4e3714fb64027a0b4c38721020000") - nwutxo2ser, _ := hex.DecodeString("0200000001f51b0bb5d945dd5532448a4d3fb88134d0bd90493813515f9c2ddb1fa15b9ba60000000048473044022047d83caf88d398245c006374bfa9f27ae968f5f51d640cacd5a214ed2cba397a02204519b26035496855f574a72b73bdcfa46d53995faf64c8f0ab394b628cc5383901feffffff020ccb9f3800000000160014e13544a3c718faa6c5ad7089a6660383c12b072700a3e11100000000160014a5439b477c116b79bd4c7c5131f3e58d54f27bb721020000") - nwutxo3ser, _ := hex.DecodeString("0200000001eb452f0fc9a8c39edb79f7174763f3cb25dc56db455926e411719a115ef16509000000004847304402205aa80cc615eb4b3f6e89696db4eadd192581a6c46f5c09807d3d98ece1d77355022025007e58c1992a1e5d877ee324bfe0a65db26d29f80941cfa277ac3efbcad2a701feffffff02bce9a9320000000017a9141590e852ac66eb8798afeb2a5ed67c568a2d6561870084d717000000001976a914a57ea05eacf94900d5fb92bccd273cfdb90af36f88ac21020000") - - nwutxo1 := wire.NewMsgTx(2) - err := nwutxo1.Deserialize(bytes.NewReader(nwutxo1ser)) - if err != nil { - t.Fatalf("Error deserializing transaction: %v", err) - } - nwutxo2 := wire.NewMsgTx(2) - err = nwutxo2.Deserialize(bytes.NewReader(nwutxo2ser)) - if err != nil { - t.Fatalf("Error deserializing transaction: %v", err) - } - nwutxo3 := wire.NewMsgTx(2) - err = nwutxo3.Deserialize(bytes.NewReader(nwutxo3ser)) - if err != nil { - t.Fatalf("Error deserializing transaction: %v", err) - } - - // import the PSBT - psbt1, err := NewFromRawBytes(bytes.NewReader([]byte(psbt1B64)), true) - if err != nil { - t.Fatalf("Failed to create PSBT: %v", err) - } - - // check that we recognize the finality of the first input - if !isFinalized(psbt1, 0) { - t.Fatalf("First input incorrectly read as not finalized.") - } - - // Add NonWitnessUtxo fields for each of the other three inputs - u := Updater{Upsbt: psbt1} - u.AddInNonWitnessUtxo(nwutxo1, 1) - u.AddInNonWitnessUtxo(nwutxo2, 2) - u.AddInNonWitnessUtxo(nwutxo3, 3) - - // Signatures for each of those inputs were created with Core: - sig1, _ := hex.DecodeString("304402205676877e6162ce40a49ee5a74443cdc1e7915637c42da7b872c2ec2298fd371b02203c1d4a05b1e2a7a588d9ec9b8d4892d2cd59bebe0e777483477a0ec692ebbe6d01") - pub1, _ := hex.DecodeString("02534f23cb88a048b649672967263bd7570312d5d31d066fa7b303970010a77b2b") - redeemScript1, _ := hex.DecodeString("00142412be29368c0260cb841eecd9b59d7e01174aa1") - - sig2, _ := hex.DecodeString("3044022065d0a349709b8d8043cfd644cf6c196c1f601a22e1b3fdfbf8c0cc2a80fe2f1702207c87d36b666a8862e81ec5df288707f517d2f35ea1548feb82019de2c8de90f701") - pub2, _ := hex.DecodeString("0257d88eaf1e79b72ea0a33ae89b57dae95ea68499bdc6770257e010ab899f0abb") - - sig3, _ := hex.DecodeString("30440220290abcaacbd759c4f989762a9ee3468a9231788aab8f50bf65955d8597d8dd3602204d7e394f4419dc5392c6edba6945837458dd750a030ac67a746231903a8eb7db01") - pub3, _ := hex.DecodeString("0388025f50bb51c0469421ed13381f22f9d46a070ec2837e055c49c5876f0d0968") - - // Add the signatures and any scripts needed to the inputs - res, err := u.Sign(1, sig1, pub1, redeemScript1, nil) - if res != 0 || err != nil { - t.Fatalf("Failed to sign at index %v res %v err %v", 1, res, err) - } - res, err = u.Sign(2, sig2, pub2, nil, nil) - if res != 0 || err != nil { - t.Fatalf("Failed to sign at index %v res %v err %v", 2, res, err) - } - res, err = u.Sign(3, sig3, pub3, nil, nil) - if res != 0 || err != nil { - t.Fatalf("Failed to sign at index %v res %v err %v", 3, res, err) - } - - // Attempt to finalize the rest of the transaction - _, err = MaybeFinalize(psbt1, 1) - if err != nil { - t.Fatalf("Failed to finalize input 1 %v", err) - } - _, err = MaybeFinalize(psbt1, 2) - if err != nil { - t.Fatalf("Failed to finalize input 2 %v", err) - } - _, err = MaybeFinalize(psbt1, 3) - if err != nil { - t.Fatalf("Failed to finalize input 3 %v", err) - } - - // Finally we can check whether both the B64 encoding of the PSBT, - // and the final network serialized signed transaction, that we generated - // with Core using the 2 wallets, matches what this code produces: - expectedFinalizedPsbt := "cHNidP8BAM4CAAAABHtBMXY+SX95xidmWJP67CTQ02FPUpbNhIxNplAdlvk+AQAAAAD/////G2mt4bX7+sVi1jdbuBa5Q/xsJdgzFCgdHHSZq3ewK6YAAAAAAP/////NrbZb7GzfAg4kOqFWAIbXabq4cAvtVGv+eecIIv1KggEAAAAA/////73s9ifprgErlaONH1rgpNs3l6+t+mz2XGTHsTVWCem/AQAAAAD/////AfAmclMAAAAAF6kUQwsEC5nzbdY5meON2ZQ2thmeFgOHAAAAAAABASAAZc0dAAAAABepFPAv3VTMu5+4WN+/HIji6kG9RpzKhwEHFxYAFLN3PqXSyIHWKqm4ah5m9erc/3OoAQhrAkcwRAIgH7kzGO2iskfCvX0dgkDuzfqJ7tAu7KUZOeykTkJ1SYkCIBv4QRZK1hLz45D0gs+Lz93OE4s37lkPVE+SlXZtazWEASEC3jaf19MMferBn0Bn5lxXJGOqoqmfSvnHclQvB5gJ3nEAAQEgAMLrCwAAAAAXqRQt0lx42y4uCTduq5yzQuGwMAWr5IcBBxcWABQkEr4pNowCYMuEHuzZtZ1+ARdKoQEIawJHMEQCIFZ2h35hYs5ApJ7lp0RDzcHnkVY3xC2nuHLC7CKY/TcbAiA8HUoFseKnpYjZ7JuNSJLSzVm+vg53dINHeg7Gkuu+bQEhAlNPI8uIoEi2SWcpZyY711cDEtXTHQZvp7MDlwAQp3srAAEBHwCj4REAAAAAFgAUpUObR3wRa3m9THxRMfPljVTye7cBCGsCRzBEAiBl0KNJcJuNgEPP1kTPbBlsH2AaIuGz/fv4wMwqgP4vFwIgfIfTa2ZqiGLoHsXfKIcH9RfS816hVI/rggGd4sjekPcBIQJX2I6vHnm3LqCjOuibV9rpXqaEmb3GdwJX4BCriZ8KuwABAL0CAAAAAetFLw/JqMOe23n3F0dj88sl3FbbRVkm5BFxmhFe8WUJAAAAAEhHMEQCIFqoDMYV60s/bolpbbTq3RklgabEb1wJgH09mOzh13NVAiAlAH5YwZkqHl2HfuMkv+CmXbJtKfgJQc+id6w++8rSpwH+////ArzpqTIAAAAAF6kUFZDoUqxm64eYr+sqXtZ8VootZWGHAITXFwAAAAAZdqkUpX6gXqz5SQDV+5K8zSc8/bkK82+IrCECAAABB2pHMEQCICkKvKrL11nE+Yl2Kp7jRoqSMXiKq49Qv2WVXYWX2N02AiBNfjlPRBncU5LG7bppRYN0WN11CgMKxnp0YjGQOo632wEhA4gCX1C7UcBGlCHtEzgfIvnUagcOwoN+BVxJxYdvDQloAAEAFgAUwfkHKuoqnUr73OvefZHe1x64WBYA" - calculatedPsbt, err := u.Upsbt.B64Encode() - if err != nil { - t.Fatalf("Failed to base64 encode") - } - if expectedFinalizedPsbt != calculatedPsbt { - t.Fatalf("Failed to generate correct PSBT") - } - - expectedNetworkSer, _ := hex.DecodeString("020000000001047b4131763e497f79c627665893faec24d0d3614f5296cd848c4da6501d96f93e0100000017160014b3773ea5d2c881d62aa9b86a1e66f5eadcff73a8ffffffff1b69ade1b5fbfac562d6375bb816b943fc6c25d83314281d1c7499ab77b02ba600000000171600142412be29368c0260cb841eecd9b59d7e01174aa1ffffffffcdadb65bec6cdf020e243aa1560086d769bab8700bed546bfe79e70822fd4a820100000000ffffffffbdecf627e9ae012b95a38d1f5ae0a4db3797afadfa6cf65c64c7b1355609e9bf010000006a4730440220290abcaacbd759c4f989762a9ee3468a9231788aab8f50bf65955d8597d8dd3602204d7e394f4419dc5392c6edba6945837458dd750a030ac67a746231903a8eb7db01210388025f50bb51c0469421ed13381f22f9d46a070ec2837e055c49c5876f0d0968ffffffff01f02672530000000017a914430b040b99f36dd63999e38dd99436b6199e1603870247304402201fb93318eda2b247c2bd7d1d8240eecdfa89eed02eeca51939eca44e4275498902201bf841164ad612f3e390f482cf8bcfddce138b37ee590f544f9295766d6b3584012102de369fd7d30c7deac19f4067e65c572463aaa2a99f4af9c772542f079809de710247304402205676877e6162ce40a49ee5a74443cdc1e7915637c42da7b872c2ec2298fd371b02203c1d4a05b1e2a7a588d9ec9b8d4892d2cd59bebe0e777483477a0ec692ebbe6d012102534f23cb88a048b649672967263bd7570312d5d31d066fa7b303970010a77b2b02473044022065d0a349709b8d8043cfd644cf6c196c1f601a22e1b3fdfbf8c0cc2a80fe2f1702207c87d36b666a8862e81ec5df288707f517d2f35ea1548feb82019de2c8de90f701210257d88eaf1e79b72ea0a33ae89b57dae95ea68499bdc6770257e010ab899f0abb0000000000") - tx, err := Extract(psbt1) - if err != nil { - t.Fatalf("Failed to extract: %v", err) - } - var b bytes.Buffer - if err := tx.Serialize(&b); err != nil { - t.Fatalf("unable to encode tx: %v", err) - } - if !bytes.Equal(expectedNetworkSer, b.Bytes()) { - t.Fatalf("Expected serialized transaction was not produced: %x", b.Bytes()) - } -} - -// TestEmptyInputSerialization tests the special serialization case for a wire -// transaction that has no inputs. -func TestEmptyInputSerialization(t *testing.T) { - // Create and serialize a new, empty PSBT. The wire package will assume - // it's a non-witness transaction, as there are no inputs. - psbt, err := New(nil, nil, 2, 0, nil) - if err != nil { - t.Fatalf("failed to create empty PSBT: %v", err) - } - var buf bytes.Buffer - err = psbt.Serialize(&buf) - if err != nil { - t.Fatalf("failed to serialize empty PSBT: %v", err) - } - - // Try to deserialize the empty transaction again. The wire package will - // assume it's a witness transaction because of the special case where - // there are no inputs. This assumption is wrong and the first attempt - // will fail. But a workaround should try again to deserialize the TX - // with the non-witness format. - psbt2, err := NewFromRawBytes(&buf, false) - if err != nil { - t.Fatalf("failed to deserialize empty PSBT: %v", err) - } - if len(psbt2.UnsignedTx.TxIn) > 0 || len(psbt2.UnsignedTx.TxOut) > 0 { - t.Fatalf("deserialized transaction not empty") - } -} - -// TestWitnessForNonWitnessUtxo makes sure that a packet that only has a non- -// witness UTXO set can still be signed correctly by adding witness data. This -// is to make sure that PSBTs following the CVE-2020-14199 bugfix are not -// rejected. See https://github.com/bitcoin/bitcoin/pull/19215. -func TestWitnessForNonWitnessUtxo(t *testing.T) { - // Our witness UTXO is index 1 of this raw transaction from the test - // vectors. - prevTxRaw, _ := hex.DecodeString("0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000") - prevTx := wire.NewMsgTx(2) - err := prevTx.Deserialize(bytes.NewReader(prevTxRaw)) - if err != nil { - t.Fatalf("failed to deserialize previous TX: %v", err) - } - - // First create a packet that contains one input and one output. - outPkScript, _ := hex.DecodeString(CUTestHexData["scriptPubkey1"]) - packet := &Packet{ - UnsignedTx: &wire.MsgTx{ - Version: 2, - LockTime: 0, - TxIn: []*wire.TxIn{{ - PreviousOutPoint: wire.OutPoint{ - Hash: prevTx.TxHash(), - Index: 1, - }, - }}, - TxOut: []*wire.TxOut{{ - PkScript: outPkScript, - Value: 1.9 * btcutil.SatoshiPerBitcoin, - }}, - }, - Inputs: []PInput{{}}, - Outputs: []POutput{{}}, - } - - // Create an updater for the packet. This also performs a sanity check. - updater, err := NewUpdater(packet) - if err != nil { - t.Fatalf("failed to sanity check raw packet: %v", err) - } - - // Now add our witness UTXO to the input. But because hardware wallets - // that are patched against CVE-2020-14199 require the full non-witness - // UTXO to be set for all inputs, we do what Core does and add the full - // transaction in the NonWitnessUtxo instead of just the outpoint in - // WitnessUtxo. - err = updater.AddInNonWitnessUtxo(prevTx, 0) - if err != nil { - t.Fatalf("failed to update non-witness UTXO: %v", err) - } - - // Then add the redeem scripts and witness scripts. - redeemScript, _ := hex.DecodeString(CUTestHexData["Input2RedeemScript"]) - err = updater.AddInRedeemScript(redeemScript, 0) - if err != nil { - t.Fatalf("failed to update redeem script: %v", err) - } - witnessScript, _ := hex.DecodeString(CUTestHexData["Input2WitnessScript"]) - err = updater.AddInWitnessScript(witnessScript, 0) - if err != nil { - t.Fatalf("failed to update redeem script: %v", err) - } - - // Add the first of the two partial signatures. - sig1, _ := hex.DecodeString("3044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01") - pub1, _ := hex.DecodeString("03089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc") - res, err := updater.Sign(0, sig1, pub1, nil, nil) - if err != nil { - t.Fatalf("failed to sign with pubkey 1: %v", err) - } - if res != SignSuccesful { - t.Fatalf("signing was not successful, got result %v", res) - } - - // Check that the finalization procedure fails here due to not - // meeting the multisig policy - success, err := MaybeFinalize(packet, 0) - if success { - t.Fatalf("Incorrectly succeeded in finalizing without sigs") - } - if err != ErrUnsupportedScriptType { - t.Fatalf("Got unexpected error type: %v", err) - } - - // Add the second partial signature. - sig2, _ := hex.DecodeString("3044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d201") - pub2, _ := hex.DecodeString("023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73") - res, err = updater.Sign(0, sig2, pub2, nil, nil) - if err != nil { - t.Fatalf("failed to sign with pubkey 2: %v", err) - } - if res != SignSuccesful { - t.Fatalf("signing was not successful, got result %v", res) - } - - // Finally make sure we can finalize the packet and extract the raw TX. - err = MaybeFinalizeAll(packet) - if err != nil { - t.Fatalf("error finalizing PSBT: %v", err) - } - _, err = Extract(packet) - if err != nil { - t.Fatalf("unable to extract funding TX: %v", err) - } -} diff --git a/psbt/signer.go b/psbt/signer.go deleted file mode 100644 index 588265317..000000000 --- a/psbt/signer.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) 2018 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package psbt - -// signer encapsulates the role 'Signer' as specified in BIP174; it controls -// the insertion of signatures; the Sign() function will attempt to insert -// signatures using Updater.addPartialSignature, after first ensuring the Psbt -// is in the correct state. - -import ( - "github.com/btcsuite/btcd/txscript" -) - -// SignOutcome is a enum-like value that expresses the outcome of a call to the -// Sign method. -type SignOutcome int - -const ( - // SignSuccesful indicates that the partial signature was successfully - // attached. - SignSuccesful = 0 - - // SignFinalized indicates that this input is already finalized, so the provided - // signature was *not* attached - SignFinalized = 1 - - // SignInvalid indicates that the provided signature data was not valid. In this case - // an error will also be returned. - SignInvalid = -1 -) - -// Sign allows the caller to sign a PSBT at a particular input; they -// must provide a signature and a pubkey, both as byte slices; they can also -// optionally provide both witnessScript and/or redeemScript, otherwise these -// arguments must be set as nil (and in that case, they must already be present -// in the PSBT if required for signing to succeed). -// -// This serves as a wrapper around Updater.addPartialSignature; it ensures that -// the redeemScript and witnessScript are updated as needed (note that the -// Updater is allowed to add redeemScripts and witnessScripts independently, -// before signing), and ensures that the right form of utxo field -// (NonWitnessUtxo or WitnessUtxo) is included in the input so that signature -// insertion (and then finalization) can take place. -func (u *Updater) Sign(inIndex int, sig []byte, pubKey []byte, - redeemScript []byte, witnessScript []byte) (SignOutcome, error) { - - if isFinalized(u.Upsbt, inIndex) { - return SignFinalized, nil - } - - // Add the witnessScript to the PSBT in preparation. If it already - // exists, it will be overwritten. - if witnessScript != nil { - err := u.AddInWitnessScript(witnessScript, inIndex) - if err != nil { - return SignInvalid, err - } - } - - // Add the redeemScript to the PSBT in preparation. If it already - // exists, it will be overwritten. - if redeemScript != nil { - err := u.AddInRedeemScript(redeemScript, inIndex) - if err != nil { - return SignInvalid, err - } - } - - // At this point, the PSBT must have the requisite witnessScript or - // redeemScript fields for signing to succeed. - // - // Case 1: if witnessScript is present, it must be of type witness; - // if not, signature insertion will of course fail. - switch { - case u.Upsbt.Inputs[inIndex].WitnessScript != nil: - if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil { - err := nonWitnessToWitness(u.Upsbt, inIndex) - if err != nil { - return SignInvalid, err - } - } - - err := u.addPartialSignature(inIndex, sig, pubKey) - if err != nil { - return SignInvalid, err - } - - // Case 2: no witness script, only redeem script; can be legacy p2sh or - // p2sh-wrapped p2wkh. - case u.Upsbt.Inputs[inIndex].RedeemScript != nil: - // We only need to decide if the input is witness, and we don't - // rely on the witnessutxo/nonwitnessutxo in the PSBT, instead - // we check the redeemScript content. - if txscript.IsWitnessProgram(redeemScript) { - if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil { - err := nonWitnessToWitness(u.Upsbt, inIndex) - if err != nil { - return SignInvalid, err - } - } - } - - // If it is not a valid witness program, we here assume that - // the provided WitnessUtxo/NonWitnessUtxo field was correct. - err := u.addPartialSignature(inIndex, sig, pubKey) - if err != nil { - return SignInvalid, err - } - - // Case 3: Neither provided only works for native p2wkh, or non-segwit - // non-p2sh. To check if it's segwit, check the scriptPubKey of the - // output. - default: - if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil { - outIndex := u.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index - script := u.Upsbt.Inputs[inIndex].NonWitnessUtxo.TxOut[outIndex].PkScript - - if txscript.IsWitnessProgram(script) { - err := nonWitnessToWitness(u.Upsbt, inIndex) - if err != nil { - return SignInvalid, err - } - } - } - - err := u.addPartialSignature(inIndex, sig, pubKey) - if err != nil { - return SignInvalid, err - } - } - - return SignSuccesful, nil -} - -// nonWitnessToWitness extracts the TxOut from the existing NonWitnessUtxo -// field in the given PSBT input and sets it as type witness by replacing the -// NonWitnessUtxo field with a WitnessUtxo field. See -// https://github.com/bitcoin/bitcoin/pull/14197. -func nonWitnessToWitness(p *Packet, inIndex int) error { - outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index - txout := p.Inputs[inIndex].NonWitnessUtxo.TxOut[outIndex] - - // TODO(guggero): For segwit v1, we'll want to remove the NonWitnessUtxo - // from the packet. For segwit v0 it is unsafe to only rely on the - // witness UTXO. See https://github.com/bitcoin/bitcoin/pull/19215. - // p.Inputs[inIndex].NonWitnessUtxo = nil - - u := Updater{ - Upsbt: p, - } - - return u.AddInWitnessUtxo(txout, inIndex) -} diff --git a/psbt/sort.go b/psbt/sort.go deleted file mode 100644 index 2232d68fe..000000000 --- a/psbt/sort.go +++ /dev/null @@ -1,102 +0,0 @@ -package psbt - -import ( - "bytes" - "sort" - - "github.com/btcsuite/btcd/chaincfg/chainhash" -) - -// InPlaceSort modifies the passed packet's wire TX inputs and outputs to be -// sorted based on BIP 69. The sorting happens in a way that the packet's -// partial inputs and outputs are also modified to match the sorted TxIn and -// TxOuts of the wire transaction. -// -// WARNING: This function must NOT be called with packages that already contain -// (partial) witness data since it will mutate the transaction if it's not -// already sorted. This can cause issues if you mutate a tx in a block, for -// example, which would invalidate the block. It could also cause cached hashes, -// such as in a btcutil.Tx to become invalidated. -// -// The function should only be used if the caller is creating the transaction or -// is otherwise 100% positive mutating will not cause adverse affects due to -// other dependencies. -func InPlaceSort(packet *Packet) error { - // To make sure we don't run into any nil pointers or array index - // violations during sorting, do a very basic sanity check first. - err := VerifyInputOutputLen(packet, false, false) - if err != nil { - return err - } - - sort.Sort(&sortableInputs{p: packet}) - sort.Sort(&sortableOutputs{p: packet}) - - return nil -} - -// sortableInputs is a simple wrapper around a packet that implements the -// sort.Interface for sorting the wire and partial inputs of a packet. -type sortableInputs struct { - p *Packet -} - -// sortableOutputs is a simple wrapper around a packet that implements the -// sort.Interface for sorting the wire and partial outputs of a packet. -type sortableOutputs struct { - p *Packet -} - -// For sortableInputs and sortableOutputs, three functions are needed to make -// them sortable with sort.Sort() -- Len, Less, and Swap. -// Len and Swap are trivial. Less is BIP 69 specific. -func (s *sortableInputs) Len() int { return len(s.p.UnsignedTx.TxIn) } -func (s sortableOutputs) Len() int { return len(s.p.UnsignedTx.TxOut) } - -// Swap swaps two inputs. -func (s *sortableInputs) Swap(i, j int) { - tx := s.p.UnsignedTx - tx.TxIn[i], tx.TxIn[j] = tx.TxIn[j], tx.TxIn[i] - s.p.Inputs[i], s.p.Inputs[j] = s.p.Inputs[j], s.p.Inputs[i] -} - -// Swap swaps two outputs. -func (s *sortableOutputs) Swap(i, j int) { - tx := s.p.UnsignedTx - tx.TxOut[i], tx.TxOut[j] = tx.TxOut[j], tx.TxOut[i] - s.p.Outputs[i], s.p.Outputs[j] = s.p.Outputs[j], s.p.Outputs[i] -} - -// Less is the input comparison function. First sort based on input hash -// (reversed / rpc-style), then index. -func (s *sortableInputs) Less(i, j int) bool { - ins := s.p.UnsignedTx.TxIn - - // Input hashes are the same, so compare the index. - ihash := ins[i].PreviousOutPoint.Hash - jhash := ins[j].PreviousOutPoint.Hash - if ihash == jhash { - return ins[i].PreviousOutPoint.Index < - ins[j].PreviousOutPoint.Index - } - - // At this point, the hashes are not equal, so reverse them to - // big-endian and return the result of the comparison. - const hashSize = chainhash.HashSize - for b := 0; b < hashSize/2; b++ { - ihash[b], ihash[hashSize-1-b] = ihash[hashSize-1-b], ihash[b] - jhash[b], jhash[hashSize-1-b] = jhash[hashSize-1-b], jhash[b] - } - return bytes.Compare(ihash[:], jhash[:]) == -1 -} - -// Less is the output comparison function. First sort based on amount (smallest -// first), then PkScript. -func (s *sortableOutputs) Less(i, j int) bool { - outs := s.p.UnsignedTx.TxOut - - if outs[i].Value == outs[j].Value { - return bytes.Compare(outs[i].PkScript, outs[j].PkScript) < 0 - } - return outs[i].Value < outs[j].Value -} diff --git a/psbt/sort_test.go b/psbt/sort_test.go deleted file mode 100644 index 3dee0f48d..000000000 --- a/psbt/sort_test.go +++ /dev/null @@ -1,167 +0,0 @@ -package psbt - -import ( - "reflect" - "testing" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" -) - -func TestInPlaceSort(t *testing.T) { - testCases := []struct { - name string - packet *Packet - expectedTxIn []*wire.TxIn - expectedTxOut []*wire.TxOut - expectedPIn []PInput - expectedPOut []POutput - expectErr bool - }{{ - name: "packet nil", - packet: nil, - expectErr: true, - }, { - name: "no inputs or outputs", - packet: &Packet{UnsignedTx: &wire.MsgTx{}}, - expectErr: false, - }, { - name: "inputs only", - packet: &Packet{ - UnsignedTx: &wire.MsgTx{ - TxIn: []*wire.TxIn{{ - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash{99, 88}, - Index: 7, - }, - }, { - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash{77, 88}, - Index: 12, - }, - }, { - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash{77, 88}, - Index: 7, - }, - }}, - }, - // Abuse the SighashType as an index to make sure the - // partial inputs are also sorted together with the wire - // inputs. - Inputs: []PInput{{ - SighashType: 0, - }, { - SighashType: 1, - }, { - SighashType: 2, - }}, - }, - expectedTxIn: []*wire.TxIn{{ - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash{77, 88}, - Index: 7, - }, - }, { - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash{77, 88}, - Index: 12, - }, - }, { - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash{99, 88}, - Index: 7, - }, - }}, - expectedPIn: []PInput{{ - SighashType: 2, - }, { - SighashType: 1, - }, { - SighashType: 0, - }}, - expectErr: false, - }, { - name: "outputs only", - packet: &Packet{ - UnsignedTx: &wire.MsgTx{ - TxOut: []*wire.TxOut{{ - PkScript: []byte{99, 88}, - Value: 7, - }, { - PkScript: []byte{77, 88}, - Value: 12, - }, { - PkScript: []byte{77, 88}, - Value: 7, - }}, - }, - // Abuse the RedeemScript as an index to make sure the - // partial inputs are also sorted together with the wire - // inputs. - Outputs: []POutput{{ - RedeemScript: []byte{0}, - }, { - RedeemScript: []byte{1}, - }, { - RedeemScript: []byte{2}, - }}, - }, - expectedTxOut: []*wire.TxOut{{ - PkScript: []byte{77, 88}, - Value: 7, - }, { - PkScript: []byte{99, 88}, - Value: 7, - }, { - PkScript: []byte{77, 88}, - Value: 12, - }}, - expectedPOut: []POutput{{ - RedeemScript: []byte{2}, - }, { - RedeemScript: []byte{0}, - }, { - RedeemScript: []byte{1}, - }}, - expectErr: false, - }} - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - p := tc.packet - err := InPlaceSort(p) - if (tc.expectErr && err == nil) || - (!tc.expectErr && err != nil) { - - t.Fatalf("got error '%v' but wanted it to be "+ - "nil: %v", err, tc.expectErr) - } - - // Don't continue on this special test case. - if p == nil { - return - } - - tx := p.UnsignedTx - if !reflect.DeepEqual(tx.TxIn, tc.expectedTxIn) { - t.Fatalf("unexpected txin, got %#v wanted %#v", - tx.TxIn, tc.expectedTxIn) - } - if !reflect.DeepEqual(tx.TxOut, tc.expectedTxOut) { - t.Fatalf("unexpected txout, got %#v wanted %#v", - tx.TxOut, tc.expectedTxOut) - } - - if !reflect.DeepEqual(p.Inputs, tc.expectedPIn) { - t.Fatalf("unexpected pin, got %#v wanted %#v", - p.Inputs, tc.expectedPIn) - } - if !reflect.DeepEqual(p.Outputs, tc.expectedPOut) { - t.Fatalf("unexpected pout, got %#v wanted %#v", - p.Inputs, tc.expectedPOut) - } - }) - } -} diff --git a/psbt/types.go b/psbt/types.go deleted file mode 100644 index 1b4a26a4d..000000000 --- a/psbt/types.go +++ /dev/null @@ -1,149 +0,0 @@ -package psbt - -// GlobalType is the set of types that are used at the global scope level -// within the PSBT. -type GlobalType uint8 - -const ( - // UnsignedTxType is the global scope key that houses the unsigned - // transaction of the PSBT. The value is a transaction in network - // serialization. The scriptSigs and witnesses for each input must be - // empty. The transaction must be in the old serialization format - // (without witnesses). A PSBT must have a transaction, otherwise it is - // invalid. - UnsignedTxType GlobalType = 0 - - // XpubType houses a global xpub for the entire PSBT packet. - // - // The key ({0x01}|{xpub}) is he 78 byte serialized extended public key - // as defined by BIP 32. Extended public keys are those that can be - // used to derive public keys used in the inputs and outputs of this - // transaction. It should be the public key at the highest hardened - // derivation index so that - // the unhardened child keys used in the transaction can be derived. - // - // The value is the master key fingerprint as defined by BIP 32 - // concatenated with the derivation path of the public key. The - // derivation path is represented as 32-bit little endian unsigned - // integer indexes concatenated with each other. The number of 32 bit - // unsigned integer indexes must match the depth provided in the - // extended public key. - XpubType GlobalType = 1 - - // VersionType houses the global version number of this PSBT. There is - // no key (only contains the byte type), then the value if omitted, is - // assumed to be zero. - VersionType GlobalType = 0xFB - - // ProprietaryGlobalType is used to house any proper chary global-scope - // keys within the PSBT. - // - // The key is ({0xFC}||{subtype}|{key data}) a variable length - // identifier prefix, followed by a subtype, followed by the key data - // itself. - // - // The value is any data as defined by the proprietary type user. - ProprietaryGlobalType = 0xFC -) - -// InputType is the set of types that are defined for each input included -// within the PSBT. -type InputType uint32 - -const ( - // NonWitnessUtxoType has no key ({0x00}) and houses the transaction in - // network serialization format the current input spends from. This - // should only be present for inputs which spend non-segwit outputs. - // However, if it is unknown whether an input spends a segwit output, - // this type should be used. The entire input transaction is needed in - // order to be able to verify the values of the input (pre-segwit they - // aren't in the signature digest). - NonWitnessUtxoType InputType = 0 - - // WitnessUtxoType has no key ({0x01}), and houses the entire - // transaction output in network serialization which the current input - // spends from. This should only be present for inputs which spend - // segwit outputs, including P2SH embedded ones (value || script). - WitnessUtxoType InputType = 1 - - // PartialSigType is used to include a partial signature with key - // ({0x02}|{public key}). - // - // The value is the signature as would be pushed to the stack from a - // scriptSig or witness.. - PartialSigType InputType = 2 - - // SighashType is an empty key ({0x03}). - // - // The value contains the 32-bit unsigned integer specifying the - // sighash type to be used for this input. Signatures for this input - // must use the sighash type, finalizers must fail to finalize inputs - // which have signatures that do not match the specified sighash type. - // Signers who cannot produce signatures with the sighash type must not - // provide a signature. - SighashType InputType = 3 - - // RedeemScriptInputType is an empty key ({0x40}). - // - // The value is the redeem script of the input if present. - RedeemScriptInputType InputType = 4 - - // WitnessScriptInputType is an empty key ({0x05}). - // - // The value is the witness script of this input, if it has one. - WitnessScriptInputType InputType = 5 - - // Bip32DerivationInputType is a type that carries the pubkey along - // with the key ({0x06}|{public key}). - // - // The value is master key fingerprint as defined by BIP 32 - // concatenated with the derivation path of the public key. The - // derivation path is represented as 32 bit unsigned integer indexes - // concatenated with each other. Public keys are those that will be - // needed to sign this input. - Bip32DerivationInputType InputType = 6 - - // FinalScriptSigType is an empty key ({0x07}). - // - // The value contains a fully constructed scriptSig with signatures and - // any other scripts necessary for the input to pass validation. - FinalScriptSigType InputType = 7 - - // FinalScriptWitnessType is an empty key ({0x08}). The value is a - // fully constructed scriptWitness with signatures and any other - // scripts necessary for the input to pass validation. - FinalScriptWitnessType InputType = 8 - - // ProprietaryInputType is a custom type for use by devs. - // - // The key ({0xFC}||{subtype}|{key data}), is a Variable length - // identifier prefix, followed by a subtype, followed by the key data - // itself. - // - // The value is any value data as defined by the proprietary type user. - ProprietaryInputType InputType = 0xFC -) - -// OutputType is the set of types defined per output within the PSBT. -type OutputType uint32 - -const ( - // RedeemScriptOutputType is an empty key ({0x00}> - // - // The value is the redeemScript for this output if it has one. - RedeemScriptOutputType OutputType = 0 - - // WitnessScriptOutputType is an empty key ({0x01}). - // - // The value is the witness script of this input, if it has one. - WitnessScriptOutputType OutputType = 1 - - j // Bip32DerivationOutputType is used to communicate derivation information - // needed to spend this output. The key is ({0x02}|{public key}). - // - // The value is master key fingerprint concatenated with the derivation - // path of the public key. The derivation path is represented as 32-bit - // little endian unsigned integer indexes concatenated with each other. - // Public keys are those needed to spend this output. - Bip32DerivationOutputType OutputType = 2 -) diff --git a/psbt/updater.go b/psbt/updater.go deleted file mode 100644 index 01fb2f3e8..000000000 --- a/psbt/updater.go +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright (c) 2018 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package psbt - -// The Updater requires provision of a single PSBT and is able to add data to -// both input and output sections. It can be called repeatedly to add more -// data. It also allows addition of signatures via the addPartialSignature -// function; this is called internally to the package in the Sign() function of -// Updater, located in signer.go - -import ( - "bytes" - "crypto/sha256" - - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" -) - -// Updater encapsulates the role 'Updater' as specified in BIP174; it accepts -// Psbt structs and has methods to add fields to the inputs and outputs. -type Updater struct { - Upsbt *Packet -} - -// NewUpdater returns a new instance of Updater, if the passed Psbt struct is -// in a valid form, else an error. -func NewUpdater(p *Packet) (*Updater, error) { - if err := p.SanityCheck(); err != nil { - return nil, err - } - - return &Updater{Upsbt: p}, nil - -} - -// AddInNonWitnessUtxo adds the utxo information for an input which is -// non-witness. This requires provision of a full transaction (which is the -// source of the corresponding prevOut), and the input index. If addition of -// this key-value pair to the Psbt fails, an error is returned. -func (p *Updater) AddInNonWitnessUtxo(tx *wire.MsgTx, inIndex int) error { - if inIndex > len(p.Upsbt.Inputs)-1 { - return ErrInvalidPrevOutNonWitnessTransaction - } - - p.Upsbt.Inputs[inIndex].NonWitnessUtxo = tx - - if err := p.Upsbt.SanityCheck(); err != nil { - return ErrInvalidPsbtFormat - } - - return nil -} - -// AddInWitnessUtxo adds the utxo information for an input which is witness. -// This requires provision of a full transaction *output* (which is the source -// of the corresponding prevOut); not the full transaction because BIP143 means -// the output information is sufficient, and the input index. If addition of -// this key-value pair to the Psbt fails, an error is returned. -func (p *Updater) AddInWitnessUtxo(txout *wire.TxOut, inIndex int) error { - if inIndex > len(p.Upsbt.Inputs)-1 { - return ErrInvalidPsbtFormat - } - - p.Upsbt.Inputs[inIndex].WitnessUtxo = txout - - if err := p.Upsbt.SanityCheck(); err != nil { - return ErrInvalidPsbtFormat - } - - return nil -} - -// addPartialSignature allows the Updater role to insert fields of type partial -// signature into a Psbt, consisting of both the pubkey (as keydata) and the -// ECDSA signature (as value). Note that the Signer role is encapsulated in -// this function; signatures are only allowed to be added that follow the -// sanity-check on signing rules explained in the BIP under `Signer`; if the -// rules are not satisfied, an ErrInvalidSignatureForInput is returned. -// -// NOTE: This function does *not* validate the ECDSA signature itself. -func (p *Updater) addPartialSignature(inIndex int, sig []byte, - pubkey []byte) error { - - partialSig := PartialSig{ - PubKey: pubkey, Signature: sig, - } - - // First validate the passed (sig, pub). - if !partialSig.checkValid() { - return ErrInvalidPsbtFormat - } - - pInput := p.Upsbt.Inputs[inIndex] - - // First check; don't add duplicates. - for _, x := range pInput.PartialSigs { - if bytes.Equal(x.PubKey, partialSig.PubKey) { - return ErrDuplicateKey - } - } - - // Attaching signature without utxo field is not allowed. - if pInput.WitnessUtxo == nil && pInput.NonWitnessUtxo == nil { - return ErrInvalidPsbtFormat - } - - // Next, we perform a series of additional sanity checks. - if pInput.NonWitnessUtxo != nil { - if len(p.Upsbt.UnsignedTx.TxIn) < inIndex+1 { - return ErrInvalidPrevOutNonWitnessTransaction - } - - if pInput.NonWitnessUtxo.TxHash() != - p.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Hash { - return ErrInvalidSignatureForInput - } - - // To validate that the redeem script matches, we must pull out - // the scriptPubKey of the corresponding output and compare - // that with the P2SH scriptPubKey that is generated by - // redeemScript. - if pInput.RedeemScript != nil { - outIndex := p.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index - scriptPubKey := pInput.NonWitnessUtxo.TxOut[outIndex].PkScript - scriptHash := btcutil.Hash160(pInput.RedeemScript) - - scriptHashScript, err := txscript.NewScriptBuilder(). - AddOp(txscript.OP_HASH160). - AddData(scriptHash). - AddOp(txscript.OP_EQUAL). - Script() - if err != nil { - return err - } - - if !bytes.Equal(scriptHashScript, scriptPubKey) { - return ErrInvalidSignatureForInput - } - } - - } - - // It could be that we set both the non-witness and witness UTXO fields - // in case it's from a wallet that patched the CVE-2020-14199 - // vulnerability. We detect whether the input being spent is actually a - // witness input and then copy it over to the witness UTXO field in the - // signer. Run the witness checks as well, even if we might already have - // checked the script hash. But that should be a negligible performance - // penalty. - if pInput.WitnessUtxo != nil { - scriptPubKey := pInput.WitnessUtxo.PkScript - - var script []byte - if pInput.RedeemScript != nil { - scriptHash := btcutil.Hash160(pInput.RedeemScript) - scriptHashScript, err := txscript.NewScriptBuilder(). - AddOp(txscript.OP_HASH160). - AddData(scriptHash). - AddOp(txscript.OP_EQUAL). - Script() - if err != nil { - return err - } - - if !bytes.Equal(scriptHashScript, scriptPubKey) { - return ErrInvalidSignatureForInput - } - - script = pInput.RedeemScript - } else { - script = scriptPubKey - } - - // If a witnessScript field is present, this is a P2WSH, - // whether nested or not (that is handled by the assignment to - // `script` above); in that case, sanity check that `script` is - // the p2wsh of witnessScript. Contrariwise, if no - // witnessScript field is present, this will be signed as - // p2wkh. - if pInput.WitnessScript != nil { - witnessScriptHash := sha256.Sum256(pInput.WitnessScript) - witnessScriptHashScript, err := txscript.NewScriptBuilder(). - AddOp(txscript.OP_0). - AddData(witnessScriptHash[:]). - Script() - if err != nil { - return err - } - - if !bytes.Equal(script, witnessScriptHashScript[:]) { - return ErrInvalidSignatureForInput - } - } else { - // Otherwise, this is a p2wkh input. - pubkeyHash := btcutil.Hash160(pubkey) - pubkeyHashScript, err := txscript.NewScriptBuilder(). - AddOp(txscript.OP_0). - AddData(pubkeyHash). - Script() - if err != nil { - return err - } - - // Validate that we're able to properly reconstruct the - // witness program. - if !bytes.Equal(pubkeyHashScript, script) { - return ErrInvalidSignatureForInput - } - } - } - - p.Upsbt.Inputs[inIndex].PartialSigs = append( - p.Upsbt.Inputs[inIndex].PartialSigs, &partialSig, - ) - - if err := p.Upsbt.SanityCheck(); err != nil { - return err - } - - // Addition of a non-duplicate-key partial signature cannot violate - // sanity-check rules. - return nil -} - -// AddInSighashType adds the sighash type information for an input. The -// sighash type is passed as a 32 bit unsigned integer, along with the index -// for the input. An error is returned if addition of this key-value pair to -// the Psbt fails. -func (p *Updater) AddInSighashType(sighashType txscript.SigHashType, - inIndex int) error { - - p.Upsbt.Inputs[inIndex].SighashType = sighashType - - if err := p.Upsbt.SanityCheck(); err != nil { - return err - } - return nil -} - -// AddInRedeemScript adds the redeem script information for an input. The -// redeem script is passed serialized, as a byte slice, along with the index of -// the input. An error is returned if addition of this key-value pair to the -// Psbt fails. -func (p *Updater) AddInRedeemScript(redeemScript []byte, - inIndex int) error { - - p.Upsbt.Inputs[inIndex].RedeemScript = redeemScript - - if err := p.Upsbt.SanityCheck(); err != nil { - return ErrInvalidPsbtFormat - } - - return nil -} - -// AddInWitnessScript adds the witness script information for an input. The -// witness script is passed serialized, as a byte slice, along with the index -// of the input. An error is returned if addition of this key-value pair to the -// Psbt fails. -func (p *Updater) AddInWitnessScript(witnessScript []byte, - inIndex int) error { - - p.Upsbt.Inputs[inIndex].WitnessScript = witnessScript - - if err := p.Upsbt.SanityCheck(); err != nil { - return err - } - - return nil -} - -// AddInBip32Derivation takes a master key fingerprint as defined in BIP32, a -// BIP32 path as a slice of uint32 values, and a serialized pubkey as a byte -// slice, along with the integer index of the input, and inserts this data into -// that input. -// -// NOTE: This can be called multiple times for the same input. An error is -// returned if addition of this key-value pair to the Psbt fails. -func (p *Updater) AddInBip32Derivation(masterKeyFingerprint uint32, - bip32Path []uint32, pubKeyData []byte, inIndex int) error { - - bip32Derivation := Bip32Derivation{ - PubKey: pubKeyData, - MasterKeyFingerprint: masterKeyFingerprint, - Bip32Path: bip32Path, - } - - if !bip32Derivation.checkValid() { - return ErrInvalidPsbtFormat - } - - // Don't allow duplicate keys - for _, x := range p.Upsbt.Inputs[inIndex].Bip32Derivation { - if bytes.Equal(x.PubKey, bip32Derivation.PubKey) { - return ErrDuplicateKey - } - } - - p.Upsbt.Inputs[inIndex].Bip32Derivation = append( - p.Upsbt.Inputs[inIndex].Bip32Derivation, &bip32Derivation, - ) - - if err := p.Upsbt.SanityCheck(); err != nil { - return err - } - - return nil -} - -// AddOutBip32Derivation takes a master key fingerprint as defined in BIP32, a -// BIP32 path as a slice of uint32 values, and a serialized pubkey as a byte -// slice, along with the integer index of the output, and inserts this data -// into that output. -// -// NOTE: That this can be called multiple times for the same output. An error -// is returned if addition of this key-value pair to the Psbt fails. -func (p *Updater) AddOutBip32Derivation(masterKeyFingerprint uint32, - bip32Path []uint32, pubKeyData []byte, outIndex int) error { - - bip32Derivation := Bip32Derivation{ - PubKey: pubKeyData, - MasterKeyFingerprint: masterKeyFingerprint, - Bip32Path: bip32Path, - } - - if !bip32Derivation.checkValid() { - return ErrInvalidPsbtFormat - } - - // Don't allow duplicate keys - for _, x := range p.Upsbt.Outputs[outIndex].Bip32Derivation { - if bytes.Equal(x.PubKey, bip32Derivation.PubKey) { - return ErrDuplicateKey - } - } - - p.Upsbt.Outputs[outIndex].Bip32Derivation = append( - p.Upsbt.Outputs[outIndex].Bip32Derivation, &bip32Derivation, - ) - - if err := p.Upsbt.SanityCheck(); err != nil { - return err - } - - return nil -} - -// AddOutRedeemScript takes a redeem script as a byte slice and appends it to -// the output at index outIndex. -func (p *Updater) AddOutRedeemScript(redeemScript []byte, - outIndex int) error { - - p.Upsbt.Outputs[outIndex].RedeemScript = redeemScript - - if err := p.Upsbt.SanityCheck(); err != nil { - return ErrInvalidPsbtFormat - } - - return nil -} - -// AddOutWitnessScript takes a witness script as a byte slice and appends it to -// the output at index outIndex. -func (p *Updater) AddOutWitnessScript(witnessScript []byte, - outIndex int) error { - - p.Upsbt.Outputs[outIndex].WitnessScript = witnessScript - - if err := p.Upsbt.SanityCheck(); err != nil { - return err - } - - return nil -} diff --git a/psbt/utils.go b/psbt/utils.go deleted file mode 100644 index 494d04070..000000000 --- a/psbt/utils.go +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright (c) 2018 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package psbt - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "io" - "sort" - - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" -) - -// WriteTxWitness is a utility function due to non-exported witness -// serialization (writeTxWitness encodes the bitcoin protocol encoding for a -// transaction input's witness into w). -func WriteTxWitness(w io.Writer, wit [][]byte) error { - if err := wire.WriteVarInt(w, 0, uint64(len(wit))); err != nil { - return err - } - - for _, item := range wit { - err := wire.WriteVarBytes(w, 0, item) - if err != nil { - return err - } - } - return nil -} - -// writePKHWitness writes a witness for a p2wkh spending input -func writePKHWitness(sig []byte, pub []byte) ([]byte, error) { - var ( - buf bytes.Buffer - witnessItems = [][]byte{sig, pub} - ) - - if err := WriteTxWitness(&buf, witnessItems); err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -// checkIsMultisigScript is a utility function to check whether a given -// redeemscript fits the standard multisig template used in all P2SH based -// multisig, given a set of pubkeys for redemption. -func checkIsMultiSigScript(pubKeys [][]byte, sigs [][]byte, - script []byte) bool { - - // First insist that the script type is multisig. - if txscript.GetScriptClass(script) != txscript.MultiSigTy { - return false - } - - // Inspect the script to ensure that the number of sigs and pubkeys is - // correct - _, numSigs, err := txscript.CalcMultiSigStats(script) - if err != nil { - return false - } - - // If the number of sigs provided, doesn't match the number of required - // pubkeys, then we can't proceed as we're not yet final. - if numSigs != len(pubKeys) || numSigs != len(sigs) { - return false - } - - return true -} - -// extractKeyOrderFromScript is a utility function to extract an ordered list -// of signatures, given a serialized script (redeemscript or witness script), a -// list of pubkeys and the signatures corresponding to those pubkeys. This -// function is used to ensure that the signatures will be embedded in the final -// scriptSig or scriptWitness in the correct order. -func extractKeyOrderFromScript(script []byte, expectedPubkeys [][]byte, - sigs [][]byte) ([][]byte, error) { - - // If this isn't a proper finalized multi-sig script, then we can't - // proceed. - if !checkIsMultiSigScript(expectedPubkeys, sigs, script) { - return nil, ErrUnsupportedScriptType - } - - // Arrange the pubkeys and sigs into a slice of format: - // * [[pub,sig], [pub,sig],..] - type sigWithPub struct { - pubKey []byte - sig []byte - } - var pubsSigs []sigWithPub - for i, pub := range expectedPubkeys { - pubsSigs = append(pubsSigs, sigWithPub{ - pubKey: pub, - sig: sigs[i], - }) - } - - // Now that we have the set of (pubkey, sig) pairs, we'll construct a - // position map that we can use to swap the order in the slice above to - // match how things are laid out in the script. - type positionEntry struct { - index int - value sigWithPub - } - var positionMap []positionEntry - - // For each pubkey in our pubsSigs slice, we'll now construct a proper - // positionMap entry, based on _where_ in the script the pubkey first - // appears. - for _, p := range pubsSigs { - pos := bytes.Index(script, p.pubKey) - if pos < 0 { - return nil, errors.New("script does not contain pubkeys") - } - - positionMap = append(positionMap, positionEntry{ - index: pos, - value: p, - }) - } - - // Now that we have the position map full populated, we'll use the - // index data to properly sort the entries in the map based on where - // they appear in the script. - sort.Slice(positionMap, func(i, j int) bool { - return positionMap[i].index < positionMap[j].index - }) - - // Finally, we can simply iterate through the position map in order to - // extract the proper signature ordering. - sortedSigs := make([][]byte, 0, len(positionMap)) - for _, x := range positionMap { - sortedSigs = append(sortedSigs, x.value.sig) - } - - return sortedSigs, nil -} - -// getMultisigScriptWitness creates a full psbt serialized Witness field for -// the transaction, given the public keys and signatures to be appended. This -// function will only accept witnessScripts of the type M of N multisig. This -// is used for both p2wsh and nested p2wsh multisig cases. -func getMultisigScriptWitness(witnessScript []byte, pubKeys [][]byte, - sigs [][]byte) ([]byte, error) { - - // First using the script as a guide, we'll properly order the sigs - // according to how their corresponding pubkeys appear in the - // witnessScript. - orderedSigs, err := extractKeyOrderFromScript( - witnessScript, pubKeys, sigs, - ) - if err != nil { - return nil, err - } - - // Now that we know the proper order, we'll append each of the - // signatures into a new witness stack, then top it off with the - // witness script at the end, prepending the nil as we need the extra - // pop.. - witnessElements := make(wire.TxWitness, 0, len(sigs)+2) - witnessElements = append(witnessElements, nil) - for _, os := range orderedSigs { - witnessElements = append(witnessElements, os) - } - witnessElements = append(witnessElements, witnessScript) - - // Now that we have the full witness stack, we'll serialize it in the - // expected format, and return the final bytes. - var buf bytes.Buffer - if err = WriteTxWitness(&buf, witnessElements); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// checkSigHashFlags compares the sighash flag byte on a signature with the -// value expected according to any PsbtInSighashType field in this section of -// the PSBT, and returns true if they match, false otherwise. -// If no SighashType field exists, it is assumed to be SIGHASH_ALL. -// -// TODO(waxwing): sighash type not restricted to one byte in future? -func checkSigHashFlags(sig []byte, input *PInput) bool { - expectedSighashType := txscript.SigHashAll - if input.SighashType != 0 { - expectedSighashType = input.SighashType - } - - return expectedSighashType == txscript.SigHashType(sig[len(sig)-1]) -} - -// serializeKVpair writes out a kv pair using a varbyte prefix for each. -func serializeKVpair(w io.Writer, key []byte, value []byte) error { - if err := wire.WriteVarBytes(w, 0, key); err != nil { - return err - } - - return wire.WriteVarBytes(w, 0, value) -} - -// serializeKVPairWithType writes out to the passed writer a type coupled with -// a key. -func serializeKVPairWithType(w io.Writer, kt uint8, keydata []byte, - value []byte) error { - - // If the key has no data, then we write a blank slice. - if keydata == nil { - keydata = []byte{} - } - - // The final key to be written is: {type} || {keyData} - serializedKey := append([]byte{kt}, keydata...) - return serializeKVpair(w, serializedKey, value) -} - -// getKey retrieves a single key - both the key type and the keydata (if -// present) from the stream and returns the key type as an integer, or -1 if -// the key was of zero length. This integer is is used to indicate the presence -// of a separator byte which indicates the end of a given key-value pair list, -// and the keydata as a byte slice or nil if none is present. -func getKey(r io.Reader) (int, []byte, error) { - - // For the key, we read the varint separately, instead of using the - // available ReadVarBytes, because we have a specific treatment of 0x00 - // here: - count, err := wire.ReadVarInt(r, 0) - if err != nil { - return -1, nil, ErrInvalidPsbtFormat - } - if count == 0 { - // A separator indicates end of key-value pair list. - return -1, nil, nil - } - - // Check that we don't attempt to decode a dangerously large key. - if count > MaxPsbtKeyLength { - return -1, nil, ErrInvalidKeydata - } - - // Next, we ready out the designated number of bytes, which may include - // a type, key, and optional data. - keyTypeAndData := make([]byte, count) - if _, err := io.ReadFull(r, keyTypeAndData[:]); err != nil { - return -1, nil, err - } - - keyType := int(string(keyTypeAndData)[0]) - - // Note that the second return value will usually be empty, since most - // keys contain no more than the key type byte. - if len(keyTypeAndData) == 1 { - return keyType, nil, nil - } - - // Otherwise, we return the key, along with any data that it may - // contain. - return keyType, keyTypeAndData[1:], nil - -} - -// readTxOut is a limited version of wire.ReadTxOut, because the latter is not -// exported. -func readTxOut(txout []byte) (*wire.TxOut, error) { - if len(txout) < 10 { - return nil, ErrInvalidPsbtFormat - } - - valueSer := binary.LittleEndian.Uint64(txout[:8]) - scriptPubKey := txout[9:] - - return wire.NewTxOut(int64(valueSer), scriptPubKey), nil -} - -// SumUtxoInputValues tries to extract the sum of all inputs specified in the -// UTXO fields of the PSBT. An error is returned if an input is specified that -// does not contain any UTXO information. -func SumUtxoInputValues(packet *Packet) (int64, error) { - // We take the TX ins of the unsigned TX as the truth for how many - // inputs there should be, as the fields in the extra data part of the - // PSBT can be empty. - if len(packet.UnsignedTx.TxIn) != len(packet.Inputs) { - return 0, fmt.Errorf("TX input length doesn't match PSBT " + - "input length") - } - - inputSum := int64(0) - for idx, in := range packet.Inputs { - switch { - case in.WitnessUtxo != nil: - // Witness UTXOs only need to reference the TxOut. - inputSum += in.WitnessUtxo.Value - - case in.NonWitnessUtxo != nil: - // Non-witness UTXOs reference to the whole transaction - // the UTXO resides in. - utxOuts := in.NonWitnessUtxo.TxOut - txIn := packet.UnsignedTx.TxIn[idx] - - // Check that utxOuts actually has enough space to - // contain the previous outpoint's index. - opIdx := txIn.PreviousOutPoint.Index - if opIdx >= uint32(len(utxOuts)) { - return 0, fmt.Errorf("input %d has malformed "+ - "TxOut field", idx) - } - - inputSum += utxOuts[txIn.PreviousOutPoint.Index].Value - - default: - return 0, fmt.Errorf("input %d has no UTXO information", - idx) - } - } - return inputSum, nil -} - -// TxOutsEqual returns true if two transaction outputs are equal. -func TxOutsEqual(out1, out2 *wire.TxOut) bool { - if out1 == nil || out2 == nil { - return out1 == out2 - } - return out1.Value == out2.Value && - bytes.Equal(out1.PkScript, out2.PkScript) -} - -// VerifyOutputsEqual verifies that the two slices of transaction outputs are -// deep equal to each other. We do the length check and manual loop to provide -// better error messages to the user than just returning "not equal". -func VerifyOutputsEqual(outs1, outs2 []*wire.TxOut) error { - if len(outs1) != len(outs2) { - return fmt.Errorf("number of outputs are different") - } - for idx, out := range outs1 { - // There is a byte slice in the output so we can't use the - // equality operator. - if !TxOutsEqual(out, outs2[idx]) { - return fmt.Errorf("output %d is different", idx) - } - } - return nil -} - -// VerifyInputPrevOutpointsEqual verifies that the previous outpoints of the -// two slices of transaction inputs are deep equal to each other. We do the -// length check and manual loop to provide better error messages to the user -// than just returning "not equal". -func VerifyInputPrevOutpointsEqual(ins1, ins2 []*wire.TxIn) error { - if len(ins1) != len(ins2) { - return fmt.Errorf("number of inputs are different") - } - for idx, in := range ins1 { - if in.PreviousOutPoint != ins2[idx].PreviousOutPoint { - return fmt.Errorf("previous outpoint of input %d is "+ - "different", idx) - } - } - return nil -} - -// VerifyInputOutputLen makes sure a packet is non-nil, contains a non-nil wire -// transaction and that the wire input/output lengths match the partial input/ -// output lengths. A caller also can specify if they expect any inputs and/or -// outputs to be contained in the packet. -func VerifyInputOutputLen(packet *Packet, needInputs, needOutputs bool) error { - if packet == nil || packet.UnsignedTx == nil { - return fmt.Errorf("PSBT packet cannot be nil") - } - - if len(packet.UnsignedTx.TxIn) != len(packet.Inputs) { - return fmt.Errorf("invalid PSBT, wire inputs don't match " + - "partial inputs") - } - if len(packet.UnsignedTx.TxOut) != len(packet.Outputs) { - return fmt.Errorf("invalid PSBT, wire outputs don't match " + - "partial outputs") - } - - if needInputs && len(packet.UnsignedTx.TxIn) == 0 { - return fmt.Errorf("PSBT packet must contain at least one " + - "input") - } - if needOutputs && len(packet.UnsignedTx.TxOut) == 0 { - return fmt.Errorf("PSBT packet must contain at least one " + - "output") - } - - return nil -} - -// NewFromSignedTx is a utility function to create a packet from an -// already-signed transaction. Returned are: an unsigned transaction -// serialization, a list of scriptSigs, one per input, and a list of witnesses, -// one per input. -func NewFromSignedTx(tx *wire.MsgTx) (*Packet, [][]byte, - []wire.TxWitness, error) { - - scriptSigs := make([][]byte, 0, len(tx.TxIn)) - witnesses := make([]wire.TxWitness, 0, len(tx.TxIn)) - tx2 := tx.Copy() - - // Blank out signature info in inputs - for i, tin := range tx2.TxIn { - tin.SignatureScript = nil - scriptSigs = append(scriptSigs, tx.TxIn[i].SignatureScript) - tin.Witness = nil - witnesses = append(witnesses, tx.TxIn[i].Witness) - } - - // Outputs always contain: (value, scriptPubkey) so don't need - // amending. Now tx2 is tx with all signing data stripped out - unsignedPsbt, err := NewFromUnsignedTx(tx2) - if err != nil { - return nil, nil, nil, err - } - return unsignedPsbt, scriptSigs, witnesses, nil -} diff --git a/psbt/utils_test.go b/psbt/utils_test.go deleted file mode 100644 index 90593ffca..000000000 --- a/psbt/utils_test.go +++ /dev/null @@ -1,370 +0,0 @@ -package psbt - -import ( - "bytes" - "reflect" - "testing" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" -) - -func TestSumUtxoInputValues(t *testing.T) { - // Expect sum to fail for packet with non-matching txIn and PInputs. - tx := wire.NewMsgTx(2) - badPacket, err := NewFromUnsignedTx(tx) - if err != nil { - t.Fatalf("could not create packet from TX: %v", err) - } - badPacket.Inputs = append(badPacket.Inputs, PInput{}) - - _, err = SumUtxoInputValues(badPacket) - if err == nil { - t.Fatalf("expected sum of bad packet to fail") - } - - // Expect sum to fail if any inputs don't have UTXO information added. - op := []*wire.OutPoint{{}, {}} - noUtxoInfoPacket, err := New(op, nil, 2, 0, []uint32{0, 0}) - if err != nil { - t.Fatalf("could not create new packet: %v", err) - } - - _, err = SumUtxoInputValues(noUtxoInfoPacket) - if err == nil { - t.Fatalf("expected sum of missing UTXO info to fail") - } - - // Create a packet that is OK and contains both witness and non-witness - // UTXO information. - okPacket, err := New(op, nil, 2, 0, []uint32{0, 0}) - if err != nil { - t.Fatalf("could not create new packet: %v", err) - } - okPacket.Inputs[0].WitnessUtxo = &wire.TxOut{Value: 1234} - okPacket.Inputs[1].NonWitnessUtxo = &wire.MsgTx{ - TxOut: []*wire.TxOut{{Value: 6543}}, - } - - sum, err := SumUtxoInputValues(okPacket) - if err != nil { - t.Fatalf("could not sum input: %v", err) - } - if sum != (1234 + 6543) { - t.Fatalf("unexpected sum, got %d wanted %d", sum, 1234+6543) - } - - // Create a malformed packet where NonWitnessUtxo.TxOut does not - // contain the index specified by the PreviousOutPoint in the - // packet's Unsigned.TxIn field. - badOp := []*wire.OutPoint{{}, {Index: 500}} - malformedPacket, err := New(badOp, nil, 2, 0, []uint32{0, 0}) - if err != nil { - t.Fatalf("could not create malformed packet: %v", err) - } - malformedPacket.Inputs[0].WitnessUtxo = &wire.TxOut{Value: 1234} - malformedPacket.Inputs[1].NonWitnessUtxo = &wire.MsgTx{ - TxOut: []*wire.TxOut{{Value: 6543}}, - } - - _, err = SumUtxoInputValues(malformedPacket) - if err == nil { - t.Fatalf("expected sum of malformed packet to fail") - } -} - -func TestTxOutsEqual(t *testing.T) { - testCases := []struct { - name string - out1 *wire.TxOut - out2 *wire.TxOut - expectEqual bool - }{{ - name: "both nil", - out1: nil, - out2: nil, - expectEqual: true, - }, { - name: "one nil", - out1: nil, - out2: &wire.TxOut{}, - expectEqual: false, - }, { - name: "both empty", - out1: &wire.TxOut{}, - out2: &wire.TxOut{}, - expectEqual: true, - }, { - name: "one pk script set", - out1: &wire.TxOut{}, - out2: &wire.TxOut{ - PkScript: []byte("foo"), - }, - expectEqual: false, - }, { - name: "both fully set", - out1: &wire.TxOut{ - Value: 1234, - PkScript: []byte("bar"), - }, - out2: &wire.TxOut{ - Value: 1234, - PkScript: []byte("bar"), - }, - expectEqual: true, - }} - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - result := TxOutsEqual(tc.out1, tc.out2) - if result != tc.expectEqual { - t.Fatalf("unexpected result, got %v wanted %v", - result, tc.expectEqual) - } - }) - } -} - -func TestVerifyOutputsEqual(t *testing.T) { - testCases := []struct { - name string - outs1 []*wire.TxOut - outs2 []*wire.TxOut - expectErr bool - }{{ - name: "both nil", - outs1: nil, - outs2: nil, - expectErr: false, - }, { - name: "one nil", - outs1: nil, - outs2: []*wire.TxOut{{}}, - expectErr: true, - }, { - name: "both empty", - outs1: []*wire.TxOut{{}}, - outs2: []*wire.TxOut{{}}, - expectErr: false, - }, { - name: "one pk script set", - outs1: []*wire.TxOut{{}}, - outs2: []*wire.TxOut{{ - PkScript: []byte("foo"), - }}, - expectErr: true, - }, { - name: "both fully set", - outs1: []*wire.TxOut{{ - Value: 1234, - PkScript: []byte("bar"), - }, {}}, - outs2: []*wire.TxOut{{ - Value: 1234, - PkScript: []byte("bar"), - }, {}}, - expectErr: false, - }} - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - err := VerifyOutputsEqual(tc.outs1, tc.outs2) - if (tc.expectErr && err == nil) || - (!tc.expectErr && err != nil) { - - t.Fatalf("got error '%v' but wanted it to be "+ - "nil: %v", err, tc.expectErr) - } - }) - } -} - -func TestVerifyInputPrevOutpointsEqual(t *testing.T) { - testCases := []struct { - name string - ins1 []*wire.TxIn - ins2 []*wire.TxIn - expectErr bool - }{{ - name: "both nil", - ins1: nil, - ins2: nil, - expectErr: false, - }, { - name: "one nil", - ins1: nil, - ins2: []*wire.TxIn{{}}, - expectErr: true, - }, { - name: "both empty", - ins1: []*wire.TxIn{{}}, - ins2: []*wire.TxIn{{}}, - expectErr: false, - }, { - name: "one previous output set", - ins1: []*wire.TxIn{{}}, - ins2: []*wire.TxIn{{ - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash{11, 22, 33}, - Index: 7, - }, - }}, - expectErr: true, - }, { - name: "both fully set", - ins1: []*wire.TxIn{{ - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash{11, 22, 33}, - Index: 7, - }, - }, {}}, - ins2: []*wire.TxIn{{ - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash{11, 22, 33}, - Index: 7, - }, - }, {}}, - expectErr: false, - }} - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - err := VerifyInputPrevOutpointsEqual(tc.ins1, tc.ins2) - if (tc.expectErr && err == nil) || - (!tc.expectErr && err != nil) { - - t.Fatalf("got error '%v' but wanted it to be "+ - "nil: %v", err, tc.expectErr) - } - }) - } -} - -func TestVerifyInputOutputLen(t *testing.T) { - testCases := []struct { - name string - packet *Packet - needInputs bool - needOutputs bool - expectErr bool - }{{ - name: "packet nil", - packet: nil, - expectErr: true, - }, { - name: "wire tx nil", - packet: &Packet{}, - expectErr: true, - }, { - name: "both empty don't need outputs", - packet: &Packet{ - UnsignedTx: &wire.MsgTx{}, - }, - expectErr: false, - }, { - name: "both empty but need outputs", - packet: &Packet{ - UnsignedTx: &wire.MsgTx{}, - }, - needOutputs: true, - expectErr: true, - }, { - name: "both empty but need inputs", - packet: &Packet{ - UnsignedTx: &wire.MsgTx{}, - }, - needInputs: true, - expectErr: true, - }, { - name: "input len mismatch", - packet: &Packet{ - UnsignedTx: &wire.MsgTx{ - TxIn: []*wire.TxIn{{}}, - }, - }, - needInputs: true, - expectErr: true, - }, { - name: "output len mismatch", - packet: &Packet{ - UnsignedTx: &wire.MsgTx{ - TxOut: []*wire.TxOut{{}}, - }, - }, - needOutputs: true, - expectErr: true, - }, { - name: "all fully set", - packet: &Packet{ - UnsignedTx: &wire.MsgTx{ - TxIn: []*wire.TxIn{{}}, - TxOut: []*wire.TxOut{{}}, - }, - Inputs: []PInput{{}}, - Outputs: []POutput{{}}, - }, - needInputs: true, - needOutputs: true, - expectErr: false, - }} - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - err := VerifyInputOutputLen( - tc.packet, tc.needInputs, tc.needOutputs, - ) - if (tc.expectErr && err == nil) || - (!tc.expectErr && err != nil) { - - t.Fatalf("got error '%v' but wanted it to be "+ - "nil: %v", err, tc.expectErr) - } - }) - } -} - -func TestNewFromSignedTx(t *testing.T) { - orig := &wire.MsgTx{ - TxIn: []*wire.TxIn{{ - PreviousOutPoint: wire.OutPoint{}, - SignatureScript: []byte("script"), - Witness: [][]byte{[]byte("witness")}, - Sequence: 1234, - }}, - TxOut: []*wire.TxOut{{ - PkScript: []byte{77, 88}, - Value: 99, - }}, - } - - packet, scripts, witnesses, err := NewFromSignedTx(orig) - if err != nil { - t.Fatalf("could not create packet from signed TX: %v", err) - } - - tx := packet.UnsignedTx - expectedTxIn := []*wire.TxIn{{ - PreviousOutPoint: wire.OutPoint{}, - Sequence: 1234, - }} - if !reflect.DeepEqual(tx.TxIn, expectedTxIn) { - t.Fatalf("unexpected txin, got %#v wanted %#v", - tx.TxIn, expectedTxIn) - } - if !reflect.DeepEqual(tx.TxOut, orig.TxOut) { - t.Fatalf("unexpected txout, got %#v wanted %#v", - tx.TxOut, orig.TxOut) - } - if len(scripts) != 1 || !bytes.Equal(scripts[0], []byte("script")) { - t.Fatalf("script not extracted correctly") - } - if len(witnesses) != 1 || - !bytes.Equal(witnesses[0][0], []byte("witness")) { - - t.Fatalf("witness not extracted correctly") - } -} diff --git a/tx.go b/tx.go deleted file mode 100644 index 5633fef90..000000000 --- a/tx.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil - -import ( - "bytes" - "io" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" -) - -// TxIndexUnknown is the value returned for a transaction index that is unknown. -// This is typically because the transaction has not been inserted into a block -// yet. -const TxIndexUnknown = -1 - -// Tx defines a bitcoin transaction that provides easier and more efficient -// manipulation of raw transactions. It also memoizes the hash for the -// transaction on its first access so subsequent accesses don't have to repeat -// the relatively expensive hashing operations. -type Tx struct { - msgTx *wire.MsgTx // Underlying MsgTx - txHash *chainhash.Hash // Cached transaction hash - txHashWitness *chainhash.Hash // Cached transaction witness hash - txHasWitness *bool // If the transaction has witness data - txIndex int // Position within a block or TxIndexUnknown -} - -// MsgTx returns the underlying wire.MsgTx for the transaction. -func (t *Tx) MsgTx() *wire.MsgTx { - // Return the cached transaction. - return t.msgTx -} - -// Hash returns the hash of the transaction. This is equivalent to -// calling TxHash on the underlying wire.MsgTx, however it caches the -// result so subsequent calls are more efficient. -func (t *Tx) Hash() *chainhash.Hash { - // Return the cached hash if it has already been generated. - if t.txHash != nil { - return t.txHash - } - - // Cache the hash and return it. - hash := t.msgTx.TxHash() - t.txHash = &hash - return &hash -} - -// WitnessHash returns the witness hash (wtxid) of the transaction. This is -// equivalent to calling WitnessHash on the underlying wire.MsgTx, however it -// caches the result so subsequent calls are more efficient. -func (t *Tx) WitnessHash() *chainhash.Hash { - // Return the cached hash if it has already been generated. - if t.txHashWitness != nil { - return t.txHashWitness - } - - // Cache the hash and return it. - hash := t.msgTx.WitnessHash() - t.txHashWitness = &hash - return &hash -} - -// HasWitness returns false if none of the inputs within the transaction -// contain witness data, true false otherwise. This equivalent to calling -// HasWitness on the underlying wire.MsgTx, however it caches the result so -// subsequent calls are more efficient. -func (t *Tx) HasWitness() bool { - if t.txHasWitness != nil { - return *t.txHasWitness - } - - hasWitness := t.msgTx.HasWitness() - t.txHasWitness = &hasWitness - return hasWitness -} - -// Index returns the saved index of the transaction within a block. This value -// will be TxIndexUnknown if it hasn't already explicitly been set. -func (t *Tx) Index() int { - return t.txIndex -} - -// SetIndex sets the index of the transaction in within a block. -func (t *Tx) SetIndex(index int) { - t.txIndex = index -} - -// NewTx returns a new instance of a bitcoin transaction given an underlying -// wire.MsgTx. See Tx. -func NewTx(msgTx *wire.MsgTx) *Tx { - return &Tx{ - msgTx: msgTx, - txIndex: TxIndexUnknown, - } -} - -// NewTxFromBytes returns a new instance of a bitcoin transaction given the -// serialized bytes. See Tx. -func NewTxFromBytes(serializedTx []byte) (*Tx, error) { - br := bytes.NewReader(serializedTx) - return NewTxFromReader(br) -} - -// NewTxFromReader returns a new instance of a bitcoin transaction given a -// Reader to deserialize the transaction. See Tx. -func NewTxFromReader(r io.Reader) (*Tx, error) { - // Deserialize the bytes into a MsgTx. - var msgTx wire.MsgTx - err := msgTx.Deserialize(r) - if err != nil { - return nil, err - } - - t := Tx{ - msgTx: &msgTx, - txIndex: TxIndexUnknown, - } - return &t, nil -} diff --git a/tx_test.go b/tx_test.go deleted file mode 100644 index 6105b30b2..000000000 --- a/tx_test.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil_test - -import ( - "bytes" - "io" - "reflect" - "testing" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcutil" - "github.com/davecgh/go-spew/spew" -) - -// TestTx tests the API for Tx. -func TestTx(t *testing.T) { - testTx := Block100000.Transactions[0] - tx := btcutil.NewTx(testTx) - - // Ensure we get the same data back out. - if msgTx := tx.MsgTx(); !reflect.DeepEqual(msgTx, testTx) { - t.Errorf("MsgTx: mismatched MsgTx - got %v, want %v", - spew.Sdump(msgTx), spew.Sdump(testTx)) - } - - // Ensure transaction index set and get work properly. - wantIndex := 0 - tx.SetIndex(0) - if gotIndex := tx.Index(); gotIndex != wantIndex { - t.Errorf("Index: mismatched index - got %v, want %v", - gotIndex, wantIndex) - } - - // Hash for block 100,000 transaction 0. - wantHashStr := "8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87" - wantHash, err := chainhash.NewHashFromStr(wantHashStr) - if err != nil { - t.Errorf("NewHashFromStr: %v", err) - } - - // Request the hash multiple times to test generation and caching. - for i := 0; i < 2; i++ { - hash := tx.Hash() - if !hash.IsEqual(wantHash) { - t.Errorf("Hash #%d mismatched hash - got %v, want %v", i, - hash, wantHash) - } - } -} - -// TestNewTxFromBytes tests creation of a Tx from serialized bytes. -func TestNewTxFromBytes(t *testing.T) { - // Serialize the test transaction. - testTx := Block100000.Transactions[0] - var testTxBuf bytes.Buffer - err := testTx.Serialize(&testTxBuf) - if err != nil { - t.Errorf("Serialize: %v", err) - } - testTxBytes := testTxBuf.Bytes() - - // Create a new transaction from the serialized bytes. - tx, err := btcutil.NewTxFromBytes(testTxBytes) - if err != nil { - t.Errorf("NewTxFromBytes: %v", err) - return - } - - // Ensure the generated MsgTx is correct. - if msgTx := tx.MsgTx(); !reflect.DeepEqual(msgTx, testTx) { - t.Errorf("MsgTx: mismatched MsgTx - got %v, want %v", - spew.Sdump(msgTx), spew.Sdump(testTx)) - } -} - -// TestTxErrors tests the error paths for the Tx API. -func TestTxErrors(t *testing.T) { - // Serialize the test transaction. - testTx := Block100000.Transactions[0] - var testTxBuf bytes.Buffer - err := testTx.Serialize(&testTxBuf) - if err != nil { - t.Errorf("Serialize: %v", err) - } - testTxBytes := testTxBuf.Bytes() - - // Truncate the transaction byte buffer to force errors. - shortBytes := testTxBytes[:4] - _, err = btcutil.NewTxFromBytes(shortBytes) - if err != io.EOF { - t.Errorf("NewTxFromBytes: did not get expected error - "+ - "got %v, want %v", err, io.EOF) - } -} - -// TestTxHasWitness tests the HasWitness() method. -func TestTxHasWitness(t *testing.T) { - msgTx := Block100000.Transactions[0] // contains witness data - tx := btcutil.NewTx(msgTx) - - tx.WitnessHash() // Populate the witness hash cache - tx.HasWitness() // Should not fail (see btcsuite/btcd#1543) - - if !tx.HasWitness() { - t.Errorf("HasWitness: got false, want true") - } - - for _, msgTxWithoutWitness := range Block100000.Transactions[1:] { - txWithoutWitness := btcutil.NewTx(msgTxWithoutWitness) - if txWithoutWitness.HasWitness() { - t.Errorf("HasWitness: got false, want true") - } - } -} - -// TestTxWitnessHash tests the WitnessHash() method. -func TestTxWitnessHash(t *testing.T) { - msgTx := Block100000.Transactions[0] // contains witness data - tx := btcutil.NewTx(msgTx) - - if tx.WitnessHash().IsEqual(tx.Hash()) { - t.Errorf("WitnessHash: witness hash and tx id must NOT be same - "+ - "got %v, want %v", tx.WitnessHash(), tx.Hash()) - } - - for _, msgTxWithoutWitness := range Block100000.Transactions[1:] { - txWithoutWitness := btcutil.NewTx(msgTxWithoutWitness) - if !txWithoutWitness.WitnessHash().IsEqual(txWithoutWitness.Hash()) { - t.Errorf("WitnessHash: witness hash and tx id must be same - "+ - "got %v, want %v", txWithoutWitness.WitnessHash(), txWithoutWitness.Hash()) - } - } -} diff --git a/txsort/README.md b/txsort/README.md deleted file mode 100644 index bd410ec2f..000000000 --- a/txsort/README.md +++ /dev/null @@ -1,31 +0,0 @@ -txsort -====== - -[![Build Status](http://img.shields.io/travis/btcsuite/btcutil.svg)](https://travis-ci.org/btcsuite/btcutil) -[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) -[![GoDoc](http://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcutil/txsort) - -Package txsort provides the transaction sorting according to [BIP 69](https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki). - -BIP 69 defines a standard lexicographical sort order of transaction inputs and -outputs. This is useful to standardize transactions for faster multi-party -agreement as well as preventing information leaks in a single-party use case. - -The BIP goes into more detail, but for a quick and simplistic overview, the -order for inputs is defined as first sorting on the previous output hash and -then on the index as a tie breaker. The order for outputs is defined as first -sorting on the amount and then on the raw public key script bytes as a tie -breaker. - -A comprehensive suite of tests is provided to ensure proper functionality. - -## Installation and Updating - -```bash -$ go get -u github.com/btcsuite/btcutil/txsort -``` - -## License - -Package txsort is licensed under the [copyfree](http://copyfree.org) ISC -License. diff --git a/txsort/doc.go b/txsort/doc.go deleted file mode 100644 index e89c4d23d..000000000 --- a/txsort/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -/* -Package txsort provides the transaction sorting according to BIP 69. - -Overview - -BIP 69 defines a standard lexicographical sort order of transaction inputs and -outputs. This is useful to standardize transactions for faster multi-party -agreement as well as preventing information leaks in a single-party use case. - -The BIP goes into more detail, but for a quick and simplistic overview, the -order for inputs is defined as first sorting on the previous output hash and -then on the index as a tie breaker. The order for outputs is defined as first -sorting on the amount and then on the raw public key script bytes as a tie -breaker. -*/ -package txsort diff --git a/txsort/testdata/bip69-1.hex b/txsort/testdata/bip69-1.hex deleted file mode 100644 index dbd18422b..000000000 --- a/txsort/testdata/bip69-1.hex +++ /dev/null @@ -1 +0,0 @@ -0100000011aad553bb1650007e9982a8ac79d227cd8c831e1573b11f25573a37664e5f3e64000000006a47304402205438cedd30ee828b0938a863e08d810526123746c1f4abee5b7bc2312373450c02207f26914f4275f8f0040ab3375bacc8c5d610c095db8ed0785de5dc57456591a601210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffc26f3eb7932f7acddc5ddd26602b77e7516079b03090a16e2c2f5485d1fde028000000006b483045022100f81d98c1de9bb61063a5e6671d191b400fda3a07d886e663799760393405439d0220234303c9af4bad3d665f00277fe70cdd26cd56679f114a40d9107249d29c979401210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff456a9e597129f5df2e11b842833fc19a94c563f57449281d3cd01249a830a1f0000000006a47304402202310b00924794ef68a8f09564fd0bb128838c66bc45d1a3f95c5cab52680f166022039fc99138c29f6c434012b14aca651b1c02d97324d6bd9dd0ffced0782c7e3bd01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff571fb3e02278217852dd5d299947e2b7354a639adc32ec1fa7b82cfb5dec530e000000006b483045022100d276251f1f4479d8521269ec8b1b45c6f0e779fcf1658ec627689fa8a55a9ca50220212a1e307e6182479818c543e1b47d62e4fc3ce6cc7fc78183c7071d245839df01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff5d8de50362ff33d3526ac3602e9ee25c1a349def086a7fc1d9941aaeb9e91d38010000006b4830450221008768eeb1240451c127b88d89047dd387d13357ce5496726fc7813edc6acd55ac022015187451c3fb66629af38fdb061dfb39899244b15c45e4a7ccc31064a059730d01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff60ad3408b89ea19caf3abd5e74e7a084344987c64b1563af52242e9d2a8320f3000000006b4830450221009be4261ec050ebf33fa3d47248c7086e4c247cafbb100ea7cee4aa81cd1383f5022008a70d6402b153560096c849d7da6fe61c771a60e41ff457aac30673ceceafee01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffe9b483a8ac4129780c88d1babe41e89dc10a26dedbf14f80a28474e9a11104de010000006b4830450221009bc40eee321b39b5dc26883f79cd1f5a226fc6eed9e79e21d828f4c23190c57e022078182fd6086e265589105023d9efa4cba83f38c674a499481bd54eee196b033f01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffe28db9462d3004e21e765e03a45ecb147f136a20ba8bca78ba60ebfc8e2f8b3b000000006a47304402200fb572b7c6916515452e370c2b6f97fcae54abe0793d804a5a53e419983fae1602205191984b6928bf4a1e25b00e5b5569a0ce1ecb82db2dea75fe4378673b53b9e801210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff7a1ef65ff1b7b7740c662ab6c9735ace4a16279c23a1db5709ed652918ffff54010000006a47304402206bc218a925f7280d615c8ea4f0131a9f26e7fc64cff6eeeb44edb88aba14f1910220779d5d67231bc2d2d93c3c5ab74dcd193dd3d04023e58709ad7ffbf95161be6201210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff850cecf958468ca7ffa6a490afe13b8c271b1326b0ddc1fdfdf9f3c7e365fdba000000006a473044022047df98cc26bd2bfdc5b2b97c27aead78a214810ff023e721339292d5ce50823d02205fe99dc5f667908974dae40cc7a9475af7fa6671ba44f64a00fcd01fa12ab523012102ca46fa75454650afba1784bc7b079d687e808634411e4beff1f70e44596308a1ffffffff8640e312040e476cf6727c60ca3f4a3ad51623500aacdda96e7728dbdd99e8a5000000006a47304402205566aa84d3d84226d5ab93e6f253b57b3ef37eb09bb73441dae35de86271352a02206ee0b7f800f73695a2073a2967c9ad99e19f6ddf18ce877adf822e408ba9291e01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff91c1889c5c24b93b56e643121f7a05a34c10c5495c450504c7b5afcb37e11d7a000000006b483045022100df61d45bbaa4571cdd6c5c822cba458cdc55285cdf7ba9cd5bb9fc18096deb9102201caf8c771204df7fd7c920c4489da7bc3a60e1d23c1a97e237c63afe53250b4a01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff2470947216eb81ea0eeeb4fe19362ec05767db01c3aa3006bb499e8b6d6eaa26010000006a473044022031501a0b2846b8822a32b9947b058d89d32fc758e009fc2130c2e5effc925af70220574ef3c9e350cef726c75114f0701fd8b188c6ec5f84adce0ed5c393828a5ae001210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff0abcd77d65cc14363f8262898335f184d6da5ad060ff9e40bf201741022c2b40010000006b483045022100a6ac110802b699f9a2bff0eea252d32e3d572b19214d49d8bb7405efa2af28f1022033b7563eb595f6d7ed7ec01734e17b505214fe0851352ed9c3c8120d53268e9a01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffa43bebbebf07452a893a95bfea1d5db338d23579be172fe803dce02eeb7c037d010000006b483045022100ebc77ed0f11d15fe630fe533dc350c2ddc1c81cfeb81d5a27d0587163f58a28c02200983b2a32a1014bab633bfc9258083ac282b79566b6b3fa45c1e6758610444f401210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffb102113fa46ce949616d9cda00f6b10231336b3928eaaac6bfe42d1bf3561d6c010000006a473044022010f8731929a55c1c49610722e965635529ed895b2292d781b183d465799906b20220098359adcbc669cd4b294cc129b110fe035d2f76517248f4b7129f3bf793d07f01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffb861fab2cde188499758346be46b5fbec635addfc4e7b0c8a07c0a908f2b11b4000000006a47304402207328142bb02ef5d6496a210300f4aea71f67683b842fa3df32cae6c88b49a9bb022020f56ddff5042260cfda2c9f39b7dec858cc2f4a76a987cd2dc25945b04e15fe01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff027064d817000000001976a9144a5fba237213a062f6f57978f796390bdcf8d01588ac00902f50090000001976a9145be32612930b8323add2212a4ec03c1562084f8488ac00000000 \ No newline at end of file diff --git a/txsort/testdata/bip69-2.hex b/txsort/testdata/bip69-2.hex deleted file mode 100644 index c42d0d2dc..000000000 --- a/txsort/testdata/bip69-2.hex +++ /dev/null @@ -1 +0,0 @@ -010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00000000 \ No newline at end of file diff --git a/txsort/testdata/bip69-3.hex b/txsort/testdata/bip69-3.hex deleted file mode 100644 index 6fd859a9c..000000000 --- a/txsort/testdata/bip69-3.hex +++ /dev/null @@ -1 +0,0 @@ -0100000001d992e5a888a86d4c7a6a69167a4728ee69497509740fc5f456a24528c340219a000000008b483045022100f0519bdc9282ff476da1323b8ef7ffe33f495c1a8d52cc522b437022d83f6a230220159b61d197fbae01b4a66622a23bc3f1def65d5fa24efd5c26fa872f3a246b8e014104839f9023296a1fabb133140128ca2709f6818c7d099491690bd8ac0fd55279def6a2ceb6ab7b5e4a71889b6e739f09509565eec789e86886f6f936fa42097adeffffffff02000fe208010000001976a914948c765a6914d43f2a7ac177da2c2f6b52de3d7c88ac00e32321000000001976a9140c34f4e29ab5a615d5ea28d4817f12b137d62ed588ac00000000 \ No newline at end of file diff --git a/txsort/testdata/bip69-4.hex b/txsort/testdata/bip69-4.hex deleted file mode 100644 index f41d23f8b..000000000 --- a/txsort/testdata/bip69-4.hex +++ /dev/null @@ -1 +0,0 @@ -01000000059daf0abe7a92618546a9dbcfd65869b6178c66ec21ccfda878c1175979cfd9ef000000004a493046022100c2f7f25be5de6ce88ac3c1a519514379e91f39b31ddff279a3db0b1a229b708b022100b29efbdbd9837cc6a6c7318aa4900ed7e4d65662c34d1622a2035a3a5534a99a01ffffffffd516330ebdf075948da56db13d22632a4fb941122df2884397dda45d451acefb0000000048473044022051243debe6d4f2b433bee0cee78c5c4073ead0e3bde54296dbed6176e128659c022044417bfe16f44eb7b6eb0cdf077b9ce972a332e15395c09ca5e4f602958d266101ffffffffe1f5aa33961227b3c344e57179417ce01b7ccd421117fe2336289b70489883f900000000484730440220593252bb992ce3c85baf28d6e3aa32065816271d2c822398fe7ee28a856bc943022066d429dd5025d3c86fd8fd8a58e183a844bd94aa312cefe00388f57c85b0ca3201ffffffffe207e83718129505e6a7484831442f668164ae659fddb82e9e5421a081fb90d50000000049483045022067cf27eb733e5bcae412a586b25a74417c237161a084167c2a0b439abfebdcb2022100efcc6baa6824b4c5205aa967e0b76d31abf89e738d4b6b014e788c9a8cccaf0c01ffffffffe23b8d9d80a9e9d977fab3c94dbe37befee63822443c3ec5ae5a713ede66c3940000000049483045022020f2eb35036666b1debe0d1d2e77a36d5d9c4e96c1dba23f5100f193dbf524790221008ce79bc1321fb4357c6daee818038d41544749127751726e46b2b320c8b565a201ffffffff0200ba1dd2050000001976a914366a27645806e817a6cd40bc869bdad92fe5509188ac40420f00000000001976a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac00000000 \ No newline at end of file diff --git a/txsort/testdata/bip69-5.hex b/txsort/testdata/bip69-5.hex deleted file mode 100644 index e1cb4d613..000000000 --- a/txsort/testdata/bip69-5.hex +++ /dev/null @@ -1 +0,0 @@ -01000000011f636d0003f673b3aeea4971daef16b8eed784cf6e8019a5ae7da4985fbb06e5000000008a47304402205103941e2b11e746dfa817888d422f6e7f4d16dbbfb8ffa61d15ffb924a84b8802202fe861b0f23f17139d15a3374bfc6c7196d371f3d1a324e31cc0aadbba87e53c0141049e7e1b251a7e26cae9ee7553b278ef58ef3c28b4b20134d51b747d9b18b0a19b94b66cef320e2549dec0ea3d725cb4c742f368928b1fb74b4603e24a1e262c80ffffffff0240420f00000000001976a914bcfa0e27218a7c97257b351b03a9eac95c25a23988ac40420f00000000001976a9140c6a68f20bafc678164d171ee4f077adfa9b091688ac00000000 \ No newline at end of file diff --git a/txsort/txsort.go b/txsort/txsort.go deleted file mode 100644 index f72a7db97..000000000 --- a/txsort/txsort.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2015-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -// Provides functions for sorting tx inputs and outputs according to BIP 69 -// (https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki) - -package txsort - -import ( - "bytes" - "sort" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" -) - -// InPlaceSort modifies the passed transaction inputs and outputs to be sorted -// based on BIP 69. -// -// WARNING: This function must NOT be called with published transactions since -// it will mutate the transaction if it's not already sorted. This can cause -// issues if you mutate a tx in a block, for example, which would invalidate the -// block. It could also cause cached hashes, such as in a btcutil.Tx to become -// invalidated. -// -// The function should only be used if the caller is creating the transaction or -// is otherwise 100% positive mutating will not cause adverse affects due to -// other dependencies. -func InPlaceSort(tx *wire.MsgTx) { - sort.Sort(sortableInputSlice(tx.TxIn)) - sort.Sort(sortableOutputSlice(tx.TxOut)) -} - -// Sort returns a new transaction with the inputs and outputs sorted based on -// BIP 69. The passed transaction is not modified and the new transaction -// might have a different hash if any sorting was done. -func Sort(tx *wire.MsgTx) *wire.MsgTx { - txCopy := tx.Copy() - sort.Sort(sortableInputSlice(txCopy.TxIn)) - sort.Sort(sortableOutputSlice(txCopy.TxOut)) - return txCopy -} - -// IsSorted checks whether tx has inputs and outputs sorted according to BIP -// 69. -func IsSorted(tx *wire.MsgTx) bool { - if !sort.IsSorted(sortableInputSlice(tx.TxIn)) { - return false - } - if !sort.IsSorted(sortableOutputSlice(tx.TxOut)) { - return false - } - return true -} - -type sortableInputSlice []*wire.TxIn -type sortableOutputSlice []*wire.TxOut - -// For SortableInputSlice and SortableOutputSlice, three functions are needed -// to make it sortable with sort.Sort() -- Len, Less, and Swap -// Len and Swap are trivial. Less is BIP 69 specific. -func (s sortableInputSlice) Len() int { return len(s) } -func (s sortableOutputSlice) Len() int { return len(s) } -func (s sortableOutputSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s sortableInputSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -// Input comparison function. -// First sort based on input hash (reversed / rpc-style), then index. -func (s sortableInputSlice) Less(i, j int) bool { - // Input hashes are the same, so compare the index. - ihash := s[i].PreviousOutPoint.Hash - jhash := s[j].PreviousOutPoint.Hash - if ihash == jhash { - return s[i].PreviousOutPoint.Index < s[j].PreviousOutPoint.Index - } - - // At this point, the hashes are not equal, so reverse them to - // big-endian and return the result of the comparison. - const hashSize = chainhash.HashSize - for b := 0; b < hashSize/2; b++ { - ihash[b], ihash[hashSize-1-b] = ihash[hashSize-1-b], ihash[b] - jhash[b], jhash[hashSize-1-b] = jhash[hashSize-1-b], jhash[b] - } - return bytes.Compare(ihash[:], jhash[:]) == -1 -} - -// Output comparison function. -// First sort based on amount (smallest first), then PkScript. -func (s sortableOutputSlice) Less(i, j int) bool { - if s[i].Value == s[j].Value { - return bytes.Compare(s[i].PkScript, s[j].PkScript) < 0 - } - return s[i].Value < s[j].Value -} diff --git a/txsort/txsort_test.go b/txsort/txsort_test.go deleted file mode 100644 index 59f4d6c95..000000000 --- a/txsort/txsort_test.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) 2015-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package txsort_test - -import ( - "bytes" - "encoding/hex" - "io/ioutil" - "path/filepath" - "testing" - - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil/txsort" -) - -// TestSort ensures the transaction sorting works according to the BIP. -func TestSort(t *testing.T) { - tests := []struct { - name string - hexFile string - isSorted bool - unsortedHash string - sortedHash string - }{ - { - name: "first test case from BIP 69 - sorts inputs only, based on hash", - hexFile: "bip69-1.hex", - isSorted: false, - unsortedHash: "0a6a357e2f7796444e02638749d9611c008b253fb55f5dc88b739b230ed0c4c3", - sortedHash: "839503cb611a3e3734bd521c608f881be2293ff77b7384057ab994c794fce623", - }, - { - name: "second test case from BIP 69 - already sorted", - hexFile: "bip69-2.hex", - isSorted: true, - unsortedHash: "28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f", - sortedHash: "28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f", - }, - { - name: "block 100001 tx[1] - sorts outputs only, based on amount", - hexFile: "bip69-3.hex", - isSorted: false, - unsortedHash: "fbde5d03b027d2b9ba4cf5d4fecab9a99864df2637b25ea4cbcb1796ff6550ca", - sortedHash: "0a8c246c55f6b82f094d211f4f57167bf2ea4898741d218b09bdb2536fd8d13f", - }, - { - name: "block 100001 tx[2] - sorts both inputs and outputs", - hexFile: "bip69-4.hex", - isSorted: false, - unsortedHash: "8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb", - sortedHash: "a3196553b928b0b6154b002fa9a1ce875adabc486fedaaaf4c17430fd4486329", - }, - { - name: "block 100998 tx[6] - sorts outputs only, based on output script", - hexFile: "bip69-5.hex", - isSorted: false, - unsortedHash: "ff85e8fc92e71bbc217e3ea9a3bacb86b435e52b6df0b089d67302c293a2b81d", - sortedHash: "9a6c24746de024f77cac9b2138694f11101d1c66289261224ca52a25155a7c94", - }, - } - - for _, test := range tests { - // Load and deserialize the test transaction. - filePath := filepath.Join("testdata", test.hexFile) - txHexBytes, err := ioutil.ReadFile(filePath) - if err != nil { - t.Errorf("ReadFile (%s): failed to read test file: %v", - test.name, err) - continue - } - txBytes, err := hex.DecodeString(string(txHexBytes)) - if err != nil { - t.Errorf("DecodeString (%s): failed to decode tx: %v", - test.name, err) - continue - } - var tx wire.MsgTx - err = tx.Deserialize(bytes.NewReader(txBytes)) - if err != nil { - t.Errorf("Deserialize (%s): unexpected error %v", - test.name, err) - continue - } - - // Ensure the sort order of the original transaction matches the - // expected value. - if got := txsort.IsSorted(&tx); got != test.isSorted { - t.Errorf("IsSorted (%s): sort does not match "+ - "expected - got %v, want %v", test.name, got, - test.isSorted) - continue - } - - // Sort the transaction and ensure the resulting hash is the - // expected value. - sortedTx := txsort.Sort(&tx) - if got := sortedTx.TxHash().String(); got != test.sortedHash { - t.Errorf("Sort (%s): sorted hash does not match "+ - "expected - got %v, want %v", test.name, got, - test.sortedHash) - continue - } - - // Ensure the original transaction is not modified. - if got := tx.TxHash().String(); got != test.unsortedHash { - t.Errorf("Sort (%s): unsorted hash does not match "+ - "expected - got %v, want %v", test.name, got, - test.unsortedHash) - continue - } - - // Now sort the transaction using the mutable version and ensure - // the resulting hash is the expected value. - txsort.InPlaceSort(&tx) - if got := tx.TxHash().String(); got != test.sortedHash { - t.Errorf("SortMutate (%s): sorted hash does not match "+ - "expected - got %v, want %v", test.name, got, - test.sortedHash) - continue - } - } -} diff --git a/wif.go b/wif.go deleted file mode 100644 index 8b17dc985..000000000 --- a/wif.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil - -import ( - "bytes" - "errors" - - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcutil/base58" -) - -// ErrMalformedPrivateKey describes an error where a WIF-encoded private -// key cannot be decoded due to being improperly formatted. This may occur -// if the byte length is incorrect or an unexpected magic number was -// encountered. -var ErrMalformedPrivateKey = errors.New("malformed private key") - -// compressMagic is the magic byte used to identify a WIF encoding for -// an address created from a compressed serialized public key. -const compressMagic byte = 0x01 - -// WIF contains the individual components described by the Wallet Import Format -// (WIF). A WIF string is typically used to represent a private key and its -// associated address in a way that may be easily copied and imported into or -// exported from wallet software. WIF strings may be decoded into this -// structure by calling DecodeWIF or created with a user-provided private key -// by calling NewWIF. -type WIF struct { - // PrivKey is the private key being imported or exported. - PrivKey *btcec.PrivateKey - - // CompressPubKey specifies whether the address controlled by the - // imported or exported private key was created by hashing a - // compressed (33-byte) serialized public key, rather than an - // uncompressed (65-byte) one. - CompressPubKey bool - - // netID is the bitcoin network identifier byte used when - // WIF encoding the private key. - netID byte -} - -// NewWIF creates a new WIF structure to export an address and its private key -// as a string encoded in the Wallet Import Format. The compress argument -// specifies whether the address intended to be imported or exported was created -// by serializing the public key compressed rather than uncompressed. -func NewWIF(privKey *btcec.PrivateKey, net *chaincfg.Params, compress bool) (*WIF, error) { - if net == nil { - return nil, errors.New("no network") - } - return &WIF{privKey, compress, net.PrivateKeyID}, nil -} - -// IsForNet returns whether or not the decoded WIF structure is associated -// with the passed bitcoin network. -func (w *WIF) IsForNet(net *chaincfg.Params) bool { - return w.netID == net.PrivateKeyID -} - -// DecodeWIF creates a new WIF structure by decoding the string encoding of -// the import format. -// -// The WIF string must be a base58-encoded string of the following byte -// sequence: -// -// * 1 byte to identify the network, must be 0x80 for mainnet or 0xef for -// either testnet3 or the regression test network -// * 32 bytes of a binary-encoded, big-endian, zero-padded private key -// * Optional 1 byte (equal to 0x01) if the address being imported or exported -// was created by taking the RIPEMD160 after SHA256 hash of a serialized -// compressed (33-byte) public key -// * 4 bytes of checksum, must equal the first four bytes of the double SHA256 -// of every byte before the checksum in this sequence -// -// If the base58-decoded byte sequence does not match this, DecodeWIF will -// return a non-nil error. ErrMalformedPrivateKey is returned when the WIF -// is of an impossible length or the expected compressed pubkey magic number -// does not equal the expected value of 0x01. ErrChecksumMismatch is returned -// if the expected WIF checksum does not match the calculated checksum. -func DecodeWIF(wif string) (*WIF, error) { - decoded := base58.Decode(wif) - decodedLen := len(decoded) - var compress bool - - // Length of base58 decoded WIF must be 32 bytes + an optional 1 byte - // (0x01) if compressed, plus 1 byte for netID + 4 bytes of checksum. - switch decodedLen { - case 1 + btcec.PrivKeyBytesLen + 1 + 4: - if decoded[33] != compressMagic { - return nil, ErrMalformedPrivateKey - } - compress = true - case 1 + btcec.PrivKeyBytesLen + 4: - compress = false - default: - return nil, ErrMalformedPrivateKey - } - - // Checksum is first four bytes of double SHA256 of the identifier byte - // and privKey. Verify this matches the final 4 bytes of the decoded - // private key. - var tosum []byte - if compress { - tosum = decoded[:1+btcec.PrivKeyBytesLen+1] - } else { - tosum = decoded[:1+btcec.PrivKeyBytesLen] - } - cksum := chainhash.DoubleHashB(tosum)[:4] - if !bytes.Equal(cksum, decoded[decodedLen-4:]) { - return nil, ErrChecksumMismatch - } - - netID := decoded[0] - privKeyBytes := decoded[1 : 1+btcec.PrivKeyBytesLen] - privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes) - return &WIF{privKey, compress, netID}, nil -} - -// String creates the Wallet Import Format string encoding of a WIF structure. -// See DecodeWIF for a detailed breakdown of the format and requirements of -// a valid WIF string. -func (w *WIF) String() string { - // Precalculate size. Maximum number of bytes before base58 encoding - // is one byte for the network, 32 bytes of private key, possibly one - // extra byte if the pubkey is to be compressed, and finally four - // bytes of checksum. - encodeLen := 1 + btcec.PrivKeyBytesLen + 4 - if w.CompressPubKey { - encodeLen++ - } - - a := make([]byte, 0, encodeLen) - a = append(a, w.netID) - // Pad and append bytes manually, instead of using Serialize, to - // avoid another call to make. - a = paddedAppend(btcec.PrivKeyBytesLen, a, w.PrivKey.D.Bytes()) - if w.CompressPubKey { - a = append(a, compressMagic) - } - cksum := chainhash.DoubleHashB(a)[:4] - a = append(a, cksum...) - return base58.Encode(a) -} - -// SerializePubKey serializes the associated public key of the imported or -// exported private key in either a compressed or uncompressed format. The -// serialization format chosen depends on the value of w.CompressPubKey. -func (w *WIF) SerializePubKey() []byte { - pk := (*btcec.PublicKey)(&w.PrivKey.PublicKey) - if w.CompressPubKey { - return pk.SerializeCompressed() - } - return pk.SerializeUncompressed() -} - -// paddedAppend appends the src byte slice to dst, returning the new slice. -// If the length of the source is smaller than the passed size, leading zero -// bytes are appended to the dst slice before appending src. -func paddedAppend(size uint, dst, src []byte) []byte { - for i := 0; i < int(size)-len(src); i++ { - dst = append(dst, 0) - } - return append(dst, src...) -} diff --git a/wif_test.go b/wif_test.go deleted file mode 100644 index 589f2ac53..000000000 --- a/wif_test.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2013 - 2020 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil_test - -import ( - "bytes" - "encoding/hex" - "testing" - - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg" - . "github.com/btcsuite/btcutil" -) - -func TestEncodeDecodeWIF(t *testing.T) { - validEncodeCases := []struct { - privateKey []byte // input - net *chaincfg.Params // input - compress bool // input - wif string // output - publicKey []byte // output - name string // name of subtest - }{ - { - privateKey: []byte{ - 0x0c, 0x28, 0xfc, 0xa3, 0x86, 0xc7, 0xa2, 0x27, - 0x60, 0x0b, 0x2f, 0xe5, 0x0b, 0x7c, 0xae, 0x11, - 0xec, 0x86, 0xd3, 0xbf, 0x1f, 0xbe, 0x47, 0x1b, - 0xe8, 0x98, 0x27, 0xe1, 0x9d, 0x72, 0xaa, 0x1d}, - net: &chaincfg.MainNetParams, - compress: false, - wif: "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ", - publicKey: []byte{ - 0x04, 0xd0, 0xde, 0x0a, 0xae, 0xae, 0xfa, 0xd0, - 0x2b, 0x8b, 0xdc, 0x8a, 0x01, 0xa1, 0xb8, 0xb1, - 0x1c, 0x69, 0x6b, 0xd3, 0xd6, 0x6a, 0x2c, 0x5f, - 0x10, 0x78, 0x0d, 0x95, 0xb7, 0xdf, 0x42, 0x64, - 0x5c, 0xd8, 0x52, 0x28, 0xa6, 0xfb, 0x29, 0x94, - 0x0e, 0x85, 0x8e, 0x7e, 0x55, 0x84, 0x2a, 0xe2, - 0xbd, 0x11, 0x5d, 0x1e, 0xd7, 0xcc, 0x0e, 0x82, - 0xd9, 0x34, 0xe9, 0x29, 0xc9, 0x76, 0x48, 0xcb, - 0x0a}, - name: "encodeValidUncompressedMainNetWif", - }, - { - privateKey: []byte{ - 0xdd, 0xa3, 0x5a, 0x14, 0x88, 0xfb, 0x97, 0xb6, - 0xeb, 0x3f, 0xe6, 0xe9, 0xef, 0x2a, 0x25, 0x81, - 0x4e, 0x39, 0x6f, 0xb5, 0xdc, 0x29, 0x5f, 0xe9, - 0x94, 0xb9, 0x67, 0x89, 0xb2, 0x1a, 0x03, 0x98}, - net: &chaincfg.TestNet3Params, - compress: true, - wif: "cV1Y7ARUr9Yx7BR55nTdnR7ZXNJphZtCCMBTEZBJe1hXt2kB684q", - publicKey: []byte{ - 0x02, 0xee, 0xc2, 0x54, 0x06, 0x61, 0xb0, 0xc3, - 0x9d, 0x27, 0x15, 0x70, 0x74, 0x24, 0x13, 0xbd, - 0x02, 0x93, 0x2d, 0xd0, 0x09, 0x34, 0x93, 0xfd, - 0x0b, 0xec, 0xed, 0x0b, 0x7f, 0x93, 0xad, 0xde, - 0xc4}, - name: "encodeValidCompressedTestNet3Wif", - }, - } - - for _, validCase := range validEncodeCases { - validCase := validCase - - t.Run(validCase.name, func(t *testing.T) { - priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), validCase.privateKey) - wif, err := NewWIF(priv, validCase.net, validCase.compress) - if err != nil { - t.Fatalf("NewWIF failed: expected no error, got '%v'", err) - } - - if !wif.IsForNet(validCase.net) { - t.Fatal("IsForNet failed: got 'false', want 'true'") - } - - if gotPubKey := wif.SerializePubKey(); !bytes.Equal(gotPubKey, validCase.publicKey) { - t.Fatalf("SerializePubKey failed: got '%s', want '%s'", - hex.EncodeToString(gotPubKey), hex.EncodeToString(validCase.publicKey)) - } - - // Test that encoding the WIF structure matches the expected string. - got := wif.String() - if got != validCase.wif { - t.Fatalf("NewWIF failed: want '%s', got '%s'", - validCase.wif, got) - } - - // Test that decoding the expected string results in the original WIF - // structure. - decodedWif, err := DecodeWIF(got) - if err != nil { - t.Fatalf("DecodeWIF failed: expected no error, got '%v'", err) - } - if decodedWifString := decodedWif.String(); decodedWifString != validCase.wif { - t.Fatalf("NewWIF failed: want '%v', got '%v'", validCase.wif, decodedWifString) - } - }) - } - - invalidDecodeCases := []struct { - name string - wif string - err error - }{ - { - name: "decodeInvalidLengthWif", - wif: "deadbeef", - err: ErrMalformedPrivateKey, - }, - { - name: "decodeInvalidCompressMagicWif", - wif: "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sfZr2ym", - err: ErrMalformedPrivateKey, - }, - { - name: "decodeInvalidChecksumWif", - wif: "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTj", - err: ErrChecksumMismatch, - }, - } - - for _, invalidCase := range invalidDecodeCases { - invalidCase := invalidCase - - t.Run(invalidCase.name, func(t *testing.T) { - decodedWif, err := DecodeWIF(invalidCase.wif) - if decodedWif != nil { - t.Fatalf("DecodeWIF: unexpectedly succeeded - got '%v', want '%v'", - decodedWif, nil) - } - if err != invalidCase.err { - t.Fatalf("DecodeWIF: expected error '%v', got '%v'", - invalidCase.err, err) - } - }) - } - - t.Run("encodeInvalidNetworkWif", func(t *testing.T) { - privateKey := []byte{ - 0x0c, 0x28, 0xfc, 0xa3, 0x86, 0xc7, 0xa2, 0x27, - 0x60, 0x0b, 0x2f, 0xe5, 0x0b, 0x7c, 0xae, 0x11, - 0xec, 0x86, 0xd3, 0xbf, 0x1f, 0xbe, 0x47, 0x1b, - 0xe8, 0x98, 0x27, 0xe1, 0x9d, 0x72, 0xaa, 0x1d} - priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), privateKey) - - wif, err := NewWIF(priv, nil, true) - - if wif != nil { - t.Fatalf("NewWIF: unexpectedly succeeded - got '%v', want '%v'", - wif, nil) - } - if err == nil || err.Error() != "no network" { - t.Fatalf("NewWIF: expected error 'no network', got '%v'", err) - } - }) -}