utxos.go raw
1 package wallet
2
3 import (
4 "github.com/p9c/p9/pkg/btcaddr"
5 "github.com/p9c/p9/pkg/txscript"
6 "github.com/p9c/p9/pkg/walletdb"
7 "github.com/p9c/p9/pkg/wire"
8 )
9
10 // OutputSelectionPolicy describes the rules for selecting an output from the wallet.
11 type OutputSelectionPolicy struct {
12 Account uint32
13 RequiredConfirmations int32
14 }
15
16 func (p *OutputSelectionPolicy) meetsRequiredConfs(txHeight, curHeight int32) bool {
17 return confirmed(p.RequiredConfirmations, txHeight, curHeight)
18 }
19
20 // UnspentOutputs fetches all unspent outputs from the wallet that match rules described in the passed policy.
21 func (w *Wallet) UnspentOutputs(policy OutputSelectionPolicy) ([]*TransactionOutput, error) {
22 var outputResults []*TransactionOutput
23 e := walletdb.View(
24 w.db, func(tx walletdb.ReadTx) (e error) {
25 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
26 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
27 syncBlock := w.Manager.SyncedTo()
28 // TODO: actually stream outputs from the db instead of fetching all of them at once.
29 outputs, e := w.TxStore.UnspentOutputs(txmgrNs)
30 if e != nil {
31 return e
32 }
33 for _, output := range outputs {
34 // Ignore outputs that haven't reached the required number of confirmations.
35 if !policy.meetsRequiredConfs(output.Height, syncBlock.Height) {
36 continue
37 }
38 // Ignore outputs that are not controlled by the account.
39 var addrs []btcaddr.Address
40 _, addrs, _, e = txscript.ExtractPkScriptAddrs(
41 output.PkScript,
42 w.chainParams,
43 )
44 if e != nil || len(addrs) == 0 {
45 // Cannot determine which account this belongs to without a valid address.
46 //
47 // TODO: Fix this by saving outputs per account, or accounts per output.
48 continue
49 }
50 var outputAcct uint32
51 _, outputAcct, e = w.Manager.AddrAccount(addrmgrNs, addrs[0])
52 if e != nil {
53 return e
54 }
55 if outputAcct != policy.Account {
56 continue
57 }
58 // Stakebase isn't exposed by wtxmgr so those will be OutputKindNormal for now.
59 outputSource := OutputKindNormal
60 if output.FromCoinBase {
61 outputSource = OutputKindCoinbase
62 }
63 result := &TransactionOutput{
64 OutPoint: output.OutPoint,
65 Output: wire.TxOut{
66 Value: int64(output.Amount),
67 PkScript: output.PkScript,
68 },
69 OutputKind: outputSource,
70 ContainingBlock: BlockIdentity(output.Block),
71 ReceiveTime: output.Received,
72 }
73 outputResults = append(outputResults, result)
74 }
75 return nil
76 },
77 )
78 return outputResults, e
79 }
80