multi: expand SendOutputs and CreateSimpleTx to take utxos

This commit updates the interface methods from
`lnwallet.WalletController` to take optional input set which can be used
to create the tx.
This commit is contained in:
Ononiwu Maureen 2024-03-01 20:25:20 +01:00 committed by yyforyongyu
parent 468ca87499
commit 99339f706f
No known key found for this signature in database
GPG Key ID: 9BCD95C4FF296868
8 changed files with 66 additions and 38 deletions

View File

@ -811,8 +811,8 @@ func (w *WalletKit) SendOutputs(ctx context.Context,
// requirement, we can request that the wallet attempts to create this
// transaction.
tx, err := w.cfg.Wallet.SendOutputs(
outputsToCreate, chainfee.SatPerKWeight(req.SatPerKw), minConfs,
label, coinSelectionStrategy,
nil, outputsToCreate, chainfee.SatPerKWeight(req.SatPerKw),
minConfs, label, coinSelectionStrategy,
)
if err != nil {
return nil, err

View File

@ -16,6 +16,7 @@ import (
base "github.com/btcsuite/btcwallet/wallet"
"github.com/btcsuite/btcwallet/wallet/txauthor"
"github.com/btcsuite/btcwallet/wtxmgr"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
)
@ -33,6 +34,10 @@ type WalletController struct {
Utxos []*lnwallet.Utxo
}
// A compile time check to ensure this mocked WalletController implements the
// WalletController.
var _ lnwallet.WalletController = (*WalletController)(nil)
// BackEnd returns "mock" to signify a mock wallet controller.
func (w *WalletController) BackEnd() string {
return "mock"
@ -140,15 +145,15 @@ func (w *WalletController) ImportTaprootScript(waddrmgr.KeyScope,
}
// SendOutputs currently returns dummy values.
func (w *WalletController) SendOutputs([]*wire.TxOut,
chainfee.SatPerKWeight, int32, string,
base.CoinSelectionStrategy) (*wire.MsgTx, error) {
func (w *WalletController) SendOutputs(fn.Set[wire.OutPoint], []*wire.TxOut,
chainfee.SatPerKWeight, int32, string, base.CoinSelectionStrategy) (
*wire.MsgTx, error) {
return nil, nil
}
// CreateSimpleTx currently returns dummy values.
func (w *WalletController) CreateSimpleTx([]*wire.TxOut,
func (w *WalletController) CreateSimpleTx(fn.Set[wire.OutPoint], []*wire.TxOut,
chainfee.SatPerKWeight, int32, base.CoinSelectionStrategy,
bool) (*txauthor.AuthoredTx, error) {

View File

@ -19,6 +19,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/chain"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/wallet"
base "github.com/btcsuite/btcwallet/wallet"
"github.com/btcsuite/btcwallet/wallet/txauthor"
"github.com/btcsuite/btcwallet/wallet/txrules"
@ -26,6 +27,7 @@ import (
"github.com/btcsuite/btcwallet/wtxmgr"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/kvdb"
@ -977,8 +979,9 @@ func (b *BtcWallet) ImportTaprootScript(scope waddrmgr.KeyScope,
// NOTE: This method requires the global coin selection lock to be held.
//
// This is a part of the WalletController interface.
func (b *BtcWallet) SendOutputs(outputs []*wire.TxOut,
feeRate chainfee.SatPerKWeight, minConfs int32, label string,
func (b *BtcWallet) SendOutputs(inputs fn.Set[wire.OutPoint],
outputs []*wire.TxOut, feeRate chainfee.SatPerKWeight,
minConfs int32, label string,
strategy base.CoinSelectionStrategy) (*wire.MsgTx, error) {
// Convert our fee rate from sat/kw to sat/kb since it's required by
@ -995,6 +998,14 @@ func (b *BtcWallet) SendOutputs(outputs []*wire.TxOut,
return nil, lnwallet.ErrInvalidMinconf
}
// Use selected UTXOs if specified, otherwise default selection.
if len(inputs) != 0 {
return b.wallet.SendOutputsWithInput(
outputs, nil, defaultAccount, minConfs, feeSatPerKB,
strategy, label, inputs.ToSlice(),
)
}
return b.wallet.SendOutputs(
outputs, nil, defaultAccount, minConfs, feeSatPerKB,
strategy, label,
@ -1014,10 +1025,10 @@ func (b *BtcWallet) SendOutputs(outputs []*wire.TxOut,
// NOTE: This method requires the global coin selection lock to be held.
//
// This is a part of the WalletController interface.
func (b *BtcWallet) CreateSimpleTx(outputs []*wire.TxOut,
feeRate chainfee.SatPerKWeight, minConfs int32,
strategy base.CoinSelectionStrategy,
dryRun bool) (*txauthor.AuthoredTx, error) {
func (b *BtcWallet) CreateSimpleTx(inputs fn.Set[wire.OutPoint],
outputs []*wire.TxOut, feeRate chainfee.SatPerKWeight, minConfs int32,
strategy base.CoinSelectionStrategy, dryRun bool) (
*txauthor.AuthoredTx, error) {
// The fee rate is passed in using units of sat/kw, so we'll convert
// this to sat/KB as the CreateSimpleTx method requires this unit.
@ -1047,9 +1058,12 @@ func (b *BtcWallet) CreateSimpleTx(outputs []*wire.TxOut,
}
}
// Add the optional inputs to the transaction.
optFunc := wallet.WithCustomSelectUtxos(inputs.ToSlice())
return b.wallet.CreateSimpleTx(
nil, defaultAccount, outputs, minConfs, feeSatPerKB,
strategy, dryRun,
strategy, dryRun, []wallet.TxCreateOption{optFunc}...,
)
}

View File

@ -18,6 +18,7 @@ import (
base "github.com/btcsuite/btcwallet/wallet"
"github.com/btcsuite/btcwallet/wallet/txauthor"
"github.com/btcsuite/btcwallet/wtxmgr"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
)
@ -344,8 +345,8 @@ type WalletController interface {
// be used when crafting the transaction.
//
// NOTE: This method requires the global coin selection lock to be held.
SendOutputs(outputs []*wire.TxOut, feeRate chainfee.SatPerKWeight,
minConfs int32, label string,
SendOutputs(inputs fn.Set[wire.OutPoint], outputs []*wire.TxOut,
feeRate chainfee.SatPerKWeight, minConfs int32, label string,
strategy base.CoinSelectionStrategy) (*wire.MsgTx, error)
// CreateSimpleTx creates a Bitcoin transaction paying to the specified
@ -360,9 +361,10 @@ type WalletController interface {
// SHOULD NOT be broadcasted.
//
// NOTE: This method requires the global coin selection lock to be held.
CreateSimpleTx(outputs []*wire.TxOut, feeRate chainfee.SatPerKWeight,
minConfs int32, strategy base.CoinSelectionStrategy,
dryRun bool) (*txauthor.AuthoredTx, error)
CreateSimpleTx(inputs fn.Set[wire.OutPoint], outputs []*wire.TxOut,
feeRate chainfee.SatPerKWeight, minConfs int32,
strategy base.CoinSelectionStrategy, dryRun bool) (
*txauthor.AuthoredTx, error)
// GetTransactionDetails returns a detailed description of a transaction
// given its transaction hash.

View File

@ -17,6 +17,7 @@ import (
"github.com/btcsuite/btcwallet/wallet/txauthor"
"github.com/btcsuite/btcwallet/wtxmgr"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
)
@ -35,6 +36,10 @@ type mockWalletController struct {
Utxos []*Utxo
}
// A compile time check to ensure that mockWalletController implements the
// WalletController.
var _ WalletController = (*mockWalletController)(nil)
// BackEnd returns "mock" to signify a mock wallet controller.
func (w *mockWalletController) BackEnd() string {
return "mock"
@ -145,7 +150,7 @@ func (w *mockWalletController) ImportTaprootScript(waddrmgr.KeyScope,
}
// SendOutputs currently returns dummy values.
func (w *mockWalletController) SendOutputs([]*wire.TxOut,
func (w *mockWalletController) SendOutputs(fn.Set[wire.OutPoint], []*wire.TxOut,
chainfee.SatPerKWeight, int32, string,
base.CoinSelectionStrategy) (*wire.MsgTx, error) {
@ -153,9 +158,9 @@ func (w *mockWalletController) SendOutputs([]*wire.TxOut,
}
// CreateSimpleTx currently returns dummy values.
func (w *mockWalletController) CreateSimpleTx([]*wire.TxOut,
chainfee.SatPerKWeight, int32, base.CoinSelectionStrategy,
bool) (*txauthor.AuthoredTx, error) {
func (w *mockWalletController) CreateSimpleTx(fn.Set[wire.OutPoint],
[]*wire.TxOut, chainfee.SatPerKWeight, int32,
base.CoinSelectionStrategy, bool) (*txauthor.AuthoredTx, error) {
return nil, nil
}

View File

@ -22,6 +22,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/waddrmgr"
basewallet "github.com/btcsuite/btcwallet/wallet"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lncfg"
@ -120,12 +121,13 @@ func (r *RPCKeyRing) NewAddress(addrType lnwallet.AddressType, change bool,
// NOTE: This is a part of the WalletController interface.
//
// NOTE: This method only signs with BIP49/84 keys.
func (r *RPCKeyRing) SendOutputs(outputs []*wire.TxOut,
feeRate chainfee.SatPerKWeight, minConfs int32, label string,
func (r *RPCKeyRing) SendOutputs(inputs fn.Set[wire.OutPoint],
outputs []*wire.TxOut, feeRate chainfee.SatPerKWeight,
minConfs int32, label string,
strategy basewallet.CoinSelectionStrategy) (*wire.MsgTx, error) {
tx, err := r.WalletController.SendOutputs(
outputs, feeRate, minConfs, label, strategy,
inputs, outputs, feeRate, minConfs, label, strategy,
)
if err != nil && err != basewallet.ErrTxUnsigned {
return nil, err

View File

@ -169,7 +169,7 @@ func sendCoins(t *testing.T, miner *rpctest.Harness,
t.Helper()
tx, err := sender.SendOutputs(
[]*wire.TxOut{output}, feeRate, minConf, labels.External,
nil, []*wire.TxOut{output}, feeRate, minConf, labels.External,
sender.Cfg.CoinSelectionStrategy,
)
require.NoError(t, err, "unable to send transaction")
@ -1193,7 +1193,7 @@ func testListTransactionDetails(miner *rpctest.Harness,
require.NoError(t, err, "unable to make output script")
burnOutput := wire.NewTxOut(outputAmt, outputScript)
burnTX, err := alice.SendOutputs(
[]*wire.TxOut{burnOutput}, 2500, 1, labels.External,
nil, []*wire.TxOut{burnOutput}, 2500, 1, labels.External,
alice.Cfg.CoinSelectionStrategy,
)
require.NoError(t, err, "unable to create burn tx")
@ -1453,7 +1453,7 @@ func testTransactionSubscriptions(miner *rpctest.Harness,
burnOutput := wire.NewTxOut(outputAmt, outputScript)
tx, err := alice.SendOutputs(
[]*wire.TxOut{burnOutput}, 2500, 1, labels.External,
nil, []*wire.TxOut{burnOutput}, 2500, 1, labels.External,
alice.Cfg.CoinSelectionStrategy,
)
require.NoError(t, err, "unable to create tx")
@ -1642,7 +1642,7 @@ func newTx(t *testing.T, r *rpctest.Harness, pubKey *btcec.PublicKey,
PkScript: keyScript,
}
tx, err := alice.SendOutputs(
[]*wire.TxOut{newOutput}, 2500, 1, labels.External,
nil, []*wire.TxOut{newOutput}, 2500, 1, labels.External,
alice.Cfg.CoinSelectionStrategy,
)
require.NoError(t, err, "unable to create output")
@ -1958,7 +1958,7 @@ func testSignOutputUsingTweaks(r *rpctest.Harness,
PkScript: keyScript,
}
tx, err := alice.SendOutputs(
[]*wire.TxOut{newOutput}, 2500, 1, labels.External,
nil, []*wire.TxOut{newOutput}, 2500, 1, labels.External,
alice.Cfg.CoinSelectionStrategy,
)
if err != nil {
@ -2077,7 +2077,7 @@ func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet,
PkScript: script,
}
tx, err := w.SendOutputs(
[]*wire.TxOut{output}, 2500, 1, labels.External,
nil, []*wire.TxOut{output}, 2500, 1, labels.External,
w.Cfg.CoinSelectionStrategy,
)
require.NoError(t, err, "unable to send outputs")
@ -2302,7 +2302,7 @@ func testSpendUnconfirmed(miner *rpctest.Harness,
PkScript: alicePkScript,
}
_, err = bob.SendOutputs(
[]*wire.TxOut{output}, txFeeRate, 0, labels.External,
nil, []*wire.TxOut{output}, txFeeRate, 0, labels.External,
bob.Cfg.CoinSelectionStrategy,
)
if err == nil {
@ -2329,7 +2329,7 @@ func testSpendUnconfirmed(miner *rpctest.Harness,
// First, verify that we don't have enough balance to send the coins
// using confirmed outputs only.
_, err = bob.SendOutputs(
[]*wire.TxOut{output}, txFeeRate, 1, labels.External,
nil, []*wire.TxOut{output}, txFeeRate, 1, labels.External,
bob.Cfg.CoinSelectionStrategy,
)
if err == nil {
@ -2571,7 +2571,7 @@ func testCreateSimpleTx(r *rpctest.Harness, w *lnwallet.LightningWallet,
// Now try creating a tx spending to these outputs.
createTx, createErr := w.CreateSimpleTx(
outputs, feeRate, minConfs,
nil, outputs, feeRate, minConfs,
w.Cfg.CoinSelectionStrategy, true,
)
switch {
@ -2590,7 +2590,7 @@ func testCreateSimpleTx(r *rpctest.Harness, w *lnwallet.LightningWallet,
// only difference is that the dry run tx is not signed, and
// that the change output position might be different.
tx, sendErr := w.SendOutputs(
outputs, feeRate, minConfs, labels.External,
nil, outputs, feeRate, minConfs, labels.External,
w.Cfg.CoinSelectionStrategy,
)
switch {

View File

@ -1077,7 +1077,7 @@ func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64,
// We first do a dry run, to sanity check we won't spend our wallet
// balance below the reserved amount.
authoredTx, err := r.server.cc.Wallet.CreateSimpleTx(
outputs, feeRate, minConfs, strategy, true,
nil, outputs, feeRate, minConfs, strategy, true,
)
if err != nil {
return nil, err
@ -1098,7 +1098,7 @@ func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64,
// If that checks out, we're fairly confident that creating sending to
// these outputs will keep the wallet balance above the reserve.
tx, err := r.server.cc.Wallet.SendOutputs(
outputs, feeRate, minConfs, label, strategy,
nil, outputs, feeRate, minConfs, label, strategy,
)
if err != nil {
return nil, err
@ -1207,7 +1207,7 @@ func (r *rpcServer) EstimateFee(ctx context.Context,
wallet := r.server.cc.Wallet
err = wallet.WithCoinSelectLock(func() error {
tx, err = wallet.CreateSimpleTx(
outputs, feePerKw, minConfs, coinSelectionStrategy,
nil, outputs, feePerKw, minConfs, coinSelectionStrategy,
true,
)
return err