Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wallet: add new PSBT funding and finalizing methods #711

Merged
merged 6 commits into from
Sep 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ require (
github.com/btcsuite/btcd v0.20.1-beta.0.20200513120220-b470eee47728
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
github.com/btcsuite/btcutil/psbt v1.0.3-0.20200826194809-5f93e33af2b0
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0
github.com/btcsuite/btcwallet/walletdb v1.3.3
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9
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/psbt v1.0.3-0.20200826194809-5f93e33af2b0 h1:3Zumkyl6PWyHuVJ04me0xeD9CnPOhNgeGpapFbzy7O4=
github.com/btcsuite/btcutil/psbt v1.0.3-0.20200826194809-5f93e33af2b0/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ=
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/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
Expand Down
55 changes: 32 additions & 23 deletions wallet/createtx.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func (w *Wallet) txToOutputs(outputs []*wire.TxOut, account uint32,
}
defer dbtx.Rollback()

addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
addrmgrNs, changeSource := w.addrMgrWithChangeSource(dbtx, account)

// Get current block's height and hash.
bs, err := chainClient.BlockStamp()
Expand All @@ -134,28 +134,6 @@ func (w *Wallet) txToOutputs(outputs []*wire.TxOut, account uint32,
}

inputSource := makeInputSource(eligible)
changeSource := func() ([]byte, error) {
// Derive the change output script. We'll use the default key
// scope responsible for P2WPKH addresses to do so. As a hack to
// allow spending from the imported account, change addresses
// are created from account 0.
var changeAddr btcutil.Address
var err error
changeKeyScope := waddrmgr.KeyScopeBIP0084
if account == waddrmgr.ImportedAddrAccount {
changeAddr, err = w.newChangeAddress(
addrmgrNs, 0, changeKeyScope,
)
} else {
changeAddr, err = w.newChangeAddress(
addrmgrNs, account, changeKeyScope,
)
}
if err != nil {
return nil, err
}
return txscript.PayToAddrScript(changeAddr)
}
tx, err = txauthor.NewUnsignedTransaction(outputs, feeSatPerKb,
inputSource, changeSource)
if err != nil {
Expand Down Expand Up @@ -270,6 +248,37 @@ func (w *Wallet) findEligibleOutputs(dbtx walletdb.ReadTx, account uint32, minco
return eligible, nil
}

// addrMgrWithChangeSource returns the address manager bucket and a change
// source function that returns change addresses from said address manager.
func (w *Wallet) addrMgrWithChangeSource(dbtx walletdb.ReadWriteTx,
account uint32) (walletdb.ReadWriteBucket, txauthor.ChangeSource) {

addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
changeSource := func() ([]byte, error) {
// Derive the change output script. We'll use the default key
// scope responsible for P2WPKH addresses to do so. As a hack to
// allow spending from the imported account, change addresses
// are created from account 0.
var changeAddr btcutil.Address
var err error
changeKeyScope := waddrmgr.KeyScopeBIP0084
if account == waddrmgr.ImportedAddrAccount {
changeAddr, err = w.newChangeAddress(
addrmgrNs, 0, changeKeyScope,
)
} else {
changeAddr, err = w.newChangeAddress(
addrmgrNs, account, changeKeyScope,
)
}
if err != nil {
return nil, err
}
return txscript.PayToAddrScript(changeAddr)
}
return addrmgrNs, changeSource
}

// validateMsgTx verifies transaction input scripts for tx. All previous output
// scripts from outputs redeemed by the transaction, in the same order they are
// spent, must be passed in the prevScripts slice.
Expand Down
84 changes: 49 additions & 35 deletions wallet/createtx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ import (
"github.com/btcsuite/btcwallet/wtxmgr"
)

var (
testBlockHash, _ = chainhash.NewHashFromStr(
"00000000000000017188b968a371bab95aa43522665353b646e41865abae" +
"02a4",
)
testBlockHeight int32 = 276425
)

// TestTxToOutput checks that no new address is added to he database if we
// request a dry run of the txToOutputs call. It also makes sure a subsequent
// non-dry run call produces a similar transaction to the dry-run.
Expand Down Expand Up @@ -45,41 +53,7 @@ func TestTxToOutputsDryRun(t *testing.T) {
txOut,
},
}

var b bytes.Buffer
if err := incomingTx.Serialize(&b); err != nil {
t.Fatalf("unable to serialize tx: %v", err)
}
txBytes := b.Bytes()

rec, err := wtxmgr.NewTxRecord(txBytes, time.Now())
if err != nil {
t.Fatalf("unable to create tx record: %v", err)
}

// The block meta will be inserted to tell the wallet this is a
// confirmed transaction.
blockHash, _ := chainhash.NewHashFromStr(
"00000000000000017188b968a371bab95aa43522665353b646e41865abae02a4")
block := &wtxmgr.BlockMeta{
Block: wtxmgr.Block{Hash: *blockHash, Height: 276425},
Time: time.Unix(1387737310, 0),
}

if err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(wtxmgrNamespaceKey)
err = w.TxStore.InsertTx(ns, rec, block)
if err != nil {
return err
}
err = w.TxStore.AddCredit(ns, rec, block, 0, false)
if err != nil {
return err
}
return nil
}); err != nil {
t.Fatalf("failed inserting tx: %v", err)
}
addUtxo(t, w, incomingTx)

// Now tell the wallet to create a transaction paying to the specified
// outputs.
Expand Down Expand Up @@ -175,3 +149,43 @@ func TestTxToOutputsDryRun(t *testing.T) {
"than wet run")
}
}

// addUtxo add the given transaction to the wallet's database marked as a
// confirmed UTXO .
func addUtxo(t *testing.T, w *Wallet, incomingTx *wire.MsgTx) {
var b bytes.Buffer
if err := incomingTx.Serialize(&b); err != nil {
t.Fatalf("unable to serialize tx: %v", err)
}
txBytes := b.Bytes()

rec, err := wtxmgr.NewTxRecord(txBytes, time.Now())
if err != nil {
t.Fatalf("unable to create tx record: %v", err)
}

// The block meta will be inserted to tell the wallet this is a
// confirmed transaction.
block := &wtxmgr.BlockMeta{
Block: wtxmgr.Block{
Hash: *testBlockHash,
Height: testBlockHeight,
},
Time: time.Unix(1387737310, 0),
}

if err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(wtxmgrNamespaceKey)
err = w.TxStore.InsertTx(ns, rec, block)
if err != nil {
return err
}
err = w.TxStore.AddCredit(ns, rec, block, 0, false)
if err != nil {
return err
}
return nil
}); err != nil {
t.Fatalf("failed inserting tx: %v", err)
}
}
Loading