// Copyright (c) 2016 The Decred developers // 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 wallet import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcwallet/walletdb" ) // OutputSelectionPolicy describes the rules for selecting an output from the // wallet. type OutputSelectionPolicy struct { Account uint32 RequiredConfirmations int32 } func (p *OutputSelectionPolicy) meetsRequiredConfs(txHeight, curHeight int32) bool { return confirmed(p.RequiredConfirmations, txHeight, curHeight) } // UnspentOutputs fetches all unspent outputs from the wallet that match rules // described in the passed policy. func (w *Wallet) UnspentOutputs(policy OutputSelectionPolicy) ([]*TransactionOutput, error) { var outputResults []*TransactionOutput err := walletdb.View(w.db, func(tx walletdb.ReadTx) error { addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey) txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey) syncBlock := w.Manager.SyncedTo() // TODO: actually stream outputs from the db instead of fetching // all of them at once. outputs, err := w.TxStore.UnspentOutputs(txmgrNs) if err != nil { return err } for _, output := range outputs { // Ignore outputs that haven't reached the required // number of confirmations. if !policy.meetsRequiredConfs(output.Height, syncBlock.Height) { continue } // Ignore outputs that are not controlled by the account. _, addrs, _, err := txscript.ExtractPkScriptAddrs(output.PkScript, w.chainParams) if err != nil || len(addrs) == 0 { // Cannot determine which account this belongs // to without a valid address. TODO: Fix this // by saving outputs per account, or accounts // per output. continue } outputAcct, err := w.Manager.AddrAccount(addrmgrNs, addrs[0]) if err != nil { return err } if outputAcct != policy.Account { continue } // Stakebase isn't exposed by wtxmgr so those will be // OutputKindNormal for now. outputSource := OutputKindNormal if output.FromCoinBase { outputSource = OutputKindCoinbase } result := &TransactionOutput{ OutPoint: output.OutPoint, Output: wire.TxOut{ Value: int64(output.Amount), PkScript: output.PkScript, }, OutputKind: outputSource, ContainingBlock: BlockIdentity(output.Block), ReceiveTime: output.Received, } outputResults = append(outputResults, result) } return nil }) return outputResults, err }