4656a00705
This changes the database access APIs and each of the "manager" packages (waddrmgr/wstakemgr) so that transactions are opened (only) by the wallet package and the namespace buckets that each manager expects to operate on are passed in as parameters. This helps improve the atomicity situation as it means that many calls to these APIs can be grouped together into a single database transaction. This change does not attempt to completely fix the "half-processed" block problem. Mined transactions are still added to the wallet database under their own database transaction as this is how they are notified by the consensus JSON-RPC server (as loose transactions, without the rest of the block that contains them). It will make updating to a fixed notification model significantly easier, as the same "manager" APIs can still be used, but grouped into a single atomic transaction.
90 lines
2.6 KiB
Go
90 lines
2.6 KiB
Go
// 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
|
|
}
|