decred.org/dcrwallet/v3@v3.1.0/wallet/createtx.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  	"context"
    10  	"crypto/rand"
    11  	"crypto/tls"
    12  	"encoding/binary"
    13  	"net"
    14  	"sort"
    15  	"time"
    16  
    17  	"decred.org/cspp/v2"
    18  	"decred.org/cspp/v2/coinjoin"
    19  	"decred.org/dcrwallet/v3/deployments"
    20  	"decred.org/dcrwallet/v3/errors"
    21  	"decred.org/dcrwallet/v3/internal/uniformprng"
    22  	"decred.org/dcrwallet/v3/rpc/client/dcrd"
    23  	"decred.org/dcrwallet/v3/wallet/txauthor"
    24  	"decred.org/dcrwallet/v3/wallet/txrules"
    25  	"decred.org/dcrwallet/v3/wallet/txsizes"
    26  	"decred.org/dcrwallet/v3/wallet/udb"
    27  	"decred.org/dcrwallet/v3/wallet/walletdb"
    28  	"github.com/decred/dcrd/blockchain/stake/v5"
    29  	blockchain "github.com/decred/dcrd/blockchain/standalone/v2"
    30  	"github.com/decred/dcrd/chaincfg/chainhash"
    31  	"github.com/decred/dcrd/chaincfg/v3"
    32  	"github.com/decred/dcrd/dcrec"
    33  	"github.com/decred/dcrd/dcrutil/v4"
    34  	"github.com/decred/dcrd/txscript/v4"
    35  	"github.com/decred/dcrd/txscript/v4/sign"
    36  	"github.com/decred/dcrd/txscript/v4/stdaddr"
    37  	"github.com/decred/dcrd/txscript/v4/stdscript"
    38  	"github.com/decred/dcrd/wire"
    39  )
    40  
    41  // --------------------------------------------------------------------------------
    42  // Constants and simple functions
    43  
    44  const (
    45  	revocationFeeLimit = 1 << 14
    46  
    47  	// maxStandardTxSize is the maximum size allowed for transactions that
    48  	// are considered standard and will therefore be relayed and considered
    49  	// for mining.
    50  	// TODO: import from dcrd.
    51  	maxStandardTxSize = 100000
    52  
    53  	// sanityVerifyFlags are the flags used to enable and disable features of
    54  	// the txscript engine used for sanity checking of transactions signed by
    55  	// the wallet.
    56  	sanityVerifyFlags = txscript.ScriptDiscourageUpgradableNops |
    57  		txscript.ScriptVerifyCleanStack |
    58  		txscript.ScriptVerifyCheckLockTimeVerify |
    59  		txscript.ScriptVerifyCheckSequenceVerify |
    60  		txscript.ScriptVerifyTreasury
    61  )
    62  
    63  // Input provides transaction inputs referencing spendable outputs.
    64  type Input struct {
    65  	OutPoint wire.OutPoint
    66  	PrevOut  wire.TxOut
    67  }
    68  
    69  // --------------------------------------------------------------------------------
    70  // Transaction creation
    71  
    72  // OutputSelectionAlgorithm specifies the algorithm to use when selecting outputs
    73  // to construct a transaction.
    74  type OutputSelectionAlgorithm uint
    75  
    76  const (
    77  	// OutputSelectionAlgorithmDefault describes the default output selection
    78  	// algorithm.  It is not optimized for any particular use case.
    79  	OutputSelectionAlgorithmDefault = iota
    80  
    81  	// OutputSelectionAlgorithmAll describes the output selection algorithm of
    82  	// picking every possible available output.  This is useful for sweeping.
    83  	OutputSelectionAlgorithmAll
    84  )
    85  
    86  // NewUnsignedTransaction constructs an unsigned transaction using unspent
    87  // account outputs.
    88  //
    89  // The changeSource and inputSource parameters are optional and can be nil.
    90  // When the changeSource is nil and change output should be added, an internal
    91  // change address is created for the account.  When the inputSource is nil,
    92  // the inputs will be selected by the wallet.
    93  func (w *Wallet) NewUnsignedTransaction(ctx context.Context, outputs []*wire.TxOut,
    94  	relayFeePerKb dcrutil.Amount, account uint32, minConf int32,
    95  	algo OutputSelectionAlgorithm, changeSource txauthor.ChangeSource, inputSource txauthor.InputSource) (*txauthor.AuthoredTx, error) {
    96  
    97  	const op errors.Op = "wallet.NewUnsignedTransaction"
    98  
    99  	ignoreInput := func(op *wire.OutPoint) bool {
   100  		_, ok := w.lockedOutpoints[outpoint{op.Hash, op.Index}]
   101  		return ok
   102  	}
   103  	defer w.lockedOutpointMu.Unlock()
   104  	w.lockedOutpointMu.Lock()
   105  
   106  	var authoredTx *txauthor.AuthoredTx
   107  	var changeSourceUpdates []func(walletdb.ReadWriteTx) error
   108  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
   109  		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
   110  		_, tipHeight := w.txStore.MainChainTip(dbtx)
   111  
   112  		if account != udb.ImportedAddrAccount {
   113  			lastAcct, err := w.manager.LastAccount(addrmgrNs)
   114  			if err != nil {
   115  				return err
   116  			}
   117  			if account > lastAcct {
   118  				return errors.E(errors.NotExist, "missing account")
   119  			}
   120  		}
   121  
   122  		if inputSource == nil {
   123  			sourceImpl := w.txStore.MakeInputSource(dbtx, account,
   124  				minConf, tipHeight, ignoreInput)
   125  			switch algo {
   126  			case OutputSelectionAlgorithmDefault:
   127  				inputSource = sourceImpl.SelectInputs
   128  			case OutputSelectionAlgorithmAll:
   129  				// Wrap the source with one that always fetches the max amount
   130  				// available and ignores insufficient balance issues.
   131  				inputSource = func(dcrutil.Amount) (*txauthor.InputDetail, error) {
   132  					inputDetail, err := sourceImpl.SelectInputs(dcrutil.MaxAmount)
   133  					if errors.Is(err, errors.InsufficientBalance) {
   134  						err = nil
   135  					}
   136  					return inputDetail, err
   137  				}
   138  			default:
   139  				return errors.E(errors.Invalid,
   140  					errors.Errorf("unknown output selection algorithm %v", algo))
   141  			}
   142  		}
   143  
   144  		if changeSource == nil {
   145  			changeSource = &p2PKHChangeSource{
   146  				persist: w.deferPersistReturnedChild(ctx, &changeSourceUpdates),
   147  				account: account,
   148  				wallet:  w,
   149  				ctx:     context.Background(),
   150  			}
   151  		}
   152  
   153  		var err error
   154  		authoredTx, err = txauthor.NewUnsignedTransaction(outputs, relayFeePerKb,
   155  			inputSource, changeSource, w.chainParams.MaxTxSize)
   156  		if err != nil {
   157  			return err
   158  		}
   159  
   160  		return nil
   161  	})
   162  	if err != nil {
   163  		return nil, errors.E(op, err)
   164  	}
   165  	if len(changeSourceUpdates) != 0 {
   166  		err := walletdb.Update(ctx, w.db, func(tx walletdb.ReadWriteTx) error {
   167  			for _, up := range changeSourceUpdates {
   168  				err := up(tx)
   169  				if err != nil {
   170  					return err
   171  				}
   172  			}
   173  			return nil
   174  		})
   175  		if err != nil {
   176  			return nil, errors.E(op, err)
   177  		}
   178  	}
   179  	return authoredTx, nil
   180  }
   181  
   182  // secretSource is an implementation of txauthor.SecretSource for the wallet's
   183  // address manager.
   184  type secretSource struct {
   185  	*udb.Manager
   186  	addrmgrNs walletdb.ReadBucket
   187  	doneFuncs []func()
   188  }
   189  
   190  func (s *secretSource) GetKey(addr stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error) {
   191  	privKey, done, err := s.Manager.PrivateKey(s.addrmgrNs, addr)
   192  	if err != nil {
   193  		return nil, 0, false, err
   194  	}
   195  	s.doneFuncs = append(s.doneFuncs, done)
   196  	return privKey.Serialize(), dcrec.STEcdsaSecp256k1, true, nil
   197  }
   198  
   199  func (s *secretSource) GetScript(addr stdaddr.Address) ([]byte, error) {
   200  	return s.Manager.RedeemScript(s.addrmgrNs, addr)
   201  }
   202  
   203  // CreatedTx holds the state of a newly-created transaction and the change
   204  // output (if one was added).
   205  type CreatedTx struct {
   206  	MsgTx       *wire.MsgTx
   207  	ChangeAddr  stdaddr.Address
   208  	ChangeIndex int // negative if no change
   209  	Fee         dcrutil.Amount
   210  }
   211  
   212  // insertIntoTxMgr inserts a newly created transaction into the tx store
   213  // as unconfirmed.
   214  func (w *Wallet) insertIntoTxMgr(dbtx walletdb.ReadWriteTx, msgTx *wire.MsgTx) (*udb.TxRecord, error) {
   215  	// Create transaction record and insert into the db.
   216  	rec, err := udb.NewTxRecordFromMsgTx(msgTx, time.Now())
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  
   221  	err = w.txStore.InsertMemPoolTx(dbtx, rec)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  	return rec, nil
   226  }
   227  
   228  // insertCreditsIntoTxMgr inserts the wallet credits from msgTx to the wallet's
   229  // transaction store. It assumes msgTx is a regular transaction, which will
   230  // cause balance issues if this is called from a code path where msgtx is not
   231  // guaranteed to be a regular tx.
   232  func (w *Wallet) insertCreditsIntoTxMgr(op errors.Op, dbtx walletdb.ReadWriteTx, msgTx *wire.MsgTx, rec *udb.TxRecord) error {
   233  	addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
   234  
   235  	// Check every output to determine whether it is controlled by a wallet
   236  	// key.  If so, mark the output as a credit.
   237  	for i, output := range msgTx.TxOut {
   238  		_, addrs := stdscript.ExtractAddrs(output.Version, output.PkScript, w.chainParams)
   239  		for _, addr := range addrs {
   240  			ma, err := w.manager.Address(addrmgrNs, addr)
   241  			if err == nil {
   242  				// TODO: Credits should be added with the
   243  				// account they belong to, so wtxmgr is able to
   244  				// track per-account balances.
   245  				err = w.txStore.AddCredit(dbtx, rec, nil,
   246  					uint32(i), ma.Internal(), ma.Account())
   247  				if err != nil {
   248  					return errors.E(op, err)
   249  				}
   250  				err = w.markUsedAddress(op, dbtx, ma)
   251  				if err != nil {
   252  					return err
   253  				}
   254  				log.Debugf("Marked address %v used", addr)
   255  				continue
   256  			}
   257  
   258  			// Missing addresses are skipped.  Other errors should
   259  			// be propagated.
   260  			if !errors.Is(err, errors.NotExist) {
   261  				return errors.E(op, err)
   262  			}
   263  		}
   264  	}
   265  
   266  	return nil
   267  }
   268  
   269  // insertMultisigOutIntoTxMgr inserts a multisignature output into the
   270  // transaction store database.
   271  func (w *Wallet) insertMultisigOutIntoTxMgr(dbtx walletdb.ReadWriteTx, msgTx *wire.MsgTx, index uint32) error {
   272  	// Create transaction record and insert into the db.
   273  	rec, err := udb.NewTxRecordFromMsgTx(msgTx, time.Now())
   274  	if err != nil {
   275  		return err
   276  	}
   277  
   278  	return w.txStore.AddMultisigOut(dbtx, rec, nil, index)
   279  }
   280  
   281  // checkHighFees performs a high fee check if enabled and possible, returning an
   282  // error if the transaction pays high fees.
   283  func (w *Wallet) checkHighFees(totalInput dcrutil.Amount, tx *wire.MsgTx) error {
   284  	if w.allowHighFees {
   285  		return nil
   286  	}
   287  	if txrules.PaysHighFees(totalInput, tx) {
   288  		return errors.E(errors.Policy, "high fee")
   289  	}
   290  	return nil
   291  }
   292  
   293  // publishAndWatch publishes an authored transaction to the network and begins watching for
   294  // relevant transactions.
   295  func (w *Wallet) publishAndWatch(ctx context.Context, op errors.Op, n NetworkBackend, tx *wire.MsgTx,
   296  	watch []wire.OutPoint) error {
   297  
   298  	if n == nil {
   299  		var err error
   300  		n, err = w.NetworkBackend()
   301  		if err != nil {
   302  			return errors.E(op, err)
   303  		}
   304  	}
   305  
   306  	err := n.PublishTransactions(ctx, tx)
   307  	if err != nil {
   308  		hash := tx.TxHash()
   309  		log.Errorf("Abandoning transaction %v which failed to publish", &hash)
   310  		if err := w.AbandonTransaction(ctx, &hash); err != nil {
   311  			log.Errorf("Cannot abandon %v: %v", &hash, err)
   312  		}
   313  		return errors.E(op, err)
   314  	}
   315  
   316  	// Watch for future relevant transactions.
   317  	_, err = w.watchHDAddrs(ctx, false, n)
   318  	if err != nil {
   319  		log.Errorf("Failed to watch for future address usage after publishing "+
   320  			"transaction: %v", err)
   321  	}
   322  	if len(watch) > 0 {
   323  		err := n.LoadTxFilter(ctx, false, nil, watch)
   324  		if err != nil {
   325  			log.Errorf("Failed to watch outpoints: %v", err)
   326  		}
   327  	}
   328  	return nil
   329  }
   330  
   331  type authorTx struct {
   332  	outputs            []*wire.TxOut
   333  	account            uint32
   334  	changeAccount      uint32
   335  	minconf            int32
   336  	randomizeChangeIdx bool
   337  	txFee              dcrutil.Amount
   338  	dontSignTx         bool
   339  	isTreasury         bool
   340  
   341  	atx                 *txauthor.AuthoredTx
   342  	changeSourceUpdates []func(walletdb.ReadWriteTx) error
   343  	watch               []wire.OutPoint
   344  }
   345  
   346  // authorTx creates a (typically signed) transaction which includes each output
   347  // from outputs.  Previous outputs to redeem are chosen from the passed
   348  // account's UTXO set and minconf policy. An additional output may be added to
   349  // return change to the wallet.  An appropriate fee is included based on the
   350  // wallet's current relay fee.  The wallet must be unlocked to create the
   351  // transaction.
   352  func (w *Wallet) authorTx(ctx context.Context, op errors.Op, a *authorTx) error {
   353  	var unlockOutpoints []*wire.OutPoint
   354  	defer func() {
   355  		for _, op := range unlockOutpoints {
   356  			delete(w.lockedOutpoints, outpoint{op.Hash, op.Index})
   357  		}
   358  		w.lockedOutpointMu.Unlock()
   359  	}()
   360  	ignoreInput := func(op *wire.OutPoint) bool {
   361  		_, ok := w.lockedOutpoints[outpoint{op.Hash, op.Index}]
   362  		return ok
   363  	}
   364  	w.lockedOutpointMu.Lock()
   365  
   366  	var atx *txauthor.AuthoredTx
   367  	var changeSourceUpdates []func(walletdb.ReadWriteTx) error
   368  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
   369  		addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
   370  
   371  		// Create the unsigned transaction.
   372  		_, tipHeight := w.txStore.MainChainTip(dbtx)
   373  		inputSource := w.txStore.MakeInputSource(dbtx, a.account,
   374  			a.minconf, tipHeight, ignoreInput)
   375  		var changeSource txauthor.ChangeSource
   376  		if a.isTreasury {
   377  			changeSource = &p2PKHTreasuryChangeSource{
   378  				persist: w.deferPersistReturnedChild(ctx,
   379  					&changeSourceUpdates),
   380  				account: a.changeAccount,
   381  				wallet:  w,
   382  				ctx:     ctx,
   383  			}
   384  		} else {
   385  			changeSource = &p2PKHChangeSource{
   386  				persist: w.deferPersistReturnedChild(ctx,
   387  					&changeSourceUpdates),
   388  				account:   a.changeAccount,
   389  				wallet:    w,
   390  				ctx:       ctx,
   391  				gapPolicy: gapPolicyWrap,
   392  			}
   393  		}
   394  		var err error
   395  		atx, err = txauthor.NewUnsignedTransaction(a.outputs, a.txFee,
   396  			inputSource.SelectInputs, changeSource,
   397  			w.chainParams.MaxTxSize)
   398  		if err != nil {
   399  			return err
   400  		}
   401  		for _, in := range atx.Tx.TxIn {
   402  			prev := &in.PreviousOutPoint
   403  			w.lockedOutpoints[outpoint{prev.Hash, prev.Index}] = struct{}{}
   404  			unlockOutpoints = append(unlockOutpoints, prev)
   405  		}
   406  
   407  		// Randomize change position, if change exists, before signing.
   408  		// This doesn't affect the serialize size, so the change amount
   409  		// will still be valid.
   410  		if atx.ChangeIndex >= 0 && a.randomizeChangeIdx {
   411  			atx.RandomizeChangePosition()
   412  		}
   413  
   414  		// TADDs need to use version 3 txs.
   415  		if a.isTreasury {
   416  			// This check ensures that if NewUnsignedTransaction is
   417  			// updated to generate a different transaction version
   418  			// we error out loudly instead of failing to validate
   419  			// in some obscure way.
   420  			//
   421  			// TODO: maybe isTreasury should be passed into
   422  			// NewUnsignedTransaction?
   423  			if atx.Tx.Version != wire.TxVersion {
   424  				return errors.E(op, "violated assumption: "+
   425  					"expected unsigned tx to be version 1")
   426  			}
   427  			atx.Tx.Version = wire.TxVersionTreasury
   428  		}
   429  
   430  		if !a.dontSignTx {
   431  			// Sign the transaction.
   432  			secrets := &secretSource{Manager: w.manager, addrmgrNs: addrmgrNs}
   433  			err = atx.AddAllInputScripts(secrets)
   434  			for _, done := range secrets.doneFuncs {
   435  				done()
   436  			}
   437  		}
   438  		return err
   439  	})
   440  	if err != nil {
   441  		return errors.E(op, err)
   442  	}
   443  
   444  	// Warn when spending UTXOs controlled by imported keys created change for
   445  	// the default account.
   446  	if atx.ChangeIndex >= 0 && a.account == udb.ImportedAddrAccount {
   447  		changeAmount := dcrutil.Amount(atx.Tx.TxOut[atx.ChangeIndex].Value)
   448  		log.Warnf("Spend from imported account produced change: moving"+
   449  			" %v from imported account into default account.", changeAmount)
   450  	}
   451  
   452  	err = w.checkHighFees(atx.TotalInput, atx.Tx)
   453  	if err != nil {
   454  		return errors.E(op, err)
   455  	}
   456  
   457  	if !a.dontSignTx {
   458  		// Ensure valid signatures were created.
   459  		err = validateMsgTx(op, atx.Tx, atx.PrevScripts)
   460  		if err != nil {
   461  			return errors.E(op, err)
   462  		}
   463  	}
   464  
   465  	a.atx = atx
   466  	a.changeSourceUpdates = changeSourceUpdates
   467  	return nil
   468  }
   469  
   470  // recordAuthoredTx records an authored transaction to the wallet's database.  It
   471  // also updates the database for change addresses used by the new transaction.
   472  //
   473  // As a side effect of recording the transaction to the wallet, clients
   474  // subscribed to new tx notifications will also be notified of the new
   475  // transaction.
   476  func (w *Wallet) recordAuthoredTx(ctx context.Context, op errors.Op, a *authorTx) error {
   477  	rec, err := udb.NewTxRecordFromMsgTx(a.atx.Tx, time.Now())
   478  	if err != nil {
   479  		return errors.E(op, err)
   480  	}
   481  
   482  	w.lockedOutpointMu.Lock()
   483  	defer w.lockedOutpointMu.Unlock()
   484  
   485  	// To avoid a race between publishing a transaction and potentially opening
   486  	// a database view during PublishTransaction, the update must be committed
   487  	// before publishing the transaction to the network.
   488  	var watch []wire.OutPoint
   489  	err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
   490  		for _, up := range a.changeSourceUpdates {
   491  			err := up(dbtx)
   492  			if err != nil {
   493  				return err
   494  			}
   495  		}
   496  
   497  		// TODO: this can be improved by not using the same codepath as notified
   498  		// relevant transactions, since this does a lot of extra work.
   499  		var err error
   500  		watch, err = w.processTransactionRecord(ctx, dbtx, rec, nil, nil)
   501  		return err
   502  	})
   503  	if err != nil {
   504  		return errors.E(op, err)
   505  	}
   506  
   507  	a.watch = watch
   508  	return nil
   509  }
   510  
   511  // txToMultisig spends funds to a multisig output, partially signs the
   512  // transaction, then returns fund
   513  func (w *Wallet) txToMultisig(ctx context.Context, op errors.Op, account uint32, amount dcrutil.Amount, pubkeys [][]byte,
   514  	nRequired int8, minconf int32) (*CreatedTx, stdaddr.Address, []byte, error) {
   515  
   516  	defer w.lockedOutpointMu.Unlock()
   517  	w.lockedOutpointMu.Lock()
   518  
   519  	var created *CreatedTx
   520  	var addr stdaddr.Address
   521  	var msScript []byte
   522  	err := walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
   523  		var err error
   524  		created, addr, msScript, err = w.txToMultisigInternal(ctx, op, dbtx,
   525  			account, amount, pubkeys, nRequired, minconf)
   526  		return err
   527  	})
   528  	if err != nil {
   529  		return nil, nil, nil, errors.E(op, err)
   530  	}
   531  	return created, addr, msScript, nil
   532  }
   533  
   534  func (w *Wallet) txToMultisigInternal(ctx context.Context, op errors.Op, dbtx walletdb.ReadWriteTx, account uint32, amount dcrutil.Amount,
   535  	pubkeys [][]byte, nRequired int8, minconf int32) (*CreatedTx, stdaddr.Address, []byte, error) {
   536  
   537  	addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
   538  
   539  	txToMultisigError := func(err error) (*CreatedTx, stdaddr.Address, []byte, error) {
   540  		return nil, nil, nil, err
   541  	}
   542  
   543  	n, err := w.NetworkBackend()
   544  	if err != nil {
   545  		return txToMultisigError(err)
   546  	}
   547  
   548  	// Get current block's height and hash.
   549  	_, topHeight := w.txStore.MainChainTip(dbtx)
   550  
   551  	// Add in some extra for fees. TODO In the future, make a better
   552  	// fee estimator.
   553  	var feeEstForTx dcrutil.Amount
   554  	switch w.chainParams.Net {
   555  	case wire.MainNet:
   556  		feeEstForTx = 5e7
   557  	case 0x48e7a065: // testnet2
   558  		feeEstForTx = 5e7
   559  	case wire.TestNet3:
   560  		feeEstForTx = 5e7
   561  	default:
   562  		feeEstForTx = 3e4
   563  	}
   564  	amountRequired := amount + feeEstForTx
   565  
   566  	// Instead of taking reward addresses by arg, just create them now  and
   567  	// automatically find all eligible outputs from all current utxos.
   568  	const minAmount = 0
   569  	const maxResults = 0
   570  	eligible, err := w.findEligibleOutputsAmount(dbtx, account, minconf,
   571  		amountRequired, topHeight, minAmount, maxResults)
   572  	if err != nil {
   573  		return txToMultisigError(errors.E(op, err))
   574  	}
   575  	if eligible == nil {
   576  		return txToMultisigError(errors.E(op, "not enough funds to send to multisig address"))
   577  	}
   578  	for i := range eligible {
   579  		op := &eligible[i].OutPoint
   580  		w.lockedOutpoints[outpoint{op.Hash, op.Index}] = struct{}{}
   581  	}
   582  	defer func() {
   583  		for i := range eligible {
   584  			op := &eligible[i].OutPoint
   585  			delete(w.lockedOutpoints, outpoint{op.Hash, op.Index})
   586  		}
   587  	}()
   588  
   589  	msgtx := wire.NewMsgTx()
   590  	scriptSizes := make([]int, 0, len(eligible))
   591  	// Fill out inputs.
   592  	forSigning := make([]Input, 0, len(eligible))
   593  	totalInput := dcrutil.Amount(0)
   594  	for _, e := range eligible {
   595  		txIn := wire.NewTxIn(&e.OutPoint, e.PrevOut.Value, nil)
   596  		msgtx.AddTxIn(txIn)
   597  		totalInput += dcrutil.Amount(e.PrevOut.Value)
   598  		forSigning = append(forSigning, e)
   599  		scriptSizes = append(scriptSizes, txsizes.RedeemP2SHSigScriptSize)
   600  	}
   601  
   602  	// Insert a multi-signature output, then insert this P2SH
   603  	// hash160 into the address manager and the transaction
   604  	// manager.
   605  	msScript, err := stdscript.MultiSigScriptV0(int(nRequired), pubkeys...)
   606  	if err != nil {
   607  		return txToMultisigError(errors.E(op, err))
   608  	}
   609  	_, err = w.manager.ImportScript(addrmgrNs, msScript)
   610  	if err != nil {
   611  		// We don't care if we've already used this address.
   612  		if !errors.Is(err, errors.Exist) {
   613  			return txToMultisigError(errors.E(op, err))
   614  		}
   615  	}
   616  	scAddr, err := stdaddr.NewAddressScriptHashV0(msScript, w.chainParams)
   617  	if err != nil {
   618  		return txToMultisigError(errors.E(op, err))
   619  	}
   620  	vers, p2shScript := scAddr.PaymentScript()
   621  	txOut := &wire.TxOut{
   622  		Value:    int64(amount),
   623  		PkScript: p2shScript,
   624  		Version:  vers,
   625  	}
   626  	msgtx.AddTxOut(txOut)
   627  
   628  	// Add change if we need it.
   629  	changeSize := 0
   630  	if totalInput > amount+feeEstForTx {
   631  		changeSize = txsizes.P2PKHPkScriptSize
   632  	}
   633  	feeSize := txsizes.EstimateSerializeSize(scriptSizes, msgtx.TxOut, changeSize)
   634  	feeEst := txrules.FeeForSerializeSize(w.RelayFee(), feeSize)
   635  
   636  	if totalInput < amount+feeEst {
   637  		return txToMultisigError(errors.E(op, errors.InsufficientBalance))
   638  	}
   639  	if totalInput > amount+feeEst {
   640  		changeSource := p2PKHChangeSource{
   641  			persist: w.persistReturnedChild(ctx, dbtx),
   642  			account: account,
   643  			wallet:  w,
   644  			ctx:     ctx,
   645  		}
   646  
   647  		pkScript, vers, err := changeSource.Script()
   648  		if err != nil {
   649  			return txToMultisigError(err)
   650  		}
   651  		change := totalInput - (amount + feeEst)
   652  		msgtx.AddTxOut(&wire.TxOut{
   653  			Value:    int64(change),
   654  			Version:  vers,
   655  			PkScript: pkScript,
   656  		})
   657  	}
   658  
   659  	err = w.signP2PKHMsgTx(msgtx, forSigning, addrmgrNs)
   660  	if err != nil {
   661  		return txToMultisigError(errors.E(op, err))
   662  	}
   663  
   664  	err = w.checkHighFees(totalInput, msgtx)
   665  	if err != nil {
   666  		return txToMultisigError(errors.E(op, err))
   667  	}
   668  
   669  	err = n.PublishTransactions(ctx, msgtx)
   670  	if err != nil {
   671  		return txToMultisigError(errors.E(op, err))
   672  	}
   673  
   674  	// Request updates from dcrd for new transactions sent to this
   675  	// script hash address.
   676  	err = n.LoadTxFilter(ctx, false, []stdaddr.Address{scAddr}, nil)
   677  	if err != nil {
   678  		return txToMultisigError(errors.E(op, err))
   679  	}
   680  
   681  	err = w.insertMultisigOutIntoTxMgr(dbtx, msgtx, 0)
   682  	if err != nil {
   683  		return txToMultisigError(errors.E(op, err))
   684  	}
   685  
   686  	created := &CreatedTx{
   687  		MsgTx:       msgtx,
   688  		ChangeAddr:  nil,
   689  		ChangeIndex: -1,
   690  	}
   691  
   692  	return created, scAddr, msScript, nil
   693  }
   694  
   695  // validateMsgTx verifies transaction input scripts for tx.  All previous output
   696  // scripts from outputs redeemed by the transaction, in the same order they are
   697  // spent, must be passed in the prevScripts slice.
   698  func validateMsgTx(op errors.Op, tx *wire.MsgTx, prevScripts [][]byte) error {
   699  	for i, prevScript := range prevScripts {
   700  		vm, err := txscript.NewEngine(prevScript, tx, i,
   701  			sanityVerifyFlags, scriptVersionAssumed, nil)
   702  		if err != nil {
   703  			return errors.E(op, err)
   704  		}
   705  		err = vm.Execute()
   706  		if err != nil {
   707  			prevOut := &tx.TxIn[i].PreviousOutPoint
   708  			sigScript := tx.TxIn[i].SignatureScript
   709  
   710  			log.Errorf("Script validation failed (outpoint %v pkscript %x sigscript %x): %v",
   711  				prevOut, prevScript, sigScript, err)
   712  			return errors.E(op, errors.ScriptFailure, err)
   713  		}
   714  	}
   715  	return nil
   716  }
   717  
   718  func creditScripts(credits []Input) [][]byte {
   719  	scripts := make([][]byte, 0, len(credits))
   720  	for _, c := range credits {
   721  		scripts = append(scripts, c.PrevOut.PkScript)
   722  	}
   723  	return scripts
   724  }
   725  
   726  // compressWallet compresses all the utxos in a wallet into a single change
   727  // address. For use when it becomes dusty.
   728  func (w *Wallet) compressWallet(ctx context.Context, op errors.Op, maxNumIns int, account uint32, changeAddr stdaddr.Address) (*chainhash.Hash, error) {
   729  	defer w.lockedOutpointMu.Unlock()
   730  	w.lockedOutpointMu.Lock()
   731  
   732  	var hash *chainhash.Hash
   733  	err := walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
   734  		var err error
   735  		hash, err = w.compressWalletInternal(ctx, op, dbtx, maxNumIns, account, changeAddr)
   736  		return err
   737  	})
   738  	if err != nil {
   739  		return nil, errors.E(op, err)
   740  	}
   741  	return hash, nil
   742  }
   743  
   744  func (w *Wallet) compressWalletInternal(ctx context.Context, op errors.Op, dbtx walletdb.ReadWriteTx, maxNumIns int, account uint32,
   745  	changeAddr stdaddr.Address) (*chainhash.Hash, error) {
   746  
   747  	addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
   748  
   749  	n, err := w.NetworkBackend()
   750  	if err != nil {
   751  		return nil, errors.E(op, err)
   752  	}
   753  
   754  	// Get current block's height
   755  	_, tipHeight := w.txStore.MainChainTip(dbtx)
   756  
   757  	minconf := int32(1)
   758  	eligible, err := w.findEligibleOutputs(dbtx, account, minconf, tipHeight)
   759  	if err != nil {
   760  		return nil, errors.E(op, err)
   761  	}
   762  	if len(eligible) <= 1 {
   763  		return nil, errors.E(op, "too few outputs to consolidate")
   764  	}
   765  	for i := range eligible {
   766  		op := eligible[i].OutPoint
   767  		w.lockedOutpoints[outpoint{op.Hash, op.Index}] = struct{}{}
   768  	}
   769  
   770  	defer func() {
   771  		for i := range eligible {
   772  			op := &eligible[i].OutPoint
   773  			delete(w.lockedOutpoints, outpoint{op.Hash, op.Index})
   774  		}
   775  	}()
   776  
   777  	// Check if output address is default, and generate a new address if needed
   778  	if changeAddr == nil {
   779  		const accountName = "" // not used, so can be faked.
   780  		changeAddr, err = w.newChangeAddress(ctx, op, w.persistReturnedChild(ctx, dbtx),
   781  			accountName, account, gapPolicyIgnore)
   782  		if err != nil {
   783  			return nil, errors.E(op, err)
   784  		}
   785  	}
   786  	vers, pkScript := changeAddr.PaymentScript()
   787  	msgtx := wire.NewMsgTx()
   788  	msgtx.AddTxOut(&wire.TxOut{
   789  		Value:    0,
   790  		PkScript: pkScript,
   791  		Version:  vers,
   792  	})
   793  	maximumTxSize := w.chainParams.MaxTxSize
   794  	if w.chainParams.Net == wire.MainNet {
   795  		maximumTxSize = maxStandardTxSize
   796  	}
   797  
   798  	// Add the txins using all the eligible outputs.
   799  	totalAdded := dcrutil.Amount(0)
   800  	scriptSizes := make([]int, 0, maxNumIns)
   801  	forSigning := make([]Input, 0, maxNumIns)
   802  	count := 0
   803  	for _, e := range eligible {
   804  		if count >= maxNumIns {
   805  			break
   806  		}
   807  		// Add the size of a wire.OutPoint
   808  		if msgtx.SerializeSize() > maximumTxSize {
   809  			break
   810  		}
   811  
   812  		txIn := wire.NewTxIn(&e.OutPoint, e.PrevOut.Value, nil)
   813  		msgtx.AddTxIn(txIn)
   814  		totalAdded += dcrutil.Amount(e.PrevOut.Value)
   815  		forSigning = append(forSigning, e)
   816  		scriptSizes = append(scriptSizes, txsizes.RedeemP2PKHSigScriptSize)
   817  		count++
   818  	}
   819  
   820  	// Get an initial fee estimate based on the number of selected inputs
   821  	// and added outputs, with no change.
   822  	szEst := txsizes.EstimateSerializeSize(scriptSizes, msgtx.TxOut, 0)
   823  	feeEst := txrules.FeeForSerializeSize(w.RelayFee(), szEst)
   824  
   825  	msgtx.TxOut[0].Value = int64(totalAdded - feeEst)
   826  
   827  	err = w.signP2PKHMsgTx(msgtx, forSigning, addrmgrNs)
   828  	if err != nil {
   829  		return nil, errors.E(op, err)
   830  	}
   831  	err = validateMsgTx(op, msgtx, creditScripts(forSigning))
   832  	if err != nil {
   833  		return nil, errors.E(op, err)
   834  	}
   835  
   836  	err = w.checkHighFees(totalAdded, msgtx)
   837  	if err != nil {
   838  		return nil, errors.E(op, err)
   839  	}
   840  
   841  	err = n.PublishTransactions(ctx, msgtx)
   842  	if err != nil {
   843  		return nil, errors.E(op, err)
   844  	}
   845  
   846  	// Insert the transaction and credits into the transaction manager.
   847  	rec, err := w.insertIntoTxMgr(dbtx, msgtx)
   848  	if err != nil {
   849  		return nil, errors.E(op, err)
   850  	}
   851  	err = w.insertCreditsIntoTxMgr(op, dbtx, msgtx, rec)
   852  	if err != nil {
   853  		return nil, err
   854  	}
   855  
   856  	txHash := msgtx.TxHash()
   857  	log.Infof("Successfully consolidated funds in transaction %v", &txHash)
   858  
   859  	return &txHash, nil
   860  }
   861  
   862  // makeTicket creates a ticket from a split transaction output. It can optionally
   863  // create a ticket that pays a fee to a pool if a pool input and pool address are
   864  // passed.
   865  func makeTicket(params *chaincfg.Params, inputPool *Input, input *Input, addrVote stdaddr.StakeAddress,
   866  	addrSubsidy stdaddr.StakeAddress, ticketCost int64, addrPool stdaddr.StakeAddress) (*wire.MsgTx, error) {
   867  
   868  	mtx := wire.NewMsgTx()
   869  
   870  	if addrPool != nil && inputPool != nil {
   871  		txIn := wire.NewTxIn(&inputPool.OutPoint, inputPool.PrevOut.Value, []byte{})
   872  		mtx.AddTxIn(txIn)
   873  	}
   874  
   875  	txIn := wire.NewTxIn(&input.OutPoint, input.PrevOut.Value, []byte{})
   876  	mtx.AddTxIn(txIn)
   877  
   878  	// Create a new script which pays to the provided address with an
   879  	// SStx tagged output.
   880  	if addrVote == nil {
   881  		return nil, errors.E(errors.Invalid, "nil vote address")
   882  	}
   883  	vers, pkScript := addrVote.VotingRightsScript()
   884  
   885  	txOut := &wire.TxOut{
   886  		Value:    ticketCost,
   887  		PkScript: pkScript,
   888  		Version:  vers,
   889  	}
   890  	mtx.AddTxOut(txOut)
   891  
   892  	// Obtain the commitment amounts.
   893  	var amountsCommitted []int64
   894  	userSubsidyNullIdx := 0
   895  	var err error
   896  	if addrPool == nil {
   897  		_, amountsCommitted, err = stake.SStxNullOutputAmounts(
   898  			[]int64{input.PrevOut.Value}, []int64{0}, ticketCost)
   899  		if err != nil {
   900  			return nil, err
   901  		}
   902  
   903  	} else {
   904  		_, amountsCommitted, err = stake.SStxNullOutputAmounts(
   905  			[]int64{inputPool.PrevOut.Value, input.PrevOut.Value}, []int64{0, 0}, ticketCost)
   906  		if err != nil {
   907  			return nil, err
   908  		}
   909  		userSubsidyNullIdx = 1
   910  	}
   911  
   912  	// Zero value P2PKH addr.
   913  	zeroed := [20]byte{}
   914  	addrZeroed, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(zeroed[:], params)
   915  	if err != nil {
   916  		return nil, err
   917  	}
   918  
   919  	// 2. (Optional) If we're passed a pool address, make an extra
   920  	// commitment to the pool.
   921  	if addrPool != nil {
   922  		vers, pkScript := addrPool.RewardCommitmentScript(
   923  			amountsCommitted[0], 0, revocationFeeLimit)
   924  		txout := &wire.TxOut{
   925  			Value:    0,
   926  			PkScript: pkScript,
   927  			Version:  vers,
   928  		}
   929  		mtx.AddTxOut(txout)
   930  
   931  		// Create a new script which pays to the provided address with an
   932  		// SStx change tagged output.
   933  		vers, pkScript = addrZeroed.StakeChangeScript()
   934  
   935  		txOut = &wire.TxOut{
   936  			Value:    0,
   937  			PkScript: pkScript,
   938  			Version:  vers,
   939  		}
   940  		mtx.AddTxOut(txOut)
   941  	}
   942  
   943  	// 3. Create the commitment and change output paying to the user.
   944  	//
   945  	// Create an OP_RETURN push containing the pubkeyhash to send rewards to.
   946  	// Apply limits to revocations for fees while not allowing
   947  	// fees for votes.
   948  	vers, pkScript = addrSubsidy.RewardCommitmentScript(
   949  		amountsCommitted[userSubsidyNullIdx], 0, revocationFeeLimit)
   950  	if err != nil {
   951  		return nil, errors.E(errors.Invalid,
   952  			errors.Errorf("commitment address %v", addrSubsidy))
   953  	}
   954  	txout := &wire.TxOut{
   955  		Value:    0,
   956  		PkScript: pkScript,
   957  		Version:  vers,
   958  	}
   959  	mtx.AddTxOut(txout)
   960  
   961  	// Create a new script which pays to the provided address with an
   962  	// SStx change tagged output.
   963  	vers, pkScript = addrZeroed.StakeChangeScript()
   964  	txOut = &wire.TxOut{
   965  		Value:    0,
   966  		PkScript: pkScript,
   967  		Version:  vers,
   968  	}
   969  	mtx.AddTxOut(txOut)
   970  
   971  	// Make sure we generated a valid SStx.
   972  	if err := stake.CheckSStx(mtx); err != nil {
   973  		return nil, errors.E(errors.Op("stake.CheckSStx"), errors.Bug, err)
   974  	}
   975  
   976  	return mtx, nil
   977  }
   978  
   979  var p2pkhSizedScript = make([]byte, 25)
   980  
   981  func (w *Wallet) mixedSplit(ctx context.Context, req *PurchaseTicketsRequest, neededPerTicket dcrutil.Amount) (tx *wire.MsgTx, outIndexes []int, err error) {
   982  	// Use txauthor to perform input selection and change amount
   983  	// calculations for the unmixed portions of the coinjoin.
   984  	mixOut := make([]*wire.TxOut, req.Count)
   985  	for i := 0; i < req.Count; i++ {
   986  		mixOut[i] = &wire.TxOut{Value: int64(neededPerTicket), Version: 0, PkScript: p2pkhSizedScript}
   987  	}
   988  	relayFee := w.RelayFee()
   989  	var changeSourceUpdates []func(walletdb.ReadWriteTx) error
   990  	defer func() {
   991  		if err != nil {
   992  			return
   993  		}
   994  
   995  		err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
   996  			for _, f := range changeSourceUpdates {
   997  				if err := f(dbtx); err != nil {
   998  					return err
   999  				}
  1000  			}
  1001  			return nil
  1002  		})
  1003  	}()
  1004  	var unlockOutpoints []*wire.OutPoint
  1005  	defer func() {
  1006  		if len(unlockOutpoints) != 0 {
  1007  			w.lockedOutpointMu.Lock()
  1008  			for _, op := range unlockOutpoints {
  1009  				delete(w.lockedOutpoints, outpoint{op.Hash, op.Index})
  1010  			}
  1011  			w.lockedOutpointMu.Unlock()
  1012  		}
  1013  	}()
  1014  	ignoreInput := func(op *wire.OutPoint) bool {
  1015  		_, ok := w.lockedOutpoints[outpoint{op.Hash, op.Index}]
  1016  		return ok
  1017  	}
  1018  
  1019  	w.lockedOutpointMu.Lock()
  1020  	var atx *txauthor.AuthoredTx
  1021  	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1022  		_, tipHeight := w.txStore.MainChainTip(dbtx)
  1023  		inputSource := w.txStore.MakeInputSource(dbtx, req.SourceAccount,
  1024  			req.MinConf, tipHeight, ignoreInput)
  1025  		changeSource := &p2PKHChangeSource{
  1026  			persist:   w.deferPersistReturnedChild(ctx, &changeSourceUpdates),
  1027  			account:   req.ChangeAccount,
  1028  			wallet:    w,
  1029  			ctx:       ctx,
  1030  			gapPolicy: gapPolicyIgnore,
  1031  		}
  1032  		var err error
  1033  		atx, err = txauthor.NewUnsignedTransaction(mixOut, relayFee,
  1034  			inputSource.SelectInputs, changeSource,
  1035  			w.chainParams.MaxTxSize)
  1036  		if err != nil {
  1037  			return err
  1038  		}
  1039  		for _, in := range atx.Tx.TxIn {
  1040  			prev := &in.PreviousOutPoint
  1041  			w.lockedOutpoints[outpoint{prev.Hash, prev.Index}] = struct{}{}
  1042  			unlockOutpoints = append(unlockOutpoints, prev)
  1043  		}
  1044  		return nil
  1045  	})
  1046  	w.lockedOutpointMu.Unlock()
  1047  	if err != nil {
  1048  		return
  1049  	}
  1050  	for _, in := range atx.Tx.TxIn {
  1051  		log.Infof("selected input %v (%v) for ticket purchase split transaction",
  1052  			in.PreviousOutPoint, dcrutil.Amount(in.ValueIn))
  1053  	}
  1054  
  1055  	var change *wire.TxOut
  1056  	if atx.ChangeIndex >= 0 {
  1057  		change = atx.Tx.TxOut[atx.ChangeIndex]
  1058  	}
  1059  	if change != nil && dcrutil.Amount(change.Value) < smallestMixChange(relayFee) {
  1060  		change = nil
  1061  	}
  1062  	const (
  1063  		txVersion = 1
  1064  		locktime  = 0
  1065  		expiry    = 0
  1066  	)
  1067  	pairing := coinjoin.EncodeDesc(coinjoin.P2PKHv0, int64(neededPerTicket), txVersion, locktime, expiry)
  1068  	cj := w.newCsppJoin(ctx, change, neededPerTicket, req.MixedSplitAccount, req.MixedAccountBranch, req.Count)
  1069  	for i, in := range atx.Tx.TxIn {
  1070  		cj.addTxIn(atx.PrevScripts[i], in)
  1071  	}
  1072  
  1073  	csppSession, err := cspp.NewSession(rand.Reader, debugLog, pairing, req.Count)
  1074  	if err != nil {
  1075  		return
  1076  	}
  1077  	var conn net.Conn
  1078  	if req.DialCSPPServer != nil {
  1079  		conn, err = req.DialCSPPServer(ctx, "tcp", req.CSPPServer)
  1080  	} else {
  1081  		conn, err = tls.Dial("tcp", req.CSPPServer, nil)
  1082  	}
  1083  	if err != nil {
  1084  		return
  1085  	}
  1086  	defer conn.Close()
  1087  	log.Infof("Dialed CSPPServer %v -> %v", conn.LocalAddr(), conn.RemoteAddr())
  1088  	err = csppSession.DiceMix(ctx, conn, cj)
  1089  	if err != nil {
  1090  		return
  1091  	}
  1092  	splitTx := cj.tx
  1093  	splitTxHash := splitTx.TxHash()
  1094  	log.Infof("Completed CoinShuffle++ mix of ticket split transaction %v", &splitTxHash)
  1095  	return splitTx, cj.mixOutputIndexes(), nil
  1096  }
  1097  
  1098  func (w *Wallet) individualSplit(ctx context.Context, req *PurchaseTicketsRequest, neededPerTicket dcrutil.Amount) (tx *wire.MsgTx, outIndexes []int, err error) {
  1099  	// Fetch the single use split address to break tickets into, to
  1100  	// immediately be consumed as tickets.
  1101  	//
  1102  	// This opens a write transaction.
  1103  	splitTxAddr, err := w.NewInternalAddress(ctx, req.SourceAccount, WithGapPolicyWrap())
  1104  	if err != nil {
  1105  		return
  1106  	}
  1107  
  1108  	vers, splitPkScript := splitTxAddr.PaymentScript()
  1109  
  1110  	// Create the split transaction by using txToOutputs. This varies
  1111  	// based upon whether or not the user is using a stake pool or not.
  1112  	// For the default stake pool implementation, the user pays out the
  1113  	// first ticket commitment of a smaller amount to the pool, while
  1114  	// paying themselves with the larger ticket commitment.
  1115  	var splitOuts []*wire.TxOut
  1116  	for i := 0; i < req.Count; i++ {
  1117  		splitOuts = append(splitOuts, &wire.TxOut{
  1118  			Value:    int64(neededPerTicket),
  1119  			PkScript: splitPkScript,
  1120  			Version:  vers,
  1121  		})
  1122  		outIndexes = append(outIndexes, i)
  1123  	}
  1124  
  1125  	const op errors.Op = "individualSplit"
  1126  	a := &authorTx{
  1127  		outputs:            splitOuts,
  1128  		account:            req.SourceAccount,
  1129  		changeAccount:      req.ChangeAccount,
  1130  		minconf:            req.MinConf,
  1131  		randomizeChangeIdx: false,
  1132  		txFee:              w.RelayFee(),
  1133  		dontSignTx:         req.DontSignTx,
  1134  		isTreasury:         false,
  1135  	}
  1136  	err = w.authorTx(ctx, op, a)
  1137  	if err != nil {
  1138  		return
  1139  	}
  1140  	err = w.recordAuthoredTx(ctx, op, a)
  1141  	if err != nil {
  1142  		return
  1143  	}
  1144  	if !req.DontSignTx {
  1145  		err = w.publishAndWatch(ctx, op, nil, a.atx.Tx, a.watch)
  1146  		if err != nil {
  1147  			return
  1148  		}
  1149  	}
  1150  
  1151  	tx = a.atx.Tx
  1152  	return
  1153  }
  1154  
  1155  func (w *Wallet) vspSplit(ctx context.Context, req *PurchaseTicketsRequest, neededPerTicket, vspFee dcrutil.Amount) (tx *wire.MsgTx, outIndexes []int, err error) {
  1156  	// Fetch the single use split address to break tickets into, to
  1157  	// immediately be consumed as tickets.
  1158  	//
  1159  	// This opens a write transaction.
  1160  	splitTxAddr, err := w.NewInternalAddress(ctx, req.SourceAccount, WithGapPolicyWrap())
  1161  	if err != nil {
  1162  		return
  1163  	}
  1164  
  1165  	vers, splitPkScript := splitTxAddr.PaymentScript()
  1166  
  1167  	// Create the split transaction by using authorTx. This varies
  1168  	// based upon whether or not the user is using a stake pool or not.
  1169  	// For the default stake pool implementation, the user pays out the
  1170  	// first ticket commitment of a smaller amount to the pool, while
  1171  	// paying themselves with the larger ticket commitment.
  1172  	var splitOuts []*wire.TxOut
  1173  	for i := 0; i < req.Count; i++ {
  1174  		userAmt := neededPerTicket - vspFee
  1175  		splitOuts = append(splitOuts, &wire.TxOut{
  1176  			Value:    int64(vspFee),
  1177  			PkScript: splitPkScript,
  1178  			Version:  vers,
  1179  		})
  1180  		splitOuts = append(splitOuts, &wire.TxOut{
  1181  			Value:    int64(userAmt),
  1182  			PkScript: splitPkScript,
  1183  			Version:  vers,
  1184  		})
  1185  		outIndexes = append(outIndexes, i*2)
  1186  	}
  1187  
  1188  	const op errors.Op = "vspSplit"
  1189  	a := &authorTx{
  1190  		outputs:            splitOuts,
  1191  		account:            req.SourceAccount,
  1192  		changeAccount:      req.ChangeAccount,
  1193  		minconf:            req.MinConf,
  1194  		randomizeChangeIdx: false,
  1195  		txFee:              w.RelayFee(),
  1196  		dontSignTx:         req.DontSignTx,
  1197  		isTreasury:         false,
  1198  	}
  1199  	err = w.authorTx(ctx, op, a)
  1200  	if err != nil {
  1201  		return
  1202  	}
  1203  	err = w.recordAuthoredTx(ctx, op, a)
  1204  	if err != nil {
  1205  		return
  1206  	}
  1207  	if !req.DontSignTx {
  1208  		err = w.publishAndWatch(ctx, op, nil, a.atx.Tx, a.watch)
  1209  		if err != nil {
  1210  			return
  1211  		}
  1212  	}
  1213  
  1214  	tx = a.atx.Tx
  1215  	return
  1216  }
  1217  
  1218  var errVSPFeeRequiresUTXOSplit = errors.New("paying VSP fee requires UTXO split")
  1219  
  1220  // purchaseTickets indicates to the wallet that a ticket should be purchased
  1221  // using all currently available funds.  The ticket address parameter in the
  1222  // request can be nil in which case the ticket address associated with the
  1223  // wallet instance will be used.  Also, when the spend limit in the request is
  1224  // greater than or equal to 0, tickets that cost more than that limit will
  1225  // return an error that not enough funds are available.
  1226  func (w *Wallet) purchaseTickets(ctx context.Context, op errors.Op,
  1227  	n NetworkBackend, req *PurchaseTicketsRequest) (*PurchaseTicketsResponse, error) {
  1228  	// Ensure the minimum number of required confirmations is positive.
  1229  	if req.MinConf < 0 {
  1230  		return nil, errors.E(op, errors.Invalid, "negative minconf")
  1231  	}
  1232  	// Need a positive or zero expiry that is higher than the next block to
  1233  	// generate.
  1234  	if req.Expiry < 0 {
  1235  		return nil, errors.E(op, errors.Invalid, "negative expiry")
  1236  	}
  1237  
  1238  	// Perform a sanity check on expiry.
  1239  	var tipHeight int32
  1240  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1241  		_, tipHeight = w.txStore.MainChainTip(dbtx)
  1242  		return nil
  1243  	})
  1244  	if err != nil {
  1245  		return nil, err
  1246  	}
  1247  	if req.Expiry <= tipHeight+1 && req.Expiry > 0 {
  1248  		return nil, errors.E(op, errors.Invalid, "expiry height must be above next block height")
  1249  	}
  1250  
  1251  	stakeAddrFunc := func(op errors.Op, account, branch uint32) (stdaddr.StakeAddress, uint32, error) {
  1252  		const accountName = "" // not used, so can be faked.
  1253  		a, err := w.nextAddress(ctx, op, w.persistReturnedChild(ctx, nil), accountName,
  1254  			account, branch, WithGapPolicyIgnore())
  1255  		if err != nil {
  1256  			return nil, 0, err
  1257  		}
  1258  		var idx uint32
  1259  		if xpa, ok := a.(*xpubAddress); ok {
  1260  			idx = xpa.child
  1261  		}
  1262  		switch a := a.(type) {
  1263  		case stdaddr.StakeAddress:
  1264  			return a, idx, nil
  1265  		default:
  1266  			return nil, 0, errors.E(errors.Invalid, "account does "+
  1267  				"not return compatible stake addresses")
  1268  		}
  1269  	}
  1270  
  1271  	if w.addressReuse && req.CSPPServer == "" {
  1272  		xpub := w.addressBuffers[udb.DefaultAccountNum].albExternal.branchXpub
  1273  		addr, err := deriveChildAddress(xpub, 0, w.chainParams)
  1274  		if err != nil {
  1275  			err = errors.E(op, err)
  1276  		}
  1277  		stakeAddr, ok := addr.(stdaddr.StakeAddress)
  1278  		if !ok {
  1279  			err = errors.E(op, errors.Invalid, "account does not return "+
  1280  				"compatible stake addresses")
  1281  		}
  1282  		stakeAddrFunc = func(errors.Op, uint32, uint32) (stdaddr.StakeAddress, uint32, error) {
  1283  			return stakeAddr, 0, err
  1284  		}
  1285  	}
  1286  
  1287  	// Calculate the current ticket price.  If the DCP0001 deployment is not
  1288  	// active, fallback to querying the ticket price over RPC.
  1289  	ticketPrice, err := w.NextStakeDifficulty(ctx)
  1290  	if errors.Is(err, errors.Deployment) {
  1291  		ticketPrice, err = n.StakeDifficulty(ctx)
  1292  	}
  1293  	if err != nil {
  1294  		return nil, err
  1295  	}
  1296  
  1297  	// Try to get the pool address from the request. If none exists
  1298  	// in the request, try to get the global pool address. Then do
  1299  	// the same for pool fees, but check sanity too.
  1300  	poolAddress := req.VSPAddress
  1301  	if poolAddress == nil {
  1302  		poolAddress = w.poolAddress
  1303  	}
  1304  	poolFees := req.VSPFees
  1305  	if poolFees == 0.0 {
  1306  		poolFees = w.poolFees
  1307  	}
  1308  	if poolAddress != nil && poolFees == 0.0 {
  1309  		return nil, errors.E(op, errors.Invalid, "stakepool fee percent unset")
  1310  	}
  1311  
  1312  	var stakeSubmissionPkScriptSize int
  1313  
  1314  	// The stake submission pkScript is tagged by an OP_SSTX.
  1315  	switch req.VotingAddress.(type) {
  1316  	case *stdaddr.AddressScriptHashV0:
  1317  		stakeSubmissionPkScriptSize = txsizes.P2SHPkScriptSize + 1
  1318  	case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0, PubKeyHashAddress, nil:
  1319  		stakeSubmissionPkScriptSize = txsizes.P2PKHPkScriptSize + 1
  1320  	default:
  1321  		return nil, errors.E(op, errors.Invalid,
  1322  			"ticket address must either be P2SH or P2PKH")
  1323  	}
  1324  
  1325  	// Make sure that we have enough funds. Calculate different
  1326  	// ticket required amounts depending on whether or not a
  1327  	// pool output is needed. If the ticket fee increment is
  1328  	// unset in the request, use the global ticket fee increment.
  1329  	var neededPerTicket dcrutil.Amount
  1330  	var estSize int
  1331  	ticketRelayFee := w.RelayFee()
  1332  
  1333  	if poolAddress == nil {
  1334  		// A solo ticket has:
  1335  		//   - a single input redeeming a P2PKH for the worst case size
  1336  		//   - a P2PKH or P2SH stake submission output
  1337  		//   - a ticket commitment output
  1338  		//   - an OP_SSTXCHANGE tagged P2PKH or P2SH change output
  1339  		//
  1340  		//   NB: The wallet currently only supports P2PKH change addresses.
  1341  		//   The network supports both P2PKH and P2SH change addresses however.
  1342  		inSizes := []int{txsizes.RedeemP2PKHSigScriptSize}
  1343  		outSizes := []int{stakeSubmissionPkScriptSize,
  1344  			txsizes.TicketCommitmentScriptSize, txsizes.P2PKHPkScriptSize + 1}
  1345  		estSize = txsizes.EstimateSerializeSizeFromScriptSizes(inSizes,
  1346  			outSizes, 0)
  1347  	} else {
  1348  		// A pool ticket has:
  1349  		//   - two inputs redeeming a P2PKH for the worst case size
  1350  		//   - a P2PKH or P2SH stake submission output
  1351  		//   - two ticket commitment outputs
  1352  		//   - two OP_SSTXCHANGE tagged P2PKH or P2SH change outputs
  1353  		//
  1354  		//   NB: The wallet currently only supports P2PKH change addresses.
  1355  		//   The network supports both P2PKH and P2SH change addresses however.
  1356  		inSizes := []int{txsizes.RedeemP2PKHSigScriptSize,
  1357  			txsizes.RedeemP2PKHSigScriptSize}
  1358  		outSizes := []int{stakeSubmissionPkScriptSize,
  1359  			txsizes.TicketCommitmentScriptSize, txsizes.TicketCommitmentScriptSize,
  1360  			txsizes.P2PKHPkScriptSize + 1, txsizes.P2PKHPkScriptSize + 1}
  1361  		estSize = txsizes.EstimateSerializeSizeFromScriptSizes(inSizes,
  1362  			outSizes, 0)
  1363  	}
  1364  
  1365  	ticketFee := txrules.FeeForSerializeSize(ticketRelayFee, estSize)
  1366  	neededPerTicket = ticketFee + ticketPrice
  1367  
  1368  	// If we need to calculate the amount for a pool fee percentage,
  1369  	// do so now.
  1370  	var vspFee dcrutil.Amount
  1371  	if poolAddress != nil {
  1372  		// poolAddress is only used with the legacy stakepool
  1373  		const dcp0010Active = false
  1374  		vspFee = txrules.StakePoolTicketFee(ticketPrice, ticketFee,
  1375  			tipHeight, poolFees, w.ChainParams(), dcp0010Active)
  1376  	}
  1377  
  1378  	// After tickets are created and published, watch for future
  1379  	// relevant transactions
  1380  	var watchOutPoints []wire.OutPoint
  1381  	defer func() {
  1382  		// Cancellation of the request context should not prevent the
  1383  		// watching of addresses and outpoints that need to be watched.
  1384  		// A better solution would be to watch for the data first,
  1385  		// before publishing transactions.
  1386  		ctx := context.TODO()
  1387  		_, err := w.watchHDAddrs(ctx, false, n)
  1388  		if err != nil {
  1389  			log.Errorf("Failed to watch for future addresses after ticket "+
  1390  				"purchases: %v", err)
  1391  		}
  1392  		if len(watchOutPoints) > 0 {
  1393  			err := n.LoadTxFilter(ctx, false, nil, watchOutPoints)
  1394  			if err != nil {
  1395  				log.Errorf("Failed to watch outpoints: %v", err)
  1396  			}
  1397  		}
  1398  	}()
  1399  
  1400  	var vspFeeCredits, ticketCredits [][]Input
  1401  	unlockCredits := true
  1402  	total := func(ins []Input) (v int64) {
  1403  		for _, in := range ins {
  1404  			v += in.PrevOut.Value
  1405  		}
  1406  		return
  1407  	}
  1408  	if req.VSPFeePaymentProcess != nil {
  1409  		if req.VSPFeeProcess == nil {
  1410  			return nil, errors.E(op, errors.Bug, "VSPFeeProcess "+
  1411  				"may not be nil if VSPServerProcess is non-nil")
  1412  		}
  1413  		feePrice, err := req.VSPFeeProcess(ctx)
  1414  		if err != nil {
  1415  			return nil, err
  1416  		}
  1417  		// In SPV mode, DCP0010 is assumed to have activated.  This
  1418  		// results in a larger fee calculation for the purposes of UTXO
  1419  		// selection.  In RPC mode the actual activation can be
  1420  		// determined.
  1421  		dcp0010Active := true
  1422  		switch n := n.(type) {
  1423  		case *dcrd.RPC:
  1424  			dcp0010Active, err = deployments.DCP0010Active(ctx,
  1425  				tipHeight, w.chainParams, n)
  1426  			if err != nil {
  1427  				return nil, err
  1428  			}
  1429  		}
  1430  		fee := txrules.StakePoolTicketFee(ticketPrice, ticketFee,
  1431  			tipHeight, feePrice, w.chainParams,
  1432  			dcp0010Active)
  1433  
  1434  		// Reserve outputs for number of buys.
  1435  		vspFeeCredits = make([][]Input, 0, req.Count)
  1436  		ticketCredits = make([][]Input, 0, req.Count)
  1437  		defer func() {
  1438  			if unlockCredits {
  1439  				for _, credit := range vspFeeCredits {
  1440  					for _, c := range credit {
  1441  						log.Debugf("unlocked unneeded credit for vsp fee tx: %v",
  1442  							c.OutPoint.String())
  1443  						w.UnlockOutpoint(&c.OutPoint.Hash, c.OutPoint.Index)
  1444  					}
  1445  				}
  1446  			}
  1447  		}()
  1448  		if req.extraSplitOutput != nil {
  1449  			vspFeeCredits = make([][]Input, 1)
  1450  			vspFeeCredits[0] = []Input{*req.extraSplitOutput}
  1451  			op := &req.extraSplitOutput.OutPoint
  1452  			w.LockOutpoint(&op.Hash, op.Index)
  1453  		}
  1454  		var lowBalance bool
  1455  		for i := 0; i < req.Count; i++ {
  1456  			if req.extraSplitOutput == nil {
  1457  				credits, err := w.ReserveOutputsForAmount(ctx,
  1458  					req.SourceAccount, fee, req.MinConf)
  1459  
  1460  				if errors.Is(err, errors.InsufficientBalance) {
  1461  					lowBalance = true
  1462  					break
  1463  				}
  1464  				if err != nil {
  1465  					log.Errorf("ReserveOutputsForAmount failed: %v", err)
  1466  					return nil, err
  1467  				}
  1468  				vspFeeCredits = append(vspFeeCredits, credits)
  1469  			}
  1470  
  1471  			credits, err := w.ReserveOutputsForAmount(ctx, req.SourceAccount,
  1472  				ticketPrice, req.MinConf)
  1473  			if errors.Is(err, errors.InsufficientBalance) {
  1474  				lowBalance = true
  1475  				credits, _ = w.reserveOutputs(ctx, req.SourceAccount,
  1476  					req.MinConf)
  1477  				if len(credits) != 0 {
  1478  					ticketCredits = append(ticketCredits, credits)
  1479  				}
  1480  				break
  1481  			}
  1482  			if err != nil {
  1483  				log.Errorf("ReserveOutputsForAmount failed: %v", err)
  1484  				return nil, err
  1485  			}
  1486  			ticketCredits = append(ticketCredits, credits)
  1487  		}
  1488  		for _, credits := range ticketCredits {
  1489  			for _, c := range credits {
  1490  				log.Debugf("unlocked credit for ticket tx: %v",
  1491  					c.OutPoint.String())
  1492  				w.UnlockOutpoint(&c.OutPoint.Hash, c.OutPoint.Index)
  1493  			}
  1494  		}
  1495  		if lowBalance {
  1496  			// When there is UTXO contention between reserved fee
  1497  			// UTXOs and the tickets that can be purchased, UTXOs
  1498  			// which were selected for paying VSP fees are instead
  1499  			// allocated towards purchasing tickets.  We sort the
  1500  			// UTXOs picked for fees and tickets by decreasing
  1501  			// amounts and incrementally reserve them for ticket
  1502  			// purchases while reducing the total number of fees
  1503  			// (and therefore tickets) that will be purchased.  The
  1504  			// final UTXOs chosen for ticket purchases must be
  1505  			// unlocked for UTXO selection to work, while all inputs
  1506  			// for fee payments must be locked.
  1507  			credits := vspFeeCredits[:len(vspFeeCredits):len(vspFeeCredits)]
  1508  			credits = append(credits, ticketCredits...)
  1509  			sort.Slice(credits, func(i, j int) bool {
  1510  				return total(credits[i]) > total(credits[j])
  1511  			})
  1512  			if len(credits) == 0 {
  1513  				return nil, errors.E(errors.InsufficientBalance)
  1514  			}
  1515  			if req.Count > len(credits)-1 {
  1516  				req.Count = len(credits) - 1
  1517  			}
  1518  			var freedBalance int64
  1519  			extraSplit := true
  1520  			for req.Count > 1 {
  1521  				for _, c := range credits[0] {
  1522  					freedBalance += c.PrevOut.Value
  1523  					w.UnlockOutpoint(&c.OutPoint.Hash, c.OutPoint.Index)
  1524  				}
  1525  				credits = credits[1:]
  1526  				// XXX this is a bad estimate because it doesn't
  1527  				// consider the transaction fees
  1528  				if freedBalance > int64(ticketPrice)*int64(req.Count) {
  1529  					extraSplit = false
  1530  					break
  1531  				}
  1532  				req.Count--
  1533  			}
  1534  			vspFeeCredits = credits
  1535  			var remaining int64
  1536  			for _, c := range vspFeeCredits {
  1537  				remaining += total(c)
  1538  				for i := range c {
  1539  					w.LockOutpoint(&c[i].OutPoint.Hash, c[i].OutPoint.Index)
  1540  				}
  1541  			}
  1542  
  1543  			if req.Count < 2 && extraSplit {
  1544  				// XXX still a bad estimate
  1545  				if int64(ticketPrice) > freedBalance+remaining {
  1546  					return nil, errors.E(errors.InsufficientBalance)
  1547  				}
  1548  				// A new transaction may need to be created to
  1549  				// split a single UTXO into two: one to pay the
  1550  				// VSP fee, and a second to fund the ticket
  1551  				// purchase.  This error condition is left to
  1552  				// the caller to detect and perform.
  1553  				return nil, errVSPFeeRequiresUTXOSplit
  1554  			}
  1555  		}
  1556  		log.Infof("Reserved credits for %d tickets: total fee: %v", req.Count, fee)
  1557  		for _, credit := range vspFeeCredits {
  1558  			for _, c := range credit {
  1559  				log.Debugf("%s reserved for vsp fee transaction", c.OutPoint.String())
  1560  			}
  1561  		}
  1562  	}
  1563  
  1564  	purchaseTicketsResponse := &PurchaseTicketsResponse{}
  1565  	var splitTx *wire.MsgTx
  1566  	var splitOutputIndexes []int
  1567  	for {
  1568  		switch {
  1569  		case req.CSPPServer != "":
  1570  			splitTx, splitOutputIndexes, err = w.mixedSplit(ctx, req, neededPerTicket)
  1571  		case req.VSPAddress != nil:
  1572  			splitTx, splitOutputIndexes, err = w.vspSplit(ctx, req, neededPerTicket, vspFee)
  1573  		default:
  1574  			splitTx, splitOutputIndexes, err = w.individualSplit(ctx, req, neededPerTicket)
  1575  		}
  1576  		if errors.Is(err, errors.InsufficientBalance) && req.Count > 1 {
  1577  			req.Count--
  1578  			if len(vspFeeCredits) > 0 {
  1579  				for _, in := range vspFeeCredits[0] {
  1580  					w.UnlockOutpoint(&in.OutPoint.Hash, in.OutPoint.Index)
  1581  				}
  1582  				vspFeeCredits = vspFeeCredits[1:]
  1583  			}
  1584  			continue
  1585  		}
  1586  		if err != nil {
  1587  			return nil, errors.E(op, err)
  1588  		}
  1589  		break
  1590  	}
  1591  	purchaseTicketsResponse.SplitTx = splitTx
  1592  
  1593  	// Process and publish split tx.
  1594  	if !req.DontSignTx {
  1595  		rec, err := udb.NewTxRecordFromMsgTx(splitTx, time.Now())
  1596  		if err != nil {
  1597  			return nil, err
  1598  		}
  1599  		w.lockedOutpointMu.Lock()
  1600  		err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  1601  			watch, err := w.processTransactionRecord(ctx, dbtx, rec, nil, nil)
  1602  			watchOutPoints = append(watchOutPoints, watch...)
  1603  			return err
  1604  		})
  1605  		w.lockedOutpointMu.Unlock()
  1606  		if err != nil {
  1607  			return nil, err
  1608  		}
  1609  		w.recentlyPublishedMu.Lock()
  1610  		w.recentlyPublished[rec.Hash] = struct{}{}
  1611  		w.recentlyPublishedMu.Unlock()
  1612  		err = n.PublishTransactions(ctx, splitTx)
  1613  		if err != nil {
  1614  			return nil, err
  1615  		}
  1616  	}
  1617  
  1618  	// Calculate trickle times for published mixed tickets.
  1619  	// Random times between 20s to 1m from now are chosen for each ticket,
  1620  	// and tickets will not be published until their trickle time is reached.
  1621  	var trickleTickets []time.Time
  1622  	if req.CSPPServer != "" {
  1623  		now := time.Now()
  1624  		trickleTickets = make([]time.Time, 0, len(splitOutputIndexes))
  1625  		for range splitOutputIndexes {
  1626  			delay, err := uniformprng.Int63n(rand.Reader,
  1627  				int64(40*time.Second))
  1628  			if err != nil {
  1629  				return nil, err
  1630  			}
  1631  			t := now.Add(time.Duration(delay) + 20*time.Second)
  1632  			trickleTickets = append(trickleTickets, t)
  1633  		}
  1634  		sort.Slice(trickleTickets, func(i, j int) bool {
  1635  			t1 := trickleTickets[i]
  1636  			t2 := trickleTickets[j]
  1637  			return t1.Before(t2)
  1638  		})
  1639  	}
  1640  
  1641  	// Create each ticket.
  1642  	ticketHashes := make([]*chainhash.Hash, 0, req.Count)
  1643  	tickets := make([]*wire.MsgTx, 0, req.Count)
  1644  	outpoint := wire.OutPoint{Hash: splitTx.TxHash()}
  1645  	for _, index := range splitOutputIndexes {
  1646  		// Generate the extended outpoints that we need to use for ticket
  1647  		// inputs. There are two inputs for pool tickets corresponding to the
  1648  		// fees and the user subsidy, while user-handled tickets have only one
  1649  		// input.
  1650  		var eopPool, eop *Input
  1651  		if poolAddress == nil {
  1652  			op := outpoint
  1653  			op.Index = uint32(index)
  1654  			log.Infof("Split output is %v", &op)
  1655  			txOut := splitTx.TxOut[index]
  1656  			eop = &Input{
  1657  				OutPoint: op,
  1658  				PrevOut:  *txOut,
  1659  			}
  1660  		} else {
  1661  			vspOutPoint := outpoint
  1662  			vspOutPoint.Index = uint32(index)
  1663  			vspOutput := splitTx.TxOut[vspOutPoint.Index]
  1664  			eopPool = &Input{
  1665  				OutPoint: vspOutPoint,
  1666  				PrevOut:  *vspOutput,
  1667  			}
  1668  			myOutPoint := outpoint
  1669  			myOutPoint.Index = uint32(index + 1)
  1670  			myOutput := splitTx.TxOut[myOutPoint.Index]
  1671  			eop = &Input{
  1672  				OutPoint: myOutPoint,
  1673  				PrevOut:  *myOutput,
  1674  			}
  1675  		}
  1676  
  1677  		// If the user hasn't specified a voting address
  1678  		// to delegate voting to, just use an address from
  1679  		// this wallet. Check the passed address from the
  1680  		// request first, then check the ticket address
  1681  		// stored from the configuation. Finally, generate
  1682  		// an address.
  1683  		var addrVote stdaddr.StakeAddress
  1684  
  1685  		// If req.UseVotingAccount is true, always take the submission
  1686  		// script's address from the voting account. This is intended
  1687  		// to be used with a special account type. The signing address
  1688  		// for the same index is saved to the database. That address is
  1689  		// later used to sign messages sent to a vspd related to this
  1690  		// ticket.
  1691  		if req.UseVotingAccount {
  1692  			var idx uint32
  1693  			addrVote, idx, err = stakeAddrFunc(op, req.VotingAccount, 1)
  1694  			if err != nil {
  1695  				return nil, err
  1696  			}
  1697  			_, err := w.signingAddressAtIdx(ctx, op, w.persistReturnedChild(ctx, nil),
  1698  				req.VotingAccount, idx)
  1699  			if err != nil {
  1700  				return nil, err
  1701  			}
  1702  		} else {
  1703  			addrVote = req.VotingAddress
  1704  			if addrVote == nil && req.CSPPServer == "" {
  1705  				addrVote = w.ticketAddress
  1706  			}
  1707  			if addrVote == nil {
  1708  				addrVote, _, err = stakeAddrFunc(op, req.VotingAccount, 1)
  1709  				if err != nil {
  1710  					return nil, err
  1711  				}
  1712  			}
  1713  		}
  1714  		subsidyAccount := req.SourceAccount
  1715  		var branch uint32 = 1
  1716  		if req.CSPPServer != "" {
  1717  			subsidyAccount = req.MixedAccount
  1718  			branch = req.MixedAccountBranch
  1719  		}
  1720  		addrSubsidy, _, err := stakeAddrFunc(op, subsidyAccount, branch)
  1721  		if err != nil {
  1722  			return nil, err
  1723  		}
  1724  
  1725  		var ticket *wire.MsgTx
  1726  		w.lockedOutpointMu.Lock()
  1727  		err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
  1728  			// Generate the ticket msgTx and sign it if DontSignTx is false.
  1729  			ticket, err = makeTicket(w.chainParams, eopPool, eop, addrVote,
  1730  				addrSubsidy, int64(ticketPrice), poolAddress)
  1731  			if err != nil {
  1732  				return err
  1733  			}
  1734  			// Set the expiry.
  1735  			ticket.Expiry = uint32(req.Expiry)
  1736  
  1737  			ticketHash := ticket.TxHash()
  1738  			ticketHashes = append(ticketHashes, &ticketHash)
  1739  			tickets = append(tickets, ticket)
  1740  
  1741  			purchaseTicketsResponse.Tickets = tickets
  1742  			purchaseTicketsResponse.TicketHashes = ticketHashes
  1743  
  1744  			if req.DontSignTx {
  1745  				return nil
  1746  			}
  1747  			// Sign and publish tx if DontSignTx is false
  1748  			var forSigning []Input
  1749  			if eopPool != nil {
  1750  				forSigning = append(forSigning, *eopPool)
  1751  			}
  1752  			forSigning = append(forSigning, *eop)
  1753  
  1754  			ns := dbtx.ReadBucket(waddrmgrNamespaceKey)
  1755  			err = w.signP2PKHMsgTx(ticket, forSigning, ns)
  1756  			if err != nil {
  1757  				return err
  1758  			}
  1759  			err = validateMsgTx(op, ticket, creditScripts(forSigning))
  1760  			if err != nil {
  1761  				return err
  1762  			}
  1763  
  1764  			err = w.checkHighFees(dcrutil.Amount(eop.PrevOut.Value), ticket)
  1765  			if err != nil {
  1766  				return err
  1767  			}
  1768  
  1769  			rec, err := udb.NewTxRecordFromMsgTx(ticket, time.Now())
  1770  			if err != nil {
  1771  				return err
  1772  			}
  1773  
  1774  			watch, err := w.processTransactionRecord(ctx, dbtx, rec, nil, nil)
  1775  			watchOutPoints = append(watchOutPoints, watch...)
  1776  			if err != nil {
  1777  				return err
  1778  			}
  1779  
  1780  			w.recentlyPublishedMu.Lock()
  1781  			w.recentlyPublished[rec.Hash] = struct{}{}
  1782  			w.recentlyPublishedMu.Unlock()
  1783  
  1784  			return nil
  1785  		})
  1786  		w.lockedOutpointMu.Unlock()
  1787  		if err != nil {
  1788  			return purchaseTicketsResponse, errors.E(op, err)
  1789  		}
  1790  	}
  1791  
  1792  	for i, ticket := range tickets {
  1793  		// Wait for trickle time if this was a mixed buy.
  1794  		if len(trickleTickets) > 0 {
  1795  			t := trickleTickets[0]
  1796  			trickleTickets = trickleTickets[1:]
  1797  			timer := time.NewTimer(time.Until(t))
  1798  			select {
  1799  			case <-ctx.Done():
  1800  				if !timer.Stop() {
  1801  					<-timer.C
  1802  				}
  1803  				return purchaseTicketsResponse, errors.E(op, ctx.Err())
  1804  			case <-timer.C:
  1805  			}
  1806  		}
  1807  
  1808  		// Publish transaction
  1809  		err = n.PublishTransactions(ctx, ticket)
  1810  		if err != nil {
  1811  			return purchaseTicketsResponse, errors.E(op, err)
  1812  		}
  1813  		log.Infof("Published ticket purchase %v", ticket.TxHash())
  1814  
  1815  		// Pay VSP fee when configured to do so.
  1816  		if req.VSPFeePaymentProcess == nil {
  1817  			continue
  1818  		}
  1819  		unlockCredits = false
  1820  		feeTx := wire.NewMsgTx()
  1821  		for j := range vspFeeCredits[i] {
  1822  			in := &vspFeeCredits[i][j]
  1823  			feeTx.AddTxIn(wire.NewTxIn(&in.OutPoint, in.PrevOut.Value, nil))
  1824  		}
  1825  		ticketHash := purchaseTicketsResponse.TicketHashes[i]
  1826  		err = req.VSPFeePaymentProcess(ctx, ticketHash, feeTx)
  1827  		if err != nil {
  1828  			// unlock outpoints in case of error
  1829  			for _, outpoint := range vspFeeCredits[i] {
  1830  				w.UnlockOutpoint(&outpoint.OutPoint.Hash,
  1831  					outpoint.OutPoint.Index)
  1832  			}
  1833  			continue
  1834  		}
  1835  		// watch for outpoints change.
  1836  		_, err = udb.NewTxRecordFromMsgTx(feeTx, time.Now())
  1837  		if err != nil {
  1838  			return nil, err
  1839  		}
  1840  	}
  1841  
  1842  	return purchaseTicketsResponse, err
  1843  }
  1844  
  1845  // ReserveOutputsForAmount returns locked spendable outpoints from the given
  1846  // account.  It is the responsibility of the caller to unlock the outpoints.
  1847  func (w *Wallet) ReserveOutputsForAmount(ctx context.Context, account uint32, amount dcrutil.Amount, minconf int32) ([]Input, error) {
  1848  	defer w.lockedOutpointMu.Unlock()
  1849  	w.lockedOutpointMu.Lock()
  1850  
  1851  	var outputs []Input
  1852  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1853  		// Get current block's height
  1854  		_, tipHeight := w.txStore.MainChainTip(dbtx)
  1855  
  1856  		var err error
  1857  		const minAmount = 0
  1858  		const maxResults = 0
  1859  		outputs, err = w.findEligibleOutputsAmount(dbtx, account, minconf, amount, tipHeight,
  1860  			minAmount, maxResults)
  1861  		if err != nil {
  1862  			return err
  1863  		}
  1864  
  1865  		for _, output := range outputs {
  1866  			w.lockedOutpoints[outpoint{output.OutPoint.Hash, output.OutPoint.Index}] = struct{}{}
  1867  		}
  1868  
  1869  		return nil
  1870  	})
  1871  	if err != nil {
  1872  		return nil, err
  1873  	}
  1874  
  1875  	return outputs, nil
  1876  }
  1877  
  1878  func (w *Wallet) reserveOutputs(ctx context.Context, account uint32, minconf int32) ([]Input, error) {
  1879  	defer w.lockedOutpointMu.Unlock()
  1880  	w.lockedOutpointMu.Lock()
  1881  
  1882  	var outputs []Input
  1883  	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
  1884  		// Get current block's height
  1885  		_, tipHeight := w.txStore.MainChainTip(dbtx)
  1886  
  1887  		var err error
  1888  		outputs, err = w.findEligibleOutputs(dbtx, account, minconf, tipHeight)
  1889  		if err != nil {
  1890  			return err
  1891  		}
  1892  
  1893  		for _, output := range outputs {
  1894  			w.lockedOutpoints[outpoint{output.OutPoint.Hash, output.OutPoint.Index}] = struct{}{}
  1895  		}
  1896  
  1897  		return nil
  1898  	})
  1899  	if err != nil {
  1900  		return nil, err
  1901  	}
  1902  
  1903  	return outputs, nil
  1904  }
  1905  
  1906  // This can't be optimized to use the random selection because it must read all
  1907  // outputs.  Prefer to use findEligibleOutputsAmount with various filter options
  1908  // instead.
  1909  func (w *Wallet) findEligibleOutputs(dbtx walletdb.ReadTx, account uint32, minconf int32,
  1910  	currentHeight int32) ([]Input, error) {
  1911  
  1912  	addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  1913  
  1914  	unspent, err := w.txStore.UnspentOutputs(dbtx)
  1915  	if err != nil {
  1916  		return nil, err
  1917  	}
  1918  
  1919  	// TODO: Eventually all of these filters (except perhaps output locking)
  1920  	// should be handled by the call to UnspentOutputs (or similar).
  1921  	// Because one of these filters requires matching the output script to
  1922  	// the desired account, this change depends on making wtxmgr a waddrmgr
  1923  	// dependency and requesting unspent outputs for a single account.
  1924  	eligible := make([]Input, 0, len(unspent))
  1925  	for i := range unspent {
  1926  		output := unspent[i]
  1927  
  1928  		// Locked unspent outputs are skipped.
  1929  		if _, locked := w.lockedOutpoints[outpoint{output.Hash, output.Index}]; locked {
  1930  			continue
  1931  		}
  1932  
  1933  		// Only include this output if it meets the required number of
  1934  		// confirmations.  Coinbase transactions must have have reached
  1935  		// maturity before their outputs may be spent.
  1936  		if !confirmed(minconf, output.Height, currentHeight) {
  1937  			continue
  1938  		}
  1939  
  1940  		// Filter out unspendable outputs, that is, remove those that
  1941  		// (at this time) are not P2PKH outputs.  Other inputs must be
  1942  		// manually included in transactions and sent (for example,
  1943  		// using createrawtransaction, signrawtransaction, and
  1944  		// sendrawtransaction).
  1945  		class, addrs := stdscript.ExtractAddrs(scriptVersionAssumed, output.PkScript, w.chainParams)
  1946  		if len(addrs) != 1 {
  1947  			continue
  1948  		}
  1949  
  1950  		// Make sure everything we're trying to spend is actually mature.
  1951  		switch class {
  1952  		case stdscript.STStakeGenPubKeyHash, stdscript.STStakeGenScriptHash:
  1953  			if !coinbaseMatured(w.chainParams, output.Height, currentHeight) {
  1954  				continue
  1955  			}
  1956  		case stdscript.STStakeRevocationPubKeyHash, stdscript.STStakeRevocationScriptHash:
  1957  			if !coinbaseMatured(w.chainParams, output.Height, currentHeight) {
  1958  				continue
  1959  			}
  1960  		case stdscript.STTreasuryAdd, stdscript.STTreasuryGenPubKeyHash, stdscript.STTreasuryGenScriptHash:
  1961  			if !coinbaseMatured(w.chainParams, output.Height, currentHeight) {
  1962  				continue
  1963  			}
  1964  		case stdscript.STStakeChangePubKeyHash, stdscript.STStakeChangeScriptHash:
  1965  			if !ticketChangeMatured(w.chainParams, output.Height, currentHeight) {
  1966  				continue
  1967  			}
  1968  		case stdscript.STPubKeyHashEcdsaSecp256k1:
  1969  			if output.FromCoinBase {
  1970  				if !coinbaseMatured(w.chainParams, output.Height, currentHeight) {
  1971  					continue
  1972  				}
  1973  			}
  1974  		default:
  1975  			continue
  1976  		}
  1977  
  1978  		// Only include the output if it is associated with the passed
  1979  		// account.
  1980  		//
  1981  		// TODO: Handle multisig outputs by determining if enough of the
  1982  		// addresses are controlled.
  1983  		addrAcct, err := w.manager.AddrAccount(addrmgrNs, addrs[0])
  1984  		if err != nil || addrAcct != account {
  1985  			continue
  1986  		}
  1987  
  1988  		txOut := &wire.TxOut{
  1989  			Value:    int64(output.Amount),
  1990  			Version:  wire.DefaultPkScriptVersion, // XXX
  1991  			PkScript: output.PkScript,
  1992  		}
  1993  		eligible = append(eligible, Input{
  1994  			OutPoint: output.OutPoint,
  1995  			PrevOut:  *txOut,
  1996  		})
  1997  	}
  1998  
  1999  	return eligible, nil
  2000  }
  2001  
  2002  // findEligibleOutputsAmount uses wtxmgr to find a number of unspent outputs
  2003  // while doing maturity checks there.
  2004  func (w *Wallet) findEligibleOutputsAmount(dbtx walletdb.ReadTx, account uint32, minconf int32,
  2005  	amount dcrutil.Amount, currentHeight int32, minAmount dcrutil.Amount, maxResults int) ([]Input, error) {
  2006  
  2007  	addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
  2008  
  2009  	var eligible []Input
  2010  	var outTotal dcrutil.Amount
  2011  	seen := make(map[outpoint]struct{})
  2012  	skip := func(output *udb.Credit) bool {
  2013  		if _, ok := seen[outpoint{output.Hash, output.Index}]; ok {
  2014  			return true
  2015  		}
  2016  
  2017  		// Locked unspent outputs are skipped.
  2018  		if _, locked := w.lockedOutpoints[outpoint{output.Hash, output.Index}]; locked {
  2019  			return true
  2020  		}
  2021  
  2022  		// Only include this output if it meets the required number of
  2023  		// confirmations.  Coinbase transactions must have have reached
  2024  		// maturity before their outputs may be spent.
  2025  		if !confirmed(minconf, output.Height, currentHeight) {
  2026  			return true
  2027  		}
  2028  
  2029  		// When a minumum amount is required, skip when it is less.
  2030  		if minAmount != 0 && output.Amount < minAmount {
  2031  			return true
  2032  		}
  2033  
  2034  		// Filter out unspendable outputs, that is, remove those that
  2035  		// (at this time) are not P2PKH outputs.  Other inputs must be
  2036  		// manually included in transactions and sent (for example,
  2037  		// using createrawtransaction, signrawtransaction, and
  2038  		// sendrawtransaction).
  2039  		class, addrs := stdscript.ExtractAddrs(scriptVersionAssumed, output.PkScript, w.chainParams)
  2040  		if len(addrs) != 1 {
  2041  			return true
  2042  		}
  2043  
  2044  		// Make sure everything we're trying to spend is actually mature.
  2045  		switch class {
  2046  		case stdscript.STStakeGenPubKeyHash, stdscript.STStakeGenScriptHash:
  2047  			if !coinbaseMatured(w.chainParams, output.Height, currentHeight) {
  2048  				return true
  2049  			}
  2050  		case stdscript.STStakeRevocationPubKeyHash, stdscript.STStakeRevocationScriptHash:
  2051  			if !coinbaseMatured(w.chainParams, output.Height, currentHeight) {
  2052  				return true
  2053  			}
  2054  		case stdscript.STTreasuryAdd, stdscript.STTreasuryGenPubKeyHash, stdscript.STTreasuryGenScriptHash:
  2055  			if !coinbaseMatured(w.chainParams, output.Height, currentHeight) {
  2056  				return true
  2057  			}
  2058  		case stdscript.STStakeChangePubKeyHash, stdscript.STStakeChangeScriptHash:
  2059  			if !ticketChangeMatured(w.chainParams, output.Height, currentHeight) {
  2060  				return true
  2061  			}
  2062  		case stdscript.STPubKeyHashEcdsaSecp256k1:
  2063  			if output.FromCoinBase {
  2064  				if !coinbaseMatured(w.chainParams, output.Height, currentHeight) {
  2065  					return true
  2066  				}
  2067  			}
  2068  		default:
  2069  			return true
  2070  		}
  2071  
  2072  		// Only include the output if it is associated with the passed
  2073  		// account.  There should only be one address since this is a
  2074  		// P2PKH script.
  2075  		addrAcct, err := w.manager.AddrAccount(addrmgrNs, addrs[0])
  2076  		if err != nil || addrAcct != account {
  2077  			return true
  2078  		}
  2079  
  2080  		return false
  2081  	}
  2082  
  2083  	randTries := 0
  2084  	maxTries := 0
  2085  	if (amount != 0 || maxResults != 0) && minconf > 0 {
  2086  		numUnspent := w.txStore.UnspentOutputCount(dbtx)
  2087  		log.Debugf("Unspent bucket k/v count: %v", numUnspent)
  2088  		maxTries = numUnspent / 2
  2089  	}
  2090  	for ; randTries < maxTries; randTries++ {
  2091  		output, err := w.txStore.RandomUTXO(dbtx, minconf, currentHeight)
  2092  		if err != nil {
  2093  			return nil, err
  2094  		}
  2095  		if output == nil {
  2096  			break
  2097  		}
  2098  		if skip(output) {
  2099  			continue
  2100  		}
  2101  		seen[outpoint{output.Hash, output.Index}] = struct{}{}
  2102  
  2103  		txOut := &wire.TxOut{
  2104  			Value:    int64(output.Amount),
  2105  			Version:  wire.DefaultPkScriptVersion, // XXX
  2106  			PkScript: output.PkScript,
  2107  		}
  2108  		eligible = append(eligible, Input{
  2109  			OutPoint: output.OutPoint,
  2110  			PrevOut:  *txOut,
  2111  		})
  2112  		outTotal += output.Amount
  2113  		if amount != 0 && outTotal >= amount {
  2114  			return eligible, nil
  2115  		}
  2116  		if maxResults != 0 && len(eligible) == maxResults {
  2117  			return eligible, nil
  2118  		}
  2119  	}
  2120  	if randTries > 0 {
  2121  		log.Debugf("Abandoned random UTXO selection "+
  2122  			"attempts after %v tries", randTries)
  2123  	}
  2124  
  2125  	eligible = eligible[:0]
  2126  	seen = nil
  2127  	outTotal = 0
  2128  	unspent, err := w.txStore.UnspentOutputs(dbtx)
  2129  	if err != nil {
  2130  		return nil, err
  2131  	}
  2132  	shuffle(len(unspent), func(i, j int) {
  2133  		unspent[i], unspent[j] = unspent[j], unspent[i]
  2134  	})
  2135  
  2136  	for i := range unspent {
  2137  		output := unspent[i]
  2138  		if skip(output) {
  2139  			continue
  2140  		}
  2141  
  2142  		txOut := &wire.TxOut{
  2143  			Value:    int64(output.Amount),
  2144  			Version:  wire.DefaultPkScriptVersion, // XXX
  2145  			PkScript: output.PkScript,
  2146  		}
  2147  		eligible = append(eligible, Input{
  2148  			OutPoint: output.OutPoint,
  2149  			PrevOut:  *txOut,
  2150  		})
  2151  		outTotal += output.Amount
  2152  		if amount != 0 && outTotal >= amount {
  2153  			return eligible, nil
  2154  		}
  2155  		if maxResults != 0 && len(eligible) == maxResults {
  2156  			return eligible, nil
  2157  		}
  2158  	}
  2159  	if amount != 0 && outTotal < amount {
  2160  		return nil, errors.InsufficientBalance
  2161  	}
  2162  
  2163  	return eligible, nil
  2164  }
  2165  
  2166  // signP2PKHMsgTx sets the SignatureScript for every item in msgtx.TxIn.
  2167  // It must be called every time a msgtx is changed.
  2168  // Only P2PKH outputs are supported at this point.
  2169  func (w *Wallet) signP2PKHMsgTx(msgtx *wire.MsgTx, prevOutputs []Input, addrmgrNs walletdb.ReadBucket) error {
  2170  	if len(prevOutputs) != len(msgtx.TxIn) {
  2171  		return errors.Errorf(
  2172  			"Number of prevOutputs (%d) does not match number of tx inputs (%d)",
  2173  			len(prevOutputs), len(msgtx.TxIn))
  2174  	}
  2175  	for i, output := range prevOutputs {
  2176  		_, addrs := stdscript.ExtractAddrs(output.PrevOut.Version, output.PrevOut.PkScript, w.chainParams)
  2177  		if len(addrs) != 1 {
  2178  			continue // not error? errors.E(errors.Bug, "previous output address is not P2PKH")
  2179  		}
  2180  		apkh, ok := addrs[0].(*stdaddr.AddressPubKeyHashEcdsaSecp256k1V0)
  2181  		if !ok {
  2182  			return errors.E(errors.Bug, "previous output address is not P2PKH")
  2183  		}
  2184  
  2185  		privKey, done, err := w.manager.PrivateKey(addrmgrNs, apkh)
  2186  		if err != nil {
  2187  			return err
  2188  		}
  2189  		defer done()
  2190  
  2191  		sigscript, err := sign.SignatureScript(msgtx, i, output.PrevOut.PkScript,
  2192  			txscript.SigHashAll, privKey.Serialize(), dcrec.STEcdsaSecp256k1, true)
  2193  		if err != nil {
  2194  			return errors.E(errors.Op("txscript.SignatureScript"), err)
  2195  		}
  2196  		msgtx.TxIn[i].SignatureScript = sigscript
  2197  	}
  2198  
  2199  	return nil
  2200  }
  2201  
  2202  // signVoteOrRevocation signs a vote or revocation, specified by the isVote
  2203  // argument.  This signs the transaction by modifying tx's input scripts.
  2204  func (w *Wallet) signVoteOrRevocation(addrmgrNs walletdb.ReadBucket, ticketPurchase, tx *wire.MsgTx, isVote bool) error {
  2205  	// Create a slice of functions to run after the retreived secrets are no
  2206  	// longer needed.
  2207  	doneFuncs := make([]func(), 0, len(tx.TxIn))
  2208  	defer func() {
  2209  		for _, done := range doneFuncs {
  2210  			done()
  2211  		}
  2212  	}()
  2213  
  2214  	// Prepare functions to look up private key and script secrets so signing
  2215  	// can be performed.
  2216  	var getKey sign.KeyClosure = func(addr stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error) {
  2217  		key, done, err := w.manager.PrivateKey(addrmgrNs, addr)
  2218  		if err != nil {
  2219  			return nil, 0, false, err
  2220  		}
  2221  		doneFuncs = append(doneFuncs, done)
  2222  
  2223  		// secp256k1 pubkeys are always compressed in Decred
  2224  		return key.Serialize(), dcrec.STEcdsaSecp256k1, true, nil
  2225  	}
  2226  	var getScript sign.ScriptClosure = func(addr stdaddr.Address) ([]byte, error) {
  2227  		return w.manager.RedeemScript(addrmgrNs, addr)
  2228  	}
  2229  
  2230  	// Revocations only contain one input, which is the input that must be
  2231  	// signed.  The first input for a vote is the stakebase and the second input
  2232  	// must be signed.
  2233  	inputToSign := 0
  2234  	if isVote {
  2235  		inputToSign = 1
  2236  	}
  2237  
  2238  	// Sign the input.
  2239  	redeemTicketScript := ticketPurchase.TxOut[0].PkScript
  2240  	signedScript, err := sign.SignTxOutput(w.chainParams, tx, inputToSign,
  2241  		redeemTicketScript, txscript.SigHashAll, getKey, getScript,
  2242  		tx.TxIn[inputToSign].SignatureScript, true) // Yes treasury
  2243  	if err != nil {
  2244  		return errors.E(errors.Op("txscript.SignTxOutput"), errors.ScriptFailure, err)
  2245  	}
  2246  	tx.TxIn[inputToSign].SignatureScript = signedScript
  2247  
  2248  	return nil
  2249  }
  2250  
  2251  // signVote signs a vote transaction.  This modifies the input scripts pointed
  2252  // to by the vote transaction.
  2253  func (w *Wallet) signVote(addrmgrNs walletdb.ReadBucket, ticketPurchase, vote *wire.MsgTx) error {
  2254  	return w.signVoteOrRevocation(addrmgrNs, ticketPurchase, vote, true)
  2255  }
  2256  
  2257  // newVoteScript generates a voting script from the passed VoteBits, for
  2258  // use in a vote.
  2259  func newVoteScript(voteBits stake.VoteBits) ([]byte, error) {
  2260  	b := make([]byte, 2+len(voteBits.ExtendedBits))
  2261  	binary.LittleEndian.PutUint16(b[0:2], voteBits.Bits)
  2262  	copy(b[2:], voteBits.ExtendedBits)
  2263  	return stdscript.ProvablyPruneableScriptV0(b)
  2264  }
  2265  
  2266  // createUnsignedVote creates an unsigned vote transaction that votes using the
  2267  // ticket specified by a ticket purchase hash and transaction with the provided
  2268  // vote bits.  The block height and hash must be of the previous block the vote
  2269  // is voting on.
  2270  func createUnsignedVote(ticketHash *chainhash.Hash, ticketPurchase *wire.MsgTx,
  2271  	blockHeight int32, blockHash *chainhash.Hash, voteBits stake.VoteBits,
  2272  	subsidyCache *blockchain.SubsidyCache, params *chaincfg.Params,
  2273  	dcp0010Active, dcp0012Active bool) (*wire.MsgTx, error) {
  2274  
  2275  	// Parse the ticket purchase transaction to determine the required output
  2276  	// destinations for vote rewards or revocations.
  2277  	ticketPayKinds, ticketHash160s, ticketValues, _, _, _ :=
  2278  		stake.TxSStxStakeOutputInfo(ticketPurchase)
  2279  
  2280  	// Calculate the subsidy for votes at this height.
  2281  	ssv := blockchain.SSVOriginal
  2282  	switch {
  2283  	case dcp0012Active:
  2284  		ssv = blockchain.SSVDCP0012
  2285  	case dcp0010Active:
  2286  		ssv = blockchain.SSVDCP0010
  2287  	}
  2288  	subsidy := subsidyCache.CalcStakeVoteSubsidyV3(int64(blockHeight), ssv)
  2289  
  2290  	// Calculate the output values from this vote using the subsidy.
  2291  	voteRewardValues := stake.CalculateRewards(ticketValues,
  2292  		ticketPurchase.TxOut[0].Value, subsidy)
  2293  
  2294  	// Begin constructing the vote transaction.
  2295  	vote := wire.NewMsgTx()
  2296  
  2297  	// Add stakebase input to the vote.
  2298  	stakebaseOutPoint := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0),
  2299  		wire.TxTreeRegular)
  2300  	stakebaseInput := wire.NewTxIn(stakebaseOutPoint, subsidy,
  2301  		params.StakeBaseSigScript)
  2302  	vote.AddTxIn(stakebaseInput)
  2303  
  2304  	// Votes reference the ticket purchase with the second input.
  2305  	ticketOutPoint := wire.NewOutPoint(ticketHash, 0, wire.TxTreeStake)
  2306  	ticketInput := wire.NewTxIn(ticketOutPoint,
  2307  		ticketPurchase.TxOut[ticketOutPoint.Index].Value, nil)
  2308  	vote.AddTxIn(ticketInput)
  2309  
  2310  	// The first output references the previous block the vote is voting on.
  2311  	// This function never errors.
  2312  	blockRefScript, _ := txscript.GenerateSSGenBlockRef(*blockHash,
  2313  		uint32(blockHeight))
  2314  	vote.AddTxOut(&wire.TxOut{
  2315  		Value:    0,
  2316  		Version:  wire.DefaultPkScriptVersion, // XXX
  2317  		PkScript: blockRefScript,
  2318  	})
  2319  
  2320  	// The second output contains the votebits encode as a null data script.
  2321  	voteScript, err := newVoteScript(voteBits)
  2322  	if err != nil {
  2323  		return nil, err
  2324  	}
  2325  	vote.AddTxOut(&wire.TxOut{
  2326  		Value:    0,
  2327  		Version:  wire.DefaultPkScriptVersion, // XXX
  2328  		PkScript: voteScript,
  2329  	})
  2330  
  2331  	// All remaining outputs pay to the output destinations and amounts tagged
  2332  	// by the ticket purchase.
  2333  	for i, hash160 := range ticketHash160s {
  2334  		var addr stdaddr.StakeAddress
  2335  		var err error
  2336  		if ticketPayKinds[i] { // P2SH
  2337  			addr, err = stdaddr.NewAddressScriptHashV0FromHash(hash160, params)
  2338  		} else { // P2PKH
  2339  			addr, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(hash160, params)
  2340  		}
  2341  		if err != nil {
  2342  			return nil, err
  2343  		}
  2344  		vers, script := addr.PayVoteCommitmentScript()
  2345  		vote.AddTxOut(&wire.TxOut{
  2346  			Value:    voteRewardValues[i],
  2347  			Version:  vers,
  2348  			PkScript: script,
  2349  		})
  2350  	}
  2351  
  2352  	return vote, nil
  2353  }