Skip to content

Commit

Permalink
wallet: add utxo filter function.
Browse files Browse the repository at this point in the history
Allows to attach a utxo filter function when creating a transaction
funded by the internal wallet.
  • Loading branch information
ziggie1984 committed Apr 4, 2024
1 parent 9a7dd24 commit 2bc8246
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 13 deletions.
17 changes: 14 additions & 3 deletions wallet/createtx.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ func (w *Wallet) txToOutputs(outputs []*wire.TxOut,
coinSelectKeyScope, changeKeyScope *waddrmgr.KeyScope,
account uint32, minconf int32, feeSatPerKb btcutil.Amount,
strategy CoinSelectionStrategy, dryRun bool,
selectedUtxos []wire.OutPoint) (*txauthor.AuthoredTx, error) {
selectedUtxos []wire.OutPoint,
allowUtxo func(utxo wtxmgr.Credit) bool) (
*txauthor.AuthoredTx, error) {

chainClient, err := w.requireChainClient()
if err != nil {
Expand Down Expand Up @@ -168,7 +170,8 @@ func (w *Wallet) txToOutputs(outputs []*wire.TxOut,
}

eligible, err := w.findEligibleOutputs(
dbtx, coinSelectKeyScope, account, minconf, bs,
dbtx, coinSelectKeyScope, account, minconf,
bs, allowUtxo,
)
if err != nil {
return err
Expand Down Expand Up @@ -322,7 +325,8 @@ func (w *Wallet) txToOutputs(outputs []*wire.TxOut,

func (w *Wallet) findEligibleOutputs(dbtx walletdb.ReadTx,
keyScope *waddrmgr.KeyScope, account uint32, minconf int32,
bs *waddrmgr.BlockStamp) ([]wtxmgr.Credit, error) {
bs *waddrmgr.BlockStamp,
allowUtxo func(utxo wtxmgr.Credit) bool) ([]wtxmgr.Credit, error) {

addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
Expand All @@ -341,6 +345,13 @@ func (w *Wallet) findEligibleOutputs(dbtx walletdb.ReadTx,
for i := range unspent {
output := &unspent[i]

// Restrict the selected utxos if a filter function is provided.
if allowUtxo != nil &&
!allowUtxo(*output) {

continue
}

// Only include this output if it meets the required number of
// confirmations. Coinbase transactions must have reached
// maturity before their outputs may be spent.
Expand Down
16 changes: 9 additions & 7 deletions wallet/createtx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ var (
"02a4",
)
testBlockHeight int32 = 276425

alwaysAllowUtxo = func(utxo wtxmgr.Credit) bool { return true }
)

// TestTxToOutput checks that no new address is added to he database if we
Expand Down Expand Up @@ -79,7 +81,7 @@ func TestTxToOutputsDryRun(t *testing.T) {
// database us not inflated.
dryRunTx, err := w.txToOutputs(
txOuts, nil, nil, 0, 1, 1000, CoinSelectionLargest, true,
nil,
nil, alwaysAllowUtxo,
)
if err != nil {
t.Fatalf("unable to author tx: %v", err)
Expand All @@ -97,7 +99,7 @@ func TestTxToOutputsDryRun(t *testing.T) {

dryRunTx2, err := w.txToOutputs(
txOuts, nil, nil, 0, 1, 1000, CoinSelectionLargest, true,
nil,
nil, alwaysAllowUtxo,
)
if err != nil {
t.Fatalf("unable to author tx: %v", err)
Expand Down Expand Up @@ -133,7 +135,7 @@ func TestTxToOutputsDryRun(t *testing.T) {
// to the database.
tx, err := w.txToOutputs(
txOuts, nil, nil, 0, 1, 1000, CoinSelectionLargest, false,
nil,
nil, alwaysAllowUtxo,
)
if err != nil {
t.Fatalf("unable to author tx: %v", err)
Expand Down Expand Up @@ -283,7 +285,7 @@ func TestTxToOutputsRandom(t *testing.T) {
createTx := func() *txauthor.AuthoredTx {
tx, err := w.txToOutputs(
txOuts, nil, nil, 0, 1, feeSatPerKb,
CoinSelectionRandom, true, nil,
CoinSelectionRandom, true, nil, alwaysAllowUtxo,
)
require.NoError(t, err)
return tx
Expand Down Expand Up @@ -355,7 +357,7 @@ func TestCreateSimpleCustomChange(t *testing.T) {
}
tx1, err := w.txToOutputs(
[]*wire.TxOut{targetTxOut}, nil, nil, 0, 1, 1000,
CoinSelectionLargest, true, nil,
CoinSelectionLargest, true, nil, alwaysAllowUtxo,
)
require.NoError(t, err)

Expand All @@ -381,7 +383,7 @@ func TestCreateSimpleCustomChange(t *testing.T) {
tx2, err := w.txToOutputs(
[]*wire.TxOut{targetTxOut}, &waddrmgr.KeyScopeBIP0086,
&waddrmgr.KeyScopeBIP0084, 0, 1, 1000, CoinSelectionLargest,
true, nil,
true, nil, alwaysAllowUtxo,
)
require.NoError(t, err)

Expand Down Expand Up @@ -465,7 +467,7 @@ func TestSelectUtxosTxoToOutpoint(t *testing.T) {
}
tx1, err := w.txToOutputs(
[]*wire.TxOut{targetTxOut}, nil, nil, 0, 1, 1000,
CoinSelectionLargest, true, selectUtxos,
CoinSelectionLargest, true, selectUtxos, alwaysAllowUtxo,
)
require.NoError(t, err)

Expand Down
19 changes: 16 additions & 3 deletions wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,7 @@ type (
dryRun bool
resp chan createTxResponse
selectUtxos []wire.OutPoint
allowUtxo func(wtxmgr.Credit) bool
}
createTxResponse struct {
tx *txauthor.AuthoredTx
Expand Down Expand Up @@ -1239,9 +1240,10 @@ out:
}

tx, err := w.txToOutputs(
txr.outputs, txr.coinSelectKeyScope, txr.changeKeyScope,
txr.account, txr.minconf, txr.feeSatPerKB,
txr.coinSelectionStrategy, txr.dryRun, txr.selectUtxos,
txr.outputs, txr.coinSelectKeyScope,
txr.changeKeyScope, txr.account, txr.minconf,
txr.feeSatPerKB, txr.coinSelectionStrategy,
txr.dryRun, txr.selectUtxos, txr.allowUtxo,
)

release()
Expand All @@ -1259,6 +1261,7 @@ out:
type txCreateOptions struct {
changeKeyScope *waddrmgr.KeyScope
selectUtxos []wire.OutPoint
allowUtxo func(wtxmgr.Credit) bool
}

// TxCreateOption is a set of optional arguments to modify the tx creation
Expand Down Expand Up @@ -1289,6 +1292,15 @@ func WithCustomSelectUtxos(utxos []wire.OutPoint) TxCreateOption {
}
}

// WithUtxoFilter is used to restrict the selection of the internal wallet
// inputs by further external conditions. Utxos which pass the filter are
// considered when creating the transaction.
func WithUtxoFilter(allowUtxo func(utxo wtxmgr.Credit) bool) TxCreateOption {
return func(opts *txCreateOptions) {
opts.allowUtxo = allowUtxo
}
}

// CreateSimpleTx creates a new signed transaction spending unspent outputs with
// at least minconf confirmations spending to any number of address/amount
// pairs. Only unspent outputs belonging to the given key scope and account will
Expand Down Expand Up @@ -1333,6 +1345,7 @@ func (w *Wallet) CreateSimpleTx(coinSelectKeyScope *waddrmgr.KeyScope,
dryRun: dryRun,
resp: make(chan createTxResponse),
selectUtxos: opts.selectUtxos,
allowUtxo: opts.allowUtxo,
}
w.createTxRequests <- req
resp := <-req.resp
Expand Down

0 comments on commit 2bc8246

Please sign in to comment.