decred.org/dcrwallet/v3@v3.1.0/wallet/wallet.go (about)

     1  // Copyright (c) 2013-2016 The btcsuite developers
     2  // Copyright (c) 2015-2021 The Decred developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  package wallet
     7  
     8  import (
     9  	"bytes"
    10  	"context"
    11  	"encoding/binary"
    12  	"encoding/hex"
    13  	"fmt"
    14  	"math/big"
    15  	"runtime"
    16  	"sort"
    17  	"strconv"
    18  	"strings"
    19  	"sync"
    20  	"sync/atomic"
    21  	"time"
    22  
    23  	"decred.org/dcrwallet/v3/deployments"
    24  	"decred.org/dcrwallet/v3/errors"
    25  	"decred.org/dcrwallet/v3/internal/compat"
    26  	"decred.org/dcrwallet/v3/rpc/client/dcrd"
    27  	"decred.org/dcrwallet/v3/rpc/jsonrpc/types"
    28  	"decred.org/dcrwallet/v3/validate"
    29  	"decred.org/dcrwallet/v3/wallet/txrules"
    30  	"decred.org/dcrwallet/v3/wallet/txsizes"
    31  	"decred.org/dcrwallet/v3/wallet/udb"
    32  	"decred.org/dcrwallet/v3/wallet/walletdb"
    33  
    34  	"github.com/decred/dcrd/blockchain/stake/v5"
    35  	blockchain "github.com/decred/dcrd/blockchain/standalone/v2"
    36  	"github.com/decred/dcrd/chaincfg/chainhash"
    37  	"github.com/decred/dcrd/chaincfg/v3"
    38  	"github.com/decred/dcrd/crypto/blake256"
    39  	"github.com/decred/dcrd/dcrec"
    40  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    41  	"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
    42  	"github.com/decred/dcrd/dcrutil/v4"
    43  	gcs2 "github.com/decred/dcrd/gcs/v4"
    44  	"github.com/decred/dcrd/hdkeychain/v3"
    45  	dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v4"
    46  	"github.com/decred/dcrd/txscript/v4"
    47  	"github.com/decred/dcrd/txscript/v4/sign"
    48  	"github.com/decred/dcrd/txscript/v4/stdaddr"
    49  	"github.com/decred/dcrd/txscript/v4/stdscript"
    50  	"github.com/decred/dcrd/wire"
    51  	"golang.org/x/sync/errgroup"
    52  )
    53  
    54  const (
    55  	// InsecurePubPassphrase is the default outer encryption passphrase used
    56  	// for public data (everything but private keys).  Using a non-default
    57  	// public passphrase can prevent an attacker without the public
    58  	// passphrase from discovering all past and future wallet addresses if
    59  	// they gain access to the wallet database.
    60  	//
    61  	// NOTE: at time of writing, public encryption only applies to public
    62  	// data in the waddrmgr namespace.  Transactions are not yet encrypted.
    63  	InsecurePubPassphrase = "public"
    64  )
    65  
    66  var (
    67  	// SimulationPassphrase is the password for a wallet created for simnet
    68  	// with --createtemp.
    69  	SimulationPassphrase = []byte("password")
    70  )
    71  
    72  // Namespace bucket keys.
    73  var (
    74  	waddrmgrNamespaceKey  = []byte("waddrmgr")
    75  	wtxmgrNamespaceKey    = []byte("wtxmgr")
    76  	wstakemgrNamespaceKey = []byte("wstakemgr")
    77  )
    78  
    79  // The assumed output script version is defined to assist with refactoring to
    80  // use actual script versions.
    81  const scriptVersionAssumed = 0
    82  
    83  // StakeDifficultyInfo is a container for stake difficulty information updates.
    84  type StakeDifficultyInfo struct {
    85  	BlockHash       *chainhash.Hash
    86  	BlockHeight     int64
    87  	StakeDifficulty int64
    88  }
    89  
    90  // outpoint is a wire.OutPoint without specifying a tree.  It is used as the key
    91  // for the lockedOutpoints map.
    92  type outpoint struct {
    93  	hash  chainhash.Hash
    94  	index uint32
    95  }
    96  
    97  // Wallet is a structure containing all the components for a
    98  // complete wallet.  It contains the Armory-style key store
    99  // addresses and keys),
   100  type Wallet struct {
   101  	// disapprovePercent is an atomic. It sets the percentage of blocks to
   102  	// disapprove on simnet or testnet.
   103  	disapprovePercent uint32
   104  
   105  	// Data stores
   106  	db       walletdb.DB
   107  	manager  *udb.Manager
   108  	txStore  *udb.Store
   109  	stakeMgr *udb.StakeStore
   110  
   111  	// Handlers for stake system.
   112  	stakeSettingsLock  sync.Mutex
   113  	defaultVoteBits    stake.VoteBits
   114  	votingEnabled      bool
   115  	poolAddress        stdaddr.StakeAddress
   116  	poolFees           float64
   117  	manualTickets      bool
   118  	stakePoolEnabled   bool
   119  	stakePoolColdAddrs map[string]struct{}
   120  	subsidyCache       *blockchain.SubsidyCache
   121  	tspends            map[chainhash.Hash]wire.MsgTx
   122  	tspendPolicy       map[chainhash.Hash]stake.TreasuryVoteT
   123  	tspendKeyPolicy    map[string]stake.TreasuryVoteT // keyed by politeia key
   124  	vspTSpendPolicy    map[udb.VSPTSpend]stake.TreasuryVoteT
   125  	vspTSpendKeyPolicy map[udb.VSPTreasuryKey]stake.TreasuryVoteT
   126  
   127  	// Start up flags/settings
   128  	gapLimit        uint32
   129  	watchLast       uint32
   130  	accountGapLimit int
   131  
   132  	// initialHeight is the wallet's tip height prior to syncing with the
   133  	// network. Useful for calculating or estimating headers fetch progress
   134  	// during sync if the target header height is known or can be estimated.
   135  	initialHeight int32
   136  
   137  	networkBackend   NetworkBackend
   138  	networkBackendMu sync.Mutex
   139  
   140  	lockedOutpoints  map[outpoint]struct{}
   141  	lockedOutpointMu sync.Mutex
   142  
   143  	relayFee                dcrutil.Amount
   144  	relayFeeMu              sync.Mutex
   145  	allowHighFees           bool
   146  	disableCoinTypeUpgrades bool
   147  	recentlyPublished       map[chainhash.Hash]struct{}
   148  	recentlyPublishedMu     sync.Mutex
   149  
   150  	// Internal address handling.
   151  	addressReuse     bool
   152  	ticketAddress    stdaddr.StakeAddress
   153  	addressBuffers   map[uint32]*bip0044AccountData
   154  	addressBuffersMu sync.Mutex
   155  
   156  	// Passphrase unlock
   157  	passphraseUsedMu        sync.RWMutex
   158  	passphraseTimeoutMu     sync.Mutex
   159  	passphraseTimeoutCancel chan struct{}
   160  
   161  	// Mix rate limiting
   162  	mixSems mixSemaphores
   163  
   164  	// Cached Blake3 anchor candidate
   165  	cachedBlake3WorkDiffCandidateAnchor   *wire.BlockHeader
   166  	cachedBlake3WorkDiffCandidateAnchorMu sync.Mutex
   167  
   168  	NtfnServer *NotificationServer
   169  
   170  	chainParams        *chaincfg.Params
   171  	deploymentsByID    map[string]*chaincfg.ConsensusDeployment
   172  	minTestNetTarget   *big.Int
   173  	minTestNetDiffBits uint32
   174  }
   175  
   176  // Config represents the configuration options needed to initialize a wallet.
   177  type Config struct {
   178  	DB DB
   179  
   180  	PubPassphrase []byte
   181  
   182  	VotingEnabled bool
   183  	AddressReuse  bool
   184  	VotingAddress stdaddr.StakeAddress
   185  	PoolAddress   stdaddr.StakeAddress
   186  	PoolFees      float64
   187  
   188  	GapLimit                uint32
   189  	WatchLast               uint32
   190  	AccountGapLimit         int
   191  	MixSplitLimit           int
   192  	DisableCoinTypeUpgrades bool
   193  
   194  	StakePoolColdExtKey string
   195  	ManualTickets       bool
   196  	AllowHighFees       bool
   197  	RelayFee            dcrutil.Amount
   198  	Params              *chaincfg.Params
   199  }
   200  
   201  // DisapprovePercent returns the wallet's block disapproval percentage.
   202  func (w *Wallet) DisapprovePercent() uint32 {
   203  	return atomic.LoadUint32(&w.disapprovePercent)
   204  }
   205  
   206  // SetDisapprovePercent sets the wallet's block disapproval percentage. Do not
   207  // set on mainnet.
   208  func (w *Wallet) SetDisapprovePercent(percent uint32) {
   209  	atomic.StoreUint32(&w.disapprovePercent, percent)
   210  }
   211  
   212  // FetchOutput fetches the associated transaction output given an outpoint.
   213  // It cannot be used to fetch multi-signature outputs.
   214  func (w *Wallet) FetchOutput(ctx context.Context, outPoint *wire.OutPoint) (*wire.TxOut, error) {
   215  	const op errors.Op = "wallet.FetchOutput"
   216  
   217  	var out *wire.TxOut
   218  	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
   219  		txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
   220  		outTx, err := w.txStore.Tx(txmgrNs, &outPoint.Hash)
   221  		if err != nil {
   222  			return err
   223  		}
   224  
   225  		out = outTx.TxOut[outPoint.Index]
   226  		return err
   227  	})
   228  	if err != nil {
   229  		return nil, errors.E(op, err)
   230  	}
   231  
   232  	return out, nil
   233  }
   234  
   235  // VotingEnabled returns whether the wallet is configured to vote tickets.
   236  func (w *Wallet) VotingEnabled() bool {
   237  	w.stakeSettingsLock.Lock()
   238  	enabled := w.votingEnabled
   239  	w.stakeSettingsLock.Unlock()
   240  	return enabled
   241  }
   242  
   243  // IsTSpendCached returns whether the given hash is already cached.
   244  func (w *Wallet) IsTSpendCached(hash *chainhash.Hash) bool {
   245  	if _, ok := w.tspends[*hash]; ok {
   246  		return true
   247  	}
   248  	return false
   249  }
   250  
   251  // AddTSpend adds a tspend to the cache.
   252  func (w *Wallet) AddTSpend(tx wire.MsgTx) error {
   253  	hash := tx.TxHash()
   254  	log.Infof("TSpend arrived: %v", hash)
   255  	w.stakeSettingsLock.Lock()
   256  	defer w.stakeSettingsLock.Unlock()
   257  
   258  	if _, ok := w.tspends[hash]; ok {
   259  		return fmt.Errorf("tspend already cached")
   260  	}
   261  	w.tspends[hash] = tx
   262  	return nil
   263  }
   264  
   265  // GetAllTSpends returns all tspends currently in the cache.
   266  // Note: currently the tspend list does not get culled.
   267  func (w *Wallet) GetAllTSpends(ctx context.Context) []*wire.MsgTx {
   268  	_, height := w.MainChainTip(ctx)
   269  	w.stakeSettingsLock.Lock()
   270  	defer w.stakeSettingsLock.Unlock()
   271  
   272  	txs := make([]*wire.MsgTx, 0, len(w.tspends))
   273  	for k := range w.tspends {
   274  		v := w.tspends[k]
   275  		if uint32(height) > v.Expiry {
   276  			delete(w.tspends, k)
   277  			continue
   278  		}
   279  		txs = append(txs, &v)
   280  	}
   281  	return txs
   282  }
   283  
   284  func voteVersion(params *chaincfg.Params) uint32 {
   285  	switch params.Net {
   286  	case wire.MainNet:
   287  		return 10
   288  	case 0x48e7a065: // TestNet2
   289  		return 6
   290  	case wire.TestNet3:
   291  		return 11
   292  	case wire.SimNet:
   293  		return 11
   294  	default:
   295  		return 1
   296  	}
   297  }
   298  
   299  // CurrentAgendas returns the current stake version for the active network and
   300  // this version of the software, and all agendas defined by it.
   301  func CurrentAgendas(params *chaincfg.Params) (version uint32, agendas []chaincfg.ConsensusDeployment) {
   302  	version = voteVersion(params)
   303  	if params.Deployments == nil {
   304  		return version, nil
   305  	}
   306  	return version, params.Deployments[version]
   307  }
   308  
   309  func (w *Wallet) readDBVoteBits(dbtx walletdb.ReadTx) stake.VoteBits {
   310  	version, deployments := CurrentAgendas(w.chainParams)
   311  	vb := stake.VoteBits{
   312  		Bits:         0x0001,
   313  		ExtendedBits: make([]byte, 4),
   314  	}
   315  	binary.LittleEndian.PutUint32(vb.ExtendedBits, version)
   316  
   317  	if len(deployments) == 0 {
   318  		return vb
   319  	}
   320  
   321  	for i := range deployments {
   322  		d := &deployments[i]
   323  		choiceID := udb.DefaultAgendaPreference(dbtx, version, d.Vote.Id)
   324  		if choiceID == "" {
   325  			continue
   326  		}
   327  		for j := range d.Vote.Choices {
   328  			choice := &d.Vote.Choices[j]
   329  			if choiceID == choice.Id {
   330  				vb.Bits |= choice.Bits
   331  				break
   332  			}
   333  		}
   334  	}
   335  
   336  	return vb
   337  }
   338  
   339  func (w *Wallet) readDBTicketVoteBits(dbtx walletdb.ReadTx, ticketHash *chainhash.Hash) (stake.VoteBits, bool) {
   340  	version, deployments := CurrentAgendas(w.chainParams)
   341  	tvb := stake.VoteBits{
   342  		Bits:         0x0001,
   343  		ExtendedBits: make([]byte, 4),
   344  	}
   345  	binary.LittleEndian.PutUint32(tvb.ExtendedBits, version)
   346  
   347  	if len(deployments) == 0 {
   348  		return tvb, false
   349  	}
   350  
   351  	var hasSavedPrefs bool
   352  	for i := range deployments {
   353  		d := &deployments[i]
   354  		choiceID := udb.TicketAgendaPreference(dbtx, ticketHash, version, d.Vote.Id)
   355  		if choiceID == "" {
   356  			continue
   357  		}
   358  		hasSavedPrefs = true
   359  		for j := range d.Vote.Choices {
   360  			choice := &d.Vote.Choices[j]
   361  			if choiceID == choice.Id {
   362  				tvb.Bits |= choice.Bits
   363  				break
   364  			}
   365  		}
   366  	}
   367  	return tvb, hasSavedPrefs
   368  }
   369  
   370  func (w *Wallet) readDBTreasuryPolicies(dbtx walletdb.ReadTx) (
   371  	map[chainhash.Hash]stake.TreasuryVoteT, map[udb.VSPTSpend]stake.TreasuryVoteT, error) {
   372  	a, err := udb.TSpendPolicies(dbtx)
   373  	if err != nil {
   374  		return nil, nil, err
   375  	}
   376  	b, err := udb.VSPTSpendPolicies(dbtx)
   377  	return a, b, err
   378  }
   379  
   380  func (w *Wallet) readDBTreasuryKeyPolicies(dbtx walletdb.ReadTx) (
   381  	map[string]stake.TreasuryVoteT, map[udb.VSPTreasuryKey]stake.TreasuryVoteT, error) {
   382  	a, err := udb.TreasuryKeyPolicies(dbtx)
   383  	if err != nil {
   384  		return nil, nil, err
   385  	}
   386  	b, err := udb.VSPTreasuryKeyPolicies(dbtx)
   387  	return a, b, err
   388  }
   389  
   390  // VoteBits returns the vote bits that are described by the currently set agenda
   391  // preferences.  The previous block valid bit is always set, and must be unset
   392  // elsewhere if the previous block's regular transactions should be voted
   393  // against.
   394  func (w *Wallet) VoteBits() stake.VoteBits {
   395  	w.stakeSettingsLock.Lock()
   396  	vb := w.defaultVoteBits
   397  	w.stakeSettingsLock.Unlock()
   398  	return vb
   399  }
   400  
   401  // AgendaChoice describes a user's choice for a consensus deployment agenda.
   402  type AgendaChoice struct {
   403  	AgendaID string
   404  	ChoiceID string
   405  }
   406  
   407  type AgendaChoices []AgendaChoice
   408  
   409  // Map returns the agenda choices formatted as map["AgendaID"] = "ChoiceID".
   410  func (a AgendaChoices) Map() map[string]string {
   411  	choices := make(map[string]string, len(a))
   412  
   413  	for _, c := range a {
   414  		choices[c.AgendaID] = c.ChoiceID
   415  	}
   416  	return choices
   417  }
   418  
   419  // AgendaChoices returns the choice IDs for every agenda of the supported stake
   420  // version.  Abstains are included.  Returns choice IDs set for the specified
   421  // non-nil ticket hash, or the default choice IDs if the ticket hash is nil or
   422  // there are no choices set for the ticket.
   423  func (w *Wallet) AgendaChoices(ctx context.Context, ticketHash *chainhash.Hash) (choices AgendaChoices, voteBits uint16, err error) {
   424  	const op errors.Op = "wallet.AgendaChoices"
   425  	version, deployments := CurrentAgendas(w.chainParams)
   426  	if len(deployments) == 0 {
   427  		return nil, 0, nil
   428  	}
   429  
   430  	choices = make(AgendaChoices, len(deployments))
   431  	for i := range choices {
   432  		choices[i].AgendaID = deployments[i].Vote.Id
   433  		choices[i].ChoiceID = "abstain"
   434  	}
   435  
   436  	var ownTicket bool
   437  	var hasSavedPrefs bool
   438  
   439  	voteBits = 1
   440  	err = walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
   441  		if ticketHash != nil {
   442  			ownTicket = w.txStore.OwnTicket(tx, ticketHash)
   443  			ownTicket = ownTicket || w.stakeMgr.OwnTicket(ticketHash)
   444  			if !ownTicket {
   445  				return nil
   446  			}
   447  		}
   448  
   449  		for i := range deployments {
   450  			agenda := &deployments[i].Vote
   451  			var choice string
   452  			if ticketHash == nil {
   453  				choice = udb.DefaultAgendaPreference(tx, version, agenda.Id)
   454  			} else {
   455  				choice = udb.TicketAgendaPreference(tx, ticketHash, version, agenda.Id)
   456  			}
   457  			if choice == "" {
   458  				continue
   459  			}
   460  			hasSavedPrefs = true
   461  			choices[i].ChoiceID = choice
   462  			for j := range agenda.Choices {
   463  				if agenda.Choices[j].Id == choice {
   464  					voteBits |= agenda.Choices[j].Bits
   465  					break
   466  				}
   467  			}
   468  		}
   469  		return nil
   470  	})
   471  	if err != nil {
   472  		return nil, 0, errors.E(op, err)
   473  	}
   474  	if ticketHash != nil && !ownTicket {
   475  		return nil, 0, errors.E(errors.NotExist, "ticket not found")
   476  	}
   477  	if ticketHash != nil && !hasSavedPrefs {
   478  		// no choices set for ticket hash, return default choices.
   479  		return w.AgendaChoices(ctx, nil)
   480  	}
   481  	return choices, voteBits, nil
   482  }
   483  
   484  // SetAgendaChoices sets the choices for agendas defined by the supported stake
   485  // version.  If a choice is set multiple times, the last takes preference.  The
   486  // new votebits after each change is made are returned.
   487  // If a ticketHash is provided, agenda choices are only set for that ticket and
   488  // the new votebits for that ticket is returned.
   489  func (w *Wallet) SetAgendaChoices(ctx context.Context, ticketHash *chainhash.Hash, choices ...AgendaChoice) (voteBits uint16, err error) {
   490  	const op errors.Op = "wallet.SetAgendaChoices"
   491  	version, deployments := CurrentAgendas(w.chainParams)
   492  	if len(deployments) == 0 {
   493  		return 0, errors.E("no agendas to set for this network")
   494  	}
   495  
   496  	if ticketHash != nil {
   497  		// validate ticket ownership
   498  		var ownTicket bool
   499  		err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
   500  			ownTicket = w.txStore.OwnTicket(dbtx, ticketHash) || w.stakeMgr.OwnTicket(ticketHash)
   501  			return nil
   502  		})
   503  		if err != nil {
   504  			return 0, errors.E(op, err)
   505  		}
   506  		if !ownTicket {
   507  			return 0, errors.E(errors.NotExist, "ticket not found")
   508  		}
   509  	}
   510  
   511  	type maskChoice struct {
   512  		mask uint16
   513  		bits uint16
   514  	}
   515  	var appliedChoices []maskChoice
   516  
   517  	err = walletdb.Update(ctx, w.db, func(tx walletdb.ReadWriteTx) error {
   518  		for _, c := range choices {
   519  			var matchingAgenda *chaincfg.Vote
   520  			for i := range deployments {
   521  				if deployments[i].Vote.Id == c.AgendaID {
   522  					matchingAgenda = &deployments[i].Vote
   523  					break
   524  				}
   525  			}
   526  			if matchingAgenda == nil {
   527  				return errors.E(errors.Invalid, errors.Errorf("no agenda with ID %q", c.AgendaID))
   528  			}
   529  
   530  			var matchingChoice *chaincfg.Choice
   531  			for i := range matchingAgenda.Choices {
   532  				if matchingAgenda.Choices[i].Id == c.ChoiceID {
   533  					matchingChoice = &matchingAgenda.Choices[i]
   534  					break
   535  				}
   536  			}
   537  			if matchingChoice == nil {
   538  				return errors.E(errors.Invalid, errors.Errorf("agenda %q has no choice ID %q", c.AgendaID, c.ChoiceID))
   539  			}
   540  
   541  			var err error
   542  			if ticketHash == nil {
   543  				err = udb.SetDefaultAgendaPreference(tx, version, c.AgendaID, c.ChoiceID)
   544  			} else {
   545  				err = udb.SetTicketAgendaPreference(tx, ticketHash, version, c.AgendaID, c.ChoiceID)
   546  			}
   547  			if err != nil {
   548  				return err
   549  			}
   550  			appliedChoices = append(appliedChoices, maskChoice{
   551  				mask: matchingAgenda.Mask,
   552  				bits: matchingChoice.Bits,
   553  			})
   554  			if ticketHash != nil {
   555  				// No need to check that this ticket has prefs set,
   556  				// we just saved the per-ticket vote bits.
   557  				ticketVoteBits, _ := w.readDBTicketVoteBits(tx, ticketHash)
   558  				voteBits = ticketVoteBits.Bits
   559  			}
   560  		}
   561  		return nil
   562  	})
   563  	if err != nil {
   564  		return 0, errors.E(op, err)
   565  	}
   566  
   567  	// With the DB update successful, modify the default votebits cached by the
   568  	// wallet structure. Per-ticket votebits are not cached.
   569  	if ticketHash == nil {
   570  		w.stakeSettingsLock.Lock()
   571  		for _, c := range appliedChoices {
   572  			w.defaultVoteBits.Bits &^= c.mask // Clear all bits from this agenda
   573  			w.defaultVoteBits.Bits |= c.bits  // Set bits for this choice
   574  		}
   575  		voteBits = w.defaultVoteBits.Bits
   576  		w.stakeSettingsLock.Unlock()
   577  	}
   578  
   579  	return voteBits, nil
   580  }
   581  
   582  // TreasuryKeyPolicyForTicket returns all of the treasury key policies set for a
   583  // single ticket. It does not consider the global wallet setting.
   584  func (w *Wallet) TreasuryKeyPolicyForTicket(ticketHash *chainhash.Hash) map[string]string {
   585  	w.stakeSettingsLock.Lock()
   586  	defer w.stakeSettingsLock.Unlock()
   587  
   588  	policies := make(map[string]string)
   589  	for key, value := range w.vspTSpendKeyPolicy {
   590  		if key.Ticket.IsEqual(ticketHash) {
   591  			var choice string
   592  			switch value {
   593  			case stake.TreasuryVoteYes:
   594  				choice = "yes"
   595  			case stake.TreasuryVoteNo:
   596  				choice = "no"
   597  			default:
   598  				choice = "abstain"
   599  			}
   600  			policies[key.TreasuryKey] = choice
   601  		}
   602  	}
   603  	return policies
   604  }
   605  
   606  // TreasuryKeyPolicy returns a vote policy for provided Pi key. If there is
   607  // no policy this method returns TreasuryVoteInvalid.
   608  // A non-nil ticket hash may be used by a VSP to return per-ticket policies.
   609  func (w *Wallet) TreasuryKeyPolicy(pikey []byte, ticket *chainhash.Hash) stake.TreasuryVoteT {
   610  	w.stakeSettingsLock.Lock()
   611  	defer w.stakeSettingsLock.Unlock()
   612  
   613  	// Zero value is abstain/invalid, just return as is.
   614  	if ticket != nil {
   615  		return w.vspTSpendKeyPolicy[udb.VSPTreasuryKey{
   616  			Ticket:      *ticket,
   617  			TreasuryKey: string(pikey),
   618  		}]
   619  	}
   620  	return w.tspendKeyPolicy[string(pikey)]
   621  }
   622  
   623  // TSpendPolicyForTicket returns all of the tspend policies set for a single
   624  // ticket. It does not consider the global wallet setting.
   625  func (w *Wallet) TSpendPolicyForTicket(ticketHash *chainhash.Hash) map[string]string {
   626  	w.stakeSettingsLock.Lock()
   627  	defer w.stakeSettingsLock.Unlock()
   628  
   629  	policies := make(map[string]string)
   630  	for key, value := range w.vspTSpendPolicy {
   631  		if key.Ticket.IsEqual(ticketHash) {
   632  			var choice string
   633  			switch value {
   634  			case stake.TreasuryVoteYes:
   635  				choice = "yes"
   636  			case stake.TreasuryVoteNo:
   637  				choice = "no"
   638  			default:
   639  				choice = "abstain"
   640  			}
   641  			policies[key.TSpend.String()] = choice
   642  		}
   643  	}
   644  	return policies
   645  }
   646  
   647  // TSpendPolicy returns a vote policy for a tspend.  If a policy is set for a
   648  // particular tspend transaction, that policy is returned.  Otherwise, if the
   649  // tspend is known, any policy for the treasury key which signs the tspend is
   650  // returned.
   651  // A non-nil ticket hash may be used by a VSP to return per-ticket policies.
   652  func (w *Wallet) TSpendPolicy(tspendHash, ticketHash *chainhash.Hash) stake.TreasuryVoteT {
   653  	w.stakeSettingsLock.Lock()
   654  	defer w.stakeSettingsLock.Unlock()
   655  
   656  	// Policy preferences for specific tspends override key policies.
   657  	if ticketHash != nil {
   658  		policy, ok := w.vspTSpendPolicy[udb.VSPTSpend{
   659  			Ticket: *ticketHash,
   660  			TSpend: *tspendHash,
   661  		}]
   662  		if ok {
   663  			return policy
   664  		}
   665  	}
   666  	if policy, ok := w.tspendPolicy[*tspendHash]; ok {
   667  		return policy
   668  	}
   669  
   670  	// If this tspend is known, the pi key can be extracted from it and its
   671  	// policy is returned.
   672  	tspend, ok := w.tspends[*tspendHash]
   673  	if !ok {
   674  		return 0 // invalid/abstain
   675  	}
   676  	pikey := tspend.TxIn[0].SignatureScript[66 : 66+secp256k1.PubKeyBytesLenCompressed]
   677  
   678  	// Zero value means abstain, just return as is.
   679  	if ticketHash != nil {
   680  		policy, ok := w.vspTSpendKeyPolicy[udb.VSPTreasuryKey{
   681  			Ticket:      *ticketHash,
   682  			TreasuryKey: string(pikey),
   683  		}]
   684  		if ok {
   685  			return policy
   686  		}
   687  	}
   688  	return w.tspendKeyPolicy[string(pikey)]
   689  }
   690  
   691  // TreasuryKeyPolicy records the voting policy for treasury spend transactions
   692  // by a particular key, and possibly for a particular ticket being voted on by a
   693  // VSP.
   694  type TreasuryKeyPolicy struct {
   695  	PiKey  []byte
   696  	Ticket *chainhash.Hash // nil unless for per-ticket VSP policies
   697  	Policy stake.TreasuryVoteT
   698  }
   699  
   700  // TreasuryKeyPolicies returns all configured policies for treasury keys.
   701  func (w *Wallet) TreasuryKeyPolicies() []TreasuryKeyPolicy {
   702  	w.stakeSettingsLock.Lock()
   703  	defer w.stakeSettingsLock.Unlock()
   704  
   705  	policies := make([]TreasuryKeyPolicy, 0, len(w.tspendKeyPolicy))
   706  	for pikey, policy := range w.tspendKeyPolicy {
   707  		policies = append(policies, TreasuryKeyPolicy{
   708  			PiKey:  []byte(pikey),
   709  			Policy: policy,
   710  		})
   711  	}
   712  	for tuple, policy := range w.vspTSpendKeyPolicy {
   713  		ticketHash := tuple.Ticket // copy
   714  		pikey := []byte(tuple.TreasuryKey)
   715  		policies = append(policies, TreasuryKeyPolicy{
   716  			PiKey:  pikey,
   717  			Ticket: &ticketHash,
   718  			Policy: policy,
   719  		})
   720  	}
   721  	return policies
   722  }
   723  
   724  // SetTreasuryKeyPolicy sets a tspend vote policy for a specific Politeia
   725  // instance key.
   726  // A non-nil ticket hash may be used by a VSP to set per-ticket policies.
   727  func (w *Wallet) SetTreasuryKeyPolicy(ctx context.Context, pikey []byte,
   728  	policy stake.TreasuryVoteT, ticketHash *chainhash.Hash) error {
   729  
   730  	switch policy {
   731  	case stake.TreasuryVoteInvalid, stake.TreasuryVoteNo, stake.TreasuryVoteYes:
   732  	default:
   733  		err := errors.Errorf("invalid treasury vote policy %#x", policy)
   734  		return errors.E(errors.Invalid, err)
   735  	}
   736  
   737  	defer w.stakeSettingsLock.Unlock()
   738  	w.stakeSettingsLock.Lock()
   739  
   740  	err := walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
   741  		if ticketHash != nil {
   742  			return udb.SetVSPTreasuryKeyPolicy(dbtx, ticketHash,
   743  				pikey, policy)
   744  		}
   745  		return udb.SetTreasuryKeyPolicy(dbtx, pikey, policy)
   746  	})
   747  	if err != nil {
   748  		return err
   749  	}
   750  
   751  	if ticketHash != nil {
   752  		k := udb.VSPTreasuryKey{
   753  			Ticket:      *ticketHash,
   754  			TreasuryKey: string(pikey),
   755  		}
   756  		if policy == stake.TreasuryVoteInvalid {
   757  			delete(w.vspTSpendKeyPolicy, k)
   758  			return nil
   759  		}
   760  
   761  		w.vspTSpendKeyPolicy[k] = policy
   762  		return nil
   763  	}
   764  
   765  	if policy == stake.TreasuryVoteInvalid {
   766  		delete(w.tspendKeyPolicy, string(pikey))
   767  		return nil
   768  	}
   769  
   770  	w.tspendKeyPolicy[string(pikey)] = policy
   771  	return nil
   772  }
   773  
   774  // SetTSpendPolicy sets a tspend vote policy for a specific tspend transaction
   775  // hash.
   776  // A non-nil ticket hash may be used by a VSP to set per-ticket policies.
   777  func (w *Wallet) SetTSpendPolicy(ctx context.Context, tspendHash *chainhash.Hash,
   778  	policy stake.TreasuryVoteT, ticketHash *chainhash.Hash) error {
   779  
   780  	switch policy {
   781  	case stake.TreasuryVoteInvalid, stake.TreasuryVoteNo, stake.TreasuryVoteYes:
   782  	default:
   783  		err := errors.Errorf("invalid treasury vote policy %#x", policy)
   784  		return errors.E(errors.Invalid, err)
   785  	}
   786  
   787  	defer w.stakeSettingsLock.Unlock()
   788  	w.stakeSettingsLock.Lock()
   789  
   790  	err := walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
   791  		if ticketHash != nil {
   792  			return udb.SetVSPTSpendPolicy(dbtx, ticketHash,
   793  				tspendHash, policy)
   794  		}
   795  		return udb.SetTSpendPolicy(dbtx, tspendHash, policy)
   796  	})
   797  	if err != nil {
   798  		return err
   799  	}
   800  
   801  	if ticketHash != nil {
   802  		k := udb.VSPTSpend{
   803  			Ticket: *ticketHash,
   804  			TSpend: *tspendHash,
   805  		}
   806  		if policy == stake.TreasuryVoteInvalid {
   807  			delete(w.vspTSpendPolicy, k)
   808  			return nil
   809  		}
   810  
   811  		w.vspTSpendPolicy[k] = policy
   812  		return nil
   813  	}
   814  
   815  	if policy == stake.TreasuryVoteInvalid {
   816  		delete(w.tspendPolicy, *tspendHash)
   817  		return nil
   818  	}
   819  
   820  	w.tspendPolicy[*tspendHash] = policy
   821  	return nil
   822  }
   823  
   824  // RelayFee returns the current minimum relay fee (per kB of serialized
   825  // transaction) used when constructing transactions.
   826  func (w *Wallet) RelayFee() dcrutil.Amount {
   827  	w.relayFeeMu.Lock()
   828  	relayFee := w.relayFee
   829  	w.relayFeeMu.Unlock()
   830  	return relayFee
   831  }
   832  
   833  // SetRelayFee sets a new minimum relay fee (per kB of serialized
   834  // transaction) used when constructing transactions.
   835  func (w *Wallet) SetRelayFee(relayFee dcrutil.Amount) {
   836  	w.relayFeeMu.Lock()
   837  	w.relayFee = relayFee
   838  	w.relayFeeMu.Unlock()
   839  }
   840  
   841  // InitialHeight is the wallet's tip height prior to syncing with the network.
   842  func (w *Wallet) InitialHeight() int32 {
   843  	return w.initialHeight
   844  }
   845  
   846  // MainChainTip returns the hash and height of the tip-most block in the main
   847  // chain that the wallet is synchronized to.
   848  func (w *Wallet) MainChainTip(ctx context.Context) (hash chainhash.Hash, height int32) {
   849  	// TODO: after the main chain tip is successfully updated in the db, it
   850  	// should be saved in memory.  This will speed up access to it, and means
   851  	// there won't need to be an ignored error here for ergonomic access to the
   852  	// hash and height.
   853  	walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
   854  		hash, height = w.txStore.MainChainTip(dbtx)
   855  		return nil
   856  	})
   857  	return
   858  }
   859  
   860  // BlockInMainChain returns whether hash is a block hash of any block in the
   861  // wallet's main chain.  If the block is in the main chain, invalidated reports
   862  // whether a child block in the main chain stake invalidates the queried block.
   863  func (w *Wallet) BlockInMainChain(ctx context.Context, hash *chainhash.Hash) (haveBlock, invalidated bool, err error) {
   864  	const op errors.Op = "wallet.BlockInMainChain"
   865  	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
   866  		haveBlock, invalidated = w.txStore.BlockInMainChain(dbtx, hash)
   867  		return nil
   868  	})
   869  	if err != nil {
   870  		return false, false, errors.E(op, err)
   871  	}
   872  	return haveBlock, invalidated, nil
   873  }
   874  
   875  // BlockHeader returns the block header for a block by it's identifying hash, if
   876  // it is recorded by the wallet.
   877  func (w *Wallet) BlockHeader(ctx context.Context, blockHash *chainhash.Hash) (*wire.BlockHeader, error) {
   878  	var header *wire.BlockHeader
   879  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
   880  		var err error
   881  		header, err = w.txStore.GetBlockHeader(dbtx, blockHash)
   882  		return err
   883  	})
   884  	return header, err
   885  }
   886  
   887  // CFilterV2 returns the version 2 regular compact filter for a block along
   888  // with the key required to query it for matches against committed scripts.
   889  func (w *Wallet) CFilterV2(ctx context.Context, blockHash *chainhash.Hash) ([gcs2.KeySize]byte, *gcs2.FilterV2, error) {
   890  	var f *gcs2.FilterV2
   891  	var key [gcs2.KeySize]byte
   892  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
   893  		var err error
   894  		key, f, err = w.txStore.CFilterV2(dbtx, blockHash)
   895  		return err
   896  	})
   897  	return key, f, err
   898  }
   899  
   900  // RangeCFiltersV2 calls the function `f` for the set of version 2 committed
   901  // filters for the main chain within the specificed block range.
   902  //
   903  // The default behavior for an unspecified range is to loop over the entire
   904  // main chain.
   905  //
   906  // The function `f` may return true for the first argument to indicate no more
   907  // items should be fetched. Any returned errors by `f` also cause the loop to
   908  // fail.
   909  //
   910  // Note that the filter passed to `f` is safe for use after `f` returns.
   911  func (w *Wallet) RangeCFiltersV2(ctx context.Context, startBlock, endBlock *BlockIdentifier, f func(chainhash.Hash, [gcs2.KeySize]byte, *gcs2.FilterV2) (bool, error)) error {
   912  	const op errors.Op = "wallet.RangeCFiltersV2"
   913  
   914  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
   915  		start, end, err := w.blockRange(dbtx, startBlock, endBlock)
   916  		if err != nil {
   917  			return err
   918  		}
   919  
   920  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
   921  
   922  		rangeFn := func(block *udb.Block) (bool, error) {
   923  			key, filter, err := w.txStore.CFilterV2(dbtx, &block.Hash)
   924  			if err != nil {
   925  				return false, err
   926  			}
   927  
   928  			return f(block.Hash, key, filter)
   929  		}
   930  
   931  		return w.txStore.RangeBlocks(txmgrNs, start, end, rangeFn)
   932  	})
   933  	if err != nil {
   934  		return errors.E(op, err)
   935  	}
   936  	return nil
   937  }
   938  
   939  // watchHDAddrs loads the network backend's transaction filter with HD addresses
   940  // for transaction notifications.
   941  //
   942  // This method does nothing if the wallet's rescan point is behind the main
   943  // chain tip block and firstWatch is false.  That is, it does not watch any
   944  // addresses if the wallet's transactions are not synced with the best known
   945  // block.  There is no reason to watch addresses if there is a known possibility
   946  // of not having all relevant transactions.
   947  func (w *Wallet) watchHDAddrs(ctx context.Context, firstWatch bool, n NetworkBackend) (count uint64, err error) {
   948  	if !firstWatch {
   949  		rp, err := w.RescanPoint(ctx)
   950  		if err != nil {
   951  			return 0, err
   952  		}
   953  		if rp != nil {
   954  			return 0, nil
   955  		}
   956  	}
   957  
   958  	// Read branch keys and child counts for all derived and imported
   959  	// HD accounts.
   960  	type hdAccount struct {
   961  		externalKey, internalKey                   *hdkeychain.ExtendedKey
   962  		externalCount, internalCount               uint32
   963  		lastWatchedExternal, lastWatchedInternal   uint32
   964  		lastReturnedExternal, lastReturnedInternal uint32
   965  		lastUsedExternal, lastUsedInternal         uint32
   966  	}
   967  	hdAccounts := make(map[uint32]hdAccount)
   968  	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
   969  		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
   970  
   971  		var err error
   972  		lastAcct, err := w.manager.LastAccount(addrmgrNs)
   973  		if err != nil {
   974  			return err
   975  		}
   976  		lastImportedAcct, err := w.manager.LastImportedAccount(dbtx)
   977  		if err != nil {
   978  			return err
   979  		}
   980  
   981  		loadAccount := func(acct uint32) error {
   982  			props, err := w.manager.AccountProperties(addrmgrNs, acct)
   983  			if err != nil {
   984  				return err
   985  			}
   986  			hdAccounts[acct] = hdAccount{
   987  				externalCount:        minUint32(props.LastReturnedExternalIndex+w.gapLimit, hdkeychain.HardenedKeyStart-1),
   988  				internalCount:        minUint32(props.LastReturnedInternalIndex+w.gapLimit, hdkeychain.HardenedKeyStart-1),
   989  				lastReturnedExternal: props.LastReturnedExternalIndex,
   990  				lastReturnedInternal: props.LastReturnedInternalIndex,
   991  				lastUsedExternal:     props.LastUsedExternalIndex,
   992  				lastUsedInternal:     props.LastUsedInternalIndex,
   993  			}
   994  			return nil
   995  		}
   996  		for acct := uint32(0); acct <= lastAcct; acct++ {
   997  			err := loadAccount(acct)
   998  			if err != nil {
   999  				return err
  1000  			}
  1001  		}
  1002  		for acct := uint32(udb.ImportedAddrAccount + 1); acct <= lastImportedAcct; acct++ {
  1003  			err := loadAccount(acct)
  1004  			if err != nil {
  1005  				return err
  1006  			}
  1007  		}
  1008  		return nil
  1009  	})
  1010  	if err != nil {
  1011  		return 0, err
  1012  	}
  1013  	w.addressBuffersMu.Lock()
  1014  	for acct, ad := range w.addressBuffers {
  1015  		hd := hdAccounts[acct]
  1016  
  1017  		// Update the in-memory address tracking with the latest last
  1018  		// used index retreived from the db.
  1019  		// Because the cursor may be advanced ahead of what the database
  1020  		// would otherwise record as the last returned address, due to
  1021  		// delayed db updates during some operations, a delta is
  1022  		// calculated between the in-memory and db last returned
  1023  		// indexes, and this delta is added back to the new cursor.
  1024  		//
  1025  		// This is calculated as:
  1026  		//   delta := ad.albExternal.lastUsed + ad.albExternal.cursor - hd.lastReturnedExternal
  1027  		//   ad.albExternal.cursor = hd.lastReturnedExternal - hd.lastUsedExternal + delta
  1028  		// which simplifies to the calculation below.  An additional clamp
  1029  		// is added to prevent the cursors from going negative.
  1030  		if hd.lastUsedExternal+1 > ad.albExternal.lastUsed+1 {
  1031  			ad.albExternal.cursor += ad.albExternal.lastUsed - hd.lastUsedExternal
  1032  			if ad.albExternal.cursor > ^uint32(0)>>1 {
  1033  				ad.albExternal.cursor = 0
  1034  			}
  1035  			ad.albExternal.lastUsed = hd.lastUsedExternal
  1036  		}
  1037  		if hd.lastUsedInternal+1 > ad.albInternal.lastUsed+1 {
  1038  			ad.albInternal.cursor += ad.albInternal.lastUsed - hd.lastUsedInternal
  1039  			if ad.albInternal.cursor > ^uint32(0)>>1 {
  1040  				ad.albInternal.cursor = 0
  1041  			}
  1042  			ad.albInternal.lastUsed = hd.lastUsedInternal
  1043  		}
  1044  
  1045  		hd.externalKey = ad.albExternal.branchXpub
  1046  		hd.internalKey = ad.albInternal.branchXpub
  1047  		if firstWatch {
  1048  			ad.albExternal.lastWatched = hd.externalCount
  1049  			ad.albInternal.lastWatched = hd.internalCount
  1050  		} else {
  1051  			hd.lastWatchedExternal = ad.albExternal.lastWatched
  1052  			hd.lastWatchedInternal = ad.albInternal.lastWatched
  1053  		}
  1054  		hdAccounts[acct] = hd
  1055  	}
  1056  	w.addressBuffersMu.Unlock()
  1057  
  1058  	ctx, cancel := context.WithCancel(ctx)
  1059  	defer cancel()
  1060  	watchAddrs := make(chan []stdaddr.Address, runtime.NumCPU())
  1061  	watchError := make(chan error)
  1062  	go func() {
  1063  		for addrs := range watchAddrs {
  1064  			count += uint64(len(addrs))
  1065  			err := n.LoadTxFilter(ctx, false, addrs, nil)
  1066  			if err != nil {
  1067  				watchError <- err
  1068  				cancel()
  1069  				return
  1070  			}
  1071  		}
  1072  		watchError <- nil
  1073  	}()
  1074  	var deriveError error
  1075  	loadBranchAddrs := func(branchKey *hdkeychain.ExtendedKey, start, end uint32) {
  1076  		if start == 0 && w.watchLast != 0 && end-w.gapLimit > w.watchLast {
  1077  			start = end - w.gapLimit - w.watchLast
  1078  		}
  1079  		const step = 256
  1080  		for ; start <= end; start += step {
  1081  			addrs := make([]stdaddr.Address, 0, step)
  1082  			stop := minUint32(end+1, start+step)
  1083  			for child := start; child < stop; child++ {
  1084  				addr, err := deriveChildAddress(branchKey, child, w.chainParams)
  1085  				if errors.Is(err, hdkeychain.ErrInvalidChild) {
  1086  					continue
  1087  				}
  1088  				if err != nil {
  1089  					deriveError = err
  1090  					return
  1091  				}
  1092  				addrs = append(addrs, addr)
  1093  			}
  1094  			select {
  1095  			case watchAddrs <- addrs:
  1096  			case <-ctx.Done():
  1097  				return
  1098  			}
  1099  		}
  1100  	}
  1101  	for _, hd := range hdAccounts {
  1102  		loadBranchAddrs(hd.externalKey, hd.lastWatchedExternal, hd.externalCount)
  1103  		loadBranchAddrs(hd.internalKey, hd.lastWatchedInternal, hd.internalCount)
  1104  		if ctx.Err() != nil || deriveError != nil {
  1105  			break
  1106  		}
  1107  	}
  1108  	close(watchAddrs)
  1109  	if deriveError != nil {
  1110  		return 0, deriveError
  1111  	}
  1112  	err = <-watchError
  1113  	if err != nil {
  1114  		return 0, err
  1115  	}
  1116  
  1117  	w.addressBuffersMu.Lock()
  1118  	for acct, hd := range hdAccounts {
  1119  		ad := w.addressBuffers[acct]
  1120  		if ad.albExternal.lastWatched < hd.externalCount {
  1121  			ad.albExternal.lastWatched = hd.externalCount
  1122  		}
  1123  		if ad.albInternal.lastWatched < hd.internalCount {
  1124  			ad.albInternal.lastWatched = hd.internalCount
  1125  		}
  1126  	}
  1127  	w.addressBuffersMu.Unlock()
  1128  
  1129  	return count, nil
  1130  }
  1131  
  1132  // CoinType returns the active BIP0044 coin type. For watching-only wallets,
  1133  // which do not save the coin type keys, this method will return an error with
  1134  // code errors.WatchingOnly.
  1135  func (w *Wallet) CoinType(ctx context.Context) (uint32, error) {
  1136  	const op errors.Op = "wallet.CoinType"
  1137  	var coinType uint32
  1138  	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  1139  		var err error
  1140  		coinType, err = w.manager.CoinType(tx)
  1141  		return err
  1142  	})
  1143  	if err != nil {
  1144  		return coinType, errors.E(op, err)
  1145  	}
  1146  	return coinType, nil
  1147  }
  1148  
  1149  // CoinTypePrivKey returns the BIP0044 coin type private key.
  1150  func (w *Wallet) CoinTypePrivKey(ctx context.Context) (*hdkeychain.ExtendedKey, error) {
  1151  	const op errors.Op = "wallet.CoinTypePrivKey"
  1152  	var coinTypePrivKey *hdkeychain.ExtendedKey
  1153  	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  1154  		var err error
  1155  		coinTypePrivKey, err = w.manager.CoinTypePrivKey(tx)
  1156  		return err
  1157  	})
  1158  	if err != nil {
  1159  		return nil, errors.E(op, err)
  1160  	}
  1161  	return coinTypePrivKey, nil
  1162  }
  1163  
  1164  // LoadActiveDataFilters loads filters for all active addresses and unspent
  1165  // outpoints for this wallet.
  1166  func (w *Wallet) LoadActiveDataFilters(ctx context.Context, n NetworkBackend, reload bool) (err error) {
  1167  	const op errors.Op = "wallet.LoadActiveDataFilters"
  1168  	log.Infof("Loading active addresses and unspent outputs...")
  1169  
  1170  	if reload {
  1171  		err := n.LoadTxFilter(ctx, true, nil, nil)
  1172  		if err != nil {
  1173  			return err
  1174  		}
  1175  	}
  1176  
  1177  	buf := make([]wire.OutPoint, 0, 64)
  1178  	defer func() {
  1179  		if len(buf) > 0 && err == nil {
  1180  			err = n.LoadTxFilter(ctx, false, nil, buf)
  1181  			if err != nil {
  1182  				return
  1183  			}
  1184  		}
  1185  	}()
  1186  	watchOutPoint := func(op *wire.OutPoint) (err error) {
  1187  		buf = append(buf, *op)
  1188  		if len(buf) == cap(buf) {
  1189  			err = n.LoadTxFilter(ctx, false, nil, buf)
  1190  			buf = buf[:0]
  1191  		}
  1192  		return
  1193  	}
  1194  
  1195  	hdAddrCount, err := w.watchHDAddrs(ctx, true, n)
  1196  	if err != nil {
  1197  		return err
  1198  	}
  1199  	log.Infof("Registered for transaction notifications for %v HD address(es)", hdAddrCount)
  1200  
  1201  	// Watch individually-imported addresses (which must each be read out of
  1202  	// the DB).
  1203  	abuf := make([]stdaddr.Address, 0, 256)
  1204  	var importedAddrCount int
  1205  	watchAddress := func(a udb.ManagedAddress) error {
  1206  		addr := a.Address()
  1207  		abuf = append(abuf, addr)
  1208  		if len(abuf) == cap(abuf) {
  1209  			importedAddrCount += len(abuf)
  1210  			err := n.LoadTxFilter(ctx, false, abuf, nil)
  1211  			abuf = abuf[:0]
  1212  			return err
  1213  		}
  1214  		return nil
  1215  	}
  1216  	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1217  		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  1218  		return w.manager.ForEachAccountAddress(addrmgrNs, udb.ImportedAddrAccount, watchAddress)
  1219  	})
  1220  	if err != nil {
  1221  		return err
  1222  	}
  1223  	if len(abuf) != 0 {
  1224  		importedAddrCount += len(abuf)
  1225  		err := n.LoadTxFilter(ctx, false, abuf, nil)
  1226  		if err != nil {
  1227  			return err
  1228  		}
  1229  	}
  1230  	if importedAddrCount > 0 {
  1231  		log.Infof("Registered for transaction notifications for %v imported address(es)", importedAddrCount)
  1232  	}
  1233  
  1234  	defer w.lockedOutpointMu.Unlock()
  1235  	w.lockedOutpointMu.Lock()
  1236  	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1237  		err := w.txStore.ForEachUnspentOutpoint(dbtx, watchOutPoint)
  1238  		if err != nil {
  1239  			return err
  1240  		}
  1241  
  1242  		_, height := w.txStore.MainChainTip(dbtx)
  1243  		tickets, err := w.txStore.UnspentTickets(dbtx, height, true)
  1244  		if err != nil {
  1245  			return err
  1246  		}
  1247  		for i := range tickets {
  1248  			op := wire.OutPoint{
  1249  				Hash:  tickets[i],
  1250  				Index: 0,
  1251  				Tree:  wire.TxTreeStake,
  1252  			}
  1253  			err = watchOutPoint(&op)
  1254  			if err != nil {
  1255  				return err
  1256  			}
  1257  		}
  1258  		return nil
  1259  	})
  1260  	if err != nil {
  1261  		return errors.E(op, err)
  1262  	}
  1263  	log.Infof("Registered for transaction notifications for all relevant outputs")
  1264  
  1265  	return nil
  1266  }
  1267  
  1268  // CommittedTickets takes a list of tickets and returns a filtered list of
  1269  // tickets that are controlled by this wallet.
  1270  func (w *Wallet) CommittedTickets(ctx context.Context, tickets []*chainhash.Hash) ([]*chainhash.Hash, []stdaddr.Address, error) {
  1271  	const op errors.Op = "wallet.CommittedTickets"
  1272  	hashes := make([]*chainhash.Hash, 0, len(tickets))
  1273  	addresses := make([]stdaddr.Address, 0, len(tickets))
  1274  	// Verify we own this ticket
  1275  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1276  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  1277  		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  1278  		for _, v := range tickets {
  1279  			// Make sure ticket exists
  1280  			tx, err := w.txStore.Tx(txmgrNs, v)
  1281  			if err != nil {
  1282  				log.Debugf("%v", err)
  1283  				continue
  1284  			}
  1285  			if !stake.IsSStx(tx) {
  1286  				continue
  1287  			}
  1288  
  1289  			// Commitment outputs are at alternating output
  1290  			// indexes, starting at 1.
  1291  			var bestAddr stdaddr.StakeAddress
  1292  			var bestAmount dcrutil.Amount
  1293  
  1294  			for i := 1; i < len(tx.TxOut); i += 2 {
  1295  				scr := tx.TxOut[i].PkScript
  1296  				addr, err := stake.AddrFromSStxPkScrCommitment(scr,
  1297  					w.chainParams)
  1298  				if err != nil {
  1299  					log.Debugf("%v", err)
  1300  					break
  1301  				}
  1302  				if _, ok := addr.(*stdaddr.AddressPubKeyHashEcdsaSecp256k1V0); !ok {
  1303  					log.Tracef("Skipping commitment at "+
  1304  						"index %v: address is not "+
  1305  						"P2PKH", i)
  1306  					continue
  1307  				}
  1308  				amt, err := stake.AmountFromSStxPkScrCommitment(scr)
  1309  				if err != nil {
  1310  					log.Debugf("%v", err)
  1311  					break
  1312  				}
  1313  				if amt > bestAmount {
  1314  					bestAddr = addr
  1315  					bestAmount = amt
  1316  				}
  1317  			}
  1318  
  1319  			if bestAddr == nil {
  1320  				log.Debugf("no best address")
  1321  				continue
  1322  			}
  1323  
  1324  			// We only support Hash160 addresses.
  1325  			var hash160 []byte
  1326  			switch bestAddr := bestAddr.(type) {
  1327  			case stdaddr.Hash160er:
  1328  				hash160 = bestAddr.Hash160()[:]
  1329  			}
  1330  			if hash160 == nil || !w.manager.ExistsHash160(
  1331  				addrmgrNs, hash160) {
  1332  				log.Debugf("not our address: hash160=%x", hash160)
  1333  				continue
  1334  			}
  1335  			ticketHash := tx.TxHash()
  1336  			log.Tracef("Ticket purchase %v: best commitment"+
  1337  				" address %v amount %v", &ticketHash, bestAddr,
  1338  				bestAmount)
  1339  
  1340  			hashes = append(hashes, v)
  1341  			addresses = append(addresses, bestAddr)
  1342  		}
  1343  		return nil
  1344  	})
  1345  	if err != nil {
  1346  		return nil, nil, errors.E(op, err)
  1347  	}
  1348  
  1349  	return hashes, addresses, nil
  1350  }
  1351  
  1352  // fetchMissingCFilters checks to see if there are any missing committed filters
  1353  // then, if so requests them from the given peer.  The progress channel, if
  1354  // non-nil, is sent the first height and last height of the range of filters
  1355  // that were retrieved in that peer request.
  1356  func (w *Wallet) fetchMissingCFilters(ctx context.Context, p Peer, progress chan<- MissingCFilterProgress) error {
  1357  	var missing bool
  1358  	var height int32
  1359  
  1360  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1361  		var err error
  1362  		missing = w.txStore.IsMissingMainChainCFilters(dbtx)
  1363  		if missing {
  1364  			height, err = w.txStore.MissingCFiltersHeight(dbtx)
  1365  		}
  1366  		return err
  1367  	})
  1368  	if err != nil {
  1369  		return err
  1370  	}
  1371  	if !missing {
  1372  		return nil
  1373  	}
  1374  
  1375  	const span = 2000
  1376  	storage := make([]chainhash.Hash, span)
  1377  	storagePtrs := make([]*chainhash.Hash, span)
  1378  	for i := range storage {
  1379  		storagePtrs[i] = &storage[i]
  1380  	}
  1381  	for {
  1382  		if err := ctx.Err(); err != nil {
  1383  			return err
  1384  		}
  1385  		var hashes []chainhash.Hash
  1386  		var get []*chainhash.Hash
  1387  		var cont bool
  1388  		err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1389  			ns := dbtx.ReadBucket(wtxmgrNamespaceKey)
  1390  			var err error
  1391  			missing = w.txStore.IsMissingMainChainCFilters(dbtx)
  1392  			if !missing {
  1393  				return nil
  1394  			}
  1395  			hash, err := w.txStore.GetMainChainBlockHashForHeight(ns, height)
  1396  			if err != nil {
  1397  				return err
  1398  			}
  1399  			_, _, err = w.txStore.CFilterV2(dbtx, &hash)
  1400  			if err == nil {
  1401  				height += span
  1402  				cont = true
  1403  				return nil
  1404  			}
  1405  			storage = storage[:cap(storage)]
  1406  			hashes, err = w.txStore.GetMainChainBlockHashes(ns, &hash, true, storage)
  1407  			if err != nil {
  1408  				return err
  1409  			}
  1410  			if len(hashes) == 0 {
  1411  				const op errors.Op = "udb.GetMainChainBlockHashes"
  1412  				return errors.E(op, errors.Bug, "expected over 0 results")
  1413  			}
  1414  			get = storagePtrs[:len(hashes)]
  1415  			if get[0] != &hashes[0] {
  1416  				const op errors.Op = "udb.GetMainChainBlockHashes"
  1417  				return errors.E(op, errors.Bug, "unexpected slice reallocation")
  1418  			}
  1419  			return nil
  1420  		})
  1421  		if err != nil {
  1422  			return err
  1423  		}
  1424  		if !missing {
  1425  			return nil
  1426  		}
  1427  		if cont {
  1428  			continue
  1429  		}
  1430  
  1431  		filterData, err := p.CFiltersV2(ctx, get)
  1432  		if err != nil {
  1433  			return err
  1434  		}
  1435  
  1436  		// Validate the newly received filters against the previously
  1437  		// stored block header using the corresponding inclusion proof
  1438  		// returned by the peer.
  1439  		filters := make([]*gcs2.FilterV2, len(filterData))
  1440  		err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1441  			for i, cf := range filterData {
  1442  				header, err := w.txStore.GetBlockHeader(dbtx, get[i])
  1443  				if err != nil {
  1444  					return err
  1445  				}
  1446  				err = validate.CFilterV2HeaderCommitment(w.chainParams.Net,
  1447  					header, cf.Filter, cf.ProofIndex, cf.Proof)
  1448  				if err != nil {
  1449  					return err
  1450  				}
  1451  
  1452  				filters[i] = cf.Filter
  1453  			}
  1454  			return nil
  1455  		})
  1456  		if err != nil {
  1457  			return err
  1458  		}
  1459  
  1460  		err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  1461  			_, _, err := w.txStore.CFilterV2(dbtx, get[len(get)-1])
  1462  			if err == nil {
  1463  				cont = true
  1464  				return nil
  1465  			}
  1466  			return w.txStore.InsertMissingCFilters(dbtx, get, filters)
  1467  		})
  1468  		if err != nil {
  1469  			return err
  1470  		}
  1471  		if cont {
  1472  			continue
  1473  		}
  1474  
  1475  		if progress != nil {
  1476  			progress <- MissingCFilterProgress{BlockHeightStart: height, BlockHeightEnd: height + span - 1}
  1477  		}
  1478  		log.Infof("Fetched cfilters for blocks %v-%v", height, height+span-1)
  1479  	}
  1480  }
  1481  
  1482  // FetchMissingCFilters records any missing compact filters for main chain
  1483  // blocks.  A database upgrade requires all compact filters to be recorded for
  1484  // the main chain before any more blocks may be attached, but this information
  1485  // must be fetched at runtime after the upgrade as it is not already known at
  1486  // the time of upgrade.
  1487  func (w *Wallet) FetchMissingCFilters(ctx context.Context, p Peer) error {
  1488  	const opf = "wallet.FetchMissingCFilters(%v)"
  1489  
  1490  	err := w.fetchMissingCFilters(ctx, p, nil)
  1491  	if err != nil {
  1492  		op := errors.Opf(opf, p)
  1493  		return errors.E(op, err)
  1494  	}
  1495  	return nil
  1496  }
  1497  
  1498  // MissingCFilterProgress records the first and last height of the progress
  1499  // that was received and any errors that were received during the fetching.
  1500  type MissingCFilterProgress struct {
  1501  	Err              error
  1502  	BlockHeightStart int32
  1503  	BlockHeightEnd   int32
  1504  }
  1505  
  1506  // FetchMissingCFiltersWithProgress records any missing compact filters for main chain
  1507  // blocks.  A database upgrade requires all compact filters to be recorded for
  1508  // the main chain before any more blocks may be attached, but this information
  1509  // must be fetched at runtime after the upgrade as it is not already known at
  1510  // the time of upgrade.  This function reports to a channel with any progress
  1511  // that may have seen.
  1512  func (w *Wallet) FetchMissingCFiltersWithProgress(ctx context.Context, p Peer, progress chan<- MissingCFilterProgress) {
  1513  	const opf = "wallet.FetchMissingCFilters(%v)"
  1514  
  1515  	defer close(progress)
  1516  
  1517  	err := w.fetchMissingCFilters(ctx, p, progress)
  1518  	if err != nil {
  1519  		op := errors.Opf(opf, p)
  1520  		progress <- MissingCFilterProgress{Err: errors.E(op, err)}
  1521  	}
  1522  }
  1523  
  1524  // log2 calculates an integer approximation of log2(x).  This is used to
  1525  // approximate the cap to use when allocating memory for the block locators.
  1526  func log2(x int) int {
  1527  	res := 0
  1528  	for x != 0 {
  1529  		x /= 2
  1530  		res++
  1531  	}
  1532  	return res
  1533  }
  1534  
  1535  // BlockLocators returns block locators, suitable for use in a getheaders wire
  1536  // message or dcrd JSON-RPC request, for the blocks in sidechain and saved in
  1537  // the wallet's main chain.  For memory and lookup efficiency, many older hashes
  1538  // are skipped, with increasing gaps between included hashes.
  1539  //
  1540  // When sidechain has zero length, locators for only main chain blocks starting
  1541  // from the tip are returned.  Otherwise, locators are created starting with the
  1542  // best (last) block of sidechain and sidechain[0] must be a child of a main
  1543  // chain block (sidechain may not contain orphan blocks).
  1544  func (w *Wallet) BlockLocators(ctx context.Context, sidechain []*BlockNode) ([]*chainhash.Hash, error) {
  1545  	const op errors.Op = "wallet.BlockLocators"
  1546  	var locators []*chainhash.Hash
  1547  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1548  		var err error
  1549  		locators, err = w.blockLocators(dbtx, sidechain)
  1550  		return err
  1551  	})
  1552  	if err != nil {
  1553  		return nil, errors.E(op, err)
  1554  	}
  1555  	return locators, nil
  1556  }
  1557  
  1558  func (w *Wallet) blockLocators(dbtx walletdb.ReadTx, sidechain []*BlockNode) ([]*chainhash.Hash, error) {
  1559  	ns := dbtx.ReadBucket(wtxmgrNamespaceKey)
  1560  	var hash chainhash.Hash
  1561  	var height int32
  1562  	if len(sidechain) == 0 {
  1563  		hash, height = w.txStore.MainChainTip(dbtx)
  1564  	} else {
  1565  		n := sidechain[len(sidechain)-1]
  1566  		hash = *n.Hash
  1567  		height = int32(n.Header.Height)
  1568  	}
  1569  
  1570  	locators := make([]*chainhash.Hash, 1, 10+log2(int(height)))
  1571  	locators[0] = &hash
  1572  
  1573  	var step int32 = 1
  1574  	for height >= 0 {
  1575  		if len(sidechain) > 0 && height-int32(sidechain[0].Header.Height) >= 0 {
  1576  			n := sidechain[height-int32(sidechain[0].Header.Height)]
  1577  			hash := n.Hash
  1578  			locators = append(locators, hash)
  1579  		} else {
  1580  			hash, err := w.txStore.GetMainChainBlockHashForHeight(ns, height)
  1581  			if err != nil {
  1582  				return nil, err
  1583  			}
  1584  			locators = append(locators, &hash)
  1585  		}
  1586  
  1587  		height -= step
  1588  
  1589  		if len(locators) > 10 {
  1590  			step *= 2
  1591  		}
  1592  	}
  1593  
  1594  	return locators, nil
  1595  }
  1596  
  1597  // Consolidate consolidates as many UTXOs as are passed in the inputs argument.
  1598  // If that many UTXOs can not be found, it will use the maximum it finds. This
  1599  // will only compress UTXOs in the default account
  1600  func (w *Wallet) Consolidate(ctx context.Context, inputs int, account uint32, address stdaddr.Address) (*chainhash.Hash, error) {
  1601  	return w.compressWallet(ctx, "wallet.Consolidate", inputs, account, address)
  1602  }
  1603  
  1604  // CreateMultisigTx creates and signs a multisig transaction.
  1605  func (w *Wallet) CreateMultisigTx(ctx context.Context, account uint32, amount dcrutil.Amount,
  1606  	pubkeys [][]byte, nrequired int8, minconf int32) (*CreatedTx, stdaddr.Address, []byte, error) {
  1607  	return w.txToMultisig(ctx, "wallet.CreateMultisigTx", account, amount, pubkeys, nrequired, minconf)
  1608  }
  1609  
  1610  // PurchaseTicketsRequest describes the parameters for purchasing tickets.
  1611  type PurchaseTicketsRequest struct {
  1612  	Count            int
  1613  	SourceAccount    uint32
  1614  	VotingAddress    stdaddr.StakeAddress
  1615  	MinConf          int32
  1616  	Expiry           int32
  1617  	VotingAccount    uint32 // Used when VotingAddress == nil, or CSPPServer != ""
  1618  	UseVotingAccount bool   // Forces use of supplied voting account.
  1619  	DontSignTx       bool
  1620  
  1621  	// Mixed split buying through CoinShuffle++
  1622  	CSPPServer         string
  1623  	DialCSPPServer     DialFunc
  1624  	MixedAccount       uint32
  1625  	MixedAccountBranch uint32
  1626  	MixedSplitAccount  uint32
  1627  	ChangeAccount      uint32
  1628  
  1629  	// VSP ticket buying; not currently usable with CoinShuffle++.
  1630  	VSPAddress stdaddr.StakeAddress
  1631  	VSPFees    float64
  1632  
  1633  	// VSPServer methods
  1634  	// XXX this should be an interface
  1635  
  1636  	// VSPFeeProcessFunc Process the fee price for the vsp to register a ticket
  1637  	// so we can reserve the amount.
  1638  	VSPFeeProcess func(context.Context) (float64, error)
  1639  	// VSPFeePaymentProcess processes the payment of the vsp fee and returns
  1640  	// the paid fee tx.
  1641  	VSPFeePaymentProcess func(context.Context, *chainhash.Hash, *wire.MsgTx) error
  1642  
  1643  	// extraSplitOutput is an additional transaction output created during
  1644  	// UTXO contention, to be used as the input to pay a VSP fee
  1645  	// transaction, in order that both VSP fees and a single ticket purchase
  1646  	// may be created by spending distinct outputs.  After purchaseTickets
  1647  	// reentry, this output is locked and UTXO selection is only used to
  1648  	// fund the split transaction for a ticket purchase, without reserving
  1649  	// any additional outputs to pay the VSP fee.
  1650  	extraSplitOutput *Input
  1651  }
  1652  
  1653  // PurchaseTicketsResponse describes the response for purchasing tickets request.
  1654  type PurchaseTicketsResponse struct {
  1655  	TicketHashes []*chainhash.Hash
  1656  	Tickets      []*wire.MsgTx
  1657  	SplitTx      *wire.MsgTx
  1658  }
  1659  
  1660  // PurchaseTickets purchases tickets, returning purchase tickets response.
  1661  func (w *Wallet) PurchaseTickets(ctx context.Context, n NetworkBackend,
  1662  	req *PurchaseTicketsRequest) (*PurchaseTicketsResponse, error) {
  1663  
  1664  	const op errors.Op = "wallet.PurchaseTickets"
  1665  
  1666  	resp, err := w.purchaseTickets(ctx, op, n, req)
  1667  	if err == nil || !errors.Is(err, errVSPFeeRequiresUTXOSplit) || req.DontSignTx {
  1668  		return resp, err
  1669  	}
  1670  
  1671  	// Do not attempt to split utxos for a fee payment when spending from
  1672  	// the mixed account.  This error is rather unlikely anyways, as mixed
  1673  	// accounts probably have very many outputs.
  1674  	if req.CSPPServer != "" && req.MixedAccount == req.SourceAccount {
  1675  		return nil, errors.E(op, errors.InsufficientBalance)
  1676  	}
  1677  
  1678  	feePercent, err := req.VSPFeeProcess(ctx)
  1679  	if err != nil {
  1680  		return nil, err
  1681  	}
  1682  	sdiff, err := w.NextStakeDifficulty(ctx)
  1683  	if err != nil {
  1684  		return nil, err
  1685  	}
  1686  	_, height := w.MainChainTip(ctx)
  1687  	dcp0010Active := true
  1688  	switch n := n.(type) {
  1689  	case *dcrd.RPC:
  1690  		dcp0010Active, err = deployments.DCP0010Active(ctx,
  1691  			height, w.chainParams, n)
  1692  		if err != nil {
  1693  			return nil, err
  1694  		}
  1695  	}
  1696  	relayFee := w.RelayFee()
  1697  	vspFee := txrules.StakePoolTicketFee(sdiff, relayFee, height,
  1698  		feePercent, w.chainParams, dcp0010Active)
  1699  	a := &authorTx{
  1700  		outputs:            make([]*wire.TxOut, 0, 2),
  1701  		account:            req.SourceAccount,
  1702  		changeAccount:      req.SourceAccount, // safe-ish; this is not mixed.
  1703  		minconf:            req.MinConf,
  1704  		randomizeChangeIdx: true,
  1705  		txFee:              relayFee,
  1706  	}
  1707  	addr, err := w.NewInternalAddress(ctx, req.SourceAccount)
  1708  	if err != nil {
  1709  		return nil, err
  1710  	}
  1711  	version, script := addr.(Address).PaymentScript()
  1712  	a.outputs = append(a.outputs, &wire.TxOut{Version: version, PkScript: script})
  1713  	txsize := txsizes.EstimateSerializeSize([]int{txsizes.RedeemP2PKHInputSize},
  1714  		a.outputs, txsizes.P2PKHPkScriptSize)
  1715  	txfee := txrules.FeeForSerializeSize(relayFee, txsize)
  1716  	a.outputs[0].Value = int64(vspFee + 2*txfee)
  1717  	err = w.authorTx(ctx, op, a)
  1718  	if err != nil {
  1719  		return nil, err
  1720  	}
  1721  	err = w.recordAuthoredTx(ctx, op, a)
  1722  	if err != nil {
  1723  		return nil, err
  1724  	}
  1725  	err = w.publishAndWatch(ctx, op, nil, a.atx.Tx, a.watch)
  1726  	if err != nil {
  1727  		return nil, err
  1728  	}
  1729  	err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  1730  		for _, update := range a.changeSourceUpdates {
  1731  			err := update(dbtx)
  1732  			if err != nil {
  1733  				return err
  1734  			}
  1735  		}
  1736  		return nil
  1737  	})
  1738  	if err != nil {
  1739  		return nil, err
  1740  	}
  1741  
  1742  	req.MinConf = 0
  1743  	req.Count = 1
  1744  	var index uint32 = 0
  1745  	if a.atx.ChangeIndex == 0 {
  1746  		index = 1
  1747  	}
  1748  	req.extraSplitOutput = &Input{
  1749  		OutPoint: wire.OutPoint{
  1750  			Hash:  a.atx.Tx.TxHash(),
  1751  			Index: index,
  1752  			Tree:  0,
  1753  		},
  1754  		PrevOut: *a.atx.Tx.TxOut[index],
  1755  	}
  1756  	return w.purchaseTickets(ctx, op, n, req)
  1757  }
  1758  
  1759  // Unlock unlocks the wallet, allowing access to private keys and secret scripts.
  1760  // An unlocked wallet will be locked before returning with a Passphrase error if
  1761  // the passphrase is incorrect.
  1762  // If the wallet is currently unlocked without any timeout, timeout is ignored
  1763  // and read in a background goroutine to avoid blocking sends.
  1764  // If the wallet is locked and a non-nil timeout is provided, the wallet will be
  1765  // locked in the background after reading from the channel.
  1766  // If the wallet is already unlocked with a previous timeout, the new timeout
  1767  // replaces the prior.
  1768  func (w *Wallet) Unlock(ctx context.Context, passphrase []byte, timeout <-chan time.Time) error {
  1769  	const op errors.Op = "wallet.Unlock"
  1770  
  1771  	w.passphraseUsedMu.RLock()
  1772  	wasLocked := w.manager.IsLocked()
  1773  	err := w.manager.UnlockedWithPassphrase(passphrase)
  1774  	w.passphraseUsedMu.RUnlock()
  1775  	switch {
  1776  	case errors.Is(err, errors.WatchingOnly):
  1777  		return errors.E(op, err)
  1778  	case errors.Is(err, errors.Passphrase):
  1779  		w.Lock()
  1780  		if !wasLocked {
  1781  			log.Info("The wallet has been locked due to an incorrect passphrase.")
  1782  		}
  1783  		return errors.E(op, err)
  1784  	default:
  1785  		return errors.E(op, err)
  1786  	case errors.Is(err, errors.Locked):
  1787  		defer w.passphraseUsedMu.RUnlock()
  1788  		w.passphraseUsedMu.RLock()
  1789  		err = walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  1790  			addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
  1791  			return w.manager.Unlock(addrmgrNs, passphrase)
  1792  		})
  1793  		if err != nil {
  1794  			return errors.E(op, errors.Passphrase, err)
  1795  		}
  1796  	case err == nil:
  1797  	}
  1798  	w.replacePassphraseTimeout(wasLocked, timeout)
  1799  	return nil
  1800  }
  1801  
  1802  // SetAccountPassphrase individually-encrypts or changes the passphrase for
  1803  // account private keys.
  1804  //
  1805  // If the passphrase has zero length, the private keys are re-encrypted with the
  1806  // manager's global passphrase.
  1807  func (w *Wallet) SetAccountPassphrase(ctx context.Context, account uint32, passphrase []byte) error {
  1808  	return walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  1809  		return w.manager.SetAccountPassphrase(dbtx, account, passphrase)
  1810  	})
  1811  }
  1812  
  1813  // UnlockAccount decrypts a uniquely-encrypted account's private keys.
  1814  func (w *Wallet) UnlockAccount(ctx context.Context, account uint32, passphrase []byte) error {
  1815  	return walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1816  		return w.manager.UnlockAccount(dbtx, account, passphrase)
  1817  	})
  1818  }
  1819  
  1820  // LockAccount locks an individually-encrypted account by removing private key
  1821  // access until unlocked again.
  1822  func (w *Wallet) LockAccount(ctx context.Context, account uint32) error {
  1823  	return walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1824  		return w.manager.LockAccount(dbtx, account)
  1825  	})
  1826  }
  1827  
  1828  // AccountUnlocked returns whether an individually-encrypted account is unlocked.
  1829  func (w *Wallet) AccountUnlocked(ctx context.Context, account uint32) (bool, error) {
  1830  	const op errors.Op = "wallet.AccountUnlocked"
  1831  	var unlocked bool
  1832  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1833  		var encrypted bool
  1834  		encrypted, unlocked = w.manager.AccountHasPassphrase(dbtx, account)
  1835  		if !encrypted {
  1836  			const s = "account is not individually encrypted"
  1837  			return errors.E(errors.Invalid, s)
  1838  		}
  1839  		return nil
  1840  	})
  1841  	if err != nil {
  1842  		return false, errors.E(op, err)
  1843  	}
  1844  	return unlocked, nil
  1845  }
  1846  
  1847  func (w *Wallet) AccountHasPassphrase(ctx context.Context, account uint32) (bool, error) {
  1848  	const op errors.Op = "wallet.AccountHasPassphrase"
  1849  	var encrypted bool
  1850  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1851  		encrypted, _ = w.manager.AccountHasPassphrase(dbtx, account)
  1852  		return nil
  1853  	})
  1854  	if err != nil {
  1855  		return false, errors.E(op, err)
  1856  	}
  1857  	return encrypted, nil
  1858  }
  1859  
  1860  func (w *Wallet) replacePassphraseTimeout(wasLocked bool, newTimeout <-chan time.Time) {
  1861  	defer w.passphraseTimeoutMu.Unlock()
  1862  	w.passphraseTimeoutMu.Lock()
  1863  	hadTimeout := w.passphraseTimeoutCancel != nil
  1864  	if !wasLocked && !hadTimeout && newTimeout != nil {
  1865  		go func() { <-newTimeout }()
  1866  	} else {
  1867  		oldCancel := w.passphraseTimeoutCancel
  1868  		var newCancel chan struct{}
  1869  		if newTimeout != nil {
  1870  			newCancel = make(chan struct{}, 1)
  1871  		}
  1872  		w.passphraseTimeoutCancel = newCancel
  1873  
  1874  		if oldCancel != nil {
  1875  			oldCancel <- struct{}{}
  1876  		}
  1877  		if newTimeout != nil {
  1878  			go func() {
  1879  				select {
  1880  				case <-newTimeout:
  1881  					w.Lock()
  1882  					log.Info("The wallet has been locked due to timeout.")
  1883  				case <-newCancel:
  1884  					<-newTimeout
  1885  				}
  1886  			}()
  1887  		}
  1888  	}
  1889  	switch {
  1890  	case (wasLocked || hadTimeout) && newTimeout == nil:
  1891  		log.Info("The wallet has been unlocked without a time limit")
  1892  	case (wasLocked || !hadTimeout) && newTimeout != nil:
  1893  		log.Info("The wallet has been temporarily unlocked")
  1894  	}
  1895  }
  1896  
  1897  // Lock locks the wallet's address manager.
  1898  func (w *Wallet) Lock() {
  1899  	w.passphraseUsedMu.Lock()
  1900  	w.passphraseTimeoutMu.Lock()
  1901  	_ = w.manager.Lock()
  1902  	w.passphraseTimeoutCancel = nil
  1903  	w.passphraseTimeoutMu.Unlock()
  1904  	w.passphraseUsedMu.Unlock()
  1905  }
  1906  
  1907  // Locked returns whether the account manager for a wallet is locked.
  1908  func (w *Wallet) Locked() bool {
  1909  	return w.manager.IsLocked()
  1910  }
  1911  
  1912  // Unlocked returns whether the account manager for a wallet is unlocked.
  1913  func (w *Wallet) Unlocked() bool {
  1914  	return !w.Locked()
  1915  }
  1916  
  1917  // WatchingOnly returns whether the wallet only contains public keys.
  1918  func (w *Wallet) WatchingOnly() bool {
  1919  	return w.manager.WatchingOnly()
  1920  }
  1921  
  1922  // ChangePrivatePassphrase attempts to change the passphrase for a wallet from
  1923  // old to new.  Changing the passphrase is synchronized with all other address
  1924  // manager locking and unlocking.  The lock state will be the same as it was
  1925  // before the password change.
  1926  func (w *Wallet) ChangePrivatePassphrase(ctx context.Context, old, new []byte) error {
  1927  	const op errors.Op = "wallet.ChangePrivatePassphrase"
  1928  	defer w.passphraseUsedMu.Unlock()
  1929  	w.passphraseUsedMu.Lock()
  1930  	err := walletdb.Update(ctx, w.db, func(tx walletdb.ReadWriteTx) error {
  1931  		addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
  1932  		return w.manager.ChangePassphrase(addrmgrNs, old, new, true)
  1933  	})
  1934  	if err != nil {
  1935  		return errors.E(op, err)
  1936  	}
  1937  	return nil
  1938  }
  1939  
  1940  // ChangePublicPassphrase modifies the public passphrase of the wallet.
  1941  func (w *Wallet) ChangePublicPassphrase(ctx context.Context, old, new []byte) error {
  1942  	const op errors.Op = "wallet.ChangePublicPassphrase"
  1943  	err := walletdb.Update(ctx, w.db, func(tx walletdb.ReadWriteTx) error {
  1944  		addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
  1945  		return w.manager.ChangePassphrase(addrmgrNs, old, new, false)
  1946  	})
  1947  	if err != nil {
  1948  		return errors.E(op, err)
  1949  	}
  1950  	return nil
  1951  }
  1952  
  1953  // Balances describes a breakdown of an account's balances in various
  1954  // categories.
  1955  type Balances struct {
  1956  	Account                 uint32
  1957  	ImmatureCoinbaseRewards dcrutil.Amount
  1958  	ImmatureStakeGeneration dcrutil.Amount
  1959  	LockedByTickets         dcrutil.Amount
  1960  	Spendable               dcrutil.Amount
  1961  	Total                   dcrutil.Amount
  1962  	VotingAuthority         dcrutil.Amount
  1963  	Unconfirmed             dcrutil.Amount
  1964  }
  1965  
  1966  // AccountBalance returns the balance breakdown for a single account.
  1967  func (w *Wallet) AccountBalance(ctx context.Context, account uint32, confirms int32) (Balances, error) {
  1968  	const op errors.Op = "wallet.CalculateAccountBalance"
  1969  	var balance Balances
  1970  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1971  		var err error
  1972  		balance, err = w.txStore.AccountBalance(dbtx,
  1973  			confirms, account)
  1974  		return err
  1975  	})
  1976  	if err != nil {
  1977  		return balance, errors.E(op, err)
  1978  	}
  1979  	return balance, nil
  1980  }
  1981  
  1982  // AccountBalances returns the balance breakdowns for a each account.
  1983  func (w *Wallet) AccountBalances(ctx context.Context, confirms int32) ([]Balances, error) {
  1984  	const op errors.Op = "wallet.CalculateAccountBalances"
  1985  	var balances []Balances
  1986  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1987  		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  1988  		return w.manager.ForEachAccount(addrmgrNs, func(acct uint32) error {
  1989  			balance, err := w.txStore.AccountBalance(dbtx,
  1990  				confirms, acct)
  1991  			if err != nil {
  1992  				return err
  1993  			}
  1994  			balances = append(balances, balance)
  1995  			return nil
  1996  		})
  1997  	})
  1998  	if err != nil {
  1999  		return nil, errors.E(op, err)
  2000  	}
  2001  	return balances, nil
  2002  }
  2003  
  2004  // CurrentAddress gets the most recently requested payment address from a wallet.
  2005  // If the address has already been used (there is at least one transaction
  2006  // spending to it in the blockchain or dcrd mempool), the next chained address
  2007  // is returned.
  2008  func (w *Wallet) CurrentAddress(account uint32) (stdaddr.Address, error) {
  2009  	const op errors.Op = "wallet.CurrentAddress"
  2010  	defer w.addressBuffersMu.Unlock()
  2011  	w.addressBuffersMu.Lock()
  2012  
  2013  	data, ok := w.addressBuffers[account]
  2014  	if !ok {
  2015  		return nil, errors.E(op, errors.NotExist, errors.Errorf("no account %d", account))
  2016  	}
  2017  	buf := &data.albExternal
  2018  
  2019  	childIndex := buf.lastUsed + 1 + buf.cursor
  2020  	child, err := buf.branchXpub.Child(childIndex)
  2021  	if err != nil {
  2022  		return nil, errors.E(op, err)
  2023  	}
  2024  	addr, err := compat.HD2Address(child, w.chainParams)
  2025  	if err != nil {
  2026  		return nil, errors.E(op, err)
  2027  	}
  2028  	return addr, nil
  2029  }
  2030  
  2031  // SignHashes returns signatures of signed transaction hashes using an
  2032  // address' associated private key.
  2033  func (w *Wallet) SignHashes(ctx context.Context, hashes [][]byte, addr stdaddr.Address) ([][]byte,
  2034  	[]byte, error) {
  2035  
  2036  	var privKey *secp256k1.PrivateKey
  2037  	var done func()
  2038  	defer func() {
  2039  		if done != nil {
  2040  			done()
  2041  		}
  2042  	}()
  2043  	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  2044  		addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
  2045  		var err error
  2046  		privKey, done, err = w.manager.PrivateKey(addrmgrNs, addr)
  2047  		return err
  2048  	})
  2049  	if err != nil {
  2050  		return nil, nil, err
  2051  	}
  2052  
  2053  	signatures := make([][]byte, len(hashes))
  2054  	for i, hash := range hashes {
  2055  		sig := ecdsa.Sign(privKey, hash)
  2056  		signatures[i] = sig.Serialize()
  2057  	}
  2058  
  2059  	return signatures, privKey.PubKey().SerializeCompressed(), nil
  2060  }
  2061  
  2062  // SignMessage returns the signature of a signed message using an address'
  2063  // associated private key.
  2064  func (w *Wallet) SignMessage(ctx context.Context, msg string, addr stdaddr.Address) (sig []byte, err error) {
  2065  	const op errors.Op = "wallet.SignMessage"
  2066  	var buf bytes.Buffer
  2067  	wire.WriteVarString(&buf, 0, "Decred Signed Message:\n")
  2068  	wire.WriteVarString(&buf, 0, msg)
  2069  	messageHash := chainhash.HashB(buf.Bytes())
  2070  	var privKey *secp256k1.PrivateKey
  2071  	var done func()
  2072  	defer func() {
  2073  		if done != nil {
  2074  			done()
  2075  		}
  2076  	}()
  2077  	err = walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  2078  		addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
  2079  		var err error
  2080  		privKey, done, err = w.manager.PrivateKey(addrmgrNs, addr)
  2081  		return err
  2082  	})
  2083  	if err != nil {
  2084  		return nil, errors.E(op, err)
  2085  	}
  2086  	sig = ecdsa.SignCompact(privKey, messageHash, true)
  2087  	return sig, nil
  2088  }
  2089  
  2090  // VerifyMessage verifies that sig is a valid signature of msg and was created
  2091  // using the secp256k1 private key for addr.
  2092  func VerifyMessage(msg string, addr stdaddr.Address, sig []byte, params stdaddr.AddressParams) (bool, error) {
  2093  	const op errors.Op = "wallet.VerifyMessage"
  2094  	// Validate the signature - this just shows that it was valid for any pubkey
  2095  	// at all. Whether the pubkey matches is checked below.
  2096  	var buf bytes.Buffer
  2097  	wire.WriteVarString(&buf, 0, "Decred Signed Message:\n")
  2098  	wire.WriteVarString(&buf, 0, msg)
  2099  	expectedMessageHash := chainhash.HashB(buf.Bytes())
  2100  	pk, wasCompressed, err := ecdsa.RecoverCompact(sig, expectedMessageHash)
  2101  	if err != nil {
  2102  		return false, errors.E(op, err)
  2103  	}
  2104  
  2105  	// Reconstruct the pubkey hash address from the recovered pubkey.
  2106  	var pkHash []byte
  2107  	if wasCompressed {
  2108  		pkHash = stdaddr.Hash160(pk.SerializeCompressed())
  2109  	} else {
  2110  		pkHash = stdaddr.Hash160(pk.SerializeUncompressed())
  2111  	}
  2112  	address, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pkHash, params)
  2113  	if err != nil {
  2114  		return false, errors.E(op, err)
  2115  	}
  2116  
  2117  	// Return whether addresses match.
  2118  	return address.String() == addr.String(), nil
  2119  }
  2120  
  2121  // HaveAddress returns whether the wallet is the owner of the address a.
  2122  func (w *Wallet) HaveAddress(ctx context.Context, a stdaddr.Address) (bool, error) {
  2123  	const op errors.Op = "wallet.HaveAddress"
  2124  	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  2125  		addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
  2126  		_, err := w.manager.Address(addrmgrNs, a)
  2127  		return err
  2128  	})
  2129  	if err != nil {
  2130  		if errors.Is(err, errors.NotExist) {
  2131  			return false, nil
  2132  		}
  2133  		return false, errors.E(op, err)
  2134  	}
  2135  	return true, nil
  2136  }
  2137  
  2138  // AccountNumber returns the account number for an account name.
  2139  func (w *Wallet) AccountNumber(ctx context.Context, accountName string) (uint32, error) {
  2140  	const op errors.Op = "wallet.AccountNumber"
  2141  	var account uint32
  2142  	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  2143  		addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
  2144  		var err error
  2145  		account, err = w.manager.LookupAccount(addrmgrNs, accountName)
  2146  		return err
  2147  	})
  2148  	if err != nil {
  2149  		return 0, errors.E(op, err)
  2150  	}
  2151  	return account, nil
  2152  }
  2153  
  2154  // AccountName returns the name of an account.
  2155  func (w *Wallet) AccountName(ctx context.Context, accountNumber uint32) (string, error) {
  2156  	const op errors.Op = "wallet.AccountName"
  2157  	var accountName string
  2158  	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  2159  		addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
  2160  		var err error
  2161  		accountName, err = w.manager.AccountName(addrmgrNs, accountNumber)
  2162  		return err
  2163  	})
  2164  	if err != nil {
  2165  		return "", errors.E(op, err)
  2166  	}
  2167  	return accountName, nil
  2168  }
  2169  
  2170  // RenameAccount sets the name for an account number to newName.
  2171  func (w *Wallet) RenameAccount(ctx context.Context, account uint32, newName string) error {
  2172  	const op errors.Op = "wallet.RenameAccount"
  2173  	var props *udb.AccountProperties
  2174  	err := walletdb.Update(ctx, w.db, func(tx walletdb.ReadWriteTx) error {
  2175  		addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
  2176  		err := w.manager.RenameAccount(addrmgrNs, account, newName)
  2177  		if err != nil {
  2178  			return err
  2179  		}
  2180  		props, err = w.manager.AccountProperties(addrmgrNs, account)
  2181  		return err
  2182  	})
  2183  	if err != nil {
  2184  		return errors.E(op, err)
  2185  	}
  2186  	w.NtfnServer.notifyAccountProperties(props)
  2187  	return nil
  2188  }
  2189  
  2190  // NextAccount creates the next account and returns its account number.  The
  2191  // name must be unique to the account.  In order to support automatic seed
  2192  // restoring, new accounts may not be created when all of the previous 100
  2193  // accounts have no transaction history (this is a deviation from the BIP0044
  2194  // spec, which allows no unused account gaps).
  2195  func (w *Wallet) NextAccount(ctx context.Context, name string) (uint32, error) {
  2196  	const op errors.Op = "wallet.NextAccount"
  2197  	maxEmptyAccounts := uint32(w.accountGapLimit)
  2198  	var account uint32
  2199  	var props *udb.AccountProperties
  2200  	var xpub *hdkeychain.ExtendedKey
  2201  	err := walletdb.Update(ctx, w.db, func(tx walletdb.ReadWriteTx) error {
  2202  		addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
  2203  
  2204  		// Ensure that there is transaction history in the last 100 accounts.
  2205  		var err error
  2206  		lastAcct, err := w.manager.LastAccount(addrmgrNs)
  2207  		if err != nil {
  2208  			return err
  2209  		}
  2210  		canCreate := false
  2211  		for i := uint32(0); i < maxEmptyAccounts; i++ {
  2212  			a := lastAcct - i
  2213  			if a == 0 && i < maxEmptyAccounts-1 {
  2214  				// Less than 100 accounts total.
  2215  				canCreate = true
  2216  				break
  2217  			}
  2218  			props, err := w.manager.AccountProperties(addrmgrNs, a)
  2219  			if err != nil {
  2220  				return err
  2221  			}
  2222  			if props.LastUsedExternalIndex != ^uint32(0) || props.LastUsedInternalIndex != ^uint32(0) {
  2223  				canCreate = true
  2224  				break
  2225  			}
  2226  		}
  2227  		if !canCreate {
  2228  			return errors.Errorf("last %d accounts have no transaction history",
  2229  				maxEmptyAccounts)
  2230  		}
  2231  
  2232  		account, err = w.manager.NewAccount(addrmgrNs, name)
  2233  		if err != nil {
  2234  			return err
  2235  		}
  2236  
  2237  		props, err = w.manager.AccountProperties(addrmgrNs, account)
  2238  		if err != nil {
  2239  			return err
  2240  		}
  2241  
  2242  		xpub, err = w.manager.AccountExtendedPubKey(tx, account)
  2243  		if err != nil {
  2244  			return err
  2245  		}
  2246  
  2247  		err = w.manager.SyncAccountToAddrIndex(addrmgrNs, account,
  2248  			w.gapLimit, udb.ExternalBranch)
  2249  		if err != nil {
  2250  			return err
  2251  		}
  2252  		return w.manager.SyncAccountToAddrIndex(addrmgrNs, account,
  2253  			w.gapLimit, udb.InternalBranch)
  2254  	})
  2255  	if err != nil {
  2256  		return 0, errors.E(op, err)
  2257  	}
  2258  
  2259  	extKey, intKey, err := deriveBranches(xpub)
  2260  	if err != nil {
  2261  		return 0, errors.E(op, err)
  2262  	}
  2263  	w.addressBuffersMu.Lock()
  2264  	w.addressBuffers[account] = &bip0044AccountData{
  2265  		xpub:        xpub,
  2266  		albExternal: addressBuffer{branchXpub: extKey, lastUsed: ^uint32(0)},
  2267  		albInternal: addressBuffer{branchXpub: intKey, lastUsed: ^uint32(0)},
  2268  	}
  2269  	w.addressBuffersMu.Unlock()
  2270  
  2271  	if n, err := w.NetworkBackend(); err == nil {
  2272  		errs := make(chan error, 2)
  2273  		for _, branchKey := range []*hdkeychain.ExtendedKey{extKey, intKey} {
  2274  			branchKey := branchKey
  2275  			go func() {
  2276  				addrs, err := deriveChildAddresses(branchKey, 0,
  2277  					w.gapLimit, w.chainParams)
  2278  				if err != nil {
  2279  					errs <- err
  2280  					return
  2281  				}
  2282  				errs <- n.LoadTxFilter(ctx, false, addrs, nil)
  2283  			}()
  2284  		}
  2285  		for i := 0; i < cap(errs); i++ {
  2286  			err := <-errs
  2287  			if err != nil {
  2288  				return 0, errors.E(op, err)
  2289  			}
  2290  		}
  2291  	}
  2292  
  2293  	w.NtfnServer.notifyAccountProperties(props)
  2294  
  2295  	return account, nil
  2296  }
  2297  
  2298  // AccountXpub returns a BIP0044 account's extended public key.
  2299  func (w *Wallet) AccountXpub(ctx context.Context, account uint32) (*hdkeychain.ExtendedKey, error) {
  2300  	const op errors.Op = "wallet.AccountXpub"
  2301  
  2302  	var pubKey *hdkeychain.ExtendedKey
  2303  	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  2304  		var err error
  2305  		pubKey, err = w.manager.AccountExtendedPubKey(tx, account)
  2306  		return err
  2307  	})
  2308  	if err != nil {
  2309  		return nil, errors.E(op, err)
  2310  	}
  2311  
  2312  	return pubKey, nil
  2313  }
  2314  
  2315  // AccountXpriv returns a BIP0044 account's extended private key.  The account
  2316  // must exist and the wallet must be unlocked, otherwise this function fails.
  2317  func (w *Wallet) AccountXpriv(ctx context.Context, account uint32) (*hdkeychain.ExtendedKey, error) {
  2318  	const op errors.Op = "wallet.AccountXpriv"
  2319  
  2320  	var privKey *hdkeychain.ExtendedKey
  2321  	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  2322  		var err error
  2323  		privKey, err = w.manager.AccountExtendedPrivKey(tx, account)
  2324  		return err
  2325  	})
  2326  	if err != nil {
  2327  		return nil, errors.E(op, err)
  2328  	}
  2329  
  2330  	return privKey, nil
  2331  }
  2332  
  2333  // TxBlock returns the hash and height of a block which mines a transaction.
  2334  func (w *Wallet) TxBlock(ctx context.Context, hash *chainhash.Hash) (chainhash.Hash, int32, error) {
  2335  	var blockHash chainhash.Hash
  2336  	var height int32
  2337  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  2338  		ns := dbtx.ReadBucket(wtxmgrNamespaceKey)
  2339  		var err error
  2340  		height, err = w.txStore.TxBlockHeight(dbtx, hash)
  2341  		if err != nil {
  2342  			return err
  2343  		}
  2344  		if height == -1 {
  2345  			return nil
  2346  		}
  2347  		blockHash, err = w.txStore.GetMainChainBlockHashForHeight(ns, height)
  2348  		return err
  2349  	})
  2350  	if err != nil {
  2351  		return blockHash, 0, err
  2352  	}
  2353  	return blockHash, height, nil
  2354  }
  2355  
  2356  // TxConfirms returns the current number of block confirmations a transaction.
  2357  func (w *Wallet) TxConfirms(ctx context.Context, hash *chainhash.Hash) (int32, error) {
  2358  	var tip, txheight int32
  2359  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  2360  		_, tip = w.txStore.MainChainTip(dbtx)
  2361  		var err error
  2362  		txheight, err = w.txStore.TxBlockHeight(dbtx, hash)
  2363  		return err
  2364  	})
  2365  	if err != nil {
  2366  		return 0, err
  2367  	}
  2368  	return confirms(txheight, tip), nil
  2369  }
  2370  
  2371  // GetTransactionsByHashes returns all known transactions identified by a slice
  2372  // of transaction hashes.  It is possible that not all transactions are found,
  2373  // and in this case the known results will be returned along with an inventory
  2374  // vector of all missing transactions and an error with code
  2375  // NotExist.
  2376  func (w *Wallet) GetTransactionsByHashes(ctx context.Context, txHashes []*chainhash.Hash) (txs []*wire.MsgTx, notFound []*wire.InvVect, err error) {
  2377  	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  2378  		ns := dbtx.ReadBucket(wtxmgrNamespaceKey)
  2379  		for _, hash := range txHashes {
  2380  			tx, err := w.txStore.Tx(ns, hash)
  2381  			switch {
  2382  			case err != nil && !errors.Is(err, errors.NotExist):
  2383  				return err
  2384  			case tx == nil:
  2385  				notFound = append(notFound, wire.NewInvVect(wire.InvTypeTx, hash))
  2386  			default:
  2387  				txs = append(txs, tx)
  2388  			}
  2389  		}
  2390  		return nil
  2391  	})
  2392  	if err != nil {
  2393  		return
  2394  	}
  2395  	if len(notFound) != 0 {
  2396  		err = errors.E(errors.NotExist, "transaction(s) not found")
  2397  	}
  2398  	return
  2399  }
  2400  
  2401  // CreditCategory describes the type of wallet transaction output.  The category
  2402  // of "sent transactions" (debits) is always "send", and is not expressed by
  2403  // this type.
  2404  //
  2405  // TODO: This is a requirement of the RPC server and should be moved.
  2406  type CreditCategory byte
  2407  
  2408  // These constants define the possible credit categories.
  2409  const (
  2410  	CreditReceive CreditCategory = iota
  2411  	CreditGenerate
  2412  	CreditImmature
  2413  )
  2414  
  2415  // String returns the category as a string.  This string may be used as the
  2416  // JSON string for categories as part of listtransactions and gettransaction
  2417  // RPC responses.
  2418  func (c CreditCategory) String() string {
  2419  	switch c {
  2420  	case CreditReceive:
  2421  		return "receive"
  2422  	case CreditGenerate:
  2423  		return "generate"
  2424  	case CreditImmature:
  2425  		return "immature"
  2426  	default:
  2427  		return "unknown"
  2428  	}
  2429  }
  2430  
  2431  // recvCategory returns the category of received credit outputs from a
  2432  // transaction record.  The passed block chain height is used to distinguish
  2433  // immature from mature coinbase outputs.
  2434  //
  2435  // TODO: This is intended for use by the RPC server and should be moved out of
  2436  // this package at a later time.
  2437  func recvCategory(details *udb.TxDetails, syncHeight int32, chainParams *chaincfg.Params) CreditCategory {
  2438  	if compat.IsEitherCoinBaseTx(&details.MsgTx) {
  2439  		if coinbaseMatured(chainParams, details.Block.Height, syncHeight) {
  2440  			return CreditGenerate
  2441  		}
  2442  		return CreditImmature
  2443  	}
  2444  	return CreditReceive
  2445  }
  2446  
  2447  // listTransactions creates a object that may be marshalled to a response result
  2448  // for a listtransactions RPC.
  2449  //
  2450  // TODO: This should be moved to the jsonrpc package.
  2451  func listTransactions(tx walletdb.ReadTx, details *udb.TxDetails, addrMgr *udb.Manager, syncHeight int32, net *chaincfg.Params) (sends, receives []types.ListTransactionsResult) {
  2452  	addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
  2453  
  2454  	var (
  2455  		blockHashStr  string
  2456  		blockTime     int64
  2457  		confirmations int64
  2458  	)
  2459  	if details.Block.Height != -1 {
  2460  		blockHashStr = details.Block.Hash.String()
  2461  		blockTime = details.Block.Time.Unix()
  2462  		confirmations = int64(confirms(details.Block.Height, syncHeight))
  2463  	}
  2464  
  2465  	txHashStr := details.Hash.String()
  2466  	received := details.Received.Unix()
  2467  	generated := compat.IsEitherCoinBaseTx(&details.MsgTx)
  2468  	recvCat := recvCategory(details, syncHeight, net).String()
  2469  
  2470  	send := len(details.Debits) != 0
  2471  
  2472  	txTypeStr := types.LTTTRegular
  2473  	switch details.TxType {
  2474  	case stake.TxTypeSStx:
  2475  		txTypeStr = types.LTTTTicket
  2476  	case stake.TxTypeSSGen:
  2477  		txTypeStr = types.LTTTVote
  2478  	case stake.TxTypeSSRtx:
  2479  		txTypeStr = types.LTTTRevocation
  2480  	}
  2481  
  2482  	// Fee can only be determined if every input is a debit.
  2483  	var feeF64 float64
  2484  	if len(details.Debits) == len(details.MsgTx.TxIn) {
  2485  		var debitTotal dcrutil.Amount
  2486  		for _, deb := range details.Debits {
  2487  			debitTotal += deb.Amount
  2488  		}
  2489  		var outputTotal dcrutil.Amount
  2490  		for _, output := range details.MsgTx.TxOut {
  2491  			outputTotal += dcrutil.Amount(output.Value)
  2492  		}
  2493  		// Note: The actual fee is debitTotal - outputTotal.  However,
  2494  		// this RPC reports negative numbers for fees, so the inverse
  2495  		// is calculated.
  2496  		feeF64 = (outputTotal - debitTotal).ToCoin()
  2497  	}
  2498  
  2499  outputs:
  2500  	for i, output := range details.MsgTx.TxOut {
  2501  		// Determine if this output is a credit.  Change outputs are skipped.
  2502  		var isCredit bool
  2503  		for _, cred := range details.Credits {
  2504  			if cred.Index == uint32(i) {
  2505  				if cred.Change {
  2506  					continue outputs
  2507  				}
  2508  				isCredit = true
  2509  				break
  2510  			}
  2511  		}
  2512  
  2513  		var address string
  2514  		var accountName string
  2515  		_, addrs := stdscript.ExtractAddrs(output.Version, output.PkScript, net)
  2516  		if len(addrs) == 1 {
  2517  			addr := addrs[0]
  2518  			address = addr.String()
  2519  			account, err := addrMgr.AddrAccount(addrmgrNs, addrs[0])
  2520  			if err == nil {
  2521  				accountName, err = addrMgr.AccountName(addrmgrNs, account)
  2522  				if err != nil {
  2523  					accountName = ""
  2524  				}
  2525  			}
  2526  		}
  2527  
  2528  		amountF64 := dcrutil.Amount(output.Value).ToCoin()
  2529  		result := types.ListTransactionsResult{
  2530  			// Fields left zeroed:
  2531  			//   InvolvesWatchOnly
  2532  			//   BlockIndex
  2533  			//
  2534  			// Fields set below:
  2535  			//   Account (only for non-"send" categories)
  2536  			//   Category
  2537  			//   Amount
  2538  			//   Fee
  2539  			Address:         address,
  2540  			Vout:            uint32(i),
  2541  			Confirmations:   confirmations,
  2542  			Generated:       generated,
  2543  			BlockHash:       blockHashStr,
  2544  			BlockTime:       blockTime,
  2545  			TxID:            txHashStr,
  2546  			WalletConflicts: []string{},
  2547  			Time:            received,
  2548  			TimeReceived:    received,
  2549  			TxType:          &txTypeStr,
  2550  		}
  2551  
  2552  		// Add a received/generated/immature result if this is a credit.
  2553  		// If the output was spent, create a second result under the
  2554  		// send category with the inverse of the output amount.  It is
  2555  		// therefore possible that a single output may be included in
  2556  		// the results set zero, one, or two times.
  2557  		//
  2558  		// Since credits are not saved for outputs that are not
  2559  		// controlled by this wallet, all non-credits from transactions
  2560  		// with debits are grouped under the send category.
  2561  
  2562  		if send {
  2563  			result.Category = "send"
  2564  			result.Amount = -amountF64
  2565  			result.Fee = &feeF64
  2566  			sends = append(sends, result)
  2567  		}
  2568  		if isCredit {
  2569  			result.Account = accountName
  2570  			result.Category = recvCat
  2571  			result.Amount = amountF64
  2572  			result.Fee = nil
  2573  			receives = append(receives, result)
  2574  		}
  2575  	}
  2576  	return sends, receives
  2577  }
  2578  
  2579  // ListSinceBlock returns a slice of objects with details about transactions
  2580  // since the given block. If the block is -1 then all transactions are included.
  2581  // This is intended to be used for listsinceblock RPC replies.
  2582  func (w *Wallet) ListSinceBlock(ctx context.Context, start, end, syncHeight int32) ([]types.ListTransactionsResult, error) {
  2583  	const op errors.Op = "wallet.ListSinceBlock"
  2584  	txList := []types.ListTransactionsResult{}
  2585  	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  2586  		txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
  2587  
  2588  		rangeFn := func(details []udb.TxDetails) (bool, error) {
  2589  			for _, detail := range details {
  2590  				sends, receives := listTransactions(tx, &detail,
  2591  					w.manager, syncHeight, w.chainParams)
  2592  				txList = append(txList, receives...)
  2593  				txList = append(txList, sends...)
  2594  			}
  2595  			return false, nil
  2596  		}
  2597  
  2598  		return w.txStore.RangeTransactions(ctx, txmgrNs, start, end, rangeFn)
  2599  	})
  2600  	if err != nil {
  2601  		return nil, errors.E(op, err)
  2602  	}
  2603  	return txList, nil
  2604  }
  2605  
  2606  // ListTransactions returns a slice of objects with details about a recorded
  2607  // transaction.  This is intended to be used for listtransactions RPC
  2608  // replies.
  2609  func (w *Wallet) ListTransactions(ctx context.Context, from, count int) ([]types.ListTransactionsResult, error) {
  2610  	const op errors.Op = "wallet.ListTransactions"
  2611  	txList := []types.ListTransactionsResult{}
  2612  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  2613  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  2614  
  2615  		// Get current block.  The block height used for calculating
  2616  		// the number of tx confirmations.
  2617  		_, tipHeight := w.txStore.MainChainTip(dbtx)
  2618  
  2619  		// Need to skip the first from transactions, and after those, only
  2620  		// include the next count transactions.
  2621  		skipped := 0
  2622  		n := 0
  2623  
  2624  		rangeFn := func(details []udb.TxDetails) (bool, error) {
  2625  			// Iterate over transactions at this height in reverse order.
  2626  			// This does nothing for unmined transactions, which are
  2627  			// unsorted, but it will process mined transactions in the
  2628  			// reverse order they were marked mined.
  2629  			for i := len(details) - 1; i >= 0; i-- {
  2630  				if n >= count {
  2631  					return true, nil
  2632  				}
  2633  
  2634  				if from > skipped {
  2635  					skipped++
  2636  					continue
  2637  				}
  2638  
  2639  				sends, receives := listTransactions(dbtx, &details[i],
  2640  					w.manager, tipHeight, w.chainParams)
  2641  				txList = append(txList, sends...)
  2642  				txList = append(txList, receives...)
  2643  
  2644  				if len(sends) != 0 || len(receives) != 0 {
  2645  					n++
  2646  				}
  2647  			}
  2648  
  2649  			return false, nil
  2650  		}
  2651  
  2652  		// Return newer results first by starting at mempool height and working
  2653  		// down to the genesis block.
  2654  		return w.txStore.RangeTransactions(ctx, txmgrNs, -1, 0, rangeFn)
  2655  	})
  2656  	if err != nil {
  2657  		return nil, errors.E(op, err)
  2658  	}
  2659  
  2660  	// reverse the list so that it is sorted from old to new.
  2661  	for i, j := 0, len(txList)-1; i < j; i, j = i+1, j-1 {
  2662  		txList[i], txList[j] = txList[j], txList[i]
  2663  	}
  2664  	return txList, nil
  2665  }
  2666  
  2667  // ListAddressTransactions returns a slice of objects with details about
  2668  // recorded transactions to or from any address belonging to a set.  This is
  2669  // intended to be used for listaddresstransactions RPC replies.
  2670  func (w *Wallet) ListAddressTransactions(ctx context.Context, pkHashes map[string]struct{}) ([]types.ListTransactionsResult, error) {
  2671  	const op errors.Op = "wallet.ListAddressTransactions"
  2672  	txList := []types.ListTransactionsResult{}
  2673  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  2674  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  2675  
  2676  		// Get current block.  The block height used for calculating
  2677  		// the number of tx confirmations.
  2678  		_, tipHeight := w.txStore.MainChainTip(dbtx)
  2679  		rangeFn := func(details []udb.TxDetails) (bool, error) {
  2680  		loopDetails:
  2681  			for i := range details {
  2682  				detail := &details[i]
  2683  
  2684  				for _, cred := range detail.Credits {
  2685  					if detail.MsgTx.TxOut[cred.Index].Version != scriptVersionAssumed {
  2686  						continue
  2687  					}
  2688  					pkh := stdscript.ExtractPubKeyHashV0(detail.MsgTx.TxOut[cred.Index].PkScript)
  2689  					if _, ok := pkHashes[string(pkh)]; !ok {
  2690  						continue
  2691  					}
  2692  
  2693  					sends, receives := listTransactions(dbtx, detail,
  2694  						w.manager, tipHeight, w.chainParams)
  2695  					txList = append(txList, receives...)
  2696  					txList = append(txList, sends...)
  2697  					continue loopDetails
  2698  				}
  2699  			}
  2700  			return false, nil
  2701  		}
  2702  
  2703  		return w.txStore.RangeTransactions(ctx, txmgrNs, 0, -1, rangeFn)
  2704  	})
  2705  	if err != nil {
  2706  		return nil, errors.E(op, err)
  2707  	}
  2708  	return txList, nil
  2709  }
  2710  
  2711  // ListAllTransactions returns a slice of objects with details about a recorded
  2712  // transaction.  This is intended to be used for listalltransactions RPC
  2713  // replies.
  2714  func (w *Wallet) ListAllTransactions(ctx context.Context) ([]types.ListTransactionsResult, error) {
  2715  	const op errors.Op = "wallet.ListAllTransactions"
  2716  	txList := []types.ListTransactionsResult{}
  2717  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  2718  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  2719  
  2720  		// Get current block.  The block height used for calculating
  2721  		// the number of tx confirmations.
  2722  		_, tipHeight := w.txStore.MainChainTip(dbtx)
  2723  
  2724  		rangeFn := func(details []udb.TxDetails) (bool, error) {
  2725  			// Iterate over transactions at this height in reverse
  2726  			// order.  This does nothing for unmined transactions,
  2727  			// which are unsorted, but it will process mined
  2728  			// transactions in the reverse order they were marked
  2729  			// mined.
  2730  			for i := len(details) - 1; i >= 0; i-- {
  2731  				sends, receives := listTransactions(dbtx, &details[i],
  2732  					w.manager, tipHeight, w.chainParams)
  2733  				txList = append(txList, sends...)
  2734  				txList = append(txList, receives...)
  2735  			}
  2736  			return false, nil
  2737  		}
  2738  
  2739  		// Return newer results first by starting at mempool height and
  2740  		// working down to the genesis block.
  2741  		return w.txStore.RangeTransactions(ctx, txmgrNs, -1, 0, rangeFn)
  2742  	})
  2743  	if err != nil {
  2744  		return nil, errors.E(op, err)
  2745  	}
  2746  
  2747  	// reverse the list so that it is sorted from old to new.
  2748  	for i, j := 0, len(txList)-1; i < j; i, j = i+1, j-1 {
  2749  		txList[i], txList[j] = txList[j], txList[i]
  2750  	}
  2751  	return txList, nil
  2752  }
  2753  
  2754  // ListTransactionDetails returns the listtransaction results for a single
  2755  // transaction.
  2756  func (w *Wallet) ListTransactionDetails(ctx context.Context, txHash *chainhash.Hash) ([]types.ListTransactionsResult, error) {
  2757  	const op errors.Op = "wallet.ListTransactionDetails"
  2758  	txList := []types.ListTransactionsResult{}
  2759  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  2760  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  2761  
  2762  		// Get current block.  The block height used for calculating
  2763  		// the number of tx confirmations.
  2764  		_, tipHeight := w.txStore.MainChainTip(dbtx)
  2765  
  2766  		txd, err := w.txStore.TxDetails(txmgrNs, txHash)
  2767  		if err != nil {
  2768  			return err
  2769  		}
  2770  		sends, receives := listTransactions(dbtx, txd, w.manager, tipHeight, w.chainParams)
  2771  		txList = make([]types.ListTransactionsResult, 0, len(sends)+len(receives))
  2772  		txList = append(txList, receives...)
  2773  		txList = append(txList, sends...)
  2774  		return nil
  2775  	})
  2776  	if err != nil {
  2777  		return nil, errors.E(op, err)
  2778  	}
  2779  	return txList, nil
  2780  }
  2781  
  2782  // BlockIdentifier identifies a block by either a height in the main chain or a
  2783  // hash.
  2784  type BlockIdentifier struct {
  2785  	height int32
  2786  	hash   *chainhash.Hash
  2787  }
  2788  
  2789  // NewBlockIdentifierFromHeight constructs a BlockIdentifier for a block height.
  2790  func NewBlockIdentifierFromHeight(height int32) *BlockIdentifier {
  2791  	return &BlockIdentifier{height: height}
  2792  }
  2793  
  2794  // NewBlockIdentifierFromHash constructs a BlockIdentifier for a block hash.
  2795  func NewBlockIdentifierFromHash(hash *chainhash.Hash) *BlockIdentifier {
  2796  	return &BlockIdentifier{hash: hash}
  2797  }
  2798  
  2799  // BlockInfo records info pertaining to a block.  It does not include any
  2800  // information about wallet transactions contained in the block.
  2801  type BlockInfo struct {
  2802  	Hash             chainhash.Hash
  2803  	Height           int32
  2804  	Confirmations    int32
  2805  	Header           []byte
  2806  	Timestamp        int64
  2807  	StakeInvalidated bool
  2808  }
  2809  
  2810  // BlockInfo returns info regarding a block recorded by the wallet.
  2811  func (w *Wallet) BlockInfo(ctx context.Context, blockID *BlockIdentifier) (*BlockInfo, error) {
  2812  	const op errors.Op = "wallet.BlockInfo"
  2813  	var blockInfo *BlockInfo
  2814  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  2815  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  2816  		_, tipHeight := w.txStore.MainChainTip(dbtx)
  2817  		blockHash := blockID.hash
  2818  		if blockHash == nil {
  2819  			hash, err := w.txStore.GetMainChainBlockHashForHeight(txmgrNs,
  2820  				blockID.height)
  2821  			if err != nil {
  2822  				return err
  2823  			}
  2824  			blockHash = &hash
  2825  		}
  2826  		header, err := w.txStore.GetSerializedBlockHeader(txmgrNs, blockHash)
  2827  		if err != nil {
  2828  			return err
  2829  		}
  2830  		height := udb.ExtractBlockHeaderHeight(header)
  2831  		inMainChain, invalidated := w.txStore.BlockInMainChain(dbtx, blockHash)
  2832  		var confs int32
  2833  		if inMainChain {
  2834  			confs = confirms(height, tipHeight)
  2835  		}
  2836  		blockInfo = &BlockInfo{
  2837  			Hash:             *blockHash,
  2838  			Height:           height,
  2839  			Confirmations:    confs,
  2840  			Header:           header,
  2841  			Timestamp:        udb.ExtractBlockHeaderTime(header),
  2842  			StakeInvalidated: invalidated,
  2843  		}
  2844  		return nil
  2845  	})
  2846  	if err != nil {
  2847  		return nil, errors.E(op, err)
  2848  	}
  2849  	return blockInfo, nil
  2850  }
  2851  
  2852  // TransactionSummary returns details about a recorded transaction that is
  2853  // relevant to the wallet in some way.
  2854  func (w *Wallet) TransactionSummary(ctx context.Context, txHash *chainhash.Hash) (txSummary *TransactionSummary, confs int32, blockHash *chainhash.Hash, err error) {
  2855  	const opf = "wallet.TransactionSummary(%v)"
  2856  	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  2857  		ns := dbtx.ReadBucket(wtxmgrNamespaceKey)
  2858  		_, tipHeight := w.txStore.MainChainTip(dbtx)
  2859  		txDetails, err := w.txStore.TxDetails(ns, txHash)
  2860  		if err != nil {
  2861  			return err
  2862  		}
  2863  		txSummary = new(TransactionSummary)
  2864  		*txSummary = makeTxSummary(dbtx, w, txDetails)
  2865  		confs = confirms(txDetails.Height(), tipHeight)
  2866  		if confs > 0 {
  2867  			blockHash = &txDetails.Block.Hash
  2868  		}
  2869  		return nil
  2870  	})
  2871  	if err != nil {
  2872  		op := errors.Opf(opf, txHash)
  2873  		return nil, 0, nil, errors.E(op, err)
  2874  	}
  2875  	return txSummary, confs, blockHash, nil
  2876  }
  2877  
  2878  // fetchTicketDetails returns the ticket details of the provided ticket hash.
  2879  func (w *Wallet) fetchTicketDetails(ns walletdb.ReadBucket, hash *chainhash.Hash) (*udb.TicketDetails, error) {
  2880  	txDetail, err := w.txStore.TxDetails(ns, hash)
  2881  	if err != nil {
  2882  		return nil, err
  2883  	}
  2884  
  2885  	if txDetail.TxType != stake.TxTypeSStx {
  2886  		return nil, errors.Errorf("%v is not a ticket", hash)
  2887  	}
  2888  
  2889  	ticketDetails, err := w.txStore.TicketDetails(ns, txDetail)
  2890  	if err != nil {
  2891  		return nil, errors.Errorf("%v while trying to get ticket"+
  2892  			" details for txhash: %v", err, hash)
  2893  	}
  2894  
  2895  	return ticketDetails, nil
  2896  }
  2897  
  2898  // TicketSummary contains the properties to describe a ticket's current status
  2899  type TicketSummary struct {
  2900  	Ticket  *TransactionSummary
  2901  	Spender *TransactionSummary
  2902  	Status  TicketStatus
  2903  }
  2904  
  2905  // TicketStatus describes the current status a ticket can be observed to be.
  2906  type TicketStatus uint
  2907  
  2908  //go:generate stringer -type=TicketStatus -linecomment
  2909  
  2910  const (
  2911  	// TicketStatusUnknown any ticket that its status was unable to be determined.
  2912  	TicketStatusUnknown TicketStatus = iota // unknown
  2913  
  2914  	// TicketStatusUnmined any not yet mined ticket.
  2915  	TicketStatusUnmined // unmined
  2916  
  2917  	// TicketStatusImmature any so to be live ticket.
  2918  	TicketStatusImmature // immature
  2919  
  2920  	// TicketStatusLive any currently live ticket.
  2921  	TicketStatusLive // live
  2922  
  2923  	// TicketStatusVoted any ticket that was seen to have voted.
  2924  	TicketStatusVoted // voted
  2925  
  2926  	// TicketStatusMissed any ticket that has yet to be revoked, and was missed.
  2927  	TicketStatusMissed // missed
  2928  
  2929  	// TicketStatusExpired any ticket that has yet to be revoked, and was expired.
  2930  	// In SPV mode, this status may be used by unspent tickets definitely
  2931  	// past the expiry period, even if the ticket was actually missed rather than
  2932  	// expiring.
  2933  	TicketStatusExpired // expired
  2934  
  2935  	// TicketStatusUnspent is a matured ticket that has not been spent.  It
  2936  	// is only used under SPV mode where it is unknown if a ticket is live,
  2937  	// was missed, or expired.
  2938  	TicketStatusUnspent // unspent
  2939  
  2940  	// TicketStatusRevoked any ticket that has been previously revoked.
  2941  	//
  2942  	// Deprecated: The ticket status will be either missed or expired
  2943  	// instead.  There are no more unrevoked missed/expired tickets.
  2944  	TicketStatusRevoked // revoked
  2945  )
  2946  
  2947  func makeTicketSummary(ctx context.Context, rpc *dcrd.RPC, dbtx walletdb.ReadTx,
  2948  	w *Wallet, details *udb.TicketDetails) *TicketSummary {
  2949  
  2950  	ticketHeight := details.Ticket.Height()
  2951  	_, tipHeight := w.txStore.MainChainTip(dbtx)
  2952  
  2953  	ticketTransactionDetails := makeTxSummary(dbtx, w, details.Ticket)
  2954  	summary := &TicketSummary{
  2955  		Ticket: &ticketTransactionDetails,
  2956  		Status: TicketStatusLive,
  2957  	}
  2958  	if rpc == nil {
  2959  		summary.Status = TicketStatusUnspent
  2960  	}
  2961  	// Check if ticket is unmined or immature
  2962  	switch {
  2963  	case ticketHeight == int32(-1):
  2964  		summary.Status = TicketStatusUnmined
  2965  	case !ticketMatured(w.chainParams, ticketHeight, tipHeight):
  2966  		summary.Status = TicketStatusImmature
  2967  	}
  2968  
  2969  	if details.Spender != nil {
  2970  		spenderTransactionDetails := makeTxSummary(dbtx, w, details.Spender)
  2971  		summary.Spender = &spenderTransactionDetails
  2972  
  2973  		switch details.Spender.TxType {
  2974  		case stake.TxTypeSSGen:
  2975  			summary.Status = TicketStatusVoted
  2976  		case stake.TxTypeSSRtx:
  2977  			params := w.chainParams
  2978  			ticketHeight := details.Ticket.Block.Height
  2979  			revocationHeight := details.Spender.Block.Height
  2980  			expired := revocationHeight-ticketHeight >=
  2981  				int32(params.TicketExpiryBlocks())+
  2982  					int32(params.TicketMaturity)
  2983  			if expired {
  2984  				summary.Status = TicketStatusExpired
  2985  			} else {
  2986  				summary.Status = TicketStatusMissed
  2987  			}
  2988  		}
  2989  	}
  2990  
  2991  	return summary
  2992  }
  2993  
  2994  // GetTicketInfoPrecise returns the ticket summary and the corresponding block header
  2995  // for the provided ticket.  The ticket summary is comprised of the transaction
  2996  // summmary for the ticket, the spender (if already spent) and the ticket's
  2997  // current status.
  2998  //
  2999  // If the ticket is unmined, then the returned block header will be nil.
  3000  //
  3001  // The argument chainClient is always expected to be not nil in this case,
  3002  // otherwise one should use the alternative GetTicketInfo instead.  With
  3003  // the ability to use the rpc chain client, this function is able to determine
  3004  // whether a ticket has been missed or not.  Otherwise, it is just known to be
  3005  // unspent (possibly live or missed).
  3006  func (w *Wallet) GetTicketInfoPrecise(ctx context.Context, rpcCaller Caller, hash *chainhash.Hash) (*TicketSummary, *wire.BlockHeader, error) {
  3007  	const op errors.Op = "wallet.GetTicketInfoPrecise"
  3008  
  3009  	var ticketSummary *TicketSummary
  3010  	var blockHeader *wire.BlockHeader
  3011  
  3012  	rpc := dcrd.New(rpcCaller)
  3013  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  3014  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  3015  
  3016  		ticketDetails, err := w.fetchTicketDetails(txmgrNs, hash)
  3017  		if err != nil {
  3018  			return err
  3019  		}
  3020  
  3021  		ticketSummary = makeTicketSummary(ctx, rpc, dbtx, w, ticketDetails)
  3022  		if ticketDetails.Ticket.Block.Height == -1 {
  3023  			// unmined tickets do not have an associated block header
  3024  			return nil
  3025  		}
  3026  
  3027  		// Fetch the associated block header of the ticket.
  3028  		hBytes, err := w.txStore.GetSerializedBlockHeader(txmgrNs,
  3029  			&ticketDetails.Ticket.Block.Hash)
  3030  		if err != nil {
  3031  			return err
  3032  		}
  3033  
  3034  		blockHeader = new(wire.BlockHeader)
  3035  		err = blockHeader.FromBytes(hBytes)
  3036  		if err != nil {
  3037  			return err
  3038  		}
  3039  
  3040  		return nil
  3041  	})
  3042  	if err != nil {
  3043  		return nil, nil, errors.E(op, err)
  3044  	}
  3045  
  3046  	return ticketSummary, blockHeader, nil
  3047  }
  3048  
  3049  // GetTicketInfo returns the ticket summary and the corresponding block header
  3050  // for the provided ticket. The ticket summary is comprised of the transaction
  3051  // summmary for the ticket, the spender (if already spent) and the ticket's
  3052  // current status.
  3053  //
  3054  // If the ticket is unmined, then the returned block header will be nil.
  3055  func (w *Wallet) GetTicketInfo(ctx context.Context, hash *chainhash.Hash) (*TicketSummary, *wire.BlockHeader, error) {
  3056  	const op errors.Op = "wallet.GetTicketInfo"
  3057  
  3058  	var ticketSummary *TicketSummary
  3059  	var blockHeader *wire.BlockHeader
  3060  
  3061  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  3062  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  3063  
  3064  		ticketDetails, err := w.fetchTicketDetails(txmgrNs, hash)
  3065  		if err != nil {
  3066  			return err
  3067  		}
  3068  
  3069  		ticketSummary = makeTicketSummary(ctx, nil, dbtx, w, ticketDetails)
  3070  		if ticketDetails.Ticket.Block.Height == -1 {
  3071  			// unmined tickets do not have an associated block header
  3072  			return nil
  3073  		}
  3074  
  3075  		// Fetch the associated block header of the ticket.
  3076  		hBytes, err := w.txStore.GetSerializedBlockHeader(txmgrNs,
  3077  			&ticketDetails.Ticket.Block.Hash)
  3078  		if err != nil {
  3079  			return err
  3080  		}
  3081  
  3082  		blockHeader = new(wire.BlockHeader)
  3083  		err = blockHeader.FromBytes(hBytes)
  3084  		if err != nil {
  3085  			return err
  3086  		}
  3087  
  3088  		return nil
  3089  	})
  3090  	if err != nil {
  3091  		return nil, nil, errors.E(op, err)
  3092  	}
  3093  
  3094  	return ticketSummary, blockHeader, nil
  3095  }
  3096  
  3097  // blockRange returns the start and ending heights for the given set of block
  3098  // identifiers or the default values used to range over the entire chain
  3099  // (including unmined transactions).
  3100  func (w *Wallet) blockRange(dbtx walletdb.ReadTx, startBlock, endBlock *BlockIdentifier) (int32, int32, error) {
  3101  	var start, end int32 = 0, -1
  3102  	ns := dbtx.ReadBucket(wtxmgrNamespaceKey)
  3103  
  3104  	switch {
  3105  	case startBlock == nil:
  3106  		// Hardcoded default start of 0.
  3107  	case startBlock.hash == nil:
  3108  		start = startBlock.height
  3109  	default:
  3110  		serHeader, err := w.txStore.GetSerializedBlockHeader(ns, startBlock.hash)
  3111  		if err != nil {
  3112  			return 0, 0, err
  3113  		}
  3114  		var startHeader wire.BlockHeader
  3115  		err = startHeader.Deserialize(bytes.NewReader(serHeader))
  3116  		if err != nil {
  3117  			return 0, 0, err
  3118  		}
  3119  		start = int32(startHeader.Height)
  3120  	}
  3121  
  3122  	switch {
  3123  	case endBlock == nil:
  3124  		// Hardcoded default end of -1.
  3125  	case endBlock.hash == nil:
  3126  		end = endBlock.height
  3127  	default:
  3128  		serHeader, err := w.txStore.GetSerializedBlockHeader(ns, endBlock.hash)
  3129  		if err != nil {
  3130  			return 0, 0, err
  3131  		}
  3132  		var endHeader wire.BlockHeader
  3133  		err = endHeader.Deserialize(bytes.NewReader(serHeader))
  3134  		if err != nil {
  3135  			return 0, 0, err
  3136  		}
  3137  		end = int32(endHeader.Height)
  3138  	}
  3139  
  3140  	return start, end, nil
  3141  }
  3142  
  3143  // GetTicketsPrecise calls function f for all tickets located in between the
  3144  // given startBlock and endBlock.  TicketSummary includes TransactionSummmary
  3145  // for the ticket and the spender (if already spent) and the ticket's current
  3146  // status. The function f also receives block header of the ticket. All
  3147  // tickets on a given call belong to the same block and at least one ticket
  3148  // is present when f is called. If the ticket is unmined, the block header will
  3149  // be nil.
  3150  //
  3151  // The function f may return an error which, if non-nil, is propagated to the
  3152  // caller.  Additionally, a boolean return value allows exiting the function
  3153  // early without reading any additional transactions when true.
  3154  //
  3155  // The arguments to f may be reused and should not be kept by the caller.
  3156  //
  3157  // The argument chainClient is always expected to be not nil in this case,
  3158  // otherwise one should use the alternative GetTickets instead.  With
  3159  // the ability to use the rpc chain client, this function is able to determine
  3160  // whether tickets have been missed or not.  Otherwise, tickets are just known
  3161  // to be unspent (possibly live or missed).
  3162  func (w *Wallet) GetTicketsPrecise(ctx context.Context, rpcCaller Caller,
  3163  	f func([]*TicketSummary, *wire.BlockHeader) (bool, error),
  3164  	startBlock, endBlock *BlockIdentifier) error {
  3165  
  3166  	const op errors.Op = "wallet.GetTicketsPrecise"
  3167  
  3168  	rpc := dcrd.New(rpcCaller)
  3169  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  3170  		start, end, err := w.blockRange(dbtx, startBlock, endBlock)
  3171  		if err != nil {
  3172  			return err
  3173  		}
  3174  
  3175  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  3176  		header := &wire.BlockHeader{}
  3177  
  3178  		rangeFn := func(details []udb.TxDetails) (bool, error) {
  3179  			tickets := make([]*TicketSummary, 0, len(details))
  3180  
  3181  			for i := range details {
  3182  				ticketInfo, err := w.txStore.TicketDetails(txmgrNs, &details[i])
  3183  				if err != nil {
  3184  					return false, errors.Errorf("%v while trying to get "+
  3185  						"ticket details for txhash: %v", err, &details[i].Hash)
  3186  				}
  3187  				// Continue if not a ticket
  3188  				if ticketInfo == nil {
  3189  					continue
  3190  				}
  3191  				summary := makeTicketSummary(ctx, rpc, dbtx, w, ticketInfo)
  3192  				tickets = append(tickets, summary)
  3193  			}
  3194  
  3195  			if len(tickets) == 0 {
  3196  				return false, nil
  3197  			}
  3198  
  3199  			if details[0].Block.Height == -1 {
  3200  				return f(tickets, nil)
  3201  			}
  3202  
  3203  			blockHash := &details[0].Block.Hash
  3204  			headerBytes, err := w.txStore.GetSerializedBlockHeader(txmgrNs, blockHash)
  3205  			if err != nil {
  3206  				return false, err
  3207  			}
  3208  			header.FromBytes(headerBytes)
  3209  			return f(tickets, header)
  3210  		}
  3211  
  3212  		return w.txStore.RangeTransactions(ctx, txmgrNs, start, end, rangeFn)
  3213  	})
  3214  	if err != nil {
  3215  		return errors.E(op, err)
  3216  	}
  3217  	return nil
  3218  }
  3219  
  3220  // GetTickets calls function f for all tickets located in between the
  3221  // given startBlock and endBlock.  TicketSummary includes TransactionSummmary
  3222  // for the ticket and the spender (if already spent) and the ticket's current
  3223  // status. The function f also receives block header of the ticket. All
  3224  // tickets on a given call belong to the same block and at least one ticket
  3225  // is present when f is called. If the ticket is unmined, the block header will
  3226  // be nil.
  3227  //
  3228  // The function f may return an error which, if non-nil, is propagated to the
  3229  // caller.  Additionally, a boolean return value allows exiting the function
  3230  // early without reading any additional transactions when true.
  3231  //
  3232  // The arguments to f may be reused and should not be kept by the caller.
  3233  //
  3234  // Because this function does not have any chain client argument, tickets are
  3235  // unable to be determined whether or not they have been missed, simply unspent.
  3236  func (w *Wallet) GetTickets(ctx context.Context,
  3237  	f func([]*TicketSummary, *wire.BlockHeader) (bool, error),
  3238  	startBlock, endBlock *BlockIdentifier) error {
  3239  
  3240  	const op errors.Op = "wallet.GetTickets"
  3241  
  3242  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  3243  		start, end, err := w.blockRange(dbtx, startBlock, endBlock)
  3244  		if err != nil {
  3245  			return err
  3246  		}
  3247  
  3248  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  3249  		header := &wire.BlockHeader{}
  3250  
  3251  		rangeFn := func(details []udb.TxDetails) (bool, error) {
  3252  			tickets := make([]*TicketSummary, 0, len(details))
  3253  
  3254  			for i := range details {
  3255  				ticketInfo, err := w.txStore.TicketDetails(txmgrNs, &details[i])
  3256  				if err != nil {
  3257  					return false, errors.Errorf("%v while trying to get "+
  3258  						"ticket details for txhash: %v", err, &details[i].Hash)
  3259  				}
  3260  				// Continue if not a ticket
  3261  				if ticketInfo == nil {
  3262  					continue
  3263  				}
  3264  				summary := makeTicketSummary(ctx, nil, dbtx, w, ticketInfo)
  3265  				tickets = append(tickets, summary)
  3266  			}
  3267  
  3268  			if len(tickets) == 0 {
  3269  				return false, nil
  3270  			}
  3271  
  3272  			if details[0].Block.Height == -1 {
  3273  				return f(tickets, nil)
  3274  			}
  3275  
  3276  			blockHash := &details[0].Block.Hash
  3277  			headerBytes, err := w.txStore.GetSerializedBlockHeader(txmgrNs, blockHash)
  3278  			if err != nil {
  3279  				return false, err
  3280  			}
  3281  			header.FromBytes(headerBytes)
  3282  			return f(tickets, header)
  3283  		}
  3284  
  3285  		return w.txStore.RangeTransactions(ctx, txmgrNs, start, end, rangeFn)
  3286  	})
  3287  	if err != nil {
  3288  		return errors.E(op, err)
  3289  	}
  3290  	return nil
  3291  }
  3292  
  3293  // GetTransactions runs the function f on all transactions between a starting
  3294  // and ending block.  Blocks in the block range may be specified by either a
  3295  // height or a hash.
  3296  //
  3297  // The function f may return an error which, if non-nil, is propagated to the
  3298  // caller.  Additionally, a boolean return value allows exiting the function
  3299  // early without reading any additional transactions when true.
  3300  //
  3301  // Transaction results are organized by blocks in ascending order and unmined
  3302  // transactions in an unspecified order.  Mined transactions are saved in a
  3303  // Block structure which records properties about the block. Unmined
  3304  // transactions are returned on a Block structure with height == -1.
  3305  //
  3306  // Internally this function uses the udb store RangeTransactions function,
  3307  // therefore the notes and restrictions of that function also apply here.
  3308  func (w *Wallet) GetTransactions(ctx context.Context, f func(*Block) (bool, error), startBlock, endBlock *BlockIdentifier) error {
  3309  	const op errors.Op = "wallet.GetTransactions"
  3310  
  3311  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  3312  		start, end, err := w.blockRange(dbtx, startBlock, endBlock)
  3313  		if err != nil {
  3314  			return err
  3315  		}
  3316  
  3317  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  3318  
  3319  		rangeFn := func(details []udb.TxDetails) (bool, error) {
  3320  			// TODO: probably should make RangeTransactions not reuse the
  3321  			// details backing array memory.
  3322  			dets := make([]udb.TxDetails, len(details))
  3323  			copy(dets, details)
  3324  			details = dets
  3325  
  3326  			txs := make([]TransactionSummary, 0, len(details))
  3327  			for i := range details {
  3328  				txs = append(txs, makeTxSummary(dbtx, w, &details[i]))
  3329  			}
  3330  
  3331  			var block *Block
  3332  			if details[0].Block.Height != -1 {
  3333  				serHeader, err := w.txStore.GetSerializedBlockHeader(txmgrNs,
  3334  					&details[0].Block.Hash)
  3335  				if err != nil {
  3336  					return false, err
  3337  				}
  3338  				header := new(wire.BlockHeader)
  3339  				err = header.Deserialize(bytes.NewReader(serHeader))
  3340  				if err != nil {
  3341  					return false, err
  3342  				}
  3343  				block = &Block{
  3344  					Header:       header,
  3345  					Transactions: txs,
  3346  				}
  3347  			} else {
  3348  				block = &Block{
  3349  					Header:       nil,
  3350  					Transactions: txs,
  3351  				}
  3352  			}
  3353  
  3354  			return f(block)
  3355  		}
  3356  
  3357  		return w.txStore.RangeTransactions(ctx, txmgrNs, start, end, rangeFn)
  3358  	})
  3359  	if err != nil {
  3360  		return errors.E(op, err)
  3361  	}
  3362  	return nil
  3363  }
  3364  
  3365  // Spender queries for the transaction and input index which spends a Credit.
  3366  // If the output is not a Credit, an error with code ErrInput is returned.  If
  3367  // the output is unspent, the ErrNoExist code is used.
  3368  func (w *Wallet) Spender(ctx context.Context, out *wire.OutPoint) (*wire.MsgTx, uint32, error) {
  3369  	var spender *wire.MsgTx
  3370  	var spenderIndex uint32
  3371  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  3372  		var err error
  3373  		spender, spenderIndex, err = w.txStore.Spender(dbtx, out)
  3374  		return err
  3375  	})
  3376  	return spender, spenderIndex, err
  3377  }
  3378  
  3379  // UnspentOutput returns information about an unspent received transaction
  3380  // output. Returns error NotExist if the specified outpoint cannot be found or
  3381  // has been spent by a mined transaction. Mined transactions that are spent by
  3382  // a mempool transaction are not affected by this.
  3383  func (w *Wallet) UnspentOutput(ctx context.Context, op wire.OutPoint, includeMempool bool) (*udb.Credit, error) {
  3384  	var utxo *udb.Credit
  3385  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  3386  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  3387  		var err error
  3388  		utxo, err = w.txStore.UnspentOutput(txmgrNs, op, includeMempool)
  3389  		return err
  3390  	})
  3391  	return utxo, err
  3392  }
  3393  
  3394  // AccountProperties contains properties associated with each account, such as
  3395  // the account name, number, and the nubmer of derived and imported keys.  If no
  3396  // address usage has been recorded on any of the external or internal branches,
  3397  // the child index is ^uint32(0).
  3398  type AccountProperties struct {
  3399  	AccountNumber             uint32
  3400  	AccountName               string
  3401  	AccountType               uint8
  3402  	LastUsedExternalIndex     uint32
  3403  	LastUsedInternalIndex     uint32
  3404  	LastReturnedExternalIndex uint32
  3405  	LastReturnedInternalIndex uint32
  3406  	ImportedKeyCount          uint32
  3407  	AccountEncrypted          bool
  3408  	AccountUnlocked           bool
  3409  }
  3410  
  3411  // AccountResult is a single account result for the AccountsResult type.
  3412  type AccountResult struct {
  3413  	AccountProperties
  3414  	TotalBalance dcrutil.Amount
  3415  }
  3416  
  3417  // AccountsResult is the resutl of the wallet's Accounts method.  See that
  3418  // method for more details.
  3419  type AccountsResult struct {
  3420  	Accounts           []AccountResult
  3421  	CurrentBlockHash   *chainhash.Hash
  3422  	CurrentBlockHeight int32
  3423  }
  3424  
  3425  // Accounts returns the current names, numbers, and total balances of all
  3426  // accounts in the wallet.  The current chain tip is included in the result for
  3427  // atomicity reasons.
  3428  //
  3429  // TODO(jrick): Is the chain tip really needed, since only the total balances
  3430  // are included?
  3431  func (w *Wallet) Accounts(ctx context.Context) (*AccountsResult, error) {
  3432  	const op errors.Op = "wallet.Accounts"
  3433  	var (
  3434  		accounts  []AccountResult
  3435  		tipHash   chainhash.Hash
  3436  		tipHeight int32
  3437  	)
  3438  
  3439  	defer w.lockedOutpointMu.Unlock()
  3440  	w.lockedOutpointMu.Lock()
  3441  
  3442  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  3443  		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  3444  
  3445  		tipHash, tipHeight = w.txStore.MainChainTip(dbtx)
  3446  		unspent, err := w.txStore.UnspentOutputs(dbtx)
  3447  		if err != nil {
  3448  			return err
  3449  		}
  3450  		err = w.manager.ForEachAccount(addrmgrNs, func(acct uint32) error {
  3451  			props, err := w.manager.AccountProperties(addrmgrNs, acct)
  3452  			if err != nil {
  3453  				return err
  3454  			}
  3455  			accounts = append(accounts, AccountResult{
  3456  				AccountProperties: *props,
  3457  				// TotalBalance set below
  3458  			})
  3459  			return nil
  3460  		})
  3461  		if err != nil {
  3462  			return err
  3463  		}
  3464  		m := make(map[uint32]*dcrutil.Amount)
  3465  		for i := range accounts {
  3466  			a := &accounts[i]
  3467  			m[a.AccountNumber] = &a.TotalBalance
  3468  		}
  3469  		for i := range unspent {
  3470  			output := unspent[i]
  3471  			_, addrs := stdscript.ExtractAddrs(scriptVersionAssumed, output.PkScript, w.chainParams)
  3472  			if len(addrs) == 0 {
  3473  				continue
  3474  			}
  3475  			outputAcct, err := w.manager.AddrAccount(addrmgrNs, addrs[0])
  3476  			if err == nil {
  3477  				amt, ok := m[outputAcct]
  3478  				if ok {
  3479  					*amt += output.Amount
  3480  				}
  3481  			}
  3482  		}
  3483  		return nil
  3484  	})
  3485  	if err != nil {
  3486  		return nil, errors.E(op, err)
  3487  	}
  3488  	return &AccountsResult{
  3489  		Accounts:           accounts,
  3490  		CurrentBlockHash:   &tipHash,
  3491  		CurrentBlockHeight: tipHeight,
  3492  	}, nil
  3493  }
  3494  
  3495  // creditSlice satisifies the sort.Interface interface to provide sorting
  3496  // transaction credits from oldest to newest.  Credits with the same receive
  3497  // time and mined in the same block are not guaranteed to be sorted by the order
  3498  // they appear in the block.  Credits from the same transaction are sorted by
  3499  // output index.
  3500  type creditSlice []*udb.Credit
  3501  
  3502  func (s creditSlice) Len() int {
  3503  	return len(s)
  3504  }
  3505  
  3506  func (s creditSlice) Less(i, j int) bool {
  3507  	switch {
  3508  	// If both credits are from the same tx, sort by output index.
  3509  	case s[i].OutPoint.Hash == s[j].OutPoint.Hash:
  3510  		return s[i].OutPoint.Index < s[j].OutPoint.Index
  3511  
  3512  	// If both transactions are unmined, sort by their received date.
  3513  	case s[i].Height == -1 && s[j].Height == -1:
  3514  		return s[i].Received.Before(s[j].Received)
  3515  
  3516  	// Unmined (newer) txs always come last.
  3517  	case s[i].Height == -1:
  3518  		return false
  3519  	case s[j].Height == -1:
  3520  		return true
  3521  
  3522  	// If both txs are mined in different blocks, sort by block height.
  3523  	default:
  3524  		return s[i].Height < s[j].Height
  3525  	}
  3526  }
  3527  
  3528  func (s creditSlice) Swap(i, j int) {
  3529  	s[i], s[j] = s[j], s[i]
  3530  }
  3531  
  3532  // ListUnspent returns a slice of objects representing the unspent wallet
  3533  // transactions fitting the given criteria. The confirmations will be more than
  3534  // minconf, less than maxconf and if addresses is populated only the addresses
  3535  // contained within it will be considered.  If we know nothing about a
  3536  // transaction an empty array will be returned.
  3537  func (w *Wallet) ListUnspent(ctx context.Context, minconf, maxconf int32, addresses map[string]struct{}, accountName string) ([]*types.ListUnspentResult, error) {
  3538  	const op errors.Op = "wallet.ListUnspent"
  3539  	var results []*types.ListUnspentResult
  3540  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  3541  		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  3542  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  3543  
  3544  		_, tipHeight := w.txStore.MainChainTip(dbtx)
  3545  
  3546  		filter := len(addresses) != 0
  3547  		unspent, err := w.txStore.UnspentOutputs(dbtx)
  3548  		if err != nil {
  3549  			return err
  3550  		}
  3551  		sort.Sort(sort.Reverse(creditSlice(unspent)))
  3552  
  3553  		defaultAccountName, err := w.manager.AccountName(
  3554  			addrmgrNs, udb.DefaultAccountNum)
  3555  		if err != nil {
  3556  			return err
  3557  		}
  3558  
  3559  		for i := range unspent {
  3560  			output := unspent[i]
  3561  
  3562  			details, err := w.txStore.TxDetails(txmgrNs, &output.Hash)
  3563  			if err != nil {
  3564  				return err
  3565  			}
  3566  
  3567  			// Outputs with fewer confirmations than the minimum or more
  3568  			// confs than the maximum are excluded.
  3569  			confs := confirms(output.Height, tipHeight)
  3570  			if confs < minconf || confs > maxconf {
  3571  				continue
  3572  			}
  3573  
  3574  			// Only mature coinbase outputs are included.
  3575  			if output.FromCoinBase {
  3576  				if !coinbaseMatured(w.chainParams, output.Height, tipHeight) {
  3577  					continue
  3578  				}
  3579  			}
  3580  
  3581  			switch details.TxRecord.TxType {
  3582  			case stake.TxTypeSStx:
  3583  				// Ticket commitment, only spendable after ticket maturity.
  3584  				if output.Index == 0 {
  3585  					if !ticketMatured(w.chainParams, details.Height(), tipHeight) {
  3586  						continue
  3587  					}
  3588  				}
  3589  				// Change outputs.
  3590  				if (output.Index > 0) && (output.Index%2 == 0) {
  3591  					if !ticketChangeMatured(w.chainParams, details.Height(), tipHeight) {
  3592  						continue
  3593  					}
  3594  				}
  3595  			case stake.TxTypeSSGen:
  3596  				// All non-OP_RETURN outputs for SSGen tx are only spendable
  3597  				// after coinbase maturity many blocks.
  3598  				if !coinbaseMatured(w.chainParams, details.Height(), tipHeight) {
  3599  					continue
  3600  				}
  3601  			case stake.TxTypeSSRtx:
  3602  				// All outputs for SSRtx tx are only spendable
  3603  				// after coinbase maturity many blocks.
  3604  				if !coinbaseMatured(w.chainParams, details.Height(), tipHeight) {
  3605  					continue
  3606  				}
  3607  			}
  3608  
  3609  			// Exclude locked outputs from the result set.
  3610  			if w.LockedOutpoint(&output.OutPoint.Hash, output.OutPoint.Index) {
  3611  				continue
  3612  			}
  3613  
  3614  			// Lookup the associated account for the output.  Use the
  3615  			// default account name in case there is no associated account
  3616  			// for some reason, although this should never happen.
  3617  			//
  3618  			// This will be unnecessary once transactions and outputs are
  3619  			// grouped under the associated account in the db.
  3620  			acctName := defaultAccountName
  3621  			sc, addrs := stdscript.ExtractAddrs(scriptVersionAssumed, output.PkScript, w.chainParams)
  3622  			if len(addrs) > 0 {
  3623  				acct, err := w.manager.AddrAccount(
  3624  					addrmgrNs, addrs[0])
  3625  				if err == nil {
  3626  					s, err := w.manager.AccountName(
  3627  						addrmgrNs, acct)
  3628  					if err == nil {
  3629  						acctName = s
  3630  					}
  3631  				}
  3632  			}
  3633  			if accountName != "" && accountName != acctName {
  3634  				continue
  3635  			}
  3636  			if filter {
  3637  				for _, addr := range addrs {
  3638  					_, ok := addresses[addr.String()]
  3639  					if ok {
  3640  						goto include
  3641  					}
  3642  				}
  3643  				continue
  3644  			}
  3645  
  3646  		include:
  3647  			// At the moment watch-only addresses are not supported, so all
  3648  			// recorded outputs that are not multisig are "spendable".
  3649  			// Multisig outputs are only "spendable" if all keys are
  3650  			// controlled by this wallet.
  3651  			//
  3652  			// TODO: Each case will need updates when watch-only addrs
  3653  			// is added.  For P2PK, P2PKH, and P2SH, the address must be
  3654  			// looked up and not be watching-only.  For multisig, all
  3655  			// pubkeys must belong to the manager with the associated
  3656  			// private key (currently it only checks whether the pubkey
  3657  			// exists, since the private key is required at the moment).
  3658  			var spendable bool
  3659  			var redeemScript []byte
  3660  		scSwitch:
  3661  			switch sc {
  3662  			case stdscript.STPubKeyHashEcdsaSecp256k1:
  3663  				spendable = true
  3664  			case stdscript.STPubKeyEcdsaSecp256k1:
  3665  				spendable = true
  3666  			case stdscript.STScriptHash:
  3667  				spendable = true
  3668  				if len(addrs) != 1 {
  3669  					return errors.Errorf("invalid address count for pay-to-script-hash output")
  3670  				}
  3671  				redeemScript, err = w.manager.RedeemScript(addrmgrNs, addrs[0])
  3672  				if err != nil {
  3673  					return err
  3674  				}
  3675  			case stdscript.STStakeGenPubKeyHash, stdscript.STStakeGenScriptHash:
  3676  				spendable = true
  3677  			case stdscript.STStakeRevocationPubKeyHash, stdscript.STStakeRevocationScriptHash:
  3678  				spendable = true
  3679  			case stdscript.STStakeChangePubKeyHash, stdscript.STStakeChangeScriptHash:
  3680  				spendable = true
  3681  			case stdscript.STMultiSig:
  3682  				for _, a := range addrs {
  3683  					_, err := w.manager.Address(addrmgrNs, a)
  3684  					if err == nil {
  3685  						continue
  3686  					}
  3687  					if errors.Is(err, errors.NotExist) {
  3688  						break scSwitch
  3689  					}
  3690  					return err
  3691  				}
  3692  				spendable = true
  3693  			}
  3694  
  3695  			// If address decoding failed, the output is not spendable
  3696  			// regardless of detected script type.
  3697  			spendable = spendable && len(addrs) > 0
  3698  
  3699  			result := &types.ListUnspentResult{
  3700  				TxID:          output.OutPoint.Hash.String(),
  3701  				Vout:          output.OutPoint.Index,
  3702  				Tree:          output.OutPoint.Tree,
  3703  				Account:       acctName,
  3704  				ScriptPubKey:  hex.EncodeToString(output.PkScript),
  3705  				RedeemScript:  hex.EncodeToString(redeemScript),
  3706  				TxType:        int(details.TxType),
  3707  				Amount:        output.Amount.ToCoin(),
  3708  				Confirmations: int64(confs),
  3709  				Spendable:     spendable,
  3710  			}
  3711  
  3712  			// BUG: this should be a JSON array so that all
  3713  			// addresses can be included, or removed (and the
  3714  			// caller extracts addresses from the pkScript).
  3715  			if len(addrs) > 0 {
  3716  				result.Address = addrs[0].String()
  3717  			}
  3718  
  3719  			results = append(results, result)
  3720  		}
  3721  		return nil
  3722  	})
  3723  	if err != nil {
  3724  		return nil, errors.E(op, err)
  3725  	}
  3726  	return results, nil
  3727  }
  3728  
  3729  func (w *Wallet) LoadPrivateKey(ctx context.Context, addr stdaddr.Address) (key *secp256k1.PrivateKey,
  3730  	zero func(), err error) {
  3731  
  3732  	const op errors.Op = "wallet.LoadPrivateKey"
  3733  	err = walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  3734  		addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
  3735  		var err error
  3736  		key, zero, err = w.manager.PrivateKey(addrmgrNs, addr)
  3737  		return err
  3738  	})
  3739  	if err != nil {
  3740  		return nil, nil, errors.E(op, err)
  3741  	}
  3742  
  3743  	return key, zero, nil
  3744  }
  3745  
  3746  // DumpWIFPrivateKey returns the WIF encoded private key for a
  3747  // single wallet address.
  3748  func (w *Wallet) DumpWIFPrivateKey(ctx context.Context, addr stdaddr.Address) (string, error) {
  3749  	const op errors.Op = "wallet.DumpWIFPrivateKey"
  3750  	privKey, zero, err := w.LoadPrivateKey(ctx, addr)
  3751  	if err != nil {
  3752  		return "", errors.E(op, err)
  3753  	}
  3754  	defer zero()
  3755  	wif, err := dcrutil.NewWIF(privKey.Serialize(), w.chainParams.PrivateKeyID,
  3756  		dcrec.STEcdsaSecp256k1)
  3757  	if err != nil {
  3758  		return "", errors.E(op, err)
  3759  	}
  3760  	return wif.String(), nil
  3761  }
  3762  
  3763  // ImportPrivateKey imports a private key to the wallet and writes the new
  3764  // wallet to disk.
  3765  func (w *Wallet) ImportPrivateKey(ctx context.Context, wif *dcrutil.WIF) (string, error) {
  3766  	const op errors.Op = "wallet.ImportPrivateKey"
  3767  	// Attempt to import private key into wallet.
  3768  	var addr stdaddr.Address
  3769  	var props *udb.AccountProperties
  3770  	err := walletdb.Update(ctx, w.db, func(tx walletdb.ReadWriteTx) error {
  3771  		addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
  3772  		maddr, err := w.manager.ImportPrivateKey(addrmgrNs, wif)
  3773  		if err == nil {
  3774  			addr = maddr.Address()
  3775  			props, err = w.manager.AccountProperties(
  3776  				addrmgrNs, udb.ImportedAddrAccount)
  3777  		}
  3778  		return err
  3779  	})
  3780  	if err != nil {
  3781  		return "", errors.E(op, err)
  3782  	}
  3783  
  3784  	if n, err := w.NetworkBackend(); err == nil {
  3785  		err := n.LoadTxFilter(ctx, false, []stdaddr.Address{addr}, nil)
  3786  		if err != nil {
  3787  			return "", errors.E(op, err)
  3788  		}
  3789  	}
  3790  
  3791  	addrStr := addr.String()
  3792  	log.Infof("Imported payment address %s", addrStr)
  3793  
  3794  	w.NtfnServer.notifyAccountProperties(props)
  3795  
  3796  	// Return the payment address string of the imported private key.
  3797  	return addrStr, nil
  3798  }
  3799  
  3800  // ImportPublicKey imports a compressed secp256k1 public key and its derived
  3801  // P2PKH address.
  3802  func (w *Wallet) ImportPublicKey(ctx context.Context, pubkey []byte) (string, error) {
  3803  	const op errors.Op = "wallet.ImportPublicKey"
  3804  	// Attempt to import private key into wallet.
  3805  	var addr stdaddr.Address
  3806  	var props *udb.AccountProperties
  3807  	err := walletdb.Update(ctx, w.db, func(tx walletdb.ReadWriteTx) error {
  3808  		addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
  3809  		maddr, err := w.manager.ImportPublicKey(addrmgrNs, pubkey)
  3810  		if err == nil {
  3811  			addr = maddr.Address()
  3812  			props, err = w.manager.AccountProperties(
  3813  				addrmgrNs, udb.ImportedAddrAccount)
  3814  		}
  3815  		return err
  3816  	})
  3817  	if err != nil {
  3818  		return "", errors.E(op, err)
  3819  	}
  3820  
  3821  	if n, err := w.NetworkBackend(); err == nil {
  3822  		err := n.LoadTxFilter(ctx, false, []stdaddr.Address{addr}, nil)
  3823  		if err != nil {
  3824  			return "", errors.E(op, err)
  3825  		}
  3826  	}
  3827  
  3828  	addrStr := addr.String()
  3829  	log.Infof("Imported payment address %s", addrStr)
  3830  
  3831  	w.NtfnServer.notifyAccountProperties(props)
  3832  
  3833  	// Return the payment address string of the imported private key.
  3834  	return addrStr, nil
  3835  }
  3836  
  3837  // ImportScript imports a redeemscript to the wallet. If it also allows the
  3838  // user to specify whether or not they want the redeemscript to be rescanned,
  3839  // and how far back they wish to rescan.
  3840  func (w *Wallet) ImportScript(ctx context.Context, rs []byte) error {
  3841  	const op errors.Op = "wallet.ImportScript"
  3842  	err := walletdb.Update(ctx, w.db, func(tx walletdb.ReadWriteTx) error {
  3843  		addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
  3844  		mscriptaddr, err := w.manager.ImportScript(addrmgrNs, rs)
  3845  		if err != nil {
  3846  			return err
  3847  		}
  3848  
  3849  		addr := mscriptaddr.Address()
  3850  		if n, err := w.NetworkBackend(); err == nil {
  3851  			addrs := []stdaddr.Address{addr}
  3852  			err := n.LoadTxFilter(ctx, false, addrs, nil)
  3853  			if err != nil {
  3854  				return err
  3855  			}
  3856  		}
  3857  
  3858  		log.Infof("Imported script with P2SH address %v", addr)
  3859  		return nil
  3860  	})
  3861  	if err != nil {
  3862  		return errors.E(op, err)
  3863  	}
  3864  	return nil
  3865  }
  3866  
  3867  // VotingXprivFromSeed derives a voting xpriv from a byte seed.
  3868  func (w *Wallet) VotingXprivFromSeed(seed []byte) (*hdkeychain.ExtendedKey, error) {
  3869  	return votingXprivFromSeed(seed, w.ChainParams())
  3870  }
  3871  
  3872  // votingXprivFromSeed derives a voting xpriv from a byte seed. The key is at
  3873  // the same path as the zeroth slip0044 account key for seed.
  3874  func votingXprivFromSeed(seed []byte, params *chaincfg.Params) (*hdkeychain.ExtendedKey, error) {
  3875  	const op errors.Op = "wallet.VotingXprivFromSeed"
  3876  
  3877  	seedSize := len(seed)
  3878  	if seedSize < hdkeychain.MinSeedBytes || seedSize > hdkeychain.MaxSeedBytes {
  3879  		return nil, errors.E(op, errors.Invalid, errors.New("invalid seed length"))
  3880  	}
  3881  
  3882  	// Generate the BIP0044 HD key structure to ensure the provided seed
  3883  	// can generate the required structure with no issues.
  3884  	coinTypeLegacyKeyPriv, coinTypeSLIP0044KeyPriv, acctKeyLegacyPriv, acctKeySLIP0044Priv, err := udb.HDKeysFromSeed(seed, params)
  3885  	if err != nil {
  3886  		return nil, err
  3887  	}
  3888  	coinTypeLegacyKeyPriv.Zero()
  3889  	coinTypeSLIP0044KeyPriv.Zero()
  3890  	acctKeyLegacyPriv.Zero()
  3891  
  3892  	return acctKeySLIP0044Priv, nil
  3893  }
  3894  
  3895  // ImportVotingAccount imports a voting account to the wallet. A password and
  3896  // unique name must be supplied. The xpriv must be for the current running
  3897  // network.
  3898  func (w *Wallet) ImportVotingAccount(ctx context.Context, xpriv *hdkeychain.ExtendedKey,
  3899  	passphrase []byte, name string) (uint32, error) {
  3900  	const op errors.Op = "wallet.ImportVotingAccount"
  3901  	var accountN uint32
  3902  	err := walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  3903  		var err error
  3904  		accountN, err = w.manager.ImportVotingAccount(dbtx, xpriv, passphrase, name)
  3905  		if err != nil {
  3906  			return err
  3907  		}
  3908  		return err
  3909  	})
  3910  	if err != nil {
  3911  		return 0, errors.E(op, err)
  3912  	}
  3913  
  3914  	xpub := xpriv.Neuter()
  3915  
  3916  	extKey, intKey, err := deriveBranches(xpub)
  3917  	if err != nil {
  3918  		return 0, errors.E(op, err)
  3919  	}
  3920  
  3921  	// Internal addresses are used in signing messages and are not expected
  3922  	// to be found used on chain.
  3923  	if n, err := w.NetworkBackend(); err == nil {
  3924  		extAddrs, err := deriveChildAddresses(extKey, 0, w.gapLimit, w.chainParams)
  3925  		if err != nil {
  3926  			return 0, errors.E(op, err)
  3927  		}
  3928  		err = n.LoadTxFilter(ctx, false, extAddrs, nil)
  3929  		if err != nil {
  3930  			return 0, errors.E(op, err)
  3931  		}
  3932  	}
  3933  
  3934  	defer w.addressBuffersMu.Unlock()
  3935  	w.addressBuffersMu.Lock()
  3936  	albExternal := addressBuffer{
  3937  		branchXpub:  extKey,
  3938  		lastUsed:    ^uint32(0),
  3939  		cursor:      0,
  3940  		lastWatched: w.gapLimit - 1,
  3941  	}
  3942  	albInternal := albExternal
  3943  	albInternal.branchXpub = intKey
  3944  	w.addressBuffers[accountN] = &bip0044AccountData{
  3945  		xpub:        xpub,
  3946  		albExternal: albExternal,
  3947  		albInternal: albInternal,
  3948  	}
  3949  
  3950  	return accountN, nil
  3951  }
  3952  
  3953  func (w *Wallet) ImportXpubAccount(ctx context.Context, name string, xpub *hdkeychain.ExtendedKey) error {
  3954  	const op errors.Op = "wallet.ImportXpubAccount"
  3955  	if xpub.IsPrivate() {
  3956  		return errors.E(op, "extended key must be an xpub")
  3957  	}
  3958  
  3959  	extKey, intKey, err := deriveBranches(xpub)
  3960  	if err != nil {
  3961  		return errors.E(op, err)
  3962  	}
  3963  
  3964  	if n, err := w.NetworkBackend(); err == nil {
  3965  		extAddrs, err := deriveChildAddresses(extKey, 0, w.gapLimit, w.chainParams)
  3966  		if err != nil {
  3967  			return errors.E(op, err)
  3968  		}
  3969  		intAddrs, err := deriveChildAddresses(intKey, 0, w.gapLimit, w.chainParams)
  3970  		if err != nil {
  3971  			return errors.E(op, err)
  3972  		}
  3973  		watch := append(extAddrs, intAddrs...)
  3974  		err = n.LoadTxFilter(ctx, false, watch, nil)
  3975  		if err != nil {
  3976  			return errors.E(op, err)
  3977  		}
  3978  	}
  3979  
  3980  	var account uint32
  3981  	err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  3982  		ns := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
  3983  		err := w.manager.ImportXpubAccount(ns, name, xpub)
  3984  		if err != nil {
  3985  			return err
  3986  		}
  3987  		account, err = w.manager.LookupAccount(ns, name)
  3988  		return err
  3989  	})
  3990  	if err != nil {
  3991  		return errors.E(op, err)
  3992  	}
  3993  
  3994  	defer w.addressBuffersMu.Unlock()
  3995  	w.addressBuffersMu.Lock()
  3996  	albExternal := addressBuffer{
  3997  		branchXpub:  extKey,
  3998  		lastUsed:    ^uint32(0),
  3999  		cursor:      0,
  4000  		lastWatched: w.gapLimit - 1,
  4001  	}
  4002  	albInternal := albExternal
  4003  	albInternal.branchXpub = intKey
  4004  	w.addressBuffers[account] = &bip0044AccountData{
  4005  		xpub:        xpub,
  4006  		albExternal: albExternal,
  4007  		albInternal: albInternal,
  4008  	}
  4009  
  4010  	return nil
  4011  }
  4012  
  4013  // StakeInfoData carries counts of ticket states and other various stake data.
  4014  type StakeInfoData struct {
  4015  	BlockHeight  int64
  4016  	TotalSubsidy dcrutil.Amount
  4017  	Sdiff        dcrutil.Amount
  4018  
  4019  	OwnMempoolTix  uint32
  4020  	Unspent        uint32
  4021  	Voted          uint32
  4022  	Revoked        uint32
  4023  	UnspentExpired uint32
  4024  
  4025  	PoolSize      uint32
  4026  	AllMempoolTix uint32
  4027  	Immature      uint32
  4028  	Live          uint32
  4029  	Missed        uint32
  4030  	Expired       uint32
  4031  }
  4032  
  4033  func isVote(tx *wire.MsgTx) bool {
  4034  	return stake.IsSSGen(tx)
  4035  }
  4036  
  4037  func isRevocation(tx *wire.MsgTx) bool {
  4038  	return stake.IsSSRtx(tx)
  4039  }
  4040  
  4041  func isTreasurySpend(tx *wire.MsgTx) bool {
  4042  	return stake.IsTSpend(tx)
  4043  }
  4044  
  4045  // hasVotingAuthority returns whether the 0th output of a ticket purchase can be
  4046  // spent by a vote or revocation created by this wallet.
  4047  func (w *Wallet) hasVotingAuthority(addrmgrNs walletdb.ReadBucket, ticketPurchase *wire.MsgTx) (
  4048  	mine, havePrivKey bool, err error) {
  4049  	out := ticketPurchase.TxOut[0]
  4050  	_, addrs := stdscript.ExtractAddrs(out.Version, out.PkScript, w.chainParams)
  4051  	for _, a := range addrs {
  4052  		var hash160 *[20]byte
  4053  		switch a := a.(type) {
  4054  		case stdaddr.Hash160er:
  4055  			hash160 = a.Hash160()
  4056  		default:
  4057  			continue
  4058  		}
  4059  		if w.manager.ExistsHash160(addrmgrNs, hash160[:]) {
  4060  			haveKey, err := w.manager.HavePrivateKey(addrmgrNs, a)
  4061  			return true, haveKey, err
  4062  		}
  4063  	}
  4064  	return false, false, nil
  4065  }
  4066  
  4067  // StakeInfo collects and returns staking statistics for this wallet.
  4068  func (w *Wallet) StakeInfo(ctx context.Context) (*StakeInfoData, error) {
  4069  	const op errors.Op = "wallet.StakeInfo"
  4070  
  4071  	var res StakeInfoData
  4072  
  4073  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  4074  		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  4075  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  4076  		tipHash, tipHeight := w.txStore.MainChainTip(dbtx)
  4077  		res.BlockHeight = int64(tipHeight)
  4078  		if deployments.DCP0001.Active(tipHeight, w.chainParams.Net) {
  4079  			tipHeader, err := w.txStore.GetBlockHeader(dbtx, &tipHash)
  4080  			if err != nil {
  4081  				return err
  4082  			}
  4083  			sdiff, err := w.nextRequiredDCP0001PoSDifficulty(dbtx, tipHeader, nil)
  4084  			if err != nil {
  4085  				return err
  4086  			}
  4087  			res.Sdiff = sdiff
  4088  		}
  4089  		it := w.txStore.IterateTickets(dbtx)
  4090  		defer it.Close()
  4091  		for it.Next() {
  4092  			// Skip tickets which are not owned by this wallet.
  4093  			owned, _, err := w.hasVotingAuthority(addrmgrNs, &it.MsgTx)
  4094  			if err != nil {
  4095  				return err
  4096  			}
  4097  			if !owned {
  4098  				continue
  4099  			}
  4100  
  4101  			// Check for tickets in mempool
  4102  			if it.Block.Height == -1 {
  4103  				res.OwnMempoolTix++
  4104  				continue
  4105  			}
  4106  
  4107  			// Check for immature tickets
  4108  			if !ticketMatured(w.chainParams, it.Block.Height, tipHeight) {
  4109  				res.Immature++
  4110  				continue
  4111  			}
  4112  
  4113  			// If the ticket was spent, look up the spending tx and determine if
  4114  			// it is a vote or revocation.  If it is a vote, add the earned
  4115  			// subsidy.
  4116  			if it.SpenderHash != (chainhash.Hash{}) {
  4117  				spender, err := w.txStore.Tx(txmgrNs, &it.SpenderHash)
  4118  				if err != nil {
  4119  					return err
  4120  				}
  4121  				switch {
  4122  				case isVote(spender):
  4123  					res.Voted++
  4124  
  4125  					// Add the subsidy.
  4126  					//
  4127  					// This is not the actual subsidy that was earned by this
  4128  					// wallet, but rather the stakebase sum.  If a user uses a
  4129  					// stakepool for voting, this value will include the total
  4130  					// subsidy earned by both the user and the pool together.
  4131  					// Similarly, for stakepool wallets, this includes the
  4132  					// customer's subsidy rather than being just the subsidy
  4133  					// earned by fees.
  4134  					res.TotalSubsidy += dcrutil.Amount(spender.TxIn[0].ValueIn)
  4135  
  4136  				case isRevocation(spender):
  4137  					res.Revoked++
  4138  
  4139  				default:
  4140  					return errors.E(errors.IO, errors.Errorf("ticket spender %v is neither vote nor revocation", &it.SpenderHash))
  4141  				}
  4142  				continue
  4143  			}
  4144  
  4145  			// Ticket is matured but unspent.  Possible states are that the
  4146  			// ticket is live, expired, or missed.
  4147  			res.Unspent++
  4148  			if ticketExpired(w.chainParams, it.Block.Height, tipHeight) {
  4149  				res.UnspentExpired++
  4150  			}
  4151  		}
  4152  		return it.Err()
  4153  	})
  4154  	if err != nil {
  4155  		return nil, errors.E(op, err)
  4156  	}
  4157  	return &res, nil
  4158  }
  4159  
  4160  // StakeInfoPrecise collects and returns staking statistics for this wallet.  It
  4161  // uses RPC to query further information than StakeInfo.
  4162  func (w *Wallet) StakeInfoPrecise(ctx context.Context, rpcCaller Caller) (*StakeInfoData, error) {
  4163  	const op errors.Op = "wallet.StakeInfoPrecise"
  4164  
  4165  	res := &StakeInfoData{}
  4166  	rpc := dcrd.New(rpcCaller)
  4167  	var g errgroup.Group
  4168  	g.Go(func() error {
  4169  		unminedTicketCount, err := rpc.MempoolCount(ctx, "tickets")
  4170  		if err != nil {
  4171  			return err
  4172  		}
  4173  		res.AllMempoolTix = uint32(unminedTicketCount)
  4174  		return nil
  4175  	})
  4176  
  4177  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  4178  		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  4179  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  4180  		tipHash, tipHeight := w.txStore.MainChainTip(dbtx)
  4181  		res.BlockHeight = int64(tipHeight)
  4182  		if deployments.DCP0001.Active(tipHeight, w.chainParams.Net) {
  4183  			tipHeader, err := w.txStore.GetBlockHeader(dbtx, &tipHash)
  4184  			if err != nil {
  4185  				return err
  4186  			}
  4187  			sdiff, err := w.nextRequiredDCP0001PoSDifficulty(dbtx, tipHeader, nil)
  4188  			if err != nil {
  4189  				return err
  4190  			}
  4191  			res.Sdiff = sdiff
  4192  		}
  4193  		it := w.txStore.IterateTickets(dbtx)
  4194  		defer it.Close()
  4195  		for it.Next() {
  4196  			// Skip tickets which are not owned by this wallet.
  4197  			owned, _, err := w.hasVotingAuthority(addrmgrNs, &it.MsgTx)
  4198  			if err != nil {
  4199  				return err
  4200  			}
  4201  			if !owned {
  4202  				continue
  4203  			}
  4204  
  4205  			// Check for tickets in mempool
  4206  			if it.Block.Height == -1 {
  4207  				res.OwnMempoolTix++
  4208  				continue
  4209  			}
  4210  
  4211  			// Check for immature tickets
  4212  			if !ticketMatured(w.chainParams, it.Block.Height, tipHeight) {
  4213  				res.Immature++
  4214  				continue
  4215  			}
  4216  
  4217  			// If the ticket was spent, look up the spending tx and determine if
  4218  			// it is a vote or revocation.  If it is a vote, add the earned
  4219  			// subsidy.
  4220  			if it.SpenderHash != (chainhash.Hash{}) {
  4221  				spender, err := w.txStore.TxDetails(txmgrNs, &it.SpenderHash)
  4222  				if err != nil {
  4223  					return err
  4224  				}
  4225  				spenderTx := &spender.MsgTx
  4226  				switch {
  4227  				case isVote(spenderTx):
  4228  					res.Voted++
  4229  
  4230  					// Add the subsidy.
  4231  					//
  4232  					// This is not the actual subsidy that was earned by this
  4233  					// wallet, but rather the stakebase sum.  If a user uses a
  4234  					// stakepool for voting, this value will include the total
  4235  					// subsidy earned by both the user and the pool together.
  4236  					// Similarly, for stakepool wallets, this includes the
  4237  					// customer's subsidy rather than being just the subsidy
  4238  					// earned by fees.
  4239  					res.TotalSubsidy += dcrutil.Amount(spenderTx.TxIn[0].ValueIn)
  4240  
  4241  				case isRevocation(spenderTx):
  4242  					res.Revoked++
  4243  					// Revoked tickets must be either expired or missed.
  4244  					// Assume expired unless the revocation occurs before
  4245  					// the expiry time.  This assumption may not be accurate
  4246  					// for tickets that were missed prior to the activation
  4247  					// of DCP0009.
  4248  					params := w.chainParams
  4249  					expired := spender.Block.Height-it.Block.Height >=
  4250  						int32(params.TicketExpiryBlocks())+
  4251  							int32(params.TicketMaturity)
  4252  					if expired {
  4253  						res.Expired++
  4254  					} else {
  4255  						res.Missed++
  4256  					}
  4257  
  4258  				default:
  4259  					return errors.E(errors.IO, errors.Errorf("ticket spender %v is neither vote nor revocation", &it.SpenderHash))
  4260  				}
  4261  				continue
  4262  			}
  4263  
  4264  			// Ticket matured, unspent, and therefore live.
  4265  			res.Unspent++
  4266  			res.Live++
  4267  		}
  4268  		if err := it.Err(); err != nil {
  4269  			return err
  4270  		}
  4271  
  4272  		// Include an estimate of the live ticket pool size. The correct
  4273  		// poolsize would be the pool size to be mined into the next block,
  4274  		// which takes into account maturing stake tickets, voters, and expiring
  4275  		// tickets. There currently isn't a way to get this from the consensus
  4276  		// RPC server, so just use the current block pool size as a "good
  4277  		// enough" estimate for now.
  4278  		serHeader, err := w.txStore.GetSerializedBlockHeader(txmgrNs, &tipHash)
  4279  		if err != nil {
  4280  			return err
  4281  		}
  4282  		var tipHeader wire.BlockHeader
  4283  		err = tipHeader.Deserialize(bytes.NewReader(serHeader))
  4284  		if err != nil {
  4285  			return err
  4286  		}
  4287  		res.PoolSize = tipHeader.PoolSize
  4288  
  4289  		return nil
  4290  	})
  4291  	if err != nil {
  4292  		return nil, errors.E(op, err)
  4293  	}
  4294  
  4295  	// Wait for MempoolCount call from beginning of function to complete.
  4296  	err = g.Wait()
  4297  	if err != nil {
  4298  		return nil, errors.E(op, err)
  4299  	}
  4300  
  4301  	return res, nil
  4302  }
  4303  
  4304  // LockedOutpoint returns whether an outpoint has been marked as locked and
  4305  // should not be used as an input for created transactions.
  4306  func (w *Wallet) LockedOutpoint(txHash *chainhash.Hash, index uint32) bool {
  4307  	op := outpoint{*txHash, index}
  4308  	w.lockedOutpointMu.Lock()
  4309  	_, locked := w.lockedOutpoints[op]
  4310  	w.lockedOutpointMu.Unlock()
  4311  	return locked
  4312  }
  4313  
  4314  // LockOutpoint marks an outpoint as locked, that is, it should not be used as
  4315  // an input for newly created transactions.
  4316  func (w *Wallet) LockOutpoint(txHash *chainhash.Hash, index uint32) {
  4317  	op := outpoint{*txHash, index}
  4318  	w.lockedOutpointMu.Lock()
  4319  	w.lockedOutpoints[op] = struct{}{}
  4320  	w.lockedOutpointMu.Unlock()
  4321  }
  4322  
  4323  // UnlockOutpoint marks an outpoint as unlocked, that is, it may be used as an
  4324  // input for newly created transactions.
  4325  func (w *Wallet) UnlockOutpoint(txHash *chainhash.Hash, index uint32) {
  4326  	op := outpoint{*txHash, index}
  4327  	w.lockedOutpointMu.Lock()
  4328  	delete(w.lockedOutpoints, op)
  4329  	w.lockedOutpointMu.Unlock()
  4330  }
  4331  
  4332  // ResetLockedOutpoints resets the set of locked outpoints so all may be used
  4333  // as inputs for new transactions.
  4334  func (w *Wallet) ResetLockedOutpoints() {
  4335  	w.lockedOutpointMu.Lock()
  4336  	w.lockedOutpoints = make(map[outpoint]struct{})
  4337  	w.lockedOutpointMu.Unlock()
  4338  }
  4339  
  4340  // LockedOutpoints returns a slice of currently locked outpoints.  This is
  4341  // intended to be used by marshaling the result as a JSON array for
  4342  // listlockunspent RPC results.
  4343  func (w *Wallet) LockedOutpoints(ctx context.Context, accountName string) ([]dcrdtypes.TransactionInput, error) {
  4344  	w.lockedOutpointMu.Lock()
  4345  	allLocked := make([]outpoint, len(w.lockedOutpoints))
  4346  	i := 0
  4347  	for op := range w.lockedOutpoints {
  4348  		allLocked[i] = op
  4349  		i++
  4350  	}
  4351  	w.lockedOutpointMu.Unlock()
  4352  
  4353  	allAccts := accountName == "" || accountName == "*"
  4354  	acctLocked := make([]dcrdtypes.TransactionInput, 0, len(allLocked))
  4355  	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  4356  		addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
  4357  		txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
  4358  
  4359  		// Outpoints are not validated before they're locked, so
  4360  		// invalid outpoints may be locked. Simply ignore.
  4361  		for i := range allLocked {
  4362  			op := &allLocked[i]
  4363  			details, err := w.txStore.TxDetails(txmgrNs, &op.hash)
  4364  			if err != nil {
  4365  				if errors.Is(err, errors.NotExist) {
  4366  					// invalid txid
  4367  					continue
  4368  				}
  4369  				return err
  4370  			}
  4371  			if int(op.index) >= len(details.MsgTx.TxOut) {
  4372  				// valid txid, invalid vout
  4373  				continue
  4374  			}
  4375  			output := details.MsgTx.TxOut[op.index]
  4376  
  4377  			if !allAccts {
  4378  				// Lookup the associated account for the output.
  4379  				_, addrs := stdscript.ExtractAddrs(output.Version, output.PkScript, w.chainParams)
  4380  				if len(addrs) == 0 {
  4381  					continue
  4382  				}
  4383  				var opAcct string
  4384  				acct, err := w.manager.AddrAccount(addrmgrNs, addrs[0])
  4385  				if err == nil {
  4386  					s, err := w.manager.AccountName(addrmgrNs, acct)
  4387  					if err == nil {
  4388  						opAcct = s
  4389  					}
  4390  				}
  4391  				if opAcct != accountName {
  4392  					continue
  4393  				}
  4394  			}
  4395  
  4396  			var tree int8
  4397  			if details.TxType != stake.TxTypeRegular {
  4398  				tree = 1
  4399  			}
  4400  			acctLocked = append(acctLocked, dcrdtypes.TransactionInput{
  4401  				Amount: dcrutil.Amount(output.Value).ToCoin(),
  4402  				Txid:   op.hash.String(),
  4403  				Vout:   op.index,
  4404  				Tree:   tree,
  4405  			})
  4406  		}
  4407  		return nil
  4408  	})
  4409  
  4410  	return acctLocked, err
  4411  }
  4412  
  4413  // UnminedTransactions returns all unmined transactions from the wallet.
  4414  // Transactions are sorted in dependency order making it suitable to range them
  4415  // in order to broadcast at wallet startup.  This method skips over any
  4416  // transactions that are recorded as unpublished.
  4417  func (w *Wallet) UnminedTransactions(ctx context.Context) ([]*wire.MsgTx, error) {
  4418  	const op errors.Op = "wallet.UnminedTransactions"
  4419  	var recs []*udb.TxRecord
  4420  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  4421  		var err error
  4422  		recs, err = w.txStore.UnminedTxs(dbtx)
  4423  		return err
  4424  	})
  4425  	if err != nil {
  4426  		return nil, errors.E(op, err)
  4427  	}
  4428  	txs := make([]*wire.MsgTx, 0, len(recs))
  4429  	for i := range recs {
  4430  		if recs[i].Unpublished {
  4431  			continue
  4432  		}
  4433  		txs = append(txs, &recs[i].MsgTx)
  4434  	}
  4435  	return txs, nil
  4436  }
  4437  
  4438  // SortedActivePaymentAddresses returns a slice of all active payment
  4439  // addresses in a wallet.
  4440  func (w *Wallet) SortedActivePaymentAddresses(ctx context.Context) ([]string, error) {
  4441  	const op errors.Op = "wallet.SortedActivePaymentAddresses"
  4442  	var addrStrs []string
  4443  	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  4444  		addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
  4445  		return w.manager.ForEachActiveAddress(addrmgrNs, func(addr stdaddr.Address) error {
  4446  			addrStrs = append(addrStrs, addr.String())
  4447  			return nil
  4448  		})
  4449  	})
  4450  	if err != nil {
  4451  		return nil, errors.E(op, err)
  4452  	}
  4453  
  4454  	sort.Strings(addrStrs)
  4455  	return addrStrs, nil
  4456  }
  4457  
  4458  // confirmed checks whether a transaction at height txHeight has met minconf
  4459  // confirmations for a blockchain at height curHeight.
  4460  func confirmed(minconf, txHeight, curHeight int32) bool {
  4461  	return confirms(txHeight, curHeight) >= minconf
  4462  }
  4463  
  4464  // confirms returns the number of confirmations for a transaction in a block at
  4465  // height txHeight (or -1 for an unconfirmed tx) given the chain height
  4466  // curHeight.
  4467  func confirms(txHeight, curHeight int32) int32 {
  4468  	switch {
  4469  	case txHeight == -1, txHeight > curHeight:
  4470  		return 0
  4471  	default:
  4472  		return curHeight - txHeight + 1
  4473  	}
  4474  }
  4475  
  4476  // coinbaseMatured returns whether a transaction mined at txHeight has
  4477  // reached coinbase maturity in a chain with tip height curHeight.
  4478  func coinbaseMatured(params *chaincfg.Params, txHeight, curHeight int32) bool {
  4479  	return txHeight >= 0 && curHeight-txHeight+1 > int32(params.CoinbaseMaturity)
  4480  }
  4481  
  4482  // ticketChangeMatured returns whether a ticket change mined at
  4483  // txHeight has reached ticket maturity in a chain with a tip height
  4484  // curHeight.
  4485  func ticketChangeMatured(params *chaincfg.Params, txHeight, curHeight int32) bool {
  4486  	return txHeight >= 0 && curHeight-txHeight+1 > int32(params.SStxChangeMaturity)
  4487  }
  4488  
  4489  // ticketMatured returns whether a ticket mined at txHeight has
  4490  // reached ticket maturity in a chain with a tip height curHeight.
  4491  func ticketMatured(params *chaincfg.Params, txHeight, curHeight int32) bool {
  4492  	// dcrd has an off-by-one in the calculation of the ticket
  4493  	// maturity, which results in maturity being one block higher
  4494  	// than the params would indicate.
  4495  	return txHeight >= 0 && curHeight-txHeight > int32(params.TicketMaturity)
  4496  }
  4497  
  4498  // ticketExpired returns whether a ticket mined at txHeight has
  4499  // reached ticket expiry in a chain with a tip height curHeight.
  4500  func ticketExpired(params *chaincfg.Params, txHeight, curHeight int32) bool {
  4501  	// Ticket maturity off-by-one extends to the expiry depth as well.
  4502  	return txHeight >= 0 && curHeight-txHeight > int32(params.TicketMaturity)+int32(params.TicketExpiry)
  4503  }
  4504  
  4505  // AccountTotalReceivedResult is a single result for the
  4506  // Wallet.TotalReceivedForAccounts method.
  4507  type AccountTotalReceivedResult struct {
  4508  	AccountNumber    uint32
  4509  	AccountName      string
  4510  	TotalReceived    dcrutil.Amount
  4511  	LastConfirmation int32
  4512  }
  4513  
  4514  // TotalReceivedForAccounts iterates through a wallet's transaction history,
  4515  // returning the total amount of decred received for all accounts.
  4516  func (w *Wallet) TotalReceivedForAccounts(ctx context.Context, minConf int32) ([]AccountTotalReceivedResult, error) {
  4517  	const op errors.Op = "wallet.TotalReceivedForAccounts"
  4518  	var results []AccountTotalReceivedResult
  4519  	resultIdxs := make(map[uint32]int)
  4520  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  4521  		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  4522  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  4523  
  4524  		_, tipHeight := w.txStore.MainChainTip(dbtx)
  4525  
  4526  		err := w.manager.ForEachAccount(addrmgrNs, func(account uint32) error {
  4527  			accountName, err := w.manager.AccountName(addrmgrNs, account)
  4528  			if err != nil {
  4529  				return err
  4530  			}
  4531  			resultIdxs[account] = len(resultIdxs)
  4532  			results = append(results, AccountTotalReceivedResult{
  4533  				AccountNumber: account,
  4534  				AccountName:   accountName,
  4535  			})
  4536  			return nil
  4537  		})
  4538  		if err != nil {
  4539  			return err
  4540  		}
  4541  
  4542  		var stopHeight int32
  4543  
  4544  		if minConf > 0 {
  4545  			stopHeight = tipHeight - minConf + 1
  4546  		} else {
  4547  			stopHeight = -1
  4548  		}
  4549  
  4550  		rangeFn := func(details []udb.TxDetails) (bool, error) {
  4551  			for i := range details {
  4552  				detail := &details[i]
  4553  				for _, cred := range detail.Credits {
  4554  					pkVersion := detail.MsgTx.TxOut[cred.Index].Version
  4555  					pkScript := detail.MsgTx.TxOut[cred.Index].PkScript
  4556  					_, addrs := stdscript.ExtractAddrs(pkVersion, pkScript, w.chainParams)
  4557  					if len(addrs) == 0 {
  4558  						continue
  4559  					}
  4560  					outputAcct, err := w.manager.AddrAccount(addrmgrNs, addrs[0])
  4561  					if err == nil {
  4562  						acctIndex := resultIdxs[outputAcct]
  4563  						res := &results[acctIndex]
  4564  						res.TotalReceived += cred.Amount
  4565  						res.LastConfirmation = confirms(
  4566  							detail.Block.Height, tipHeight)
  4567  					}
  4568  				}
  4569  			}
  4570  			return false, nil
  4571  		}
  4572  		return w.txStore.RangeTransactions(ctx, txmgrNs, 0, stopHeight, rangeFn)
  4573  	})
  4574  	if err != nil {
  4575  		return nil, errors.E(op, err)
  4576  	}
  4577  	return results, nil
  4578  }
  4579  
  4580  // TotalReceivedForAddr iterates through a wallet's transaction history,
  4581  // returning the total amount of decred received for a single wallet
  4582  // address.
  4583  func (w *Wallet) TotalReceivedForAddr(ctx context.Context, addr stdaddr.Address, minConf int32) (dcrutil.Amount, error) {
  4584  	const op errors.Op = "wallet.TotalReceivedForAddr"
  4585  	var amount dcrutil.Amount
  4586  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  4587  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  4588  
  4589  		_, tipHeight := w.txStore.MainChainTip(dbtx)
  4590  
  4591  		var (
  4592  			addrStr    = addr.String()
  4593  			stopHeight int32
  4594  		)
  4595  
  4596  		if minConf > 0 {
  4597  			stopHeight = tipHeight - minConf + 1
  4598  		} else {
  4599  			stopHeight = -1
  4600  		}
  4601  		rangeFn := func(details []udb.TxDetails) (bool, error) {
  4602  			for i := range details {
  4603  				detail := &details[i]
  4604  				for _, cred := range detail.Credits {
  4605  					pkVersion := detail.MsgTx.TxOut[cred.Index].Version
  4606  					pkScript := detail.MsgTx.TxOut[cred.Index].PkScript
  4607  					_, addrs := stdscript.ExtractAddrs(pkVersion, pkScript, w.chainParams)
  4608  					for _, a := range addrs { // no addresses means non-standard credit, ignored
  4609  						if addrStr == a.String() {
  4610  							amount += cred.Amount
  4611  							break
  4612  						}
  4613  					}
  4614  				}
  4615  			}
  4616  			return false, nil
  4617  		}
  4618  		return w.txStore.RangeTransactions(ctx, txmgrNs, 0, stopHeight, rangeFn)
  4619  	})
  4620  	if err != nil {
  4621  		return 0, errors.E(op, err)
  4622  	}
  4623  	return amount, nil
  4624  }
  4625  
  4626  // SendOutputs creates and sends payment transactions. It returns the
  4627  // transaction hash upon success
  4628  func (w *Wallet) SendOutputs(ctx context.Context, outputs []*wire.TxOut, account, changeAccount uint32, minconf int32) (*chainhash.Hash, error) {
  4629  	const op errors.Op = "wallet.SendOutputs"
  4630  	relayFee := w.RelayFee()
  4631  	for _, output := range outputs {
  4632  		err := txrules.CheckOutput(output, relayFee)
  4633  		if err != nil {
  4634  			return nil, errors.E(op, err)
  4635  		}
  4636  	}
  4637  
  4638  	a := &authorTx{
  4639  		outputs:            outputs,
  4640  		account:            account,
  4641  		changeAccount:      changeAccount,
  4642  		minconf:            minconf,
  4643  		randomizeChangeIdx: true,
  4644  		txFee:              relayFee,
  4645  		dontSignTx:         false,
  4646  		isTreasury:         false,
  4647  	}
  4648  	err := w.authorTx(ctx, op, a)
  4649  	if err != nil {
  4650  		return nil, err
  4651  	}
  4652  	err = w.recordAuthoredTx(ctx, op, a)
  4653  	if err != nil {
  4654  		return nil, err
  4655  	}
  4656  	err = w.publishAndWatch(ctx, op, nil, a.atx.Tx, a.watch)
  4657  	if err != nil {
  4658  		return nil, err
  4659  	}
  4660  	hash := a.atx.Tx.TxHash()
  4661  	return &hash, nil
  4662  }
  4663  
  4664  // transaction hash upon success
  4665  func (w *Wallet) SendOutputsToTreasury(ctx context.Context, outputs []*wire.TxOut, account, changeAccount uint32, minconf int32) (*chainhash.Hash, error) {
  4666  	const op errors.Op = "wallet.SendOutputsToTreasury"
  4667  	relayFee := w.RelayFee()
  4668  	for _, output := range outputs {
  4669  		err := txrules.CheckOutput(output, relayFee)
  4670  		if err != nil {
  4671  			return nil, errors.E(op, err)
  4672  		}
  4673  	}
  4674  
  4675  	a := &authorTx{
  4676  		outputs:            outputs,
  4677  		account:            account,
  4678  		changeAccount:      changeAccount,
  4679  		minconf:            minconf,
  4680  		randomizeChangeIdx: false,
  4681  		txFee:              relayFee,
  4682  		dontSignTx:         false,
  4683  		isTreasury:         true,
  4684  	}
  4685  	err := w.authorTx(ctx, op, a)
  4686  	if err != nil {
  4687  		return nil, err
  4688  	}
  4689  	err = w.recordAuthoredTx(ctx, op, a)
  4690  	if err != nil {
  4691  		return nil, err
  4692  	}
  4693  	err = w.publishAndWatch(ctx, op, nil, a.atx.Tx, a.watch)
  4694  	if err != nil {
  4695  		return nil, err
  4696  	}
  4697  	hash := a.atx.Tx.TxHash()
  4698  	return &hash, nil
  4699  }
  4700  
  4701  // SignatureError records the underlying error when validating a transaction
  4702  // input signature.
  4703  type SignatureError struct {
  4704  	InputIndex uint32
  4705  	Error      error
  4706  }
  4707  
  4708  type sigDataSource struct {
  4709  	key    func(stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error)
  4710  	script func(stdaddr.Address) ([]byte, error)
  4711  }
  4712  
  4713  func (s sigDataSource) GetKey(a stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error) {
  4714  	return s.key(a)
  4715  }
  4716  func (s sigDataSource) GetScript(a stdaddr.Address) ([]byte, error) { return s.script(a) }
  4717  
  4718  // SignTransaction uses secrets of the wallet, as well as additional secrets
  4719  // passed in by the caller, to create and add input signatures to a transaction.
  4720  //
  4721  // Transaction input script validation is used to confirm that all signatures
  4722  // are valid.  For any invalid input, a SignatureError is added to the returns.
  4723  // The final error return is reserved for unexpected or fatal errors, such as
  4724  // being unable to determine a previous output script to redeem.
  4725  //
  4726  // The transaction pointed to by tx is modified by this function.
  4727  func (w *Wallet) SignTransaction(ctx context.Context, tx *wire.MsgTx, hashType txscript.SigHashType, additionalPrevScripts map[wire.OutPoint][]byte,
  4728  	additionalKeysByAddress map[string]*dcrutil.WIF, p2shRedeemScriptsByAddress map[string][]byte) ([]SignatureError, error) {
  4729  
  4730  	const op errors.Op = "wallet.SignTransaction"
  4731  
  4732  	var doneFuncs []func()
  4733  	defer func() {
  4734  		for _, f := range doneFuncs {
  4735  			f()
  4736  		}
  4737  	}()
  4738  
  4739  	var signErrors []SignatureError
  4740  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  4741  		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  4742  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  4743  
  4744  		for i, txIn := range tx.TxIn {
  4745  			// For an SSGen tx, skip the first input as it is a stake base
  4746  			// and doesn't need to be signed.  The transaction is expected
  4747  			// to already contain the consensus-validated stakebase script.
  4748  			if i == 0 && stake.IsSSGen(tx) {
  4749  				continue
  4750  			}
  4751  
  4752  			prevOutScript, ok := additionalPrevScripts[txIn.PreviousOutPoint]
  4753  			if !ok {
  4754  				prevHash := &txIn.PreviousOutPoint.Hash
  4755  				prevIndex := txIn.PreviousOutPoint.Index
  4756  				txDetails, err := w.txStore.TxDetails(txmgrNs, prevHash)
  4757  				if errors.Is(err, errors.NotExist) {
  4758  					return errors.Errorf("%v not found", &txIn.PreviousOutPoint)
  4759  				} else if err != nil {
  4760  					return err
  4761  				}
  4762  				prevOutScript = txDetails.MsgTx.TxOut[prevIndex].PkScript
  4763  			}
  4764  
  4765  			// Set up our callbacks that we pass to txscript so it can
  4766  			// look up the appropriate keys and scripts by address.
  4767  			var source sigDataSource
  4768  			source.key = func(addr stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error) {
  4769  				if len(additionalKeysByAddress) != 0 {
  4770  					addrStr := addr.String()
  4771  					wif, ok := additionalKeysByAddress[addrStr]
  4772  					if !ok {
  4773  						return nil, 0, false,
  4774  							errors.Errorf("no key for address (needed: %v, have %v)",
  4775  								addr, additionalKeysByAddress)
  4776  					}
  4777  					return wif.PrivKey(), dcrec.STEcdsaSecp256k1, true, nil
  4778  				}
  4779  
  4780  				key, done, err := w.manager.PrivateKey(addrmgrNs, addr)
  4781  				if err != nil {
  4782  					return nil, 0, false, err
  4783  				}
  4784  				doneFuncs = append(doneFuncs, done)
  4785  				return key.Serialize(), dcrec.STEcdsaSecp256k1, true, nil
  4786  			}
  4787  			source.script = func(addr stdaddr.Address) ([]byte, error) {
  4788  				// If keys were provided then we can only use the
  4789  				// redeem scripts provided with our inputs, too.
  4790  				if len(additionalKeysByAddress) != 0 {
  4791  					addrStr := addr.String()
  4792  					script, ok := p2shRedeemScriptsByAddress[addrStr]
  4793  					if !ok {
  4794  						return nil, errors.New("no script for " +
  4795  							"address")
  4796  					}
  4797  					return script, nil
  4798  				}
  4799  
  4800  				return w.manager.RedeemScript(addrmgrNs, addr)
  4801  			}
  4802  
  4803  			// SigHashSingle inputs can only be signed if there's a
  4804  			// corresponding output. However this could be already signed,
  4805  			// so we always verify the output.
  4806  			if (hashType&txscript.SigHashSingle) !=
  4807  				txscript.SigHashSingle || i < len(tx.TxOut) {
  4808  
  4809  				script, err := sign.SignTxOutput(w.ChainParams(),
  4810  					tx, i, prevOutScript, hashType, source, source, txIn.SignatureScript, true) // Yes treasury
  4811  				// Failure to sign isn't an error, it just means that
  4812  				// the tx isn't complete.
  4813  				if err != nil {
  4814  					signErrors = append(signErrors, SignatureError{
  4815  						InputIndex: uint32(i),
  4816  						Error:      errors.E(op, err),
  4817  					})
  4818  					continue
  4819  				}
  4820  				txIn.SignatureScript = script
  4821  			}
  4822  
  4823  			// Either it was already signed or we just signed it.
  4824  			// Find out if it is completely satisfied or still needs more.
  4825  			vm, err := txscript.NewEngine(prevOutScript, tx, i,
  4826  				sanityVerifyFlags, scriptVersionAssumed, nil)
  4827  			if err == nil {
  4828  				err = vm.Execute()
  4829  			}
  4830  			if err != nil {
  4831  				var multisigNotEnoughSigs bool
  4832  				if errors.Is(err, txscript.ErrInvalidStackOperation) {
  4833  					pkScript := additionalPrevScripts[txIn.PreviousOutPoint]
  4834  					class, addr := stdscript.ExtractAddrs(scriptVersionAssumed, pkScript, w.ChainParams())
  4835  					if class == stdscript.STScriptHash && len(addr) > 0 {
  4836  						redeemScript, _ := source.script(addr[0])
  4837  						if stdscript.IsMultiSigScriptV0(redeemScript) {
  4838  							multisigNotEnoughSigs = true
  4839  						}
  4840  					}
  4841  				}
  4842  				// Only report an error for the script engine in the event
  4843  				// that it's not a multisignature underflow, indicating that
  4844  				// we didn't have enough signatures in front of the
  4845  				// redeemScript rather than an actual error.
  4846  				if !multisigNotEnoughSigs {
  4847  					signErrors = append(signErrors, SignatureError{
  4848  						InputIndex: uint32(i),
  4849  						Error:      errors.E(op, err),
  4850  					})
  4851  				}
  4852  			}
  4853  		}
  4854  		return nil
  4855  	})
  4856  	if err != nil {
  4857  		return nil, errors.E(op, err)
  4858  	}
  4859  	return signErrors, nil
  4860  }
  4861  
  4862  // CreateSignature returns the raw signature created by the private key of addr
  4863  // for tx's idx'th input script and the serialized compressed pubkey for the
  4864  // address.
  4865  func (w *Wallet) CreateSignature(ctx context.Context, tx *wire.MsgTx, idx uint32, addr stdaddr.Address,
  4866  	hashType txscript.SigHashType, prevPkScript []byte) (sig, pubkey []byte, err error) {
  4867  	const op errors.Op = "wallet.CreateSignature"
  4868  	var privKey *secp256k1.PrivateKey
  4869  	var pubKey *secp256k1.PublicKey
  4870  	var done func()
  4871  	defer func() {
  4872  		if done != nil {
  4873  			done()
  4874  		}
  4875  	}()
  4876  
  4877  	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  4878  		ns := dbtx.ReadBucket(waddrmgrNamespaceKey)
  4879  
  4880  		var err error
  4881  		privKey, done, err = w.manager.PrivateKey(ns, addr)
  4882  		if err != nil {
  4883  			return err
  4884  		}
  4885  		pubKey = privKey.PubKey()
  4886  		return nil
  4887  	})
  4888  	if err != nil {
  4889  		return nil, nil, errors.E(op, err)
  4890  	}
  4891  
  4892  	sig, err = sign.RawTxInSignature(tx, int(idx), prevPkScript, hashType,
  4893  		privKey.Serialize(), dcrec.STEcdsaSecp256k1)
  4894  	if err != nil {
  4895  		return nil, nil, errors.E(op, err)
  4896  	}
  4897  
  4898  	return sig, pubKey.SerializeCompressed(), nil
  4899  }
  4900  
  4901  // isRelevantTx determines whether the transaction is relevant to the wallet and
  4902  // should be recorded in the database.
  4903  func (w *Wallet) isRelevantTx(dbtx walletdb.ReadTx, tx *wire.MsgTx) bool {
  4904  	addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  4905  
  4906  	for _, in := range tx.TxIn {
  4907  		// Input is relevant if it contains a saved redeem script or spends a
  4908  		// wallet output.
  4909  		rs := stdscript.MultiSigRedeemScriptFromScriptSigV0(in.SignatureScript)
  4910  		if rs != nil && w.manager.ExistsHash160(addrmgrNs,
  4911  			dcrutil.Hash160(rs)) {
  4912  			return true
  4913  		}
  4914  		if w.txStore.ExistsUTXO(dbtx, &in.PreviousOutPoint) {
  4915  			return true
  4916  		}
  4917  	}
  4918  	for _, out := range tx.TxOut {
  4919  		_, addrs := stdscript.ExtractAddrs(out.Version, out.PkScript, w.chainParams)
  4920  		for _, a := range addrs {
  4921  			var hash160 *[20]byte
  4922  			switch a := a.(type) {
  4923  			case stdaddr.Hash160er:
  4924  				hash160 = a.Hash160()
  4925  			default:
  4926  				continue
  4927  			}
  4928  			if w.manager.ExistsHash160(addrmgrNs, hash160[:]) {
  4929  				return true
  4930  			}
  4931  		}
  4932  	}
  4933  
  4934  	return false
  4935  }
  4936  
  4937  // DetermineRelevantTxs splits the given transactions into slices of relevant
  4938  // and non-wallet-relevant transactions (respectively).
  4939  func (w *Wallet) DetermineRelevantTxs(ctx context.Context, txs ...*wire.MsgTx) ([]*wire.MsgTx, []*wire.MsgTx, error) {
  4940  	var relevant, nonRelevant []*wire.MsgTx
  4941  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  4942  		for _, tx := range txs {
  4943  			switch w.isRelevantTx(dbtx, tx) {
  4944  			case true:
  4945  				relevant = append(relevant, tx)
  4946  			default:
  4947  				nonRelevant = append(nonRelevant, tx)
  4948  			}
  4949  		}
  4950  		return nil
  4951  	})
  4952  	return relevant, nonRelevant, err
  4953  }
  4954  
  4955  func (w *Wallet) appendRelevantOutpoints(relevant []wire.OutPoint, dbtx walletdb.ReadTx, tx *wire.MsgTx) []wire.OutPoint {
  4956  	addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  4957  
  4958  	if relevant == nil {
  4959  		relevant = make([]wire.OutPoint, 0, len(tx.TxOut))
  4960  	}
  4961  
  4962  	txHash := tx.TxHash()
  4963  	op := wire.OutPoint{
  4964  		Hash: txHash,
  4965  	}
  4966  	isTicket := stake.DetermineTxType(tx) == stake.TxTypeSStx
  4967  	var watchedTicketOutputZero bool
  4968  	for i, out := range tx.TxOut {
  4969  		if isTicket && i > 0 && i&1 == 1 && !watchedTicketOutputZero {
  4970  			addr, err := stake.AddrFromSStxPkScrCommitment(out.PkScript, w.chainParams)
  4971  			if err != nil {
  4972  				continue
  4973  			}
  4974  			var hash160 *[20]byte
  4975  			if addr, ok := addr.(stdaddr.Hash160er); ok {
  4976  				hash160 = addr.Hash160()
  4977  			}
  4978  			if hash160 != nil && w.manager.ExistsHash160(addrmgrNs, hash160[:]) {
  4979  				op.Index = 0
  4980  				op.Tree = wire.TxTreeStake
  4981  				relevant = append(relevant, op)
  4982  				watchedTicketOutputZero = true
  4983  				continue
  4984  			}
  4985  		}
  4986  
  4987  		class, addrs := stdscript.ExtractAddrs(out.Version, out.PkScript, w.chainParams)
  4988  		tree := wire.TxTreeRegular
  4989  		if _, isStake := txrules.StakeSubScriptType(class); isStake {
  4990  			tree = wire.TxTreeStake
  4991  		}
  4992  
  4993  		for _, a := range addrs {
  4994  			var hash160 *[20]byte
  4995  			if a, ok := a.(stdaddr.Hash160er); ok {
  4996  				hash160 = a.Hash160()
  4997  			}
  4998  			if hash160 != nil && w.manager.ExistsHash160(addrmgrNs, hash160[:]) {
  4999  				op.Index = uint32(i)
  5000  				op.Tree = tree
  5001  				relevant = append(relevant, op)
  5002  				if isTicket && i == 0 {
  5003  					watchedTicketOutputZero = true
  5004  				}
  5005  				break
  5006  			}
  5007  		}
  5008  	}
  5009  
  5010  	return relevant
  5011  }
  5012  
  5013  // AbandonTransaction removes a transaction, identified by its hash, from
  5014  // the wallet if present.  All transaction spend chains deriving from the
  5015  // transaction's outputs are also removed.  Does not error if the transaction
  5016  // doesn't already exist unmined, but will if the transaction is marked mined in
  5017  // a block on the main chain.
  5018  //
  5019  // Purged transactions may have already been published to the network and may
  5020  // still appear in future blocks, and new transactions spending the same inputs
  5021  // as purged transactions may be rejected by full nodes due to being double
  5022  // spends.  In turn, this can cause the purged transaction to be mined later and
  5023  // replace other transactions authored by the wallet.
  5024  func (w *Wallet) AbandonTransaction(ctx context.Context, hash *chainhash.Hash) error {
  5025  	const opf = "wallet.AbandonTransaction(%v)"
  5026  	err := walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  5027  		ns := dbtx.ReadWriteBucket(wtxmgrNamespaceKey)
  5028  		details, err := w.txStore.TxDetails(ns, hash)
  5029  		if err != nil {
  5030  			return err
  5031  		}
  5032  		if details.Block.Height != -1 {
  5033  			return errors.E(errors.Invalid, errors.Errorf("transaction %v is mined in main chain", hash))
  5034  		}
  5035  		return w.txStore.RemoveUnconfirmed(ns, &details.MsgTx, hash)
  5036  	})
  5037  	if err != nil {
  5038  		op := errors.Opf(opf, hash)
  5039  		return errors.E(op, err)
  5040  	}
  5041  	w.NtfnServer.notifyRemovedTransaction(*hash)
  5042  	return nil
  5043  }
  5044  
  5045  // AllowsHighFees returns whether the wallet is configured to allow or prevent
  5046  // the creation and publishing of transactions with very large fees.
  5047  func (w *Wallet) AllowsHighFees() bool {
  5048  	return w.allowHighFees
  5049  }
  5050  
  5051  // PublishTransaction saves (if relevant) and sends the transaction to the
  5052  // consensus RPC server so it can be propagated to other nodes and eventually
  5053  // mined.  If the send fails, the transaction is not added to the wallet.
  5054  //
  5055  // This method does not check if a transaction pays high fees or not, and it is
  5056  // the caller's responsibility to check this using either the current wallet
  5057  // policy or other configuration parameters.  See txrules.TxPaysHighFees for a
  5058  // check for insanely high transaction fees.
  5059  func (w *Wallet) PublishTransaction(ctx context.Context, tx *wire.MsgTx, n NetworkBackend) (*chainhash.Hash, error) {
  5060  	const opf = "wallet.PublishTransaction(%v)"
  5061  
  5062  	txHash := tx.TxHash()
  5063  
  5064  	var relevant bool
  5065  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  5066  		relevant = w.isRelevantTx(dbtx, tx)
  5067  		return nil
  5068  	})
  5069  	if err != nil {
  5070  		op := errors.Opf(opf, &txHash)
  5071  		return nil, errors.E(op, err)
  5072  	}
  5073  
  5074  	var watchOutPoints []wire.OutPoint
  5075  	if relevant {
  5076  		txBuf := new(bytes.Buffer)
  5077  		txBuf.Grow(tx.SerializeSize())
  5078  		if err = tx.Serialize(txBuf); err != nil {
  5079  			op := errors.Opf(opf, &txHash)
  5080  			return nil, errors.E(op, err)
  5081  		}
  5082  
  5083  		w.lockedOutpointMu.Lock()
  5084  		err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  5085  			rec, err := udb.NewTxRecord(txBuf.Bytes(), time.Now())
  5086  			if err != nil {
  5087  				return err
  5088  			}
  5089  			watchOutPoints, err = w.processTransactionRecord(ctx, dbtx, rec, nil, nil)
  5090  			return err
  5091  		})
  5092  		w.lockedOutpointMu.Unlock()
  5093  		if err != nil {
  5094  			op := errors.Opf(opf, &txHash)
  5095  			return nil, errors.E(op, err)
  5096  		}
  5097  	}
  5098  
  5099  	err = n.PublishTransactions(ctx, tx)
  5100  	if err != nil {
  5101  		if relevant {
  5102  			if err := w.AbandonTransaction(ctx, &txHash); err != nil {
  5103  				log.Warnf("Failed to abandon unmined transaction: %v", err)
  5104  			}
  5105  		}
  5106  		op := errors.Opf(opf, &txHash)
  5107  		return nil, errors.E(op, err)
  5108  	}
  5109  
  5110  	if len(watchOutPoints) > 0 {
  5111  		err := n.LoadTxFilter(ctx, false, nil, watchOutPoints)
  5112  		if err != nil {
  5113  			log.Errorf("Failed to watch outpoints: %v", err)
  5114  		}
  5115  	}
  5116  
  5117  	return &txHash, nil
  5118  }
  5119  
  5120  // PublishUnminedTransactions rebroadcasts all unmined transactions
  5121  // to the consensus RPC server so it can be propagated to other nodes
  5122  // and eventually mined.
  5123  func (w *Wallet) PublishUnminedTransactions(ctx context.Context, p Peer) error {
  5124  	const op errors.Op = "wallet.PublishUnminedTransactions"
  5125  	unminedTxs, err := w.UnminedTransactions(ctx)
  5126  	if err != nil {
  5127  		return errors.E(op, err)
  5128  	}
  5129  	err = p.PublishTransactions(ctx, unminedTxs...)
  5130  	if err != nil {
  5131  		return errors.E(op, err)
  5132  	}
  5133  	return nil
  5134  }
  5135  
  5136  // ChainParams returns the network parameters for the blockchain the wallet
  5137  // belongs to.
  5138  func (w *Wallet) ChainParams() *chaincfg.Params {
  5139  	return w.chainParams
  5140  }
  5141  
  5142  // NeedsAccountsSync returns whether or not the wallet is void of any generated
  5143  // keys and accounts (other than the default account), and records the genesis
  5144  // block as the main chain tip.  When these are both true, an accounts sync
  5145  // should be performed to restore, per BIP0044, any generated accounts and
  5146  // addresses from a restored seed.
  5147  func (w *Wallet) NeedsAccountsSync(ctx context.Context) (bool, error) {
  5148  	_, tipHeight := w.MainChainTip(ctx)
  5149  	if tipHeight != 0 {
  5150  		return false, nil
  5151  	}
  5152  
  5153  	needsSync := true
  5154  	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  5155  		addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
  5156  		lastAcct, err := w.manager.LastAccount(addrmgrNs)
  5157  		if err != nil {
  5158  			return err
  5159  		}
  5160  		if lastAcct != 0 {
  5161  			// There are more accounts than just the default account and no
  5162  			// accounts sync is required.
  5163  			needsSync = false
  5164  			return nil
  5165  		}
  5166  		// Begin iteration over all addresses in the first account.  If any
  5167  		// exist, "break out" of the loop with a special error.
  5168  		errBreak := errors.New("break")
  5169  		err = w.manager.ForEachAccountAddress(addrmgrNs, 0, func(udb.ManagedAddress) error {
  5170  			needsSync = false
  5171  			return errBreak
  5172  		})
  5173  		if errors.Is(err, errBreak) {
  5174  			return nil
  5175  		}
  5176  		return err
  5177  	})
  5178  	return needsSync, err
  5179  }
  5180  
  5181  // ValidatePreDCP0005CFilters verifies that all stored cfilters prior to the
  5182  // DCP0005 activation height are the expected ones.
  5183  //
  5184  // Verification is done by hashing all stored cfilter data and comparing the
  5185  // resulting hash to a known, hardcoded hash.
  5186  func (w *Wallet) ValidatePreDCP0005CFilters(ctx context.Context) error {
  5187  	const op errors.Op = "wallet.ValidatePreDCP0005CFilters"
  5188  
  5189  	// Hardcoded activation heights for mainnet and testnet3. Simnet
  5190  	// already follows DCP0005 rules.
  5191  	var end *BlockIdentifier
  5192  	switch w.chainParams.Net {
  5193  	case wire.MainNet:
  5194  		end = NewBlockIdentifierFromHeight(validate.DCP0005ActiveHeightMainNet - 1)
  5195  	case wire.TestNet3:
  5196  		end = NewBlockIdentifierFromHeight(validate.DCP0005ActiveHeightTestNet3 - 1)
  5197  	default:
  5198  		return errors.E(op, "The current network does not have pre-DCP0005 cfilters")
  5199  	}
  5200  
  5201  	// Sum up all the cfilter data.
  5202  	hasher := blake256.New()
  5203  	rangeFn := func(_ chainhash.Hash, _ [gcs2.KeySize]byte, filter *gcs2.FilterV2) (bool, error) {
  5204  		_, err := hasher.Write(filter.Bytes())
  5205  		return false, err
  5206  	}
  5207  
  5208  	err := w.RangeCFiltersV2(ctx, nil, end, rangeFn)
  5209  	if err != nil {
  5210  		return errors.E(op, err)
  5211  	}
  5212  
  5213  	// Verify against the hardcoded hash.
  5214  	var cfsethash chainhash.Hash
  5215  	err = cfsethash.SetBytes(hasher.Sum(nil))
  5216  	if err != nil {
  5217  		return errors.E(op, err)
  5218  	}
  5219  	err = validate.PreDCP0005CFilterHash(w.chainParams.Net, &cfsethash)
  5220  	if err != nil {
  5221  		return errors.E(op, err)
  5222  	}
  5223  	return nil
  5224  }
  5225  
  5226  // ImportCFiltersV2 imports the provided v2 cfilters starting at the specified
  5227  // block height. Headers for all the provided filters must have already been
  5228  // imported into the wallet, otherwise this method fails. Existing filters for
  5229  // the respective blocks are overridden.
  5230  //
  5231  // Note: No validation is performed on the contents of the imported filters.
  5232  // Importing filters that do not correspond to the actual contents of a block
  5233  // might cause the wallet to miss relevant transactions.
  5234  func (w *Wallet) ImportCFiltersV2(ctx context.Context, startBlockHeight int32, filterData [][]byte) error {
  5235  	const op errors.Op = "wallet.ImportCFiltersV2"
  5236  	err := walletdb.Update(ctx, w.db, func(tx walletdb.ReadWriteTx) error {
  5237  		return w.txStore.ImportCFiltersV2(tx, startBlockHeight, filterData)
  5238  	})
  5239  	if err != nil {
  5240  		return errors.E(op, err)
  5241  	}
  5242  	return nil
  5243  }
  5244  
  5245  // Create creates an new wallet, writing it to an empty database.  If the passed
  5246  // seed is non-nil, it is used.  Otherwise, a secure random seed of the
  5247  // recommended length is generated.
  5248  func Create(ctx context.Context, db DB, pubPass, privPass, seed []byte, params *chaincfg.Params) error {
  5249  	const op errors.Op = "wallet.Create"
  5250  	// If a seed was provided, ensure that it is of valid length. Otherwise,
  5251  	// we generate a random seed for the wallet with the recommended seed
  5252  	// length.
  5253  	if seed == nil {
  5254  		hdSeed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
  5255  		if err != nil {
  5256  			return errors.E(op, err)
  5257  		}
  5258  		seed = hdSeed
  5259  	}
  5260  	if len(seed) < hdkeychain.MinSeedBytes || len(seed) > hdkeychain.MaxSeedBytes {
  5261  		return errors.E(op, hdkeychain.ErrInvalidSeedLen)
  5262  	}
  5263  
  5264  	err := udb.Initialize(ctx, db.internal(), params, seed, pubPass, privPass)
  5265  	if err != nil {
  5266  		return errors.E(op, err)
  5267  	}
  5268  	return nil
  5269  }
  5270  
  5271  // CreateWatchOnly creates a watchonly wallet on the provided db.
  5272  func CreateWatchOnly(ctx context.Context, db DB, extendedPubKey string, pubPass []byte, params *chaincfg.Params) error {
  5273  	const op errors.Op = "wallet.CreateWatchOnly"
  5274  	err := udb.InitializeWatchOnly(ctx, db.internal(), params, extendedPubKey, pubPass)
  5275  	if err != nil {
  5276  		return errors.E(op, err)
  5277  	}
  5278  	return nil
  5279  }
  5280  
  5281  // decodeStakePoolColdExtKey decodes the string of stake pool addresses
  5282  // to search incoming tickets for. The format for the passed string is:
  5283  //
  5284  //	"xpub...:end"
  5285  //
  5286  // where xpub... is the extended public key and end is the last
  5287  // address index to scan to, exclusive. Effectively, it returns the derived
  5288  // addresses for this public key for the address indexes [0,end). The branch
  5289  // used for the derivation is always the external branch.
  5290  func decodeStakePoolColdExtKey(encStr string, params *chaincfg.Params) (map[string]struct{}, error) {
  5291  	// Default option; stake pool is disabled.
  5292  	if encStr == "" {
  5293  		return nil, nil
  5294  	}
  5295  
  5296  	// Split the string.
  5297  	splStrs := strings.Split(encStr, ":")
  5298  	if len(splStrs) != 2 {
  5299  		return nil, errors.Errorf("failed to correctly parse passed stakepool " +
  5300  			"address public key and index")
  5301  	}
  5302  
  5303  	// Parse the extended public key and ensure it's the right network.
  5304  	key, err := hdkeychain.NewKeyFromString(splStrs[0], params)
  5305  	if err != nil {
  5306  		return nil, err
  5307  	}
  5308  
  5309  	// Parse the ending index and ensure it's valid.
  5310  	end, err := strconv.Atoi(splStrs[1])
  5311  	if err != nil {
  5312  		return nil, err
  5313  	}
  5314  	if end < 0 || end > udb.MaxAddressesPerAccount {
  5315  		return nil, errors.Errorf("pool address index is invalid (got %v)",
  5316  			end)
  5317  	}
  5318  
  5319  	log.Infof("Please wait, deriving %v stake pool fees addresses "+
  5320  		"for extended public key %s", end, splStrs[0])
  5321  
  5322  	// Derive from external branch
  5323  	branchKey, err := key.Child(udb.ExternalBranch)
  5324  	if err != nil {
  5325  		return nil, err
  5326  	}
  5327  
  5328  	// Derive the addresses from [0, end) for this extended public key.
  5329  	addrs, err := deriveChildAddresses(branchKey, 0, uint32(end)+1, params)
  5330  	if err != nil {
  5331  		return nil, err
  5332  	}
  5333  
  5334  	addrMap := make(map[string]struct{})
  5335  	for i := range addrs {
  5336  		addrMap[addrs[i].String()] = struct{}{}
  5337  	}
  5338  
  5339  	return addrMap, nil
  5340  }
  5341  
  5342  // GapLimit returns the currently used gap limit.
  5343  func (w *Wallet) GapLimit() uint32 {
  5344  	return w.gapLimit
  5345  }
  5346  
  5347  // ManualTickets returns whether network syncers should avoid adding ticket
  5348  // transactions to the wallet, instead requiring the wallet administrator to
  5349  // manually record any tickets.  This can be used to prevent wallets from voting
  5350  // using tickets bought by others but which reuse our voting address.
  5351  func (w *Wallet) ManualTickets() bool {
  5352  	return w.manualTickets
  5353  }
  5354  
  5355  // Open loads an already-created wallet from the passed database and namespaces
  5356  // configuration options and sets it up it according to the rest of options.
  5357  func Open(ctx context.Context, cfg *Config) (*Wallet, error) {
  5358  	const op errors.Op = "wallet.Open"
  5359  	// Migrate to the unified DB if necessary.
  5360  	db := cfg.DB.internal()
  5361  	needsMigration, err := udb.NeedsMigration(ctx, db)
  5362  	if err != nil {
  5363  		return nil, errors.E(op, err)
  5364  	}
  5365  	if needsMigration {
  5366  		err := udb.Migrate(ctx, db, cfg.Params)
  5367  		if err != nil {
  5368  			return nil, errors.E(op, err)
  5369  		}
  5370  	}
  5371  
  5372  	// Perform upgrades as necessary.
  5373  	err = udb.Upgrade(ctx, db, cfg.PubPassphrase, cfg.Params)
  5374  	if err != nil {
  5375  		return nil, errors.E(op, err)
  5376  	}
  5377  
  5378  	// Impose a maximum difficulty target on the test network to prevent runaway
  5379  	// difficulty on testnet by ASICs and GPUs since it's not reasonable to
  5380  	// require high-powered hardware to keep the test network running smoothly.
  5381  	var minTestNetTarget *big.Int
  5382  	var minTestNetDiffBits uint32
  5383  	params := cfg.Params
  5384  	if params.Net == wire.TestNet3 {
  5385  		// This equates to a maximum difficulty of 2^6 = 64.
  5386  		const maxTestDiffShift = 6
  5387  		minTestNetTarget = new(big.Int).Rsh(cfg.Params.PowLimit, maxTestDiffShift)
  5388  		minTestNetDiffBits = blockchain.BigToCompact(minTestNetTarget)
  5389  	}
  5390  	// Deployment IDs are guaranteed to be unique across all stake versions.
  5391  	deploymentsByID := make(map[string]*chaincfg.ConsensusDeployment)
  5392  	for _, deployments := range params.Deployments {
  5393  		for i := range deployments {
  5394  			deployment := &deployments[i]
  5395  			id := deployment.Vote.Id
  5396  			if _, ok := deploymentsByID[id]; ok {
  5397  				panic(fmt.Sprintf("reused deployment ID %q", id))
  5398  			}
  5399  			deploymentsByID[id] = deployment
  5400  		}
  5401  	}
  5402  
  5403  	w := &Wallet{
  5404  		db: db,
  5405  
  5406  		// StakeOptions
  5407  		votingEnabled:      cfg.VotingEnabled,
  5408  		addressReuse:       cfg.AddressReuse,
  5409  		ticketAddress:      cfg.VotingAddress,
  5410  		poolAddress:        cfg.PoolAddress,
  5411  		poolFees:           cfg.PoolFees,
  5412  		tspends:            make(map[chainhash.Hash]wire.MsgTx),
  5413  		tspendPolicy:       make(map[chainhash.Hash]stake.TreasuryVoteT),
  5414  		tspendKeyPolicy:    make(map[string]stake.TreasuryVoteT),
  5415  		vspTSpendPolicy:    make(map[udb.VSPTSpend]stake.TreasuryVoteT),
  5416  		vspTSpendKeyPolicy: make(map[udb.VSPTreasuryKey]stake.TreasuryVoteT),
  5417  
  5418  		// LoaderOptions
  5419  		gapLimit:                cfg.GapLimit,
  5420  		watchLast:               cfg.WatchLast,
  5421  		allowHighFees:           cfg.AllowHighFees,
  5422  		accountGapLimit:         cfg.AccountGapLimit,
  5423  		disableCoinTypeUpgrades: cfg.DisableCoinTypeUpgrades,
  5424  		manualTickets:           cfg.ManualTickets,
  5425  
  5426  		// Chain params
  5427  		subsidyCache:       blockchain.NewSubsidyCache(params),
  5428  		chainParams:        params,
  5429  		deploymentsByID:    deploymentsByID,
  5430  		minTestNetTarget:   minTestNetTarget,
  5431  		minTestNetDiffBits: minTestNetDiffBits,
  5432  
  5433  		lockedOutpoints: make(map[outpoint]struct{}),
  5434  
  5435  		recentlyPublished: make(map[chainhash.Hash]struct{}),
  5436  
  5437  		addressBuffers: make(map[uint32]*bip0044AccountData),
  5438  
  5439  		mixSems: newMixSemaphores(cfg.MixSplitLimit),
  5440  	}
  5441  
  5442  	// Open database managers
  5443  	w.manager, w.txStore, w.stakeMgr, err = udb.Open(ctx, db, params, cfg.PubPassphrase)
  5444  	if err != nil {
  5445  		return nil, errors.E(op, err)
  5446  	}
  5447  	log.Infof("Opened wallet") // TODO: log balance? last sync height?
  5448  
  5449  	err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  5450  		return w.rollbackInvalidCheckpoints(dbtx)
  5451  	})
  5452  	if err != nil {
  5453  		return nil, errors.E(op, err)
  5454  	}
  5455  
  5456  	var vb stake.VoteBits
  5457  	var tspendPolicy map[chainhash.Hash]stake.TreasuryVoteT
  5458  	var treasuryKeyPolicy map[string]stake.TreasuryVoteT
  5459  	var vspTSpendPolicy map[udb.VSPTSpend]stake.TreasuryVoteT
  5460  	var vspTreasuryKeyPolicy map[udb.VSPTreasuryKey]stake.TreasuryVoteT
  5461  	err = walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
  5462  		ns := tx.ReadBucket(waddrmgrNamespaceKey)
  5463  		lastAcct, err := w.manager.LastAccount(ns)
  5464  		if err != nil {
  5465  			return err
  5466  		}
  5467  		lastImported, err := w.manager.LastImportedAccount(tx)
  5468  		if err != nil {
  5469  			return err
  5470  		}
  5471  		addAccountBuffers := func(acct uint32) error {
  5472  			xpub, err := w.manager.AccountExtendedPubKey(tx, acct)
  5473  			if err != nil {
  5474  				return err
  5475  			}
  5476  			extKey, intKey, err := deriveBranches(xpub)
  5477  			if err != nil {
  5478  				return err
  5479  			}
  5480  			props, err := w.manager.AccountProperties(ns, acct)
  5481  			if err != nil {
  5482  				return err
  5483  			}
  5484  			w.addressBuffers[acct] = &bip0044AccountData{
  5485  				xpub: xpub,
  5486  				albExternal: addressBuffer{
  5487  					branchXpub: extKey,
  5488  					lastUsed:   props.LastUsedExternalIndex,
  5489  					cursor:     props.LastReturnedExternalIndex - props.LastUsedExternalIndex,
  5490  				},
  5491  				albInternal: addressBuffer{
  5492  					branchXpub: intKey,
  5493  					lastUsed:   props.LastUsedInternalIndex,
  5494  					cursor:     props.LastReturnedInternalIndex - props.LastUsedInternalIndex,
  5495  				},
  5496  			}
  5497  			return nil
  5498  		}
  5499  		for acct := uint32(0); acct <= lastAcct; acct++ {
  5500  			if err := addAccountBuffers(acct); err != nil {
  5501  				return err
  5502  			}
  5503  		}
  5504  		for acct := uint32(udb.ImportedAddrAccount + 1); acct <= lastImported; acct++ {
  5505  			if err := addAccountBuffers(acct); err != nil {
  5506  				return err
  5507  			}
  5508  		}
  5509  
  5510  		vb = w.readDBVoteBits(tx)
  5511  
  5512  		tspendPolicy, vspTSpendPolicy, err = w.readDBTreasuryPolicies(tx)
  5513  		if err != nil {
  5514  			return err
  5515  		}
  5516  		treasuryKeyPolicy, vspTreasuryKeyPolicy, err = w.readDBTreasuryKeyPolicies(tx)
  5517  		if err != nil {
  5518  			return err
  5519  		}
  5520  
  5521  		return nil
  5522  	})
  5523  	if err != nil {
  5524  		return nil, errors.E(op, err)
  5525  	}
  5526  
  5527  	w.NtfnServer = newNotificationServer(w)
  5528  	w.defaultVoteBits = vb
  5529  	w.tspendPolicy = tspendPolicy
  5530  	w.tspendKeyPolicy = treasuryKeyPolicy
  5531  	w.vspTSpendPolicy = vspTSpendPolicy
  5532  	w.vspTSpendKeyPolicy = vspTreasuryKeyPolicy
  5533  
  5534  	w.stakePoolColdAddrs, err = decodeStakePoolColdExtKey(cfg.StakePoolColdExtKey,
  5535  		params)
  5536  	if err != nil {
  5537  		return nil, errors.E(op, err)
  5538  	}
  5539  	w.stakePoolEnabled = len(w.stakePoolColdAddrs) > 0
  5540  
  5541  	// Amounts
  5542  	w.relayFee = cfg.RelayFee
  5543  
  5544  	// Record current tip as initialHeight.
  5545  	_, w.initialHeight = w.MainChainTip(ctx)
  5546  
  5547  	return w, nil
  5548  }
  5549  
  5550  // getCoinjoinTxsSumbByAcct returns a map with key representing the account and
  5551  // the sum of coinjoin output transactions from the account.
  5552  func (w *Wallet) getCoinjoinTxsSumbByAcct(ctx context.Context) (map[uint32]int, error) {
  5553  	const op errors.Op = "wallet.getCoinjoinTxsSumbByAcct"
  5554  	coinJoinTxsByAcctSum := make(map[uint32]int)
  5555  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  5556  		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  5557  		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
  5558  		// Get current block.  The block height used for calculating
  5559  		// the number of tx confirmations.
  5560  		_, tipHeight := w.txStore.MainChainTip(dbtx)
  5561  		rangeFn := func(details []udb.TxDetails) (bool, error) {
  5562  			for _, detail := range details {
  5563  				if detail.TxType != stake.TxTypeRegular {
  5564  					continue
  5565  				}
  5566  				isMixedTx, mixDenom, _ := PossibleCoinJoin(&detail.MsgTx)
  5567  				if !isMixedTx {
  5568  					continue
  5569  				}
  5570  				for _, output := range detail.MsgTx.TxOut {
  5571  					if mixDenom != output.Value {
  5572  						continue
  5573  					}
  5574  					_, addrs := stdscript.ExtractAddrs(output.Version, output.PkScript, w.chainParams)
  5575  					if len(addrs) == 1 {
  5576  						acct, err := w.manager.AddrAccount(addrmgrNs, addrs[0])
  5577  						// mixed output belongs to wallet.
  5578  						if err == nil {
  5579  							coinJoinTxsByAcctSum[acct]++
  5580  						}
  5581  					}
  5582  				}
  5583  			}
  5584  			return false, nil
  5585  		}
  5586  		return w.txStore.RangeTransactions(ctx, txmgrNs, 0, tipHeight, rangeFn)
  5587  	})
  5588  	if err != nil {
  5589  		return nil, errors.E(op, err)
  5590  	}
  5591  
  5592  	return coinJoinTxsByAcctSum, nil
  5593  }
  5594  
  5595  // GetCoinjoinTxsSumbByAcct gets all coinjoin outputs sum by accounts. This is done
  5596  // in case we need to guess a mixed account on wallet recovery.
  5597  func (w *Wallet) GetCoinjoinTxsSumbByAcct(ctx context.Context) (map[uint32]int, error) {
  5598  	const op errors.Op = "wallet.GetCoinjoinTxsSumbByAcct"
  5599  	allTxsByAcct, err := w.getCoinjoinTxsSumbByAcct(ctx)
  5600  	if err != nil {
  5601  		return nil, errors.E(op, err)
  5602  	}
  5603  
  5604  	return allTxsByAcct, nil
  5605  }
  5606  
  5607  // GetVSPTicketsByFeeStatus returns the ticket hashes of tickets with the
  5608  // informed fee status.
  5609  func (w *Wallet) GetVSPTicketsByFeeStatus(ctx context.Context, feeStatus int) ([]chainhash.Hash, error) {
  5610  	const op errors.Op = "wallet.GetVSPTicketsByFeeStatus"
  5611  	tickets := map[chainhash.Hash]*udb.VSPTicket{}
  5612  	var err error
  5613  	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  5614  		tickets, err = udb.GetVSPTicketsByFeeStatus(dbtx, feeStatus)
  5615  		return err
  5616  	})
  5617  	if err != nil {
  5618  		return nil, errors.E(op, err)
  5619  	}
  5620  
  5621  	response := make([]chainhash.Hash, len(tickets))
  5622  	i := 0
  5623  	for hash := range tickets {
  5624  		copy(response[i][:], hash[:])
  5625  		i++
  5626  	}
  5627  
  5628  	return response, nil
  5629  }
  5630  
  5631  // SetPublished sets the informed hash as true or false.
  5632  func (w *Wallet) SetPublished(ctx context.Context, hash *chainhash.Hash, published bool) error {
  5633  	err := walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  5634  		hash := hash
  5635  		err := w.txStore.SetPublished(dbtx, hash, published)
  5636  		if err != nil {
  5637  			return err
  5638  		}
  5639  		return nil
  5640  	})
  5641  	if err != nil {
  5642  		return err
  5643  	}
  5644  	return nil
  5645  }
  5646  
  5647  type VSPTicket struct {
  5648  	FeeHash     chainhash.Hash
  5649  	FeeTxStatus uint32
  5650  	VSPHostID   uint32
  5651  	Host        string
  5652  	PubKey      []byte
  5653  }
  5654  
  5655  // VSPTicketInfo returns the various information for a given vsp ticket
  5656  func (w *Wallet) VSPTicketInfo(ctx context.Context, ticketHash *chainhash.Hash) (*VSPTicket, error) {
  5657  	var data *udb.VSPTicket
  5658  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  5659  		var err error
  5660  		data, err = udb.GetVSPTicket(dbtx, *ticketHash)
  5661  		if err != nil {
  5662  			return err
  5663  		}
  5664  		return nil
  5665  	})
  5666  	if err == nil && data == nil {
  5667  		err = errors.E(errors.NotExist)
  5668  		return nil, err
  5669  	} else if data == nil {
  5670  		return nil, err
  5671  	}
  5672  	convertedData := &VSPTicket{
  5673  		FeeHash:     data.FeeHash,
  5674  		FeeTxStatus: data.FeeTxStatus,
  5675  		VSPHostID:   data.VSPHostID,
  5676  		Host:        data.Host,
  5677  		PubKey:      data.PubKey,
  5678  	}
  5679  	return convertedData, err
  5680  }
  5681  
  5682  // VSPFeeHashForTicket returns the hash of the fee transaction associated with a
  5683  // VSP payment.
  5684  func (w *Wallet) VSPFeeHashForTicket(ctx context.Context, ticketHash *chainhash.Hash) (chainhash.Hash, error) {
  5685  	var feeHash chainhash.Hash
  5686  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  5687  		data, err := udb.GetVSPTicket(dbtx, *ticketHash)
  5688  		if err != nil {
  5689  			return err
  5690  		}
  5691  		feeHash = data.FeeHash
  5692  		return nil
  5693  	})
  5694  	if err == nil && feeHash == (chainhash.Hash{}) {
  5695  		err = errors.E(errors.NotExist)
  5696  	}
  5697  	return feeHash, err
  5698  }
  5699  
  5700  // VSPHostForTicket returns the current vsp host associated with VSP Ticket.
  5701  func (w *Wallet) VSPHostForTicket(ctx context.Context, ticketHash *chainhash.Hash) (string, error) {
  5702  	var host string
  5703  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  5704  		data, err := udb.GetVSPTicket(dbtx, *ticketHash)
  5705  		if err != nil {
  5706  			return err
  5707  		}
  5708  		host = data.Host
  5709  		return nil
  5710  	})
  5711  	if err == nil && host == "" {
  5712  		err = errors.E(errors.NotExist)
  5713  	}
  5714  	return host, err
  5715  }
  5716  
  5717  // IsVSPTicketConfirmed returns whether or not a VSP ticket has been confirmed
  5718  // by a VSP.
  5719  func (w *Wallet) IsVSPTicketConfirmed(ctx context.Context, ticketHash *chainhash.Hash) (bool, error) {
  5720  	confirmed := false
  5721  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  5722  		data, err := udb.GetVSPTicket(dbtx, *ticketHash)
  5723  		if err != nil {
  5724  			return err
  5725  		}
  5726  		if data.FeeTxStatus == uint32(udb.VSPFeeProcessConfirmed) {
  5727  			confirmed = true
  5728  		}
  5729  		return nil
  5730  	})
  5731  	return confirmed, err
  5732  }
  5733  
  5734  // UpdateVspTicketFeeToPaid updates a vsp ticket fee status to paid.
  5735  // This is needed when finishing the fee payment on VSPs Process.
  5736  func (w *Wallet) UpdateVspTicketFeeToPaid(ctx context.Context, ticketHash, feeHash *chainhash.Hash, host string, pubkey []byte) error {
  5737  	var err error
  5738  	err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  5739  		err = udb.SetVSPTicket(dbtx, ticketHash, &udb.VSPTicket{
  5740  			FeeHash:     *feeHash,
  5741  			FeeTxStatus: uint32(udb.VSPFeeProcessPaid),
  5742  			Host:        host,
  5743  			PubKey:      pubkey,
  5744  		})
  5745  		return err
  5746  	})
  5747  
  5748  	return err
  5749  }
  5750  
  5751  func (w *Wallet) UpdateVspTicketFeeToStarted(ctx context.Context, ticketHash, feeHash *chainhash.Hash, host string, pubkey []byte) error {
  5752  	var err error
  5753  	err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  5754  		err = udb.SetVSPTicket(dbtx, ticketHash, &udb.VSPTicket{
  5755  			FeeHash:     *feeHash,
  5756  			FeeTxStatus: uint32(udb.VSPFeeProcessStarted),
  5757  			Host:        host,
  5758  			PubKey:      pubkey,
  5759  		})
  5760  		return err
  5761  	})
  5762  
  5763  	return err
  5764  }
  5765  
  5766  func (w *Wallet) UpdateVspTicketFeeToErrored(ctx context.Context, ticketHash *chainhash.Hash, host string, pubkey []byte) error {
  5767  	var err error
  5768  	err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  5769  		err = udb.SetVSPTicket(dbtx, ticketHash, &udb.VSPTicket{
  5770  			FeeHash:     chainhash.Hash{},
  5771  			FeeTxStatus: uint32(udb.VSPFeeProcessErrored),
  5772  			Host:        host,
  5773  			PubKey:      pubkey,
  5774  		})
  5775  		return err
  5776  	})
  5777  
  5778  	return err
  5779  }
  5780  
  5781  func (w *Wallet) UpdateVspTicketFeeToConfirmed(ctx context.Context, ticketHash, feeHash *chainhash.Hash, host string, pubkey []byte) error {
  5782  	var err error
  5783  	err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  5784  		err = udb.SetVSPTicket(dbtx, ticketHash, &udb.VSPTicket{
  5785  			FeeHash:     *feeHash,
  5786  			FeeTxStatus: uint32(udb.VSPFeeProcessConfirmed),
  5787  			Host:        host,
  5788  			PubKey:      pubkey,
  5789  		})
  5790  		return err
  5791  	})
  5792  
  5793  	return err
  5794  }
  5795  
  5796  // ForUnspentUnexpiredTickets performs a function on every unexpired and unspent
  5797  // ticket from the wallet.
  5798  func (w *Wallet) ForUnspentUnexpiredTickets(ctx context.Context,
  5799  	f func(hash *chainhash.Hash) error) error {
  5800  
  5801  	params := w.ChainParams()
  5802  
  5803  	iter := func(ticketSummaries []*TicketSummary, _ *wire.BlockHeader) (bool, error) {
  5804  		for _, ticketSummary := range ticketSummaries {
  5805  			switch ticketSummary.Status {
  5806  			case TicketStatusLive:
  5807  			case TicketStatusImmature:
  5808  			case TicketStatusUnspent:
  5809  			default:
  5810  				continue
  5811  			}
  5812  
  5813  			ticketHash := *ticketSummary.Ticket.Hash
  5814  			err := f(&ticketHash)
  5815  			if err != nil {
  5816  				return false, err
  5817  			}
  5818  		}
  5819  
  5820  		return false, nil
  5821  	}
  5822  
  5823  	const requiredConfs = 6 + 2
  5824  	_, blockHeight := w.MainChainTip(ctx)
  5825  	startBlockNum := blockHeight -
  5826  		int32(params.TicketExpiry+uint32(params.TicketMaturity)-requiredConfs)
  5827  	startBlock := NewBlockIdentifierFromHeight(startBlockNum)
  5828  	endBlock := NewBlockIdentifierFromHeight(blockHeight)
  5829  	return w.GetTickets(ctx, iter, startBlock, endBlock)
  5830  }