decred.org/dcrwallet/v3@v3.1.0/internal/rpc/jsonrpc/methods.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 jsonrpc
     7  
     8  import (
     9  	"bytes"
    10  	"context"
    11  	"crypto/rand"
    12  	"encoding/base64"
    13  	"encoding/binary"
    14  	"encoding/hex"
    15  	"encoding/json"
    16  	"fmt"
    17  	"math/big"
    18  	"sort"
    19  	"strconv"
    20  	"strings"
    21  	"sync"
    22  	"time"
    23  
    24  	"decred.org/dcrwallet/v3/errors"
    25  	"decred.org/dcrwallet/v3/internal/loader"
    26  	"decred.org/dcrwallet/v3/internal/vsp"
    27  	"decred.org/dcrwallet/v3/p2p"
    28  	"decred.org/dcrwallet/v3/rpc/client/dcrd"
    29  	"decred.org/dcrwallet/v3/rpc/jsonrpc/types"
    30  	"decred.org/dcrwallet/v3/spv"
    31  	"decred.org/dcrwallet/v3/version"
    32  	"decred.org/dcrwallet/v3/wallet"
    33  	"decred.org/dcrwallet/v3/wallet/txauthor"
    34  	"decred.org/dcrwallet/v3/wallet/txrules"
    35  	"decred.org/dcrwallet/v3/wallet/txsizes"
    36  	"decred.org/dcrwallet/v3/wallet/udb"
    37  	"github.com/decred/dcrd/blockchain/stake/v5"
    38  	blockchain "github.com/decred/dcrd/blockchain/standalone/v2"
    39  	"github.com/decred/dcrd/chaincfg/chainhash"
    40  	"github.com/decred/dcrd/chaincfg/v3"
    41  	"github.com/decred/dcrd/dcrec"
    42  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    43  	"github.com/decred/dcrd/dcrjson/v4"
    44  	"github.com/decred/dcrd/dcrutil/v4"
    45  	"github.com/decred/dcrd/hdkeychain/v3"
    46  	dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v4"
    47  	"github.com/decred/dcrd/txscript/v4"
    48  	"github.com/decred/dcrd/txscript/v4/sign"
    49  	"github.com/decred/dcrd/txscript/v4/stdaddr"
    50  	"github.com/decred/dcrd/txscript/v4/stdscript"
    51  	"github.com/decred/dcrd/wire"
    52  	"golang.org/x/sync/errgroup"
    53  )
    54  
    55  // API version constants
    56  const (
    57  	jsonrpcSemverString = "9.0.0"
    58  	jsonrpcSemverMajor  = 9
    59  	jsonrpcSemverMinor  = 0
    60  	jsonrpcSemverPatch  = 0
    61  )
    62  
    63  const (
    64  	// sstxCommitmentString is the string to insert when a verbose
    65  	// transaction output's pkscript type is a ticket commitment.
    66  	sstxCommitmentString = "sstxcommitment"
    67  
    68  	// The assumed output script version is defined to assist with refactoring
    69  	// to use actual script versions.
    70  	scriptVersionAssumed = 0
    71  )
    72  
    73  // confirms returns the number of confirmations for a transaction in a block at
    74  // height txHeight (or -1 for an unconfirmed tx) given the chain height
    75  // curHeight.
    76  func confirms(txHeight, curHeight int32) int32 {
    77  	switch {
    78  	case txHeight == -1, txHeight > curHeight:
    79  		return 0
    80  	default:
    81  		return curHeight - txHeight + 1
    82  	}
    83  }
    84  
    85  // the registered rpc handlers
    86  var handlers = map[string]handler{
    87  	"abandontransaction":        {fn: (*Server).abandonTransaction},
    88  	"accountaddressindex":       {fn: (*Server).accountAddressIndex},
    89  	"accountsyncaddressindex":   {fn: (*Server).accountSyncAddressIndex},
    90  	"accountunlocked":           {fn: (*Server).accountUnlocked},
    91  	"addmultisigaddress":        {fn: (*Server).addMultiSigAddress},
    92  	"addtransaction":            {fn: (*Server).addTransaction},
    93  	"auditreuse":                {fn: (*Server).auditReuse},
    94  	"consolidate":               {fn: (*Server).consolidate},
    95  	"createmultisig":            {fn: (*Server).createMultiSig},
    96  	"createnewaccount":          {fn: (*Server).createNewAccount},
    97  	"createrawtransaction":      {fn: (*Server).createRawTransaction},
    98  	"createsignature":           {fn: (*Server).createSignature},
    99  	"disapprovepercent":         {fn: (*Server).disapprovePercent},
   100  	"discoverusage":             {fn: (*Server).discoverUsage},
   101  	"dumpprivkey":               {fn: (*Server).dumpPrivKey},
   102  	"fundrawtransaction":        {fn: (*Server).fundRawTransaction},
   103  	"getaccount":                {fn: (*Server).getAccount},
   104  	"getaccountaddress":         {fn: (*Server).getAccountAddress},
   105  	"getaddressesbyaccount":     {fn: (*Server).getAddressesByAccount},
   106  	"getbalance":                {fn: (*Server).getBalance},
   107  	"getbestblock":              {fn: (*Server).getBestBlock},
   108  	"getbestblockhash":          {fn: (*Server).getBestBlockHash},
   109  	"getblockcount":             {fn: (*Server).getBlockCount},
   110  	"getblockhash":              {fn: (*Server).getBlockHash},
   111  	"getblockheader":            {fn: (*Server).getBlockHeader},
   112  	"getblock":                  {fn: (*Server).getBlock},
   113  	"getcoinjoinsbyacct":        {fn: (*Server).getcoinjoinsbyacct},
   114  	"getcurrentnet":             {fn: (*Server).getCurrentNet},
   115  	"getinfo":                   {fn: (*Server).getInfo},
   116  	"getmasterpubkey":           {fn: (*Server).getMasterPubkey},
   117  	"getmultisigoutinfo":        {fn: (*Server).getMultisigOutInfo},
   118  	"getnewaddress":             {fn: (*Server).getNewAddress},
   119  	"getpeerinfo":               {fn: (*Server).getPeerInfo},
   120  	"getrawchangeaddress":       {fn: (*Server).getRawChangeAddress},
   121  	"getreceivedbyaccount":      {fn: (*Server).getReceivedByAccount},
   122  	"getreceivedbyaddress":      {fn: (*Server).getReceivedByAddress},
   123  	"getstakeinfo":              {fn: (*Server).getStakeInfo},
   124  	"gettickets":                {fn: (*Server).getTickets},
   125  	"gettransaction":            {fn: (*Server).getTransaction},
   126  	"gettxout":                  {fn: (*Server).getTxOut},
   127  	"getunconfirmedbalance":     {fn: (*Server).getUnconfirmedBalance},
   128  	"getvotechoices":            {fn: (*Server).getVoteChoices},
   129  	"getwalletfee":              {fn: (*Server).getWalletFee},
   130  	"help":                      {fn: (*Server).help},
   131  	"getcfilterv2":              {fn: (*Server).getCFilterV2},
   132  	"importcfiltersv2":          {fn: (*Server).importCFiltersV2},
   133  	"importprivkey":             {fn: (*Server).importPrivKey},
   134  	"importpubkey":              {fn: (*Server).importPubKey},
   135  	"importscript":              {fn: (*Server).importScript},
   136  	"importxpub":                {fn: (*Server).importXpub},
   137  	"listaccounts":              {fn: (*Server).listAccounts},
   138  	"listaddresstransactions":   {fn: (*Server).listAddressTransactions},
   139  	"listalltransactions":       {fn: (*Server).listAllTransactions},
   140  	"listlockunspent":           {fn: (*Server).listLockUnspent},
   141  	"listreceivedbyaccount":     {fn: (*Server).listReceivedByAccount},
   142  	"listreceivedbyaddress":     {fn: (*Server).listReceivedByAddress},
   143  	"listsinceblock":            {fn: (*Server).listSinceBlock},
   144  	"listtransactions":          {fn: (*Server).listTransactions},
   145  	"listunspent":               {fn: (*Server).listUnspent},
   146  	"lockaccount":               {fn: (*Server).lockAccount},
   147  	"lockunspent":               {fn: (*Server).lockUnspent},
   148  	"mixaccount":                {fn: (*Server).mixAccount},
   149  	"mixoutput":                 {fn: (*Server).mixOutput},
   150  	"purchaseticket":            {fn: (*Server).purchaseTicket},
   151  	"processunmanagedticket":    {fn: (*Server).processUnmanagedTicket},
   152  	"redeemmultisigout":         {fn: (*Server).redeemMultiSigOut},
   153  	"redeemmultisigouts":        {fn: (*Server).redeemMultiSigOuts},
   154  	"renameaccount":             {fn: (*Server).renameAccount},
   155  	"rescanwallet":              {fn: (*Server).rescanWallet},
   156  	"sendfrom":                  {fn: (*Server).sendFrom},
   157  	"sendfromtreasury":          {fn: (*Server).sendFromTreasury},
   158  	"sendmany":                  {fn: (*Server).sendMany},
   159  	"sendrawtransaction":        {fn: (*Server).sendRawTransaction},
   160  	"sendtoaddress":             {fn: (*Server).sendToAddress},
   161  	"sendtomultisig":            {fn: (*Server).sendToMultiSig},
   162  	"sendtotreasury":            {fn: (*Server).sendToTreasury},
   163  	"setaccountpassphrase":      {fn: (*Server).setAccountPassphrase},
   164  	"setdisapprovepercent":      {fn: (*Server).setDisapprovePercent},
   165  	"settreasurypolicy":         {fn: (*Server).setTreasuryPolicy},
   166  	"settspendpolicy":           {fn: (*Server).setTSpendPolicy},
   167  	"settxfee":                  {fn: (*Server).setTxFee},
   168  	"setvotechoice":             {fn: (*Server).setVoteChoice},
   169  	"signmessage":               {fn: (*Server).signMessage},
   170  	"signrawtransaction":        {fn: (*Server).signRawTransaction},
   171  	"signrawtransactions":       {fn: (*Server).signRawTransactions},
   172  	"stakepooluserinfo":         {fn: (*Server).stakePoolUserInfo},
   173  	"sweepaccount":              {fn: (*Server).sweepAccount},
   174  	"syncstatus":                {fn: (*Server).syncStatus},
   175  	"ticketinfo":                {fn: (*Server).ticketInfo},
   176  	"ticketsforaddress":         {fn: (*Server).ticketsForAddress},
   177  	"treasurypolicy":            {fn: (*Server).treasuryPolicy},
   178  	"tspendpolicy":              {fn: (*Server).tspendPolicy},
   179  	"unlockaccount":             {fn: (*Server).unlockAccount},
   180  	"validateaddress":           {fn: (*Server).validateAddress},
   181  	"validatepredcp0005cf":      {fn: (*Server).validatePreDCP0005CF},
   182  	"verifymessage":             {fn: (*Server).verifyMessage},
   183  	"version":                   {fn: (*Server).version},
   184  	"walletinfo":                {fn: (*Server).walletInfo},
   185  	"walletislocked":            {fn: (*Server).walletIsLocked},
   186  	"walletlock":                {fn: (*Server).walletLock},
   187  	"walletpassphrase":          {fn: (*Server).walletPassphrase},
   188  	"walletpassphrasechange":    {fn: (*Server).walletPassphraseChange},
   189  	"walletpubpassphrasechange": {fn: (*Server).walletPubPassphraseChange},
   190  
   191  	// Unimplemented/unsupported RPCs which may be found in other
   192  	// cryptocurrency wallets.
   193  	"backupwallet":         {fn: unimplemented, noHelp: true},
   194  	"getwalletinfo":        {fn: unimplemented, noHelp: true},
   195  	"importwallet":         {fn: unimplemented, noHelp: true},
   196  	"listaddressgroupings": {fn: unimplemented, noHelp: true},
   197  	"dumpwallet":           {fn: unsupported, noHelp: true},
   198  	"encryptwallet":        {fn: unsupported, noHelp: true},
   199  	"move":                 {fn: unsupported, noHelp: true},
   200  	"setaccount":           {fn: unsupported, noHelp: true},
   201  }
   202  
   203  // unimplemented handles an unimplemented RPC request with the
   204  // appropriate error.
   205  func unimplemented(*Server, context.Context, interface{}) (interface{}, error) {
   206  	return nil, &dcrjson.RPCError{
   207  		Code:    dcrjson.ErrRPCUnimplemented,
   208  		Message: "Method unimplemented",
   209  	}
   210  }
   211  
   212  // unsupported handles a standard bitcoind RPC request which is
   213  // unsupported by dcrwallet due to design differences.
   214  func unsupported(*Server, context.Context, interface{}) (interface{}, error) {
   215  	return nil, &dcrjson.RPCError{
   216  		Code:    -1,
   217  		Message: "Request unsupported by dcrwallet",
   218  	}
   219  }
   220  
   221  // lazyHandler is a closure over a requestHandler or passthrough request with
   222  // the RPC server's wallet and chain server variables as part of the closure
   223  // context.
   224  type lazyHandler func() (interface{}, *dcrjson.RPCError)
   225  
   226  // lazyApplyHandler looks up the best request handler func for the method,
   227  // returning a closure that will execute it with the (required) wallet and
   228  // (optional) consensus RPC server.  If no handlers are found and the
   229  // chainClient is not nil, the returned handler performs RPC passthrough.
   230  func lazyApplyHandler(s *Server, ctx context.Context, request *dcrjson.Request) lazyHandler {
   231  	handlerData, ok := handlers[request.Method]
   232  	if !ok {
   233  		return func() (interface{}, *dcrjson.RPCError) {
   234  			// Attempt RPC passthrough if possible
   235  			n, ok := s.walletLoader.NetworkBackend()
   236  			if !ok {
   237  				return nil, errRPCClientNotConnected
   238  			}
   239  			rpc, ok := n.(*dcrd.RPC)
   240  			if !ok {
   241  				return nil, rpcErrorf(dcrjson.ErrRPCClientNotConnected, "RPC passthrough requires dcrd RPC synchronization")
   242  			}
   243  			var resp json.RawMessage
   244  			var params = make([]interface{}, len(request.Params))
   245  			for i := range request.Params {
   246  				params[i] = request.Params[i]
   247  			}
   248  			err := rpc.Call(ctx, request.Method, &resp, params...)
   249  			if ctx.Err() != nil {
   250  				log.Warnf("Canceled RPC method %v invoked by %v: %v", request.Method, remoteAddr(ctx), err)
   251  				return nil, &dcrjson.RPCError{
   252  					Code:    dcrjson.ErrRPCMisc,
   253  					Message: ctx.Err().Error(),
   254  				}
   255  			}
   256  			if err != nil {
   257  				return nil, convertError(err)
   258  			}
   259  			return resp, nil
   260  		}
   261  	}
   262  
   263  	return func() (interface{}, *dcrjson.RPCError) {
   264  		params, err := dcrjson.ParseParams(types.Method(request.Method), request.Params)
   265  		if err != nil {
   266  			return nil, dcrjson.ErrRPCInvalidRequest
   267  		}
   268  
   269  		defer func() {
   270  			if err := ctx.Err(); err != nil {
   271  				log.Warnf("Canceled RPC method %v invoked by %v: %v", request.Method, remoteAddr(ctx), err)
   272  			}
   273  		}()
   274  		resp, err := handlerData.fn(s, ctx, params)
   275  		if err != nil {
   276  			return nil, convertError(err)
   277  		}
   278  		return resp, nil
   279  	}
   280  }
   281  
   282  // makeResponse makes the JSON-RPC response struct for the result and error
   283  // returned by a requestHandler.  The returned response is not ready for
   284  // marshaling and sending off to a client, but must be
   285  func makeResponse(id, result interface{}, err error) dcrjson.Response {
   286  	idPtr := idPointer(id)
   287  	if err != nil {
   288  		return dcrjson.Response{
   289  			ID:    idPtr,
   290  			Error: convertError(err),
   291  		}
   292  	}
   293  	resultBytes, err := json.Marshal(result)
   294  	if err != nil {
   295  		return dcrjson.Response{
   296  			ID: idPtr,
   297  			Error: &dcrjson.RPCError{
   298  				Code:    dcrjson.ErrRPCInternal.Code,
   299  				Message: "Unexpected error marshalling result",
   300  			},
   301  		}
   302  	}
   303  	return dcrjson.Response{
   304  		ID:     idPtr,
   305  		Result: json.RawMessage(resultBytes),
   306  	}
   307  }
   308  
   309  // abandonTransaction removes an unconfirmed transaction and all dependent
   310  // transactions from the wallet.
   311  func (s *Server) abandonTransaction(ctx context.Context, icmd interface{}) (interface{}, error) {
   312  	cmd := icmd.(*types.AbandonTransactionCmd)
   313  	w, ok := s.walletLoader.LoadedWallet()
   314  	if !ok {
   315  		return nil, errUnloadedWallet
   316  	}
   317  
   318  	hash, err := chainhash.NewHashFromStr(cmd.Hash)
   319  	if err != nil {
   320  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
   321  	}
   322  
   323  	err = w.AbandonTransaction(ctx, hash)
   324  	return nil, err
   325  }
   326  
   327  // accountAddressIndex returns the next address index for the passed
   328  // account and branch.
   329  func (s *Server) accountAddressIndex(ctx context.Context, icmd interface{}) (interface{}, error) {
   330  	cmd := icmd.(*types.AccountAddressIndexCmd)
   331  	w, ok := s.walletLoader.LoadedWallet()
   332  	if !ok {
   333  		return nil, errUnloadedWallet
   334  	}
   335  
   336  	account, err := w.AccountNumber(ctx, cmd.Account)
   337  	if err != nil {
   338  		if errors.Is(err, errors.NotExist) {
   339  			return nil, errAccountNotFound
   340  		}
   341  		return nil, err
   342  	}
   343  
   344  	extChild, intChild, err := w.BIP0044BranchNextIndexes(ctx, account)
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  	switch uint32(cmd.Branch) {
   349  	case udb.ExternalBranch:
   350  		return extChild, nil
   351  	case udb.InternalBranch:
   352  		return intChild, nil
   353  	default:
   354  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "invalid branch %v", cmd.Branch)
   355  	}
   356  }
   357  
   358  // accountSyncAddressIndex synchronizes the address manager and local address
   359  // pool for some account and branch to the passed index. If the current pool
   360  // index is beyond the passed index, an error is returned. If the passed index
   361  // is the same as the current pool index, nothing is returned. If the syncing
   362  // is successful, nothing is returned.
   363  func (s *Server) accountSyncAddressIndex(ctx context.Context, icmd interface{}) (interface{}, error) {
   364  	cmd := icmd.(*types.AccountSyncAddressIndexCmd)
   365  	w, ok := s.walletLoader.LoadedWallet()
   366  	if !ok {
   367  		return nil, errUnloadedWallet
   368  	}
   369  
   370  	account, err := w.AccountNumber(ctx, cmd.Account)
   371  	if err != nil {
   372  		if errors.Is(err, errors.NotExist) {
   373  			return nil, errAccountNotFound
   374  		}
   375  		return nil, err
   376  	}
   377  
   378  	branch := uint32(cmd.Branch)
   379  	index := uint32(cmd.Index)
   380  
   381  	if index >= hdkeychain.HardenedKeyStart {
   382  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
   383  			"child index %d exceeds the maximum child index for an account", index)
   384  	}
   385  
   386  	// Additional addresses need to be watched.  Since addresses are derived
   387  	// based on the last used address, this RPC no longer changes the child
   388  	// indexes that new addresses are derived from.
   389  	return nil, w.SyncLastReturnedAddress(ctx, account, branch, index)
   390  }
   391  
   392  // walletPubKeys decodes each encoded key or address to a public key.  If the
   393  // address is P2PKH, the wallet is queried for the public key.
   394  func walletPubKeys(ctx context.Context, w *wallet.Wallet, keys []string) ([][]byte, error) {
   395  	pubKeys := make([][]byte, len(keys))
   396  
   397  	for i, key := range keys {
   398  		addr, err := decodeAddress(key, w.ChainParams())
   399  		if err != nil {
   400  			return nil, err
   401  		}
   402  		switch addr := addr.(type) {
   403  		case *stdaddr.AddressPubKeyEcdsaSecp256k1V0:
   404  			pubKeys[i] = addr.SerializedPubKey()
   405  			continue
   406  		}
   407  
   408  		a, err := w.KnownAddress(ctx, addr)
   409  		if err != nil {
   410  			if errors.Is(err, errors.NotExist) {
   411  				return nil, errAddressNotInWallet
   412  			}
   413  			return nil, err
   414  		}
   415  		var pubKey []byte
   416  		switch a := a.(type) {
   417  		case wallet.PubKeyHashAddress:
   418  			pubKey = a.PubKey()
   419  		default:
   420  			err = errors.New("address has no associated public key")
   421  			return nil, rpcError(dcrjson.ErrRPCInvalidAddressOrKey, err)
   422  		}
   423  		pubKeys[i] = pubKey
   424  	}
   425  
   426  	return pubKeys, nil
   427  }
   428  
   429  // addMultiSigAddress handles an addmultisigaddress request by adding a
   430  // multisig address to the given wallet.
   431  func (s *Server) addMultiSigAddress(ctx context.Context, icmd interface{}) (interface{}, error) {
   432  	cmd := icmd.(*types.AddMultisigAddressCmd)
   433  	// If an account is specified, ensure that is the imported account.
   434  	if cmd.Account != nil && *cmd.Account != udb.ImportedAddrAccountName {
   435  		return nil, errNotImportedAccount
   436  	}
   437  
   438  	w, ok := s.walletLoader.LoadedWallet()
   439  	if !ok {
   440  		return nil, errUnloadedWallet
   441  	}
   442  
   443  	pubKeyAddrs, err := walletPubKeys(ctx, w, cmd.Keys)
   444  	if err != nil {
   445  		return nil, err
   446  	}
   447  	script, err := stdscript.MultiSigScriptV0(cmd.NRequired, pubKeyAddrs...)
   448  	if err != nil {
   449  		return nil, err
   450  	}
   451  
   452  	err = w.ImportScript(ctx, script)
   453  	if err != nil && !errors.Is(err, errors.Exist) {
   454  		return nil, err
   455  	}
   456  
   457  	return stdaddr.NewAddressScriptHashV0(script, w.ChainParams())
   458  }
   459  
   460  func (s *Server) addTransaction(ctx context.Context, icmd interface{}) (interface{}, error) {
   461  	cmd := icmd.(*types.AddTransactionCmd)
   462  	w, ok := s.walletLoader.LoadedWallet()
   463  	if !ok {
   464  		return nil, errUnloadedWallet
   465  	}
   466  
   467  	blockHash, err := chainhash.NewHashFromStr(cmd.BlockHash)
   468  	if err != nil {
   469  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
   470  	}
   471  
   472  	tx := new(wire.MsgTx)
   473  	err = tx.Deserialize(hex.NewDecoder(strings.NewReader(cmd.Transaction)))
   474  	if err != nil {
   475  		return nil, rpcError(dcrjson.ErrRPCDeserialization, err)
   476  	}
   477  
   478  	err = w.AddTransaction(ctx, tx, blockHash)
   479  	return nil, err
   480  }
   481  
   482  // auditReuse returns an object keying reused addresses to two or more outputs
   483  // referencing them.
   484  func (s *Server) auditReuse(ctx context.Context, icmd interface{}) (interface{}, error) {
   485  	cmd := icmd.(*types.AuditReuseCmd)
   486  	w, ok := s.walletLoader.LoadedWallet()
   487  	if !ok {
   488  		return nil, errUnloadedWallet
   489  	}
   490  
   491  	var since int32
   492  	if cmd.Since != nil {
   493  		since = *cmd.Since
   494  	}
   495  
   496  	reuse := make(map[string][]string)
   497  	inRange := make(map[string]struct{})
   498  	params := w.ChainParams()
   499  	err := w.GetTransactions(ctx, func(b *wallet.Block) (bool, error) {
   500  		for _, tx := range b.Transactions {
   501  			// Votes and revocations are skipped because they must
   502  			// only pay to addresses previously committed to by
   503  			// ticket purchases, and this "address reuse" is
   504  			// expected.
   505  			switch tx.Type {
   506  			case wallet.TransactionTypeVote, wallet.TransactionTypeRevocation:
   507  				continue
   508  			}
   509  			for _, out := range tx.MyOutputs {
   510  				addr := out.Address.String()
   511  				outpoints := reuse[addr]
   512  				outpoint := wire.OutPoint{Hash: *tx.Hash, Index: out.Index}
   513  				reuse[addr] = append(outpoints, outpoint.String())
   514  				if b.Header == nil || int32(b.Header.Height) >= since {
   515  					inRange[addr] = struct{}{}
   516  				}
   517  			}
   518  			if tx.Type != wallet.TransactionTypeTicketPurchase {
   519  				continue
   520  			}
   521  			ticket := new(wire.MsgTx)
   522  			err := ticket.Deserialize(bytes.NewReader(tx.Transaction))
   523  			if err != nil {
   524  				return false, err
   525  			}
   526  			for i := 1; i < len(ticket.TxOut); i += 2 { // iterate commitments
   527  				out := ticket.TxOut[i]
   528  				addr, err := stake.AddrFromSStxPkScrCommitment(out.PkScript, params)
   529  				if err != nil {
   530  					return false, err
   531  				}
   532  				have, err := w.HaveAddress(ctx, addr)
   533  				if err != nil {
   534  					return false, err
   535  				}
   536  				if !have {
   537  					continue
   538  				}
   539  				s := addr.String()
   540  				outpoints := reuse[s]
   541  				outpoint := wire.OutPoint{Hash: *tx.Hash, Index: uint32(i)}
   542  				reuse[s] = append(outpoints, outpoint.String())
   543  				if b.Header == nil || int32(b.Header.Height) >= since {
   544  					inRange[s] = struct{}{}
   545  				}
   546  			}
   547  		}
   548  		return false, nil
   549  	}, nil, nil)
   550  	if err != nil {
   551  		return nil, err
   552  	}
   553  	for s, outpoints := range reuse {
   554  		if len(outpoints) <= 1 {
   555  			delete(reuse, s)
   556  			continue
   557  		}
   558  		if _, ok := inRange[s]; !ok {
   559  			delete(reuse, s)
   560  		}
   561  	}
   562  	return reuse, nil
   563  }
   564  
   565  // consolidate handles a consolidate request by returning attempting to compress
   566  // as many inputs as given and then returning the txHash and error.
   567  func (s *Server) consolidate(ctx context.Context, icmd interface{}) (interface{}, error) {
   568  	cmd := icmd.(*types.ConsolidateCmd)
   569  	w, ok := s.walletLoader.LoadedWallet()
   570  	if !ok {
   571  		return nil, errUnloadedWallet
   572  	}
   573  
   574  	account := uint32(udb.DefaultAccountNum)
   575  	var err error
   576  	if cmd.Account != nil {
   577  		account, err = w.AccountNumber(ctx, *cmd.Account)
   578  		if err != nil {
   579  			if errors.Is(err, errors.NotExist) {
   580  				return nil, errAccountNotFound
   581  			}
   582  			return nil, err
   583  		}
   584  	}
   585  
   586  	// Set change address if specified.
   587  	var changeAddr stdaddr.Address
   588  	if cmd.Address != nil {
   589  		if *cmd.Address != "" {
   590  			addr, err := decodeAddress(*cmd.Address, w.ChainParams())
   591  			if err != nil {
   592  				return nil, err
   593  			}
   594  			changeAddr = addr
   595  		}
   596  	}
   597  
   598  	// TODO In the future this should take the optional account and
   599  	// only consolidate UTXOs found within that account.
   600  	txHash, err := w.Consolidate(ctx, cmd.Inputs, account, changeAddr)
   601  	if err != nil {
   602  		return nil, err
   603  	}
   604  
   605  	return txHash.String(), nil
   606  }
   607  
   608  // createMultiSig handles an createmultisig request by returning a
   609  // multisig address for the given inputs.
   610  func (s *Server) createMultiSig(ctx context.Context, icmd interface{}) (interface{}, error) {
   611  	cmd := icmd.(*types.CreateMultisigCmd)
   612  	w, ok := s.walletLoader.LoadedWallet()
   613  	if !ok {
   614  		return nil, errUnloadedWallet
   615  	}
   616  
   617  	pubKeys, err := walletPubKeys(ctx, w, cmd.Keys)
   618  	if err != nil {
   619  		return nil, err
   620  	}
   621  	script, err := stdscript.MultiSigScriptV0(cmd.NRequired, pubKeys...)
   622  	if err != nil {
   623  		return nil, err
   624  	}
   625  
   626  	address, err := stdaddr.NewAddressScriptHashV0(script, w.ChainParams())
   627  	if err != nil {
   628  		return nil, err
   629  	}
   630  
   631  	return types.CreateMultiSigResult{
   632  		Address:      address.String(),
   633  		RedeemScript: hex.EncodeToString(script),
   634  	}, nil
   635  }
   636  
   637  // createRawTransaction handles createrawtransaction commands.
   638  func (s *Server) createRawTransaction(ctx context.Context, icmd interface{}) (interface{}, error) {
   639  	cmd := icmd.(*types.CreateRawTransactionCmd)
   640  
   641  	// Validate expiry, if given.
   642  	if cmd.Expiry != nil && *cmd.Expiry < 0 {
   643  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "Expiry out of range")
   644  	}
   645  
   646  	// Validate the locktime, if given.
   647  	if cmd.LockTime != nil &&
   648  		(*cmd.LockTime < 0 ||
   649  			*cmd.LockTime > int64(wire.MaxTxInSequenceNum)) {
   650  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "Locktime out of range")
   651  	}
   652  
   653  	// Add all transaction inputs to a new transaction after performing
   654  	// some validity checks.
   655  	mtx := wire.NewMsgTx()
   656  	for _, input := range cmd.Inputs {
   657  		txHash, err := chainhash.NewHashFromStr(input.Txid)
   658  		if err != nil {
   659  			return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
   660  		}
   661  
   662  		switch input.Tree {
   663  		case wire.TxTreeRegular, wire.TxTreeStake:
   664  		default:
   665  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
   666  				"Tx tree must be regular or stake")
   667  		}
   668  
   669  		amt, err := dcrutil.NewAmount(input.Amount)
   670  		if err != nil {
   671  			return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
   672  		}
   673  		if amt < 0 {
   674  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
   675  				"Positive input amount is required")
   676  		}
   677  
   678  		prevOut := wire.NewOutPoint(txHash, input.Vout, input.Tree)
   679  		txIn := wire.NewTxIn(prevOut, int64(amt), nil)
   680  		if cmd.LockTime != nil && *cmd.LockTime != 0 {
   681  			txIn.Sequence = wire.MaxTxInSequenceNum - 1
   682  		}
   683  		mtx.AddTxIn(txIn)
   684  	}
   685  
   686  	// Add all transaction outputs to the transaction after performing
   687  	// some validity checks.
   688  	for encodedAddr, amount := range cmd.Amounts {
   689  		// Decode the provided address.  This also ensures the network encoded
   690  		// with the address matches the network the server is currently on.
   691  		addr, err := stdaddr.DecodeAddress(encodedAddr, s.activeNet)
   692  		if err != nil {
   693  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidAddressOrKey,
   694  				"Address %q: %v", encodedAddr, err)
   695  		}
   696  
   697  		// Ensure the address is one of the supported types.
   698  		switch addr.(type) {
   699  		case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0:
   700  		case *stdaddr.AddressScriptHashV0:
   701  		default:
   702  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidAddressOrKey,
   703  				"Invalid type: %T", addr)
   704  		}
   705  
   706  		// Create a new script which pays to the provided address.
   707  		vers, pkScript := addr.PaymentScript()
   708  
   709  		atomic, err := dcrutil.NewAmount(amount)
   710  		if err != nil {
   711  			return nil, rpcErrorf(dcrjson.ErrRPCInternal.Code,
   712  				"New amount: %v", err)
   713  		}
   714  		// Ensure amount is in the valid range for monetary amounts.
   715  		if atomic <= 0 || atomic > dcrutil.MaxAmount {
   716  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
   717  				"Amount outside valid range: %v", atomic)
   718  		}
   719  
   720  		txOut := &wire.TxOut{
   721  			Value:    int64(atomic),
   722  			Version:  vers,
   723  			PkScript: pkScript,
   724  		}
   725  		mtx.AddTxOut(txOut)
   726  	}
   727  
   728  	// Set the Locktime, if given.
   729  	if cmd.LockTime != nil {
   730  		mtx.LockTime = uint32(*cmd.LockTime)
   731  	}
   732  
   733  	// Set the Expiry, if given.
   734  	if cmd.Expiry != nil {
   735  		mtx.Expiry = uint32(*cmd.Expiry)
   736  	}
   737  
   738  	// Return the serialized and hex-encoded transaction.
   739  	sb := new(strings.Builder)
   740  	err := mtx.Serialize(hex.NewEncoder(sb))
   741  	if err != nil {
   742  		return nil, err
   743  	}
   744  	return sb.String(), nil
   745  }
   746  
   747  // createSignature creates a signature using the private key of a wallet
   748  // address for a transaction input script. The serialized compressed public
   749  // key of the address is also returned.
   750  func (s *Server) createSignature(ctx context.Context, icmd interface{}) (interface{}, error) {
   751  	cmd := icmd.(*types.CreateSignatureCmd)
   752  	w, ok := s.walletLoader.LoadedWallet()
   753  	if !ok {
   754  		return nil, errUnloadedWallet
   755  	}
   756  
   757  	serializedTx, err := hex.DecodeString(cmd.SerializedTransaction)
   758  	if err != nil {
   759  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
   760  	}
   761  
   762  	var tx wire.MsgTx
   763  	err = tx.Deserialize(bytes.NewReader(serializedTx))
   764  	if err != nil {
   765  		return nil, rpcError(dcrjson.ErrRPCDeserialization, err)
   766  	}
   767  
   768  	if cmd.InputIndex >= len(tx.TxIn) {
   769  		return nil, rpcErrorf(dcrjson.ErrRPCMisc,
   770  			"transaction input %d does not exist", cmd.InputIndex)
   771  	}
   772  
   773  	addr, err := decodeAddress(cmd.Address, w.ChainParams())
   774  	if err != nil {
   775  		return nil, err
   776  	}
   777  
   778  	hashType := txscript.SigHashType(cmd.HashType)
   779  	prevOutScript, err := hex.DecodeString(cmd.PreviousPkScript)
   780  	if err != nil {
   781  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
   782  	}
   783  
   784  	sig, pubkey, err := w.CreateSignature(ctx, &tx, uint32(cmd.InputIndex),
   785  		addr, hashType, prevOutScript)
   786  	if err != nil {
   787  		return nil, err
   788  	}
   789  
   790  	return &types.CreateSignatureResult{
   791  		Signature: hex.EncodeToString(sig),
   792  		PublicKey: hex.EncodeToString(pubkey),
   793  	}, nil
   794  }
   795  
   796  // disapprovePercent returns the wallets current disapprove percentage.
   797  func (s *Server) disapprovePercent(ctx context.Context, _ interface{}) (interface{}, error) {
   798  	w, ok := s.walletLoader.LoadedWallet()
   799  	if !ok {
   800  		return nil, errUnloadedWallet
   801  	}
   802  	return w.DisapprovePercent(), nil
   803  }
   804  
   805  func (s *Server) discoverUsage(ctx context.Context, icmd interface{}) (interface{}, error) {
   806  	cmd := icmd.(*types.DiscoverUsageCmd)
   807  	w, ok := s.walletLoader.LoadedWallet()
   808  	if !ok {
   809  		return nil, errUnloadedWallet
   810  	}
   811  
   812  	n, ok := s.walletLoader.NetworkBackend()
   813  	if !ok {
   814  		return nil, errNoNetwork
   815  	}
   816  
   817  	startBlock := w.ChainParams().GenesisHash
   818  	if cmd.StartBlock != nil {
   819  		h, err := chainhash.NewHashFromStr(*cmd.StartBlock)
   820  		if err != nil {
   821  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "startblock: %v", err)
   822  		}
   823  		startBlock = *h
   824  	}
   825  	discoverAccounts := cmd.DiscoverAccounts != nil && *cmd.DiscoverAccounts
   826  
   827  	gapLimit := w.GapLimit()
   828  	if cmd.GapLimit != nil {
   829  		gapLimit = *cmd.GapLimit
   830  	}
   831  
   832  	err := w.DiscoverActiveAddresses(ctx, n, &startBlock, discoverAccounts, gapLimit)
   833  	return nil, err
   834  }
   835  
   836  // dumpPrivKey handles a dumpprivkey request with the private key
   837  // for a single address, or an appropriate error if the wallet
   838  // is locked.
   839  func (s *Server) dumpPrivKey(ctx context.Context, icmd interface{}) (interface{}, error) {
   840  	cmd := icmd.(*types.DumpPrivKeyCmd)
   841  	w, ok := s.walletLoader.LoadedWallet()
   842  	if !ok {
   843  		return nil, errUnloadedWallet
   844  	}
   845  
   846  	addr, err := decodeAddress(cmd.Address, w.ChainParams())
   847  	if err != nil {
   848  		return nil, err
   849  	}
   850  
   851  	key, err := w.DumpWIFPrivateKey(ctx, addr)
   852  	if err != nil {
   853  		if errors.Is(err, errors.Locked) {
   854  			return nil, errWalletUnlockNeeded
   855  		}
   856  		return nil, err
   857  	}
   858  	return key, nil
   859  }
   860  
   861  func (s *Server) fundRawTransaction(ctx context.Context, icmd interface{}) (interface{}, error) {
   862  	cmd := icmd.(*types.FundRawTransactionCmd)
   863  	w, ok := s.walletLoader.LoadedWallet()
   864  	if !ok {
   865  		return nil, errUnloadedWallet
   866  	}
   867  
   868  	var (
   869  		changeAddress string
   870  		feeRate       = w.RelayFee()
   871  		confs         = int32(1)
   872  	)
   873  	if cmd.Options != nil {
   874  		opts := cmd.Options
   875  		var err error
   876  		if opts.ChangeAddress != nil {
   877  			changeAddress = *opts.ChangeAddress
   878  		}
   879  		if opts.FeeRate != nil {
   880  			feeRate, err = dcrutil.NewAmount(*opts.FeeRate)
   881  			if err != nil {
   882  				return nil, err
   883  			}
   884  		}
   885  		if opts.ConfTarget != nil {
   886  			confs = *opts.ConfTarget
   887  			if confs < 0 {
   888  				return nil, errors.New("confs must be non-negative")
   889  			}
   890  		}
   891  	}
   892  
   893  	tx := new(wire.MsgTx)
   894  	err := tx.Deserialize(hex.NewDecoder(strings.NewReader(cmd.HexString)))
   895  	if err != nil {
   896  		return nil, err
   897  	}
   898  	// Existing inputs are problematic.  Without information about
   899  	// how large the input scripts will be once signed, the wallet is
   900  	// unable to perform correct fee estimation.  If fundrawtransaction
   901  	// is changed later to work on a PSDT structure that includes this
   902  	// information, this functionality may be enabled.  For now, prevent
   903  	// the method from continuing.
   904  	if len(tx.TxIn) != 0 {
   905  		return nil, errors.New("transaction must not already have inputs")
   906  	}
   907  
   908  	accountNum, err := w.AccountNumber(ctx, cmd.FundAccount)
   909  	if err != nil {
   910  		return nil, err
   911  	}
   912  
   913  	// Because there are no other inputs, a new transaction can be created.
   914  	var changeSource txauthor.ChangeSource
   915  	if changeAddress != "" {
   916  		var err error
   917  		changeSource, err = makeScriptChangeSource(changeAddress, w.ChainParams())
   918  		if err != nil {
   919  			return nil, err
   920  		}
   921  	}
   922  	atx, err := w.NewUnsignedTransaction(ctx, tx.TxOut, feeRate, accountNum, confs,
   923  		wallet.OutputSelectionAlgorithmDefault, changeSource, nil)
   924  	if err != nil {
   925  		return nil, err
   926  	}
   927  
   928  	// Include chosen inputs and change output (if any) in decoded
   929  	// transaction.
   930  	tx.TxIn = atx.Tx.TxIn
   931  	if atx.ChangeIndex >= 0 {
   932  		tx.TxOut = append(tx.TxOut, atx.Tx.TxOut[atx.ChangeIndex])
   933  	}
   934  
   935  	// Determine the absolute fee of the funded transaction
   936  	fee := atx.TotalInput
   937  	for i := range tx.TxOut {
   938  		fee -= dcrutil.Amount(tx.TxOut[i].Value)
   939  	}
   940  
   941  	b := new(strings.Builder)
   942  	b.Grow(2 * tx.SerializeSize())
   943  	err = tx.Serialize(hex.NewEncoder(b))
   944  	if err != nil {
   945  		return nil, err
   946  	}
   947  	res := &types.FundRawTransactionResult{
   948  		Hex: b.String(),
   949  		Fee: fee.ToCoin(),
   950  	}
   951  	return res, nil
   952  }
   953  
   954  // getAddressesByAccount handles a getaddressesbyaccount request by returning
   955  // all addresses for an account, or an error if the requested account does
   956  // not exist.
   957  func (s *Server) getAddressesByAccount(ctx context.Context, icmd interface{}) (interface{}, error) {
   958  	cmd := icmd.(*types.GetAddressesByAccountCmd)
   959  	w, ok := s.walletLoader.LoadedWallet()
   960  	if !ok {
   961  		return nil, errUnloadedWallet
   962  	}
   963  
   964  	if cmd.Account == "imported" {
   965  		addrs, err := w.ImportedAddresses(ctx, cmd.Account)
   966  		if err != nil {
   967  			return nil, err
   968  		}
   969  		return knownAddressMarshaler(addrs), nil
   970  	}
   971  
   972  	account, err := w.AccountNumber(ctx, cmd.Account)
   973  	if err != nil {
   974  		if errors.Is(err, errors.NotExist) {
   975  			return nil, errAccountNotFound
   976  		}
   977  		return nil, err
   978  	}
   979  
   980  	xpub, err := w.AccountXpub(ctx, account)
   981  	if err != nil {
   982  		return nil, err
   983  	}
   984  	extBranch, err := xpub.Child(0)
   985  	if err != nil {
   986  		return nil, err
   987  	}
   988  	intBranch, err := xpub.Child(1)
   989  	if err != nil {
   990  		return nil, err
   991  	}
   992  	endExt, endInt, err := w.BIP0044BranchNextIndexes(ctx, account)
   993  	if err != nil {
   994  		return nil, err
   995  	}
   996  	params := w.ChainParams()
   997  	addrs := make([]string, 0, endExt+endInt)
   998  	appendAddrs := func(branchKey *hdkeychain.ExtendedKey, n uint32) error {
   999  		for i := uint32(0); i < n; i++ {
  1000  			child, err := branchKey.Child(i)
  1001  			if errors.Is(err, hdkeychain.ErrInvalidChild) {
  1002  				continue
  1003  			}
  1004  			if err != nil {
  1005  				return err
  1006  			}
  1007  			pkh := dcrutil.Hash160(child.SerializedPubKey())
  1008  			addr, _ := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
  1009  				pkh, params)
  1010  			addrs = append(addrs, addr.String())
  1011  		}
  1012  		return nil
  1013  	}
  1014  	err = appendAddrs(extBranch, endExt)
  1015  	if err != nil {
  1016  		return nil, err
  1017  	}
  1018  	err = appendAddrs(intBranch, endInt)
  1019  	if err != nil {
  1020  		return nil, err
  1021  	}
  1022  	return addressStringsMarshaler(addrs), nil
  1023  }
  1024  
  1025  // getBalance handles a getbalance request by returning the balance for an
  1026  // account (wallet), or an error if the requested account does not
  1027  // exist.
  1028  func (s *Server) getBalance(ctx context.Context, icmd interface{}) (interface{}, error) {
  1029  	cmd := icmd.(*types.GetBalanceCmd)
  1030  	w, ok := s.walletLoader.LoadedWallet()
  1031  	if !ok {
  1032  		return nil, errUnloadedWallet
  1033  	}
  1034  
  1035  	minConf := int32(*cmd.MinConf)
  1036  	if minConf < 0 {
  1037  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "minconf must be non-negative")
  1038  	}
  1039  
  1040  	accountName := "*"
  1041  	if cmd.Account != nil {
  1042  		accountName = *cmd.Account
  1043  	}
  1044  
  1045  	blockHash, _ := w.MainChainTip(ctx)
  1046  	result := types.GetBalanceResult{
  1047  		BlockHash: blockHash.String(),
  1048  	}
  1049  
  1050  	if accountName == "*" {
  1051  		balances, err := w.AccountBalances(ctx, int32(*cmd.MinConf))
  1052  		if err != nil {
  1053  			return nil, err
  1054  		}
  1055  
  1056  		var (
  1057  			totImmatureCoinbase dcrutil.Amount
  1058  			totImmatureStakegen dcrutil.Amount
  1059  			totLocked           dcrutil.Amount
  1060  			totSpendable        dcrutil.Amount
  1061  			totUnconfirmed      dcrutil.Amount
  1062  			totVotingAuthority  dcrutil.Amount
  1063  			cumTot              dcrutil.Amount
  1064  		)
  1065  
  1066  		balancesLen := uint32(len(balances))
  1067  		result.Balances = make([]types.GetAccountBalanceResult, 0, balancesLen)
  1068  
  1069  		for _, bal := range balances {
  1070  			accountName, err := w.AccountName(ctx, bal.Account)
  1071  			if err != nil {
  1072  				// Expect account lookup to succeed
  1073  				if errors.Is(err, errors.NotExist) {
  1074  					return nil, rpcError(dcrjson.ErrRPCInternal.Code, err)
  1075  				}
  1076  				return nil, err
  1077  			}
  1078  
  1079  			totImmatureCoinbase += bal.ImmatureCoinbaseRewards
  1080  			totImmatureStakegen += bal.ImmatureStakeGeneration
  1081  			totLocked += bal.LockedByTickets
  1082  			totSpendable += bal.Spendable
  1083  			totUnconfirmed += bal.Unconfirmed
  1084  			totVotingAuthority += bal.VotingAuthority
  1085  			cumTot += bal.Total
  1086  
  1087  			json := types.GetAccountBalanceResult{
  1088  				AccountName:             accountName,
  1089  				ImmatureCoinbaseRewards: bal.ImmatureCoinbaseRewards.ToCoin(),
  1090  				ImmatureStakeGeneration: bal.ImmatureStakeGeneration.ToCoin(),
  1091  				LockedByTickets:         bal.LockedByTickets.ToCoin(),
  1092  				Spendable:               bal.Spendable.ToCoin(),
  1093  				Total:                   bal.Total.ToCoin(),
  1094  				Unconfirmed:             bal.Unconfirmed.ToCoin(),
  1095  				VotingAuthority:         bal.VotingAuthority.ToCoin(),
  1096  			}
  1097  
  1098  			result.Balances = append(result.Balances, json)
  1099  		}
  1100  
  1101  		result.TotalImmatureCoinbaseRewards = totImmatureCoinbase.ToCoin()
  1102  		result.TotalImmatureStakeGeneration = totImmatureStakegen.ToCoin()
  1103  		result.TotalLockedByTickets = totLocked.ToCoin()
  1104  		result.TotalSpendable = totSpendable.ToCoin()
  1105  		result.TotalUnconfirmed = totUnconfirmed.ToCoin()
  1106  		result.TotalVotingAuthority = totVotingAuthority.ToCoin()
  1107  		result.CumulativeTotal = cumTot.ToCoin()
  1108  	} else {
  1109  		account, err := w.AccountNumber(ctx, accountName)
  1110  		if err != nil {
  1111  			if errors.Is(err, errors.NotExist) {
  1112  				return nil, errAccountNotFound
  1113  			}
  1114  			return nil, err
  1115  		}
  1116  
  1117  		bal, err := w.AccountBalance(ctx, account, int32(*cmd.MinConf))
  1118  		if err != nil {
  1119  			// Expect account lookup to succeed
  1120  			if errors.Is(err, errors.NotExist) {
  1121  				return nil, rpcError(dcrjson.ErrRPCInternal.Code, err)
  1122  			}
  1123  			return nil, err
  1124  		}
  1125  		json := types.GetAccountBalanceResult{
  1126  			AccountName:             accountName,
  1127  			ImmatureCoinbaseRewards: bal.ImmatureCoinbaseRewards.ToCoin(),
  1128  			ImmatureStakeGeneration: bal.ImmatureStakeGeneration.ToCoin(),
  1129  			LockedByTickets:         bal.LockedByTickets.ToCoin(),
  1130  			Spendable:               bal.Spendable.ToCoin(),
  1131  			Total:                   bal.Total.ToCoin(),
  1132  			Unconfirmed:             bal.Unconfirmed.ToCoin(),
  1133  			VotingAuthority:         bal.VotingAuthority.ToCoin(),
  1134  		}
  1135  		result.Balances = append(result.Balances, json)
  1136  	}
  1137  
  1138  	return result, nil
  1139  }
  1140  
  1141  // getBestBlock handles a getbestblock request by returning a JSON object
  1142  // with the height and hash of the most recently processed block.
  1143  func (s *Server) getBestBlock(ctx context.Context, icmd interface{}) (interface{}, error) {
  1144  	w, ok := s.walletLoader.LoadedWallet()
  1145  	if !ok {
  1146  		return nil, errUnloadedWallet
  1147  	}
  1148  
  1149  	hash, height := w.MainChainTip(ctx)
  1150  	result := &dcrdtypes.GetBestBlockResult{
  1151  		Hash:   hash.String(),
  1152  		Height: int64(height),
  1153  	}
  1154  	return result, nil
  1155  }
  1156  
  1157  // getBestBlockHash handles a getbestblockhash request by returning the hash
  1158  // of the most recently processed block.
  1159  func (s *Server) getBestBlockHash(ctx context.Context, icmd interface{}) (interface{}, error) {
  1160  	w, ok := s.walletLoader.LoadedWallet()
  1161  	if !ok {
  1162  		return nil, errUnloadedWallet
  1163  	}
  1164  
  1165  	hash, _ := w.MainChainTip(ctx)
  1166  	return hash.String(), nil
  1167  }
  1168  
  1169  // getBlockCount handles a getblockcount request by returning the chain height
  1170  // of the most recently processed block.
  1171  func (s *Server) getBlockCount(ctx context.Context, icmd interface{}) (interface{}, error) {
  1172  	w, ok := s.walletLoader.LoadedWallet()
  1173  	if !ok {
  1174  		return nil, errUnloadedWallet
  1175  	}
  1176  
  1177  	_, height := w.MainChainTip(ctx)
  1178  	return height, nil
  1179  }
  1180  
  1181  // getBlockHash handles a getblockhash request by returning the main chain hash
  1182  // for a block at some height.
  1183  func (s *Server) getBlockHash(ctx context.Context, icmd interface{}) (interface{}, error) {
  1184  	cmd := icmd.(*types.GetBlockHashCmd)
  1185  	w, ok := s.walletLoader.LoadedWallet()
  1186  	if !ok {
  1187  		return nil, errUnloadedWallet
  1188  	}
  1189  
  1190  	height := int32(cmd.Index)
  1191  	id := wallet.NewBlockIdentifierFromHeight(height)
  1192  	info, err := w.BlockInfo(ctx, id)
  1193  	if err != nil {
  1194  		return nil, err
  1195  	}
  1196  	return info.Hash.String(), nil
  1197  }
  1198  
  1199  // getBlockHeader implements the getblockheader command.
  1200  func (s *Server) getBlockHeader(ctx context.Context, icmd interface{}) (interface{}, error) {
  1201  	cmd := icmd.(*types.GetBlockHeaderCmd)
  1202  	w, ok := s.walletLoader.LoadedWallet()
  1203  	if !ok {
  1204  		return nil, errUnloadedWallet
  1205  	}
  1206  
  1207  	// Attempt RPC passthrough if connected to DCRD.
  1208  	n, err := w.NetworkBackend()
  1209  	if err != nil {
  1210  		return nil, err
  1211  	}
  1212  	if rpc, ok := n.(*dcrd.RPC); ok {
  1213  		var resp json.RawMessage
  1214  		err := rpc.Call(ctx, "getblockheader", &resp, cmd.Hash, cmd.Verbose)
  1215  		return resp, err
  1216  	}
  1217  
  1218  	blockHash, err := chainhash.NewHashFromStr(cmd.Hash)
  1219  	if err != nil {
  1220  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  1221  	}
  1222  
  1223  	blockHeader, err := w.BlockHeader(ctx, blockHash)
  1224  	if err != nil {
  1225  		return nil, err
  1226  	}
  1227  
  1228  	// When the verbose flag isn't set, simply return the serialized block
  1229  	// header as a hex-encoded string.
  1230  	if cmd.Verbose == nil || !*cmd.Verbose {
  1231  		var headerBuf bytes.Buffer
  1232  		err := blockHeader.Serialize(&headerBuf)
  1233  		if err != nil {
  1234  			return nil, rpcErrorf(dcrjson.ErrRPCInternal.Code, "Could not serialize block header: %v", err)
  1235  		}
  1236  		return hex.EncodeToString(headerBuf.Bytes()), nil
  1237  	}
  1238  
  1239  	// The verbose flag is set, so generate the JSON object and return it.
  1240  
  1241  	// Get next block hash unless there are none.
  1242  	var nextHashString string
  1243  	confirmations := int64(-1)
  1244  	mainChainHasBlock, _, err := w.BlockInMainChain(ctx, blockHash)
  1245  	if err != nil {
  1246  		return nil, rpcErrorf(dcrjson.ErrRPCInternal.Code, "Error checking if block is in mainchain: %v", err)
  1247  	}
  1248  	if mainChainHasBlock {
  1249  		blockHeight := int32(blockHeader.Height)
  1250  		_, bestHeight := w.MainChainTip(ctx)
  1251  		if blockHeight < bestHeight {
  1252  			nextBlockID := wallet.NewBlockIdentifierFromHeight(blockHeight + 1)
  1253  			nextBlockInfo, err := w.BlockInfo(ctx, nextBlockID)
  1254  			if err != nil {
  1255  				return nil, rpcErrorf(dcrjson.ErrRPCInternal.Code, "Info not found for next block: %v", err)
  1256  			}
  1257  			nextHashString = nextBlockInfo.Hash.String()
  1258  		}
  1259  		confirmations = int64(confirms(blockHeight, bestHeight))
  1260  	}
  1261  
  1262  	// Calculate past median time. Look at the last 11 blocks, starting
  1263  	// with the requested block, which is consistent with dcrd.
  1264  	iBlkHeader := blockHeader // start with the block header for the requested block
  1265  	timestamps := make([]int64, 0, 11)
  1266  	for i := 0; i < cap(timestamps); i++ {
  1267  		timestamps = append(timestamps, iBlkHeader.Timestamp.Unix())
  1268  		if iBlkHeader.Height == 0 {
  1269  			break
  1270  		}
  1271  		iBlkHeader, err = w.BlockHeader(ctx, &iBlkHeader.PrevBlock)
  1272  		if err != nil {
  1273  			return nil, rpcErrorf(dcrjson.ErrRPCInternal.Code, "Info not found for previous block: %v", err)
  1274  		}
  1275  	}
  1276  	sort.Slice(timestamps, func(i, j int) bool {
  1277  		return timestamps[i] < timestamps[j]
  1278  	})
  1279  	medianTime := timestamps[len(timestamps)/2]
  1280  
  1281  	// Determine the PoW hash.  When the v1 PoW hash differs from the
  1282  	// block hash, this is assumed to be v2 (DCP0011).  More advanced
  1283  	// selection logic will be necessary if the PoW hash changes again in
  1284  	// the future.
  1285  	powHash := blockHeader.PowHashV1()
  1286  	if powHash != *blockHash {
  1287  		powHash = blockHeader.PowHashV2()
  1288  	}
  1289  
  1290  	return &dcrdtypes.GetBlockHeaderVerboseResult{
  1291  		Hash:          blockHash.String(),
  1292  		PowHash:       powHash.String(),
  1293  		Confirmations: confirmations,
  1294  		Version:       blockHeader.Version,
  1295  		MerkleRoot:    blockHeader.MerkleRoot.String(),
  1296  		StakeRoot:     blockHeader.StakeRoot.String(),
  1297  		VoteBits:      blockHeader.VoteBits,
  1298  		FinalState:    hex.EncodeToString(blockHeader.FinalState[:]),
  1299  		Voters:        blockHeader.Voters,
  1300  		FreshStake:    blockHeader.FreshStake,
  1301  		Revocations:   blockHeader.Revocations,
  1302  		PoolSize:      blockHeader.PoolSize,
  1303  		Bits:          strconv.FormatInt(int64(blockHeader.Bits), 16),
  1304  		SBits:         dcrutil.Amount(blockHeader.SBits).ToCoin(),
  1305  		Height:        blockHeader.Height,
  1306  		Size:          blockHeader.Size,
  1307  		Time:          blockHeader.Timestamp.Unix(),
  1308  		MedianTime:    medianTime,
  1309  		Nonce:         blockHeader.Nonce,
  1310  		ExtraData:     hex.EncodeToString(blockHeader.ExtraData[:]),
  1311  		StakeVersion:  blockHeader.StakeVersion,
  1312  		Difficulty:    difficultyRatio(blockHeader.Bits, w.ChainParams()),
  1313  		ChainWork:     "", // unset because wallet is not equipped to easily calculate the cummulative chainwork
  1314  		PreviousHash:  blockHeader.PrevBlock.String(),
  1315  		NextHash:      nextHashString,
  1316  	}, nil
  1317  }
  1318  
  1319  // getBlock implements the getblock command.
  1320  func (s *Server) getBlock(ctx context.Context, icmd interface{}) (interface{}, error) {
  1321  	cmd := icmd.(*types.GetBlockCmd)
  1322  	w, ok := s.walletLoader.LoadedWallet()
  1323  	if !ok {
  1324  		return nil, errUnloadedWallet
  1325  	}
  1326  	n, err := w.NetworkBackend()
  1327  	if err != nil {
  1328  		return nil, err
  1329  	}
  1330  
  1331  	// Attempt RPC passthrough if connected to DCRD.
  1332  	if rpc, ok := n.(*dcrd.RPC); ok {
  1333  		var resp json.RawMessage
  1334  		err := rpc.Call(ctx, "getblock", &resp, cmd.Hash, cmd.Verbose, cmd.VerboseTx)
  1335  		return resp, err
  1336  	}
  1337  
  1338  	blockHash, err := chainhash.NewHashFromStr(cmd.Hash)
  1339  	if err != nil {
  1340  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  1341  	}
  1342  
  1343  	blocks, err := n.Blocks(ctx, []*chainhash.Hash{blockHash})
  1344  	if err != nil {
  1345  		return nil, err
  1346  	}
  1347  	if len(blocks) == 0 {
  1348  		// Should never happen but protects against a possible panic on
  1349  		// the following code.
  1350  		return nil, rpcErrorf(dcrjson.ErrRPCInternal.Code, "Network returned 0 blocks")
  1351  	}
  1352  
  1353  	blk := blocks[0]
  1354  
  1355  	// When the verbose flag isn't set, simply return the
  1356  	// network-serialized block as a hex-encoded string.
  1357  	if cmd.Verbose == nil || !*cmd.Verbose {
  1358  		b := new(strings.Builder)
  1359  		b.Grow(2 * blk.SerializeSize())
  1360  		err = blk.Serialize(hex.NewEncoder(b))
  1361  		if err != nil {
  1362  			return nil, rpcErrorf(dcrjson.ErrRPCInternal.Code, "Could not serialize block: %v", err)
  1363  		}
  1364  		return b.String(), nil
  1365  	}
  1366  
  1367  	// Get next block hash unless there are none.
  1368  	var nextHashString string
  1369  	blockHeader := &blk.Header
  1370  	confirmations := int64(-1)
  1371  	mainChainHasBlock, _, err := w.BlockInMainChain(ctx, blockHash)
  1372  	if err != nil {
  1373  		return nil, rpcErrorf(dcrjson.ErrRPCInternal.Code, "Error checking if block is in mainchain: %v", err)
  1374  	}
  1375  	if mainChainHasBlock {
  1376  		blockHeight := int32(blockHeader.Height)
  1377  		_, bestHeight := w.MainChainTip(ctx)
  1378  		if blockHeight < bestHeight {
  1379  			nextBlockID := wallet.NewBlockIdentifierFromHeight(blockHeight + 1)
  1380  			nextBlockInfo, err := w.BlockInfo(ctx, nextBlockID)
  1381  			if err != nil {
  1382  				return nil, rpcErrorf(dcrjson.ErrRPCInternal.Code, "Info not found for next block: %v", err)
  1383  			}
  1384  			nextHashString = nextBlockInfo.Hash.String()
  1385  		}
  1386  		confirmations = int64(confirms(blockHeight, bestHeight))
  1387  	}
  1388  
  1389  	// Calculate past median time. Look at the last 11 blocks, starting
  1390  	// with the requested block, which is consistent with dcrd.
  1391  	timestamps := make([]int64, 0, 11)
  1392  	for iBlkHeader := blockHeader; ; {
  1393  		timestamps = append(timestamps, iBlkHeader.Timestamp.Unix())
  1394  		if iBlkHeader.Height == 0 || len(timestamps) == cap(timestamps) {
  1395  			break
  1396  		}
  1397  		iBlkHeader, err = w.BlockHeader(ctx, &iBlkHeader.PrevBlock)
  1398  		if err != nil {
  1399  			return nil, rpcErrorf(dcrjson.ErrRPCInternal.Code, "Info not found for previous block: %v", err)
  1400  		}
  1401  	}
  1402  	sort.Slice(timestamps, func(i, j int) bool {
  1403  		return timestamps[i] < timestamps[j]
  1404  	})
  1405  	medianTime := timestamps[len(timestamps)/2]
  1406  
  1407  	// Determine the PoW hash.  When the v1 PoW hash differs from the
  1408  	// block hash, this is assumed to be v2 (DCP0011).  More advanced
  1409  	// selection logic will be necessary if the PoW hash changes again in
  1410  	// the future.
  1411  	powHash := blockHeader.PowHashV1()
  1412  	if powHash != *blockHash {
  1413  		powHash = blockHeader.PowHashV2()
  1414  	}
  1415  
  1416  	sbitsFloat := float64(blockHeader.SBits) / dcrutil.AtomsPerCoin
  1417  	blockReply := dcrdtypes.GetBlockVerboseResult{
  1418  		Hash:          cmd.Hash,
  1419  		PoWHash:       powHash.String(),
  1420  		Version:       blockHeader.Version,
  1421  		MerkleRoot:    blockHeader.MerkleRoot.String(),
  1422  		StakeRoot:     blockHeader.StakeRoot.String(),
  1423  		PreviousHash:  blockHeader.PrevBlock.String(),
  1424  		MedianTime:    medianTime,
  1425  		Nonce:         blockHeader.Nonce,
  1426  		VoteBits:      blockHeader.VoteBits,
  1427  		FinalState:    hex.EncodeToString(blockHeader.FinalState[:]),
  1428  		Voters:        blockHeader.Voters,
  1429  		FreshStake:    blockHeader.FreshStake,
  1430  		Revocations:   blockHeader.Revocations,
  1431  		PoolSize:      blockHeader.PoolSize,
  1432  		Time:          blockHeader.Timestamp.Unix(),
  1433  		StakeVersion:  blockHeader.StakeVersion,
  1434  		Confirmations: confirmations,
  1435  		Height:        int64(blockHeader.Height),
  1436  		Size:          int32(blk.Header.Size),
  1437  		Bits:          strconv.FormatInt(int64(blockHeader.Bits), 16),
  1438  		SBits:         sbitsFloat,
  1439  		Difficulty:    difficultyRatio(blockHeader.Bits, w.ChainParams()),
  1440  		ChainWork:     "", // unset because wallet is not equipped to easily calculate the cummulative chainwork
  1441  		ExtraData:     hex.EncodeToString(blockHeader.ExtraData[:]),
  1442  		NextHash:      nextHashString,
  1443  	}
  1444  
  1445  	// The coinbase must be version 3 once the treasury agenda is active.
  1446  	isTreasuryEnabled := blk.Transactions[0].Version >= wire.TxVersionTreasury
  1447  
  1448  	if cmd.VerboseTx == nil || !*cmd.VerboseTx {
  1449  		transactions := blk.Transactions
  1450  		txNames := make([]string, len(transactions))
  1451  		for i, tx := range transactions {
  1452  			txNames[i] = tx.TxHash().String()
  1453  		}
  1454  		blockReply.Tx = txNames
  1455  
  1456  		stransactions := blk.STransactions
  1457  		stxNames := make([]string, len(stransactions))
  1458  		for i, tx := range stransactions {
  1459  			stxNames[i] = tx.TxHash().String()
  1460  		}
  1461  		blockReply.STx = stxNames
  1462  	} else {
  1463  		txns := blk.Transactions
  1464  		rawTxns := make([]dcrdtypes.TxRawResult, len(txns))
  1465  		for i, tx := range txns {
  1466  			rawTxn, err := createTxRawResult(w.ChainParams(), tx, uint32(i), blockHeader, confirmations, isTreasuryEnabled)
  1467  			if err != nil {
  1468  				return nil, rpcErrorf(dcrjson.ErrRPCInternal.Code, "Could not create transaction: %v", err)
  1469  			}
  1470  			rawTxns[i] = *rawTxn
  1471  		}
  1472  		blockReply.RawTx = rawTxns
  1473  
  1474  		stxns := blk.STransactions
  1475  		rawSTxns := make([]dcrdtypes.TxRawResult, len(stxns))
  1476  		for i, tx := range stxns {
  1477  			rawSTxn, err := createTxRawResult(w.ChainParams(), tx, uint32(i), blockHeader, confirmations, isTreasuryEnabled)
  1478  			if err != nil {
  1479  				return nil, rpcErrorf(dcrjson.ErrRPCInternal.Code, "Could not create stake transaction: %v", err)
  1480  			}
  1481  			rawSTxns[i] = *rawSTxn
  1482  		}
  1483  		blockReply.RawSTx = rawSTxns
  1484  	}
  1485  
  1486  	return blockReply, nil
  1487  }
  1488  
  1489  func createTxRawResult(chainParams *chaincfg.Params, mtx *wire.MsgTx, blkIdx uint32, blkHeader *wire.BlockHeader,
  1490  	confirmations int64, isTreasuryEnabled bool) (*dcrdtypes.TxRawResult, error) {
  1491  
  1492  	b := new(strings.Builder)
  1493  	b.Grow(2 * mtx.SerializeSize())
  1494  	err := mtx.Serialize(hex.NewEncoder(b))
  1495  	if err != nil {
  1496  		return nil, err
  1497  	}
  1498  
  1499  	txReply := &dcrdtypes.TxRawResult{
  1500  		Hex:           b.String(),
  1501  		Txid:          mtx.CachedTxHash().String(),
  1502  		Version:       int32(mtx.Version),
  1503  		LockTime:      mtx.LockTime,
  1504  		Expiry:        mtx.Expiry,
  1505  		Vin:           createVinList(mtx, isTreasuryEnabled),
  1506  		Vout:          createVoutList(mtx, chainParams, nil, isTreasuryEnabled),
  1507  		BlockHash:     blkHeader.BlockHash().String(),
  1508  		BlockHeight:   int64(blkHeader.Height),
  1509  		BlockIndex:    blkIdx,
  1510  		Confirmations: confirmations,
  1511  		Time:          blkHeader.Timestamp.Unix(),
  1512  		Blocktime:     blkHeader.Timestamp.Unix(), // identical to Time in bitcoind too
  1513  	}
  1514  
  1515  	return txReply, nil
  1516  }
  1517  
  1518  // createVinList returns a slice of JSON objects for the inputs of the passed
  1519  // transaction.
  1520  func createVinList(mtx *wire.MsgTx, isTreasuryEnabled bool) []dcrdtypes.Vin {
  1521  	// Treasurybase transactions only have a single txin by definition.
  1522  	//
  1523  	// NOTE: This check MUST come before the coinbase check because a
  1524  	// treasurybase will be identified as a coinbase as well.
  1525  	vinList := make([]dcrdtypes.Vin, len(mtx.TxIn))
  1526  	if isTreasuryEnabled && blockchain.IsTreasuryBase(mtx) {
  1527  		txIn := mtx.TxIn[0]
  1528  		vinEntry := &vinList[0]
  1529  		vinEntry.Treasurybase = true
  1530  		vinEntry.Sequence = txIn.Sequence
  1531  		vinEntry.AmountIn = dcrutil.Amount(txIn.ValueIn).ToCoin()
  1532  		vinEntry.BlockHeight = txIn.BlockHeight
  1533  		vinEntry.BlockIndex = txIn.BlockIndex
  1534  		return vinList
  1535  	}
  1536  
  1537  	// Coinbase transactions only have a single txin by definition.
  1538  	if blockchain.IsCoinBaseTx(mtx, isTreasuryEnabled) {
  1539  		txIn := mtx.TxIn[0]
  1540  		vinEntry := &vinList[0]
  1541  		vinEntry.Coinbase = hex.EncodeToString(txIn.SignatureScript)
  1542  		vinEntry.Sequence = txIn.Sequence
  1543  		vinEntry.AmountIn = dcrutil.Amount(txIn.ValueIn).ToCoin()
  1544  		vinEntry.BlockHeight = txIn.BlockHeight
  1545  		vinEntry.BlockIndex = txIn.BlockIndex
  1546  		return vinList
  1547  	}
  1548  
  1549  	// Treasury spend transactions only have a single txin by definition.
  1550  	if isTreasuryEnabled && stake.IsTSpend(mtx) {
  1551  		txIn := mtx.TxIn[0]
  1552  		vinEntry := &vinList[0]
  1553  		vinEntry.TreasurySpend = hex.EncodeToString(txIn.SignatureScript)
  1554  		vinEntry.Sequence = txIn.Sequence
  1555  		vinEntry.AmountIn = dcrutil.Amount(txIn.ValueIn).ToCoin()
  1556  		vinEntry.BlockHeight = txIn.BlockHeight
  1557  		vinEntry.BlockIndex = txIn.BlockIndex
  1558  		return vinList
  1559  	}
  1560  
  1561  	// Stakebase transactions (votes) have two inputs: a null stake base
  1562  	// followed by an input consuming a ticket's stakesubmission.
  1563  	isSSGen := stake.IsSSGen(mtx)
  1564  
  1565  	for i, txIn := range mtx.TxIn {
  1566  		// Handle only the null input of a stakebase differently.
  1567  		if isSSGen && i == 0 {
  1568  			vinEntry := &vinList[0]
  1569  			vinEntry.Stakebase = hex.EncodeToString(txIn.SignatureScript)
  1570  			vinEntry.Sequence = txIn.Sequence
  1571  			vinEntry.AmountIn = dcrutil.Amount(txIn.ValueIn).ToCoin()
  1572  			vinEntry.BlockHeight = txIn.BlockHeight
  1573  			vinEntry.BlockIndex = txIn.BlockIndex
  1574  			continue
  1575  		}
  1576  
  1577  		// The disassembled string will contain [error] inline
  1578  		// if the script doesn't fully parse, so ignore the
  1579  		// error here.
  1580  		disbuf, _ := txscript.DisasmString(txIn.SignatureScript)
  1581  
  1582  		vinEntry := &vinList[i]
  1583  		vinEntry.Txid = txIn.PreviousOutPoint.Hash.String()
  1584  		vinEntry.Vout = txIn.PreviousOutPoint.Index
  1585  		vinEntry.Tree = txIn.PreviousOutPoint.Tree
  1586  		vinEntry.Sequence = txIn.Sequence
  1587  		vinEntry.AmountIn = dcrutil.Amount(txIn.ValueIn).ToCoin()
  1588  		vinEntry.BlockHeight = txIn.BlockHeight
  1589  		vinEntry.BlockIndex = txIn.BlockIndex
  1590  		vinEntry.ScriptSig = &dcrdtypes.ScriptSig{
  1591  			Asm: disbuf,
  1592  			Hex: hex.EncodeToString(txIn.SignatureScript),
  1593  		}
  1594  	}
  1595  
  1596  	return vinList
  1597  }
  1598  
  1599  // createVoutList returns a slice of JSON objects for the outputs of the passed
  1600  // transaction.
  1601  func createVoutList(mtx *wire.MsgTx, chainParams *chaincfg.Params, filterAddrMap map[string]struct{}, isTreasuryEnabled bool) []dcrdtypes.Vout {
  1602  	txType := stake.DetermineTxType(mtx)
  1603  	voutList := make([]dcrdtypes.Vout, 0, len(mtx.TxOut))
  1604  	for i, v := range mtx.TxOut {
  1605  		// The disassembled string will contain [error] inline if the
  1606  		// script doesn't fully parse, so ignore the error here.
  1607  		disbuf, _ := txscript.DisasmString(v.PkScript)
  1608  
  1609  		// Attempt to extract addresses from the public key script.  In
  1610  		// the case of stake submission transactions, the odd outputs
  1611  		// contain a commitment address, so detect that case
  1612  		// accordingly.
  1613  		var addrs []stdaddr.Address
  1614  		var scriptClass string
  1615  		var reqSigs uint16
  1616  		var commitAmt *dcrutil.Amount
  1617  		if txType == stake.TxTypeSStx && (i%2 != 0) {
  1618  			scriptClass = sstxCommitmentString
  1619  			addr, err := stake.AddrFromSStxPkScrCommitment(v.PkScript,
  1620  				chainParams)
  1621  			if err != nil {
  1622  				log.Warnf("failed to decode ticket "+
  1623  					"commitment addr output for tx hash "+
  1624  					"%v, output idx %v", mtx.TxHash(), i)
  1625  			} else {
  1626  				addrs = []stdaddr.Address{addr}
  1627  			}
  1628  			amt, err := stake.AmountFromSStxPkScrCommitment(v.PkScript)
  1629  			if err != nil {
  1630  				log.Warnf("failed to decode ticket "+
  1631  					"commitment amt output for tx hash %v"+
  1632  					", output idx %v", mtx.TxHash(), i)
  1633  			} else {
  1634  				commitAmt = &amt
  1635  			}
  1636  		} else {
  1637  			// Ignore the error here since an error means the script
  1638  			// couldn't parse and there is no additional information
  1639  			// about it anyways.
  1640  			var sc stdscript.ScriptType
  1641  			sc, addrs = stdscript.ExtractAddrs(v.Version, v.PkScript, chainParams)
  1642  			reqSigs = stdscript.DetermineRequiredSigs(v.Version, v.PkScript)
  1643  			scriptClass = sc.String()
  1644  		}
  1645  
  1646  		// Encode the addresses while checking if the address passes the
  1647  		// filter when needed.
  1648  		passesFilter := len(filterAddrMap) == 0
  1649  		encodedAddrs := make([]string, len(addrs))
  1650  		for j, addr := range addrs {
  1651  			encodedAddr := addr.String()
  1652  			encodedAddrs[j] = encodedAddr
  1653  
  1654  			// No need to check the map again if the filter already
  1655  			// passes.
  1656  			if passesFilter {
  1657  				continue
  1658  			}
  1659  			if _, exists := filterAddrMap[encodedAddr]; exists {
  1660  				passesFilter = true
  1661  			}
  1662  		}
  1663  
  1664  		if !passesFilter {
  1665  			continue
  1666  		}
  1667  
  1668  		var vout dcrdtypes.Vout
  1669  		voutSPK := &vout.ScriptPubKey
  1670  		vout.N = uint32(i)
  1671  		vout.Value = dcrutil.Amount(v.Value).ToCoin()
  1672  		vout.Version = v.Version
  1673  		voutSPK.Addresses = encodedAddrs
  1674  		voutSPK.Asm = disbuf
  1675  		voutSPK.Hex = hex.EncodeToString(v.PkScript)
  1676  		voutSPK.Type = scriptClass
  1677  		voutSPK.ReqSigs = int32(reqSigs)
  1678  		if commitAmt != nil {
  1679  			voutSPK.CommitAmt = dcrjson.Float64(commitAmt.ToCoin())
  1680  		}
  1681  
  1682  		voutList = append(voutList, vout)
  1683  	}
  1684  
  1685  	return voutList
  1686  }
  1687  
  1688  // difficultyRatio returns the proof-of-work difficulty as a multiple of the
  1689  // minimum difficulty using the passed bits field from the header of a block.
  1690  func difficultyRatio(bits uint32, params *chaincfg.Params) float64 {
  1691  	// The minimum difficulty is the max possible proof-of-work limit bits
  1692  	// converted back to a number.  Note this is not the same as the proof
  1693  	// of work limit directly because the block difficulty is encoded in a
  1694  	// block with the compact form which loses precision.
  1695  	max := blockchain.CompactToBig(params.PowLimitBits)
  1696  	target := blockchain.CompactToBig(bits)
  1697  
  1698  	difficulty := new(big.Rat).SetFrac(max, target)
  1699  	outString := difficulty.FloatString(8)
  1700  	diff, err := strconv.ParseFloat(outString, 64)
  1701  	if err != nil {
  1702  		log.Errorf("Cannot get difficulty: %v", err)
  1703  		return 0
  1704  	}
  1705  	return diff
  1706  }
  1707  
  1708  // syncStatus handles a syncstatus request.
  1709  func (s *Server) syncStatus(ctx context.Context, icmd interface{}) (interface{}, error) {
  1710  	w, ok := s.walletLoader.LoadedWallet()
  1711  	if !ok {
  1712  		return nil, errUnloadedWallet
  1713  	}
  1714  	n, err := w.NetworkBackend()
  1715  	if err != nil {
  1716  		return nil, err
  1717  	}
  1718  
  1719  	walletBestHash, walletBestHeight := w.MainChainTip(ctx)
  1720  	bestBlock, err := w.BlockInfo(ctx, wallet.NewBlockIdentifierFromHash(&walletBestHash))
  1721  	if err != nil {
  1722  		return nil, err
  1723  	}
  1724  	_24HoursAgo := time.Now().UTC().Add(-24 * time.Hour).Unix()
  1725  	walletBestBlockTooOld := bestBlock.Timestamp < _24HoursAgo
  1726  
  1727  	var synced bool
  1728  	var targetHeight int32
  1729  
  1730  	if syncer, ok := n.(*spv.Syncer); ok {
  1731  		synced = syncer.Synced()
  1732  		targetHeight = syncer.EstimateMainChainTip(ctx)
  1733  	} else if rpc, ok := n.(*dcrd.RPC); ok {
  1734  		var chainInfo *dcrdtypes.GetBlockChainInfoResult
  1735  		err := rpc.Call(ctx, "getblockchaininfo", &chainInfo)
  1736  		if err != nil {
  1737  			return nil, err
  1738  		}
  1739  		synced = chainInfo.Headers == int64(walletBestHeight)
  1740  		targetHeight = int32(chainInfo.Headers)
  1741  	}
  1742  
  1743  	var headersFetchProgress float32
  1744  	blocksToFetch := targetHeight - walletBestHeight
  1745  	if blocksToFetch <= 0 {
  1746  		headersFetchProgress = 1
  1747  	} else {
  1748  		totalHeadersToFetch := targetHeight - w.InitialHeight()
  1749  		headersFetchProgress = 1 - (float32(blocksToFetch) / float32(totalHeadersToFetch))
  1750  	}
  1751  
  1752  	return &types.SyncStatusResult{
  1753  		Synced:               synced,
  1754  		InitialBlockDownload: walletBestBlockTooOld,
  1755  		HeadersFetchProgress: headersFetchProgress,
  1756  	}, nil
  1757  }
  1758  
  1759  // getCurrentNet handles a getcurrentnet request.
  1760  func (s *Server) getCurrentNet(ctx context.Context, icmd interface{}) (interface{}, error) {
  1761  	return s.activeNet.Net, nil
  1762  }
  1763  
  1764  // getInfo handles a getinfo request by returning a structure containing
  1765  // information about the current state of the wallet.
  1766  func (s *Server) getInfo(ctx context.Context, icmd interface{}) (interface{}, error) {
  1767  	w, ok := s.walletLoader.LoadedWallet()
  1768  	if !ok {
  1769  		return nil, errUnloadedWallet
  1770  	}
  1771  
  1772  	tipHash, tipHeight := w.MainChainTip(ctx)
  1773  	tipHeader, err := w.BlockHeader(ctx, &tipHash)
  1774  	if err != nil {
  1775  		return nil, err
  1776  	}
  1777  
  1778  	balances, err := w.AccountBalances(ctx, 1)
  1779  	if err != nil {
  1780  		return nil, err
  1781  	}
  1782  	var spendableBalance dcrutil.Amount
  1783  	for _, balance := range balances {
  1784  		spendableBalance += balance.Spendable
  1785  	}
  1786  
  1787  	info := &types.InfoResult{
  1788  		Version:         version.Integer,
  1789  		ProtocolVersion: int32(p2p.Pver),
  1790  		WalletVersion:   version.Integer,
  1791  		Balance:         spendableBalance.ToCoin(),
  1792  		Blocks:          tipHeight,
  1793  		TimeOffset:      0,
  1794  		Connections:     0,
  1795  		Proxy:           "",
  1796  		Difficulty:      difficultyRatio(tipHeader.Bits, w.ChainParams()),
  1797  		TestNet:         w.ChainParams().Net == wire.TestNet3,
  1798  		KeypoolOldest:   0,
  1799  		KeypoolSize:     0,
  1800  		UnlockedUntil:   0,
  1801  		PaytxFee:        w.RelayFee().ToCoin(),
  1802  		RelayFee:        0,
  1803  		Errors:          "",
  1804  	}
  1805  
  1806  	n, _ := s.walletLoader.NetworkBackend()
  1807  	if rpc, ok := n.(*dcrd.RPC); ok {
  1808  		var consensusInfo dcrdtypes.InfoChainResult
  1809  		err := rpc.Call(ctx, "getinfo", &consensusInfo)
  1810  		if err != nil {
  1811  			return nil, err
  1812  		}
  1813  		info.Version = consensusInfo.Version
  1814  		info.ProtocolVersion = consensusInfo.ProtocolVersion
  1815  		info.TimeOffset = consensusInfo.TimeOffset
  1816  		info.Connections = consensusInfo.Connections
  1817  		info.Proxy = consensusInfo.Proxy
  1818  		info.RelayFee = consensusInfo.RelayFee
  1819  		info.Errors = consensusInfo.Errors
  1820  	}
  1821  
  1822  	return info, nil
  1823  }
  1824  
  1825  func decodeAddress(s string, params *chaincfg.Params) (stdaddr.Address, error) {
  1826  	// Secp256k1 pubkey as a string, handle differently.
  1827  	if len(s) == 66 || len(s) == 130 {
  1828  		pubKeyBytes, err := hex.DecodeString(s)
  1829  		if err != nil {
  1830  			return nil, err
  1831  		}
  1832  		pubKeyAddr, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0Raw(
  1833  			pubKeyBytes, params)
  1834  		if err != nil {
  1835  			return nil, err
  1836  		}
  1837  
  1838  		return pubKeyAddr, nil
  1839  	}
  1840  
  1841  	addr, err := stdaddr.DecodeAddress(s, params)
  1842  	if err != nil {
  1843  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidAddressOrKey,
  1844  			"invalid address %q: decode failed: %#q", s, err)
  1845  	}
  1846  	return addr, nil
  1847  }
  1848  
  1849  func decodeStakeAddress(s string, params *chaincfg.Params) (stdaddr.StakeAddress, error) {
  1850  	a, err := decodeAddress(s, params)
  1851  	if err != nil {
  1852  		return nil, err
  1853  	}
  1854  	if sa, ok := a.(stdaddr.StakeAddress); ok {
  1855  		return sa, nil
  1856  	}
  1857  	return nil, rpcErrorf(dcrjson.ErrRPCInvalidAddressOrKey,
  1858  		"invalid stake address %q", s)
  1859  }
  1860  
  1861  // getAccount handles a getaccount request by returning the account name
  1862  // associated with a single address.
  1863  func (s *Server) getAccount(ctx context.Context, icmd interface{}) (interface{}, error) {
  1864  	cmd := icmd.(*types.GetAccountCmd)
  1865  	w, ok := s.walletLoader.LoadedWallet()
  1866  	if !ok {
  1867  		return nil, errUnloadedWallet
  1868  	}
  1869  
  1870  	addr, err := decodeAddress(cmd.Address, w.ChainParams())
  1871  	if err != nil {
  1872  		return nil, err
  1873  	}
  1874  
  1875  	a, err := w.KnownAddress(ctx, addr)
  1876  	if err != nil {
  1877  		if errors.Is(err, errors.NotExist) {
  1878  			return nil, errAddressNotInWallet
  1879  		}
  1880  		return nil, err
  1881  	}
  1882  
  1883  	return a.AccountName(), nil
  1884  }
  1885  
  1886  // getAccountAddress handles a getaccountaddress by returning the most
  1887  // recently-created chained address that has not yet been used (does not yet
  1888  // appear in the blockchain, or any tx that has arrived in the dcrd mempool).
  1889  // If the most recently-requested address has been used, a new address (the
  1890  // next chained address in the keypool) is used.  This can fail if the keypool
  1891  // runs out (and will return dcrjson.ErrRPCWalletKeypoolRanOut if that happens).
  1892  func (s *Server) getAccountAddress(ctx context.Context, icmd interface{}) (interface{}, error) {
  1893  	cmd := icmd.(*types.GetAccountAddressCmd)
  1894  	w, ok := s.walletLoader.LoadedWallet()
  1895  	if !ok {
  1896  		return nil, errUnloadedWallet
  1897  	}
  1898  
  1899  	account, err := w.AccountNumber(ctx, cmd.Account)
  1900  	if err != nil {
  1901  		if errors.Is(err, errors.NotExist) {
  1902  			return nil, errAccountNotFound
  1903  		}
  1904  		return nil, err
  1905  	}
  1906  	addr, err := w.CurrentAddress(account)
  1907  	if err != nil {
  1908  		// Expect account lookup to succeed
  1909  		if errors.Is(err, errors.NotExist) {
  1910  			return nil, rpcError(dcrjson.ErrRPCInternal.Code, err)
  1911  		}
  1912  		return nil, err
  1913  	}
  1914  
  1915  	return addr.String(), nil
  1916  }
  1917  
  1918  // getUnconfirmedBalance handles a getunconfirmedbalance extension request
  1919  // by returning the current unconfirmed balance of an account.
  1920  func (s *Server) getUnconfirmedBalance(ctx context.Context, icmd interface{}) (interface{}, error) {
  1921  	cmd := icmd.(*types.GetUnconfirmedBalanceCmd)
  1922  	w, ok := s.walletLoader.LoadedWallet()
  1923  	if !ok {
  1924  		return nil, errUnloadedWallet
  1925  	}
  1926  
  1927  	acctName := "default"
  1928  	if cmd.Account != nil {
  1929  		acctName = *cmd.Account
  1930  	}
  1931  	account, err := w.AccountNumber(ctx, acctName)
  1932  	if err != nil {
  1933  		if errors.Is(err, errors.NotExist) {
  1934  			return nil, errAccountNotFound
  1935  		}
  1936  		return nil, err
  1937  	}
  1938  	bals, err := w.AccountBalance(ctx, account, 1)
  1939  	if err != nil {
  1940  		// Expect account lookup to succeed
  1941  		if errors.Is(err, errors.NotExist) {
  1942  			return nil, rpcError(dcrjson.ErrRPCInternal.Code, err)
  1943  		}
  1944  		return nil, err
  1945  	}
  1946  
  1947  	return (bals.Total - bals.Spendable).ToCoin(), nil
  1948  }
  1949  
  1950  // getCFilterV2 implements the getcfilterv2 command.
  1951  func (s *Server) getCFilterV2(ctx context.Context, icmd interface{}) (interface{}, error) {
  1952  	cmd := icmd.(*types.GetCFilterV2Cmd)
  1953  	blockHash, err := chainhash.NewHashFromStr(cmd.BlockHash)
  1954  	if err != nil {
  1955  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  1956  	}
  1957  
  1958  	w, ok := s.walletLoader.LoadedWallet()
  1959  	if !ok {
  1960  		return nil, errUnloadedWallet
  1961  	}
  1962  
  1963  	key, filter, err := w.CFilterV2(ctx, blockHash)
  1964  	if err != nil {
  1965  		return nil, err
  1966  	}
  1967  
  1968  	return &types.GetCFilterV2Result{
  1969  		BlockHash: cmd.BlockHash,
  1970  		Filter:    hex.EncodeToString(filter.Bytes()),
  1971  		Key:       hex.EncodeToString(key[:]),
  1972  	}, nil
  1973  }
  1974  
  1975  // importCFiltersV2 handles an importcfiltersv2 request by parsing the provided
  1976  // hex-encoded filters into bytes and importing them into the wallet.
  1977  func (s *Server) importCFiltersV2(ctx context.Context, icmd interface{}) (interface{}, error) {
  1978  	cmd := icmd.(*types.ImportCFiltersV2Cmd)
  1979  
  1980  	w, ok := s.walletLoader.LoadedWallet()
  1981  	if !ok {
  1982  		return nil, errUnloadedWallet
  1983  	}
  1984  
  1985  	filterData := make([][]byte, len(cmd.Filters))
  1986  	for i, fdhex := range cmd.Filters {
  1987  		var err error
  1988  		filterData[i], err = hex.DecodeString(fdhex)
  1989  		if err != nil {
  1990  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidParams.Code, "filter %d is not a valid hex string", i)
  1991  		}
  1992  	}
  1993  
  1994  	err := w.ImportCFiltersV2(ctx, cmd.StartHeight, filterData)
  1995  	if err != nil {
  1996  		return nil, rpcError(dcrjson.ErrRPCInvalidRequest.Code, err)
  1997  	}
  1998  
  1999  	return nil, nil
  2000  }
  2001  
  2002  // importPrivKey handles an importprivkey request by parsing
  2003  // a WIF-encoded private key and adding it to an account.
  2004  func (s *Server) importPrivKey(ctx context.Context, icmd interface{}) (interface{}, error) {
  2005  	cmd := icmd.(*types.ImportPrivKeyCmd)
  2006  	w, ok := s.walletLoader.LoadedWallet()
  2007  	if !ok {
  2008  		return nil, errUnloadedWallet
  2009  	}
  2010  
  2011  	rescan := true
  2012  	if cmd.Rescan != nil {
  2013  		rescan = *cmd.Rescan
  2014  	}
  2015  	scanFrom := int32(0)
  2016  	if cmd.ScanFrom != nil {
  2017  		scanFrom = int32(*cmd.ScanFrom)
  2018  	}
  2019  	n, ok := s.walletLoader.NetworkBackend()
  2020  	if rescan && !ok {
  2021  		return nil, errNoNetwork
  2022  	}
  2023  
  2024  	// Ensure that private keys are only imported to the correct account.
  2025  	//
  2026  	// Yes, Label is the account name.
  2027  	if cmd.Label != nil && *cmd.Label != udb.ImportedAddrAccountName {
  2028  		return nil, errNotImportedAccount
  2029  	}
  2030  
  2031  	wif, err := dcrutil.DecodeWIF(cmd.PrivKey, w.ChainParams().PrivateKeyID)
  2032  	if err != nil {
  2033  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidAddressOrKey, "WIF decode failed: %v", err)
  2034  	}
  2035  
  2036  	// Import the private key, handling any errors.
  2037  	_, err = w.ImportPrivateKey(ctx, wif)
  2038  	if err != nil {
  2039  		switch {
  2040  		case errors.Is(err, errors.Exist):
  2041  			// Do not return duplicate key errors to the client.
  2042  			return nil, nil
  2043  		case errors.Is(err, errors.Locked):
  2044  			return nil, errWalletUnlockNeeded
  2045  		default:
  2046  			return nil, err
  2047  		}
  2048  	}
  2049  
  2050  	if rescan {
  2051  		// TODO: This is not synchronized with process shutdown and
  2052  		// will cause panics when the DB is closed mid-transaction.
  2053  		go w.RescanFromHeight(context.Background(), n, scanFrom)
  2054  	}
  2055  
  2056  	return nil, nil
  2057  }
  2058  
  2059  // importPubKey handles an importpubkey request by importing a hex-encoded
  2060  // compressed 33-byte secp256k1 public key with sign byte, as well as its
  2061  // derived P2PKH address.  This method may only be used by watching-only
  2062  // wallets and with the special "imported" account.
  2063  func (s *Server) importPubKey(ctx context.Context, icmd interface{}) (interface{}, error) {
  2064  	cmd := icmd.(*types.ImportPubKeyCmd)
  2065  	w, ok := s.walletLoader.LoadedWallet()
  2066  	if !ok {
  2067  		return nil, errUnloadedWallet
  2068  	}
  2069  
  2070  	rescan := true
  2071  	if cmd.Rescan != nil {
  2072  		rescan = *cmd.Rescan
  2073  	}
  2074  	scanFrom := int32(0)
  2075  	if cmd.ScanFrom != nil {
  2076  		scanFrom = int32(*cmd.ScanFrom)
  2077  	}
  2078  	n, ok := s.walletLoader.NetworkBackend()
  2079  	if rescan && !ok {
  2080  		return nil, errNoNetwork
  2081  	}
  2082  
  2083  	if cmd.Label != nil && *cmd.Label != udb.ImportedAddrAccountName {
  2084  		return nil, errNotImportedAccount
  2085  	}
  2086  
  2087  	pk, err := hex.DecodeString(cmd.PubKey)
  2088  	if err != nil {
  2089  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  2090  	}
  2091  
  2092  	_, err = w.ImportPublicKey(ctx, pk)
  2093  	if errors.Is(err, errors.Exist) {
  2094  		// Do not return duplicate address errors, and skip any
  2095  		// rescans.
  2096  		return nil, nil
  2097  	}
  2098  	if err != nil {
  2099  		return nil, err
  2100  	}
  2101  
  2102  	if rescan {
  2103  		// TODO: This is not synchronized with process shutdown and
  2104  		// will cause panics when the DB is closed mid-transaction.
  2105  		go w.RescanFromHeight(context.Background(), n, scanFrom)
  2106  	}
  2107  
  2108  	return nil, nil
  2109  }
  2110  
  2111  // importScript imports a redeem script for a P2SH output.
  2112  func (s *Server) importScript(ctx context.Context, icmd interface{}) (interface{}, error) {
  2113  	cmd := icmd.(*types.ImportScriptCmd)
  2114  	w, ok := s.walletLoader.LoadedWallet()
  2115  	if !ok {
  2116  		return nil, errUnloadedWallet
  2117  	}
  2118  
  2119  	rescan := true
  2120  	if cmd.Rescan != nil {
  2121  		rescan = *cmd.Rescan
  2122  	}
  2123  	scanFrom := int32(0)
  2124  	if cmd.ScanFrom != nil {
  2125  		scanFrom = int32(*cmd.ScanFrom)
  2126  	}
  2127  	n, ok := s.walletLoader.NetworkBackend()
  2128  	if rescan && !ok {
  2129  		return nil, errNoNetwork
  2130  	}
  2131  
  2132  	rs, err := hex.DecodeString(cmd.Hex)
  2133  	if err != nil {
  2134  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  2135  	}
  2136  	if len(rs) == 0 {
  2137  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "empty script")
  2138  	}
  2139  
  2140  	err = w.ImportScript(ctx, rs)
  2141  	if errors.Is(err, errors.Exist) {
  2142  		return nil, nil
  2143  	}
  2144  	if err != nil {
  2145  		return nil, err
  2146  	}
  2147  
  2148  	if rescan {
  2149  		// TODO: This is not synchronized with process shutdown and
  2150  		// will cause panics when the DB is closed mid-transaction.
  2151  		go w.RescanFromHeight(context.Background(), n, scanFrom)
  2152  	}
  2153  
  2154  	return nil, nil
  2155  }
  2156  
  2157  func (s *Server) importXpub(ctx context.Context, icmd interface{}) (interface{}, error) {
  2158  	cmd := icmd.(*types.ImportXpubCmd)
  2159  	w, ok := s.walletLoader.LoadedWallet()
  2160  	if !ok {
  2161  		return nil, errUnloadedWallet
  2162  	}
  2163  
  2164  	xpub, err := hdkeychain.NewKeyFromString(cmd.Xpub, w.ChainParams())
  2165  	if err != nil {
  2166  		return nil, err
  2167  	}
  2168  
  2169  	return nil, w.ImportXpubAccount(ctx, cmd.Name, xpub)
  2170  }
  2171  
  2172  // createNewAccount handles a createnewaccount request by creating and
  2173  // returning a new account. If the last account has no transaction history
  2174  // as per BIP 0044 a new account cannot be created so an error will be returned.
  2175  func (s *Server) createNewAccount(ctx context.Context, icmd interface{}) (interface{}, error) {
  2176  	cmd := icmd.(*types.CreateNewAccountCmd)
  2177  	w, ok := s.walletLoader.LoadedWallet()
  2178  	if !ok {
  2179  		return nil, errUnloadedWallet
  2180  	}
  2181  
  2182  	// The wildcard * is reserved by the rpc server with the special meaning
  2183  	// of "all accounts", so disallow naming accounts to this string.
  2184  	if cmd.Account == "*" {
  2185  		return nil, errReservedAccountName
  2186  	}
  2187  
  2188  	_, err := w.NextAccount(ctx, cmd.Account)
  2189  	if err != nil {
  2190  		if errors.Is(err, errors.Locked) {
  2191  			return nil, rpcErrorf(dcrjson.ErrRPCWalletUnlockNeeded, "creating new accounts requires an unlocked wallet")
  2192  		}
  2193  		return nil, err
  2194  	}
  2195  	return nil, nil
  2196  }
  2197  
  2198  // renameAccount handles a renameaccount request by renaming an account.
  2199  // If the account does not exist an appropriate error will be returned.
  2200  func (s *Server) renameAccount(ctx context.Context, icmd interface{}) (interface{}, error) {
  2201  	cmd := icmd.(*types.RenameAccountCmd)
  2202  	w, ok := s.walletLoader.LoadedWallet()
  2203  	if !ok {
  2204  		return nil, errUnloadedWallet
  2205  	}
  2206  
  2207  	// The wildcard * is reserved by the rpc server with the special meaning
  2208  	// of "all accounts", so disallow naming accounts to this string.
  2209  	if cmd.NewAccount == "*" {
  2210  		return nil, errReservedAccountName
  2211  	}
  2212  
  2213  	// Check that given account exists
  2214  	account, err := w.AccountNumber(ctx, cmd.OldAccount)
  2215  	if err != nil {
  2216  		if errors.Is(err, errors.NotExist) {
  2217  			return nil, errAccountNotFound
  2218  		}
  2219  		return nil, err
  2220  	}
  2221  	err = w.RenameAccount(ctx, account, cmd.NewAccount)
  2222  	return nil, err
  2223  }
  2224  
  2225  // getMultisigOutInfo displays information about a given multisignature
  2226  // output.
  2227  func (s *Server) getMultisigOutInfo(ctx context.Context, icmd interface{}) (interface{}, error) {
  2228  	cmd := icmd.(*types.GetMultisigOutInfoCmd)
  2229  	w, ok := s.walletLoader.LoadedWallet()
  2230  	if !ok {
  2231  		return nil, errUnloadedWallet
  2232  	}
  2233  
  2234  	hash, err := chainhash.NewHashFromStr(cmd.Hash)
  2235  	if err != nil {
  2236  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  2237  	}
  2238  
  2239  	// Multisig outs are always in TxTreeRegular.
  2240  	op := &wire.OutPoint{
  2241  		Hash:  *hash,
  2242  		Index: cmd.Index,
  2243  		Tree:  wire.TxTreeRegular,
  2244  	}
  2245  
  2246  	p2shOutput, err := w.FetchP2SHMultiSigOutput(ctx, op)
  2247  	if err != nil {
  2248  		return nil, err
  2249  	}
  2250  
  2251  	// Get the list of pubkeys required to sign.
  2252  	_, pubkeyAddrs := stdscript.ExtractAddrs(scriptVersionAssumed, p2shOutput.RedeemScript, w.ChainParams())
  2253  	pubkeys := make([]string, 0, len(pubkeyAddrs))
  2254  	for _, pka := range pubkeyAddrs {
  2255  		switch pka := pka.(type) {
  2256  		case *stdaddr.AddressPubKeyEcdsaSecp256k1V0:
  2257  			pubkeys = append(pubkeys, hex.EncodeToString(pka.SerializedPubKey()))
  2258  		}
  2259  	}
  2260  
  2261  	result := &types.GetMultisigOutInfoResult{
  2262  		Address:      p2shOutput.P2SHAddress.String(),
  2263  		RedeemScript: hex.EncodeToString(p2shOutput.RedeemScript),
  2264  		M:            p2shOutput.M,
  2265  		N:            p2shOutput.N,
  2266  		Pubkeys:      pubkeys,
  2267  		TxHash:       p2shOutput.OutPoint.Hash.String(),
  2268  		Amount:       p2shOutput.OutputAmount.ToCoin(),
  2269  	}
  2270  	if !p2shOutput.ContainingBlock.None() {
  2271  		result.BlockHeight = uint32(p2shOutput.ContainingBlock.Height)
  2272  		result.BlockHash = p2shOutput.ContainingBlock.Hash.String()
  2273  	}
  2274  	if p2shOutput.Redeemer != nil {
  2275  		result.Spent = true
  2276  		result.SpentBy = p2shOutput.Redeemer.TxHash.String()
  2277  		result.SpentByIndex = p2shOutput.Redeemer.InputIndex
  2278  	}
  2279  	return result, nil
  2280  }
  2281  
  2282  // getNewAddress handles a getnewaddress request by returning a new
  2283  // address for an account.  If the account does not exist an appropriate
  2284  // error is returned.
  2285  func (s *Server) getNewAddress(ctx context.Context, icmd interface{}) (interface{}, error) {
  2286  	cmd := icmd.(*types.GetNewAddressCmd)
  2287  	w, ok := s.walletLoader.LoadedWallet()
  2288  	if !ok {
  2289  		return nil, errUnloadedWallet
  2290  	}
  2291  
  2292  	var callOpts []wallet.NextAddressCallOption
  2293  	if cmd.GapPolicy != nil {
  2294  		switch *cmd.GapPolicy {
  2295  		case "":
  2296  		case "error":
  2297  			callOpts = append(callOpts, wallet.WithGapPolicyError())
  2298  		case "ignore":
  2299  			callOpts = append(callOpts, wallet.WithGapPolicyIgnore())
  2300  		case "wrap":
  2301  			callOpts = append(callOpts, wallet.WithGapPolicyWrap())
  2302  		default:
  2303  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "unknown gap policy %q", *cmd.GapPolicy)
  2304  		}
  2305  	}
  2306  
  2307  	acctName := "default"
  2308  	if cmd.Account != nil {
  2309  		acctName = *cmd.Account
  2310  	}
  2311  	account, err := w.AccountNumber(ctx, acctName)
  2312  	if err != nil {
  2313  		if errors.Is(err, errors.NotExist) {
  2314  			return nil, errAccountNotFound
  2315  		}
  2316  		return nil, err
  2317  	}
  2318  
  2319  	addr, err := w.NewExternalAddress(ctx, account, callOpts...)
  2320  	if err != nil {
  2321  		return nil, err
  2322  	}
  2323  	return addr.String(), nil
  2324  }
  2325  
  2326  // getRawChangeAddress handles a getrawchangeaddress request by creating
  2327  // and returning a new change address for an account.
  2328  //
  2329  // Note: bitcoind allows specifying the account as an optional parameter,
  2330  // but ignores the parameter.
  2331  func (s *Server) getRawChangeAddress(ctx context.Context, icmd interface{}) (interface{}, error) {
  2332  	cmd := icmd.(*types.GetRawChangeAddressCmd)
  2333  	w, ok := s.walletLoader.LoadedWallet()
  2334  	if !ok {
  2335  		return nil, errUnloadedWallet
  2336  	}
  2337  
  2338  	acctName := "default"
  2339  	if cmd.Account != nil {
  2340  		acctName = *cmd.Account
  2341  	}
  2342  	account, err := w.AccountNumber(ctx, acctName)
  2343  	if err != nil {
  2344  		if errors.Is(err, errors.NotExist) {
  2345  			return nil, errAccountNotFound
  2346  		}
  2347  		return nil, err
  2348  	}
  2349  
  2350  	addr, err := w.NewChangeAddress(ctx, account)
  2351  	if err != nil {
  2352  		return nil, err
  2353  	}
  2354  
  2355  	// Return the new payment address string.
  2356  	return addr.String(), nil
  2357  }
  2358  
  2359  // getReceivedByAccount handles a getreceivedbyaccount request by returning
  2360  // the total amount received by addresses of an account.
  2361  func (s *Server) getReceivedByAccount(ctx context.Context, icmd interface{}) (interface{}, error) {
  2362  	cmd := icmd.(*types.GetReceivedByAccountCmd)
  2363  	w, ok := s.walletLoader.LoadedWallet()
  2364  	if !ok {
  2365  		return nil, errUnloadedWallet
  2366  	}
  2367  
  2368  	account, err := w.AccountNumber(ctx, cmd.Account)
  2369  	if err != nil {
  2370  		if errors.Is(err, errors.NotExist) {
  2371  			return nil, errAccountNotFound
  2372  		}
  2373  		return nil, err
  2374  	}
  2375  
  2376  	// Transactions are not tracked for imported xpub accounts.
  2377  	if account > udb.ImportedAddrAccount {
  2378  		return 0.0, nil
  2379  	}
  2380  
  2381  	// TODO: This is more inefficient that it could be, but the entire
  2382  	// algorithm is already dominated by reading every transaction in the
  2383  	// wallet's history.
  2384  	results, err := w.TotalReceivedForAccounts(ctx, int32(*cmd.MinConf))
  2385  	if err != nil {
  2386  		return nil, err
  2387  	}
  2388  	acctIndex := int(account)
  2389  	if account == udb.ImportedAddrAccount {
  2390  		acctIndex = len(results) - 1
  2391  	}
  2392  	return results[acctIndex].TotalReceived.ToCoin(), nil
  2393  }
  2394  
  2395  // getReceivedByAddress handles a getreceivedbyaddress request by returning
  2396  // the total amount received by a single address.
  2397  func (s *Server) getReceivedByAddress(ctx context.Context, icmd interface{}) (interface{}, error) {
  2398  	cmd := icmd.(*types.GetReceivedByAddressCmd)
  2399  	w, ok := s.walletLoader.LoadedWallet()
  2400  	if !ok {
  2401  		return nil, errUnloadedWallet
  2402  	}
  2403  
  2404  	addr, err := decodeAddress(cmd.Address, w.ChainParams())
  2405  	if err != nil {
  2406  		return nil, err
  2407  	}
  2408  	total, err := w.TotalReceivedForAddr(ctx, addr, int32(*cmd.MinConf))
  2409  	if err != nil {
  2410  		if errors.Is(err, errors.NotExist) {
  2411  			return nil, errAddressNotInWallet
  2412  		}
  2413  		return nil, err
  2414  	}
  2415  
  2416  	return total.ToCoin(), nil
  2417  }
  2418  
  2419  // getMasterPubkey handles a getmasterpubkey request by returning the wallet
  2420  // master pubkey encoded as a string.
  2421  func (s *Server) getMasterPubkey(ctx context.Context, icmd interface{}) (interface{}, error) {
  2422  	cmd := icmd.(*types.GetMasterPubkeyCmd)
  2423  	w, ok := s.walletLoader.LoadedWallet()
  2424  	if !ok {
  2425  		return nil, errUnloadedWallet
  2426  	}
  2427  
  2428  	// If no account is passed, we provide the extended public key
  2429  	// for the default account number.
  2430  	account := uint32(udb.DefaultAccountNum)
  2431  	if cmd.Account != nil {
  2432  		var err error
  2433  		account, err = w.AccountNumber(ctx, *cmd.Account)
  2434  		if err != nil {
  2435  			if errors.Is(err, errors.NotExist) {
  2436  				return nil, errAccountNotFound
  2437  			}
  2438  			return nil, err
  2439  		}
  2440  	}
  2441  
  2442  	xpub, err := w.AccountXpub(ctx, account)
  2443  	if err != nil {
  2444  		return nil, err
  2445  	}
  2446  
  2447  	log.Warnf("Attention: Extended public keys must not be shared with or " +
  2448  		"leaked to external parties, such as VSPs, in combination with " +
  2449  		"any account private key; this reveals all private keys of " +
  2450  		"this account")
  2451  
  2452  	return xpub.String(), nil
  2453  }
  2454  
  2455  // getPeerInfo responds to the getpeerinfo request.
  2456  // It gets the network backend and views the data on remote peers when in spv mode
  2457  func (s *Server) getPeerInfo(ctx context.Context, icmd interface{}) (interface{}, error) {
  2458  	w, ok := s.walletLoader.LoadedWallet()
  2459  	if !ok {
  2460  		return nil, errUnloadedWallet
  2461  	}
  2462  	n, err := w.NetworkBackend()
  2463  	if err != nil {
  2464  		return nil, err
  2465  	}
  2466  
  2467  	syncer, ok := n.(*spv.Syncer)
  2468  	if !ok {
  2469  		var resp []*types.GetPeerInfoResult
  2470  		if rpc, ok := n.(*dcrd.RPC); ok {
  2471  			err := rpc.Call(ctx, "getpeerinfo", &resp)
  2472  			if err != nil {
  2473  				return nil, err
  2474  			}
  2475  		}
  2476  		return resp, nil
  2477  	}
  2478  
  2479  	rps := syncer.GetRemotePeers()
  2480  	infos := make([]*types.GetPeerInfoResult, 0, len(rps))
  2481  
  2482  	for _, rp := range rps {
  2483  		info := &types.GetPeerInfoResult{
  2484  			ID:             int32(rp.ID()),
  2485  			Addr:           rp.RemoteAddr().String(),
  2486  			AddrLocal:      rp.LocalAddr().String(),
  2487  			Services:       fmt.Sprintf("%08d", uint64(rp.Services())),
  2488  			Version:        rp.Pver(),
  2489  			SubVer:         rp.UA(),
  2490  			StartingHeight: int64(rp.InitialHeight()),
  2491  			BanScore:       int32(rp.BanScore()),
  2492  		}
  2493  		infos = append(infos, info)
  2494  	}
  2495  	sort.Slice(infos, func(i, j int) bool {
  2496  		return infos[i].ID < infos[j].ID
  2497  	})
  2498  	return infos, nil
  2499  }
  2500  
  2501  // getStakeInfo gets a large amounts of information about the stake environment
  2502  // and a number of statistics about local staking in the wallet.
  2503  func (s *Server) getStakeInfo(ctx context.Context, icmd interface{}) (interface{}, error) {
  2504  	w, ok := s.walletLoader.LoadedWallet()
  2505  	if !ok {
  2506  		return nil, errUnloadedWallet
  2507  	}
  2508  
  2509  	var rpc *dcrd.RPC
  2510  	n, _ := s.walletLoader.NetworkBackend()
  2511  	if client, ok := n.(*dcrd.RPC); ok {
  2512  		rpc = client
  2513  	}
  2514  	var sinfo *wallet.StakeInfoData
  2515  	var err error
  2516  	if rpc != nil {
  2517  		sinfo, err = w.StakeInfoPrecise(ctx, rpc)
  2518  	} else {
  2519  		sinfo, err = w.StakeInfo(ctx)
  2520  	}
  2521  	if err != nil {
  2522  		return nil, err
  2523  	}
  2524  
  2525  	var proportionLive, proportionMissed float64
  2526  	if sinfo.PoolSize > 0 {
  2527  		proportionLive = float64(sinfo.Live) / float64(sinfo.PoolSize)
  2528  	}
  2529  	if sinfo.Missed > 0 {
  2530  		proportionMissed = float64(sinfo.Missed) / (float64(sinfo.Voted + sinfo.Missed))
  2531  	}
  2532  
  2533  	resp := &types.GetStakeInfoResult{
  2534  		BlockHeight:  sinfo.BlockHeight,
  2535  		Difficulty:   sinfo.Sdiff.ToCoin(),
  2536  		TotalSubsidy: sinfo.TotalSubsidy.ToCoin(),
  2537  
  2538  		OwnMempoolTix:  sinfo.OwnMempoolTix,
  2539  		Immature:       sinfo.Immature,
  2540  		Unspent:        sinfo.Unspent,
  2541  		Voted:          sinfo.Voted,
  2542  		Revoked:        sinfo.Revoked,
  2543  		UnspentExpired: sinfo.UnspentExpired,
  2544  
  2545  		PoolSize:         sinfo.PoolSize,
  2546  		AllMempoolTix:    sinfo.AllMempoolTix,
  2547  		Live:             sinfo.Live,
  2548  		ProportionLive:   proportionLive,
  2549  		Missed:           sinfo.Missed,
  2550  		ProportionMissed: proportionMissed,
  2551  		Expired:          sinfo.Expired,
  2552  	}
  2553  
  2554  	return resp, nil
  2555  }
  2556  
  2557  // getTickets handles a gettickets request by returning the hashes of the tickets
  2558  // currently owned by wallet, encoded as strings.
  2559  func (s *Server) getTickets(ctx context.Context, icmd interface{}) (interface{}, error) {
  2560  	cmd := icmd.(*types.GetTicketsCmd)
  2561  	w, ok := s.walletLoader.LoadedWallet()
  2562  	if !ok {
  2563  		return nil, errUnloadedWallet
  2564  	}
  2565  
  2566  	n, _ := s.walletLoader.NetworkBackend()
  2567  	rpc, ok := n.(*dcrd.RPC)
  2568  	if !ok {
  2569  		return nil, errRPCClientNotConnected
  2570  	}
  2571  
  2572  	ticketHashes, err := w.LiveTicketHashes(ctx, rpc, cmd.IncludeImmature)
  2573  	if err != nil {
  2574  		return nil, err
  2575  	}
  2576  
  2577  	// Compose a slice of strings to return.
  2578  	ticketHashStrs := make([]string, 0, len(ticketHashes))
  2579  	for i := range ticketHashes {
  2580  		ticketHashStrs = append(ticketHashStrs, ticketHashes[i].String())
  2581  	}
  2582  
  2583  	return &types.GetTicketsResult{Hashes: ticketHashStrs}, nil
  2584  }
  2585  
  2586  // getTransaction handles a gettransaction request by returning details about
  2587  // a single transaction saved by wallet.
  2588  func (s *Server) getTransaction(ctx context.Context, icmd interface{}) (interface{}, error) {
  2589  	cmd := icmd.(*types.GetTransactionCmd)
  2590  	w, ok := s.walletLoader.LoadedWallet()
  2591  	if !ok {
  2592  		return nil, errUnloadedWallet
  2593  	}
  2594  
  2595  	txHash, err := chainhash.NewHashFromStr(cmd.Txid)
  2596  	if err != nil {
  2597  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  2598  	}
  2599  
  2600  	// returns nil details when not found
  2601  	txd, err := wallet.UnstableAPI(w).TxDetails(ctx, txHash)
  2602  	if errors.Is(err, errors.NotExist) {
  2603  		return nil, rpcErrorf(dcrjson.ErrRPCNoTxInfo, "no information for transaction")
  2604  	} else if err != nil {
  2605  		return nil, err
  2606  	}
  2607  
  2608  	_, tipHeight := w.MainChainTip(ctx)
  2609  
  2610  	var b strings.Builder
  2611  	b.Grow(2 * txd.MsgTx.SerializeSize())
  2612  	err = txd.MsgTx.Serialize(hex.NewEncoder(&b))
  2613  	if err != nil {
  2614  		return nil, err
  2615  	}
  2616  
  2617  	// TODO: Add a "generated" field to this result type.  "generated":true
  2618  	// is only added if the transaction is a coinbase.
  2619  	ret := types.GetTransactionResult{
  2620  		TxID:            cmd.Txid,
  2621  		Hex:             b.String(),
  2622  		Time:            txd.Received.Unix(),
  2623  		TimeReceived:    txd.Received.Unix(),
  2624  		WalletConflicts: []string{}, // Not saved
  2625  		//Generated:     compat.IsEitherCoinBaseTx(&details.MsgTx),
  2626  	}
  2627  
  2628  	if txd.Block.Height != -1 {
  2629  		ret.BlockHash = txd.Block.Hash.String()
  2630  		ret.BlockTime = txd.Block.Time.Unix()
  2631  		ret.Confirmations = int64(confirms(txd.Block.Height,
  2632  			tipHeight))
  2633  	}
  2634  
  2635  	var (
  2636  		debitTotal  dcrutil.Amount
  2637  		creditTotal dcrutil.Amount
  2638  		fee         dcrutil.Amount
  2639  		negFeeF64   float64
  2640  	)
  2641  	for _, deb := range txd.Debits {
  2642  		debitTotal += deb.Amount
  2643  	}
  2644  	for _, cred := range txd.Credits {
  2645  		creditTotal += cred.Amount
  2646  	}
  2647  	// Fee can only be determined if every input is a debit.
  2648  	if len(txd.Debits) == len(txd.MsgTx.TxIn) {
  2649  		var outputTotal dcrutil.Amount
  2650  		for _, output := range txd.MsgTx.TxOut {
  2651  			outputTotal += dcrutil.Amount(output.Value)
  2652  		}
  2653  		fee = debitTotal - outputTotal
  2654  		negFeeF64 = (-fee).ToCoin()
  2655  	}
  2656  	ret.Amount = (creditTotal - debitTotal).ToCoin()
  2657  	ret.Fee = negFeeF64
  2658  
  2659  	details, err := w.ListTransactionDetails(ctx, txHash)
  2660  	if err != nil {
  2661  		return nil, err
  2662  	}
  2663  	ret.Details = make([]types.GetTransactionDetailsResult, len(details))
  2664  	for i, d := range details {
  2665  		ret.Details[i] = types.GetTransactionDetailsResult{
  2666  			Account:           d.Account,
  2667  			Address:           d.Address,
  2668  			Amount:            d.Amount,
  2669  			Category:          d.Category,
  2670  			InvolvesWatchOnly: d.InvolvesWatchOnly,
  2671  			Fee:               d.Fee,
  2672  			Vout:              d.Vout,
  2673  		}
  2674  	}
  2675  
  2676  	return ret, nil
  2677  }
  2678  
  2679  // getTxOut handles a gettxout request by returning details about an unspent
  2680  // output. In SPV mode, details are only returned for transaction outputs that
  2681  // are relevant to the wallet.
  2682  // To match the behavior in RPC mode, (nil, nil) is returned if the transaction
  2683  // output could not be found (never existed or was pruned) or is spent by another
  2684  // transaction already in the main chain.  Mined transactions that are spent by
  2685  // a mempool transaction are not affected by this.
  2686  func (s *Server) getTxOut(ctx context.Context, icmd interface{}) (interface{}, error) {
  2687  	cmd := icmd.(*types.GetTxOutCmd)
  2688  	w, ok := s.walletLoader.LoadedWallet()
  2689  	if !ok {
  2690  		return nil, errUnloadedWallet
  2691  	}
  2692  
  2693  	// Attempt RPC passthrough if connected to DCRD.
  2694  	n, err := w.NetworkBackend()
  2695  	if err != nil {
  2696  		return nil, err
  2697  	}
  2698  	if rpc, ok := n.(*dcrd.RPC); ok {
  2699  		var resp json.RawMessage
  2700  		err := rpc.Call(ctx, "gettxout", &resp, cmd.Txid, cmd.Vout, cmd.Tree, cmd.IncludeMempool)
  2701  		return resp, err
  2702  	}
  2703  
  2704  	txHash, err := chainhash.NewHashFromStr(cmd.Txid)
  2705  	if err != nil {
  2706  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  2707  	}
  2708  
  2709  	if cmd.Tree != wire.TxTreeRegular && cmd.Tree != wire.TxTreeStake {
  2710  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "Tx tree must be regular or stake")
  2711  	}
  2712  
  2713  	// Attempt to read the unspent txout info from wallet.
  2714  	outpoint := wire.OutPoint{Hash: *txHash, Index: cmd.Vout, Tree: cmd.Tree}
  2715  	utxo, err := w.UnspentOutput(ctx, outpoint, *cmd.IncludeMempool)
  2716  	if err != nil && !errors.Is(err, errors.NotExist) {
  2717  		return nil, err
  2718  	}
  2719  	if utxo == nil {
  2720  		return nil, nil // output is spent or does not exist.
  2721  	}
  2722  
  2723  	// Disassemble script into single line printable format.  The
  2724  	// disassembled string will contain [error] inline if the script
  2725  	// doesn't fully parse, so ignore the error here.
  2726  	disbuf, _ := txscript.DisasmString(utxo.PkScript)
  2727  
  2728  	// Get further info about the script.  Ignore the error here since an
  2729  	// error means the script couldn't parse and there is no additional
  2730  	// information about it anyways.
  2731  	scriptClass, addrs := stdscript.ExtractAddrs(scriptVersionAssumed, utxo.PkScript, s.activeNet)
  2732  	reqSigs := stdscript.DetermineRequiredSigs(scriptVersionAssumed, utxo.PkScript)
  2733  	addresses := make([]string, len(addrs))
  2734  	for i, addr := range addrs {
  2735  		addresses[i] = addr.String()
  2736  	}
  2737  
  2738  	bestHash, bestHeight := w.MainChainTip(ctx)
  2739  	var confirmations int64
  2740  	if utxo.Block.Height != -1 {
  2741  		confirmations = int64(confirms(utxo.Block.Height, bestHeight))
  2742  	}
  2743  
  2744  	return &dcrdtypes.GetTxOutResult{
  2745  		BestBlock:     bestHash.String(),
  2746  		Confirmations: confirmations,
  2747  		Value:         utxo.Amount.ToCoin(),
  2748  		ScriptPubKey: dcrdtypes.ScriptPubKeyResult{
  2749  			Asm:       disbuf,
  2750  			Hex:       hex.EncodeToString(utxo.PkScript),
  2751  			ReqSigs:   int32(reqSigs),
  2752  			Type:      scriptClass.String(),
  2753  			Addresses: addresses,
  2754  		},
  2755  		Coinbase: utxo.FromCoinBase,
  2756  	}, nil
  2757  }
  2758  
  2759  // getVoteChoices handles a getvotechoices request by returning configured vote
  2760  // preferences for each agenda of the latest supported stake version.
  2761  func (s *Server) getVoteChoices(ctx context.Context, icmd interface{}) (interface{}, error) {
  2762  	cmd := icmd.(*types.GetVoteChoicesCmd)
  2763  	w, ok := s.walletLoader.LoadedWallet()
  2764  	if !ok {
  2765  		return nil, errUnloadedWallet
  2766  	}
  2767  
  2768  	var ticketHash *chainhash.Hash
  2769  	if cmd.TicketHash != nil {
  2770  		hash, err := chainhash.NewHashFromStr(*cmd.TicketHash)
  2771  		if err != nil {
  2772  			return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  2773  		}
  2774  		ticketHash = hash
  2775  	}
  2776  
  2777  	version, agendas := wallet.CurrentAgendas(w.ChainParams())
  2778  	resp := &types.GetVoteChoicesResult{
  2779  		Version: version,
  2780  		Choices: make([]types.VoteChoice, len(agendas)),
  2781  	}
  2782  
  2783  	choices, _, err := w.AgendaChoices(ctx, ticketHash)
  2784  	if err != nil {
  2785  		return nil, err
  2786  	}
  2787  
  2788  	for i := range choices {
  2789  		resp.Choices[i] = types.VoteChoice{
  2790  			AgendaID:          choices[i].AgendaID,
  2791  			AgendaDescription: agendas[i].Vote.Description,
  2792  			ChoiceID:          choices[i].ChoiceID,
  2793  			ChoiceDescription: "", // Set below
  2794  		}
  2795  		for j := range agendas[i].Vote.Choices {
  2796  			if choices[i].ChoiceID == agendas[i].Vote.Choices[j].Id {
  2797  				resp.Choices[i].ChoiceDescription = agendas[i].Vote.Choices[j].Description
  2798  				break
  2799  			}
  2800  		}
  2801  	}
  2802  
  2803  	return resp, nil
  2804  }
  2805  
  2806  // getWalletFee returns the currently set tx fee for the requested wallet
  2807  func (s *Server) getWalletFee(ctx context.Context, icmd interface{}) (interface{}, error) {
  2808  	w, ok := s.walletLoader.LoadedWallet()
  2809  	if !ok {
  2810  		return nil, errUnloadedWallet
  2811  	}
  2812  
  2813  	return w.RelayFee().ToCoin(), nil
  2814  }
  2815  
  2816  // These generators create the following global variables in this package:
  2817  //
  2818  //   var localeHelpDescs map[string]func() map[string]string
  2819  //   var requestUsages string
  2820  //
  2821  // localeHelpDescs maps from locale strings (e.g. "en_US") to a function that
  2822  // builds a map of help texts for each RPC server method.  This prevents help
  2823  // text maps for every locale map from being rooted and created during init.
  2824  // Instead, the appropriate function is looked up when help text is first needed
  2825  // using the current locale and saved to the global below for further reuse.
  2826  //
  2827  // requestUsages contains single line usages for every supported request,
  2828  // separated by newlines.  It is set during init.  These usages are used for all
  2829  // locales.
  2830  //
  2831  //go:generate go run ../../rpchelp/genrpcserverhelp.go jsonrpc
  2832  //go:generate gofmt -w rpcserverhelp.go
  2833  
  2834  var helpDescs map[string]string
  2835  var helpDescsMu sync.Mutex // Help may execute concurrently, so synchronize access.
  2836  
  2837  // help handles the help request by returning one line usage of all available
  2838  // methods, or full help for a specific method.  The chainClient is optional,
  2839  // and this is simply a helper function for the HelpNoChainRPC and
  2840  // HelpWithChainRPC handlers.
  2841  func (s *Server) help(ctx context.Context, icmd interface{}) (interface{}, error) {
  2842  	cmd := icmd.(*types.HelpCmd)
  2843  	// TODO: The "help" RPC should use a HTTP POST client when calling down to
  2844  	// dcrd for additional help methods.  This avoids including websocket-only
  2845  	// requests in the help, which are not callable by wallet JSON-RPC clients.
  2846  	var rpc *dcrd.RPC
  2847  	n, _ := s.walletLoader.NetworkBackend()
  2848  	if client, ok := n.(*dcrd.RPC); ok {
  2849  		rpc = client
  2850  	}
  2851  	if cmd.Command == nil || *cmd.Command == "" {
  2852  		// Prepend chain server usage if it is available.
  2853  		usages := requestUsages
  2854  		if rpc != nil {
  2855  			var usage string
  2856  			err := rpc.Call(ctx, "help", &usage)
  2857  			if err != nil {
  2858  				return nil, err
  2859  			}
  2860  			if usage != "" {
  2861  				usages = "Chain server usage:\n\n" + usage + "\n\n" +
  2862  					"Wallet server usage (overrides chain requests):\n\n" +
  2863  					requestUsages
  2864  			}
  2865  		}
  2866  		return usages, nil
  2867  	}
  2868  
  2869  	defer helpDescsMu.Unlock()
  2870  	helpDescsMu.Lock()
  2871  
  2872  	if helpDescs == nil {
  2873  		// TODO: Allow other locales to be set via config or detemine
  2874  		// this from environment variables.  For now, hardcode US
  2875  		// English.
  2876  		helpDescs = localeHelpDescs["en_US"]()
  2877  	}
  2878  
  2879  	helpText, ok := helpDescs[*cmd.Command]
  2880  	if ok {
  2881  		return helpText, nil
  2882  	}
  2883  
  2884  	// Return the chain server's detailed help if possible.
  2885  	var chainHelp string
  2886  	if rpc != nil {
  2887  		err := rpc.Call(ctx, "help", &chainHelp, *cmd.Command)
  2888  		if err != nil {
  2889  			return nil, err
  2890  		}
  2891  	}
  2892  	if chainHelp != "" {
  2893  		return chainHelp, nil
  2894  	}
  2895  	return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "no help for method %q", *cmd.Command)
  2896  }
  2897  
  2898  // listAccounts handles a listaccounts request by returning a map of account
  2899  // names to their balances.
  2900  func (s *Server) listAccounts(ctx context.Context, icmd interface{}) (interface{}, error) {
  2901  	cmd := icmd.(*types.ListAccountsCmd)
  2902  	w, ok := s.walletLoader.LoadedWallet()
  2903  	if !ok {
  2904  		return nil, errUnloadedWallet
  2905  	}
  2906  
  2907  	accountBalances := map[string]float64{}
  2908  	results, err := w.AccountBalances(ctx, int32(*cmd.MinConf))
  2909  	if err != nil {
  2910  		return nil, err
  2911  	}
  2912  	for _, result := range results {
  2913  		accountName, err := w.AccountName(ctx, result.Account)
  2914  		if err != nil {
  2915  			// Expect name lookup to succeed
  2916  			if errors.Is(err, errors.NotExist) {
  2917  				return nil, rpcError(dcrjson.ErrRPCInternal.Code, err)
  2918  			}
  2919  			return nil, err
  2920  		}
  2921  		accountBalances[accountName] = result.Spendable.ToCoin()
  2922  	}
  2923  	// Return the map.  This will be marshaled into a JSON object.
  2924  	return accountBalances, nil
  2925  }
  2926  
  2927  // listLockUnspent handles a listlockunspent request by returning an slice of
  2928  // all locked outpoints.
  2929  func (s *Server) listLockUnspent(ctx context.Context, icmd interface{}) (interface{}, error) {
  2930  	w, ok := s.walletLoader.LoadedWallet()
  2931  	if !ok {
  2932  		return nil, errUnloadedWallet
  2933  	}
  2934  
  2935  	var account string
  2936  	cmd := icmd.(*types.ListLockUnspentCmd)
  2937  	if cmd.Account != nil {
  2938  		account = *cmd.Account
  2939  	}
  2940  	return w.LockedOutpoints(ctx, account)
  2941  }
  2942  
  2943  // listReceivedByAccount handles a listreceivedbyaccount request by returning
  2944  // a slice of objects, each one containing:
  2945  //
  2946  //	"account": the receiving account;
  2947  //	"amount": total amount received by the account;
  2948  //	"confirmations": number of confirmations of the most recent transaction.
  2949  //
  2950  // It takes two parameters:
  2951  //
  2952  //	"minconf": minimum number of confirmations to consider a transaction -
  2953  //	           default: one;
  2954  //	"includeempty": whether or not to include addresses that have no transactions -
  2955  //	                default: false.
  2956  func (s *Server) listReceivedByAccount(ctx context.Context, icmd interface{}) (interface{}, error) {
  2957  	cmd := icmd.(*types.ListReceivedByAccountCmd)
  2958  	w, ok := s.walletLoader.LoadedWallet()
  2959  	if !ok {
  2960  		return nil, errUnloadedWallet
  2961  	}
  2962  
  2963  	results, err := w.TotalReceivedForAccounts(ctx, int32(*cmd.MinConf))
  2964  	if err != nil {
  2965  		return nil, err
  2966  	}
  2967  
  2968  	jsonResults := make([]types.ListReceivedByAccountResult, 0, len(results))
  2969  	for _, result := range results {
  2970  		jsonResults = append(jsonResults, types.ListReceivedByAccountResult{
  2971  			Account:       result.AccountName,
  2972  			Amount:        result.TotalReceived.ToCoin(),
  2973  			Confirmations: uint64(result.LastConfirmation),
  2974  		})
  2975  	}
  2976  	return jsonResults, nil
  2977  }
  2978  
  2979  // listReceivedByAddress handles a listreceivedbyaddress request by returning
  2980  // a slice of objects, each one containing:
  2981  //
  2982  //	"account": the account of the receiving address;
  2983  //	"address": the receiving address;
  2984  //	"amount": total amount received by the address;
  2985  //	"confirmations": number of confirmations of the most recent transaction.
  2986  //
  2987  // It takes two parameters:
  2988  //
  2989  //	"minconf": minimum number of confirmations to consider a transaction -
  2990  //	           default: one;
  2991  //	"includeempty": whether or not to include addresses that have no transactions -
  2992  //	                default: false.
  2993  func (s *Server) listReceivedByAddress(ctx context.Context, icmd interface{}) (interface{}, error) {
  2994  	cmd := icmd.(*types.ListReceivedByAddressCmd)
  2995  	w, ok := s.walletLoader.LoadedWallet()
  2996  	if !ok {
  2997  		return nil, errUnloadedWallet
  2998  	}
  2999  
  3000  	// Intermediate data for each address.
  3001  	type AddrData struct {
  3002  		// Total amount received.
  3003  		amount dcrutil.Amount
  3004  		// Number of confirmations of the last transaction.
  3005  		confirmations int32
  3006  		// Hashes of transactions which include an output paying to the address
  3007  		tx []string
  3008  	}
  3009  
  3010  	_, tipHeight := w.MainChainTip(ctx)
  3011  
  3012  	// Intermediate data for all addresses.
  3013  	allAddrData := make(map[string]AddrData)
  3014  	// Create an AddrData entry for each active address in the account.
  3015  	// Otherwise we'll just get addresses from transactions later.
  3016  	sortedAddrs, err := w.SortedActivePaymentAddresses(ctx)
  3017  	if err != nil {
  3018  		return nil, err
  3019  	}
  3020  	for _, address := range sortedAddrs {
  3021  		// There might be duplicates, just overwrite them.
  3022  		allAddrData[address] = AddrData{}
  3023  	}
  3024  
  3025  	minConf := *cmd.MinConf
  3026  	var endHeight int32
  3027  	if minConf == 0 {
  3028  		endHeight = -1
  3029  	} else {
  3030  		endHeight = tipHeight - int32(minConf) + 1
  3031  	}
  3032  	err = wallet.UnstableAPI(w).RangeTransactions(ctx, 0, endHeight, func(details []udb.TxDetails) (bool, error) {
  3033  		confirmations := confirms(details[0].Block.Height, tipHeight)
  3034  		for _, tx := range details {
  3035  			for _, cred := range tx.Credits {
  3036  				pkVersion := tx.MsgTx.TxOut[cred.Index].Version
  3037  				pkScript := tx.MsgTx.TxOut[cred.Index].PkScript
  3038  				_, addrs := stdscript.ExtractAddrs(pkVersion, pkScript, w.ChainParams())
  3039  				for _, addr := range addrs {
  3040  					addrStr := addr.String()
  3041  					addrData, ok := allAddrData[addrStr]
  3042  					if ok {
  3043  						addrData.amount += cred.Amount
  3044  						// Always overwrite confirmations with newer ones.
  3045  						addrData.confirmations = confirmations
  3046  					} else {
  3047  						addrData = AddrData{
  3048  							amount:        cred.Amount,
  3049  							confirmations: confirmations,
  3050  						}
  3051  					}
  3052  					addrData.tx = append(addrData.tx, tx.Hash.String())
  3053  					allAddrData[addrStr] = addrData
  3054  				}
  3055  			}
  3056  		}
  3057  		return false, nil
  3058  	})
  3059  	if err != nil {
  3060  		return nil, err
  3061  	}
  3062  
  3063  	// Massage address data into output format.
  3064  	numAddresses := len(allAddrData)
  3065  	ret := make([]types.ListReceivedByAddressResult, numAddresses)
  3066  	idx := 0
  3067  	for address, addrData := range allAddrData {
  3068  		ret[idx] = types.ListReceivedByAddressResult{
  3069  			Address:       address,
  3070  			Amount:        addrData.amount.ToCoin(),
  3071  			Confirmations: uint64(addrData.confirmations),
  3072  			TxIDs:         addrData.tx,
  3073  		}
  3074  		idx++
  3075  	}
  3076  	return ret, nil
  3077  }
  3078  
  3079  // listSinceBlock handles a listsinceblock request by returning an array of maps
  3080  // with details of sent and received wallet transactions since the given block.
  3081  func (s *Server) listSinceBlock(ctx context.Context, icmd interface{}) (interface{}, error) {
  3082  	cmd := icmd.(*types.ListSinceBlockCmd)
  3083  	w, ok := s.walletLoader.LoadedWallet()
  3084  	if !ok {
  3085  		return nil, errUnloadedWallet
  3086  	}
  3087  
  3088  	targetConf := int32(*cmd.TargetConfirmations)
  3089  	if targetConf < 1 {
  3090  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "target_confirmations must be positive")
  3091  	}
  3092  
  3093  	tipHash, tipHeight := w.MainChainTip(ctx)
  3094  	lastBlock := &tipHash
  3095  	if targetConf > 0 {
  3096  		id := wallet.NewBlockIdentifierFromHeight((tipHeight + 1) - targetConf)
  3097  		info, err := w.BlockInfo(ctx, id)
  3098  		if err != nil {
  3099  			return nil, err
  3100  		}
  3101  
  3102  		lastBlock = &info.Hash
  3103  	}
  3104  
  3105  	// TODO: This must begin at the fork point in the main chain, not the height
  3106  	// of this block.
  3107  	var end int32
  3108  	if cmd.BlockHash != nil {
  3109  		hash, err := chainhash.NewHashFromStr(*cmd.BlockHash)
  3110  		if err != nil {
  3111  			return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  3112  		}
  3113  		header, err := w.BlockHeader(ctx, hash)
  3114  		if err != nil {
  3115  			return nil, err
  3116  		}
  3117  		end = int32(header.Height)
  3118  	}
  3119  
  3120  	txInfoList, err := w.ListSinceBlock(ctx, -1, end, tipHeight)
  3121  	if err != nil {
  3122  		return nil, err
  3123  	}
  3124  
  3125  	res := &types.ListSinceBlockResult{
  3126  		Transactions: txInfoList,
  3127  		LastBlock:    lastBlock.String(),
  3128  	}
  3129  	return res, nil
  3130  }
  3131  
  3132  // listTransactions handles a listtransactions request by returning an
  3133  // array of maps with details of sent and recevied wallet transactions.
  3134  func (s *Server) listTransactions(ctx context.Context, icmd interface{}) (interface{}, error) {
  3135  	cmd := icmd.(*types.ListTransactionsCmd)
  3136  	w, ok := s.walletLoader.LoadedWallet()
  3137  	if !ok {
  3138  		return nil, errUnloadedWallet
  3139  	}
  3140  
  3141  	// TODO: ListTransactions does not currently understand the difference
  3142  	// between transactions pertaining to one account from another.  This
  3143  	// will be resolved when wtxmgr is combined with the waddrmgr namespace.
  3144  
  3145  	if cmd.Account != nil && *cmd.Account != "*" {
  3146  		// For now, don't bother trying to continue if the user
  3147  		// specified an account, since this can't be (easily or
  3148  		// efficiently) calculated.
  3149  		return nil,
  3150  			errors.E(`Transactions can not be searched by account. ` +
  3151  				`Use "*" to reference all accounts.`)
  3152  	}
  3153  
  3154  	return w.ListTransactions(ctx, *cmd.From, *cmd.Count)
  3155  }
  3156  
  3157  // listAddressTransactions handles a listaddresstransactions request by
  3158  // returning an array of maps with details of spent and received wallet
  3159  // transactions.  The form of the reply is identical to listtransactions,
  3160  // but the array elements are limited to transaction details which are
  3161  // about the addresess included in the request.
  3162  func (s *Server) listAddressTransactions(ctx context.Context, icmd interface{}) (interface{}, error) {
  3163  	cmd := icmd.(*types.ListAddressTransactionsCmd)
  3164  	w, ok := s.walletLoader.LoadedWallet()
  3165  	if !ok {
  3166  		return nil, errUnloadedWallet
  3167  	}
  3168  
  3169  	if cmd.Account != nil && *cmd.Account != "*" {
  3170  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
  3171  			"listing transactions for addresses may only be done for all accounts")
  3172  	}
  3173  
  3174  	// Decode addresses.
  3175  	hash160Map := make(map[string]struct{})
  3176  	for _, addrStr := range cmd.Addresses {
  3177  		addr, err := decodeAddress(addrStr, w.ChainParams())
  3178  		if err != nil {
  3179  			return nil, err
  3180  		}
  3181  		hash160er, ok := addr.(stdaddr.Hash160er)
  3182  		if !ok {
  3183  			// Not tracked by the wallet so skip reporting history
  3184  			// of this address.
  3185  			continue
  3186  		}
  3187  		hash160Map[string(hash160er.Hash160()[:])] = struct{}{}
  3188  	}
  3189  
  3190  	return w.ListAddressTransactions(ctx, hash160Map)
  3191  }
  3192  
  3193  // listAllTransactions handles a listalltransactions request by returning
  3194  // a map with details of sent and recevied wallet transactions.  This is
  3195  // similar to ListTransactions, except it takes only a single optional
  3196  // argument for the account name and replies with all transactions.
  3197  func (s *Server) listAllTransactions(ctx context.Context, icmd interface{}) (interface{}, error) {
  3198  	cmd := icmd.(*types.ListAllTransactionsCmd)
  3199  	w, ok := s.walletLoader.LoadedWallet()
  3200  	if !ok {
  3201  		return nil, errUnloadedWallet
  3202  	}
  3203  
  3204  	if cmd.Account != nil && *cmd.Account != "*" {
  3205  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
  3206  			"listing all transactions may only be done for all accounts")
  3207  	}
  3208  
  3209  	return w.ListAllTransactions(ctx)
  3210  }
  3211  
  3212  // listUnspent handles the listunspent command.
  3213  func (s *Server) listUnspent(ctx context.Context, icmd interface{}) (interface{}, error) {
  3214  	cmd := icmd.(*types.ListUnspentCmd)
  3215  	w, ok := s.walletLoader.LoadedWallet()
  3216  	if !ok {
  3217  		return nil, errUnloadedWallet
  3218  	}
  3219  
  3220  	var addresses map[string]struct{}
  3221  	if cmd.Addresses != nil {
  3222  		addresses = make(map[string]struct{})
  3223  		// confirm that all of them are good:
  3224  		for _, as := range *cmd.Addresses {
  3225  			a, err := decodeAddress(as, w.ChainParams())
  3226  			if err != nil {
  3227  				return nil, err
  3228  			}
  3229  			addresses[a.String()] = struct{}{}
  3230  		}
  3231  	}
  3232  
  3233  	var account string
  3234  	if cmd.Account != nil {
  3235  		account = *cmd.Account
  3236  	}
  3237  	result, err := w.ListUnspent(ctx, int32(*cmd.MinConf), int32(*cmd.MaxConf), addresses, account)
  3238  	if err != nil {
  3239  		if errors.Is(err, errors.NotExist) {
  3240  			return nil, errAddressNotInWallet
  3241  		}
  3242  		return nil, err
  3243  	}
  3244  	return result, nil
  3245  }
  3246  
  3247  // lockUnspent handles the lockunspent command.
  3248  func (s *Server) lockUnspent(ctx context.Context, icmd interface{}) (interface{}, error) {
  3249  	cmd := icmd.(*types.LockUnspentCmd)
  3250  	w, ok := s.walletLoader.LoadedWallet()
  3251  	if !ok {
  3252  		return nil, errUnloadedWallet
  3253  	}
  3254  
  3255  	switch {
  3256  	case cmd.Unlock && len(cmd.Transactions) == 0:
  3257  		w.ResetLockedOutpoints()
  3258  	default:
  3259  		for _, input := range cmd.Transactions {
  3260  			txHash, err := chainhash.NewHashFromStr(input.Txid)
  3261  			if err != nil {
  3262  				return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  3263  			}
  3264  			if cmd.Unlock {
  3265  				w.UnlockOutpoint(txHash, input.Vout)
  3266  			} else {
  3267  				w.LockOutpoint(txHash, input.Vout)
  3268  			}
  3269  		}
  3270  	}
  3271  	return true, nil
  3272  }
  3273  
  3274  // purchaseTicket indicates to the wallet that a ticket should be purchased
  3275  // using all currently available funds. If the ticket could not be purchased
  3276  // because there are not enough eligible funds, an error will be returned.
  3277  func (s *Server) purchaseTicket(ctx context.Context, icmd interface{}) (interface{}, error) {
  3278  	// Enforce valid and positive spend limit.
  3279  	cmd := icmd.(*types.PurchaseTicketCmd)
  3280  	w, ok := s.walletLoader.LoadedWallet()
  3281  	if !ok {
  3282  		return nil, errUnloadedWallet
  3283  	}
  3284  
  3285  	n, err := w.NetworkBackend()
  3286  	if err != nil {
  3287  		return nil, err
  3288  	}
  3289  
  3290  	spendLimit, err := dcrutil.NewAmount(cmd.SpendLimit)
  3291  	if err != nil {
  3292  		return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  3293  	}
  3294  	if spendLimit < 0 {
  3295  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "negative spend limit")
  3296  	}
  3297  
  3298  	account, err := w.AccountNumber(ctx, cmd.FromAccount)
  3299  	if err != nil {
  3300  		if errors.Is(err, errors.NotExist) {
  3301  			return nil, errAccountNotFound
  3302  		}
  3303  		return nil, err
  3304  	}
  3305  
  3306  	// Override the minimum number of required confirmations if specified
  3307  	// and enforce it is positive.
  3308  	minConf := int32(1)
  3309  	if cmd.MinConf != nil {
  3310  		minConf = int32(*cmd.MinConf)
  3311  		if minConf < 0 {
  3312  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "negative minconf")
  3313  		}
  3314  	}
  3315  
  3316  	// Set ticket address if specified.
  3317  	var ticketAddr stdaddr.StakeAddress
  3318  	if cmd.TicketAddress != nil && *cmd.TicketAddress != "" {
  3319  		addr, err := decodeStakeAddress(*cmd.TicketAddress, w.ChainParams())
  3320  		if err != nil {
  3321  			return nil, err
  3322  		}
  3323  		ticketAddr = addr
  3324  	}
  3325  
  3326  	numTickets := 1
  3327  	if cmd.NumTickets != nil {
  3328  		if *cmd.NumTickets > 1 {
  3329  			numTickets = *cmd.NumTickets
  3330  		}
  3331  	}
  3332  
  3333  	// Set pool address if specified.
  3334  	var poolAddr stdaddr.StakeAddress
  3335  	var poolFee float64
  3336  	if cmd.PoolAddress != nil && *cmd.PoolAddress != "" {
  3337  		addr, err := decodeStakeAddress(*cmd.PoolAddress, w.ChainParams())
  3338  		if err != nil {
  3339  			return nil, err
  3340  		}
  3341  		poolAddr = addr
  3342  
  3343  		// Attempt to get the amount to send to
  3344  		// the pool after.
  3345  		if cmd.PoolFees == nil {
  3346  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
  3347  				"pool address set without pool fee")
  3348  		}
  3349  		poolFee = *cmd.PoolFees
  3350  		if !txrules.ValidPoolFeeRate(poolFee) {
  3351  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
  3352  				"pool fee percentage %v", poolFee)
  3353  		}
  3354  	}
  3355  
  3356  	// Set the expiry if specified.
  3357  	expiry := int32(0)
  3358  	if cmd.Expiry != nil {
  3359  		expiry = int32(*cmd.Expiry)
  3360  	}
  3361  
  3362  	dontSignTx := false
  3363  	if cmd.DontSignTx != nil {
  3364  		dontSignTx = *cmd.DontSignTx
  3365  	}
  3366  
  3367  	var csppServer string
  3368  	var mixedAccount uint32
  3369  	var mixedAccountBranch uint32
  3370  	var mixedSplitAccount uint32
  3371  	var changeAccount = account
  3372  
  3373  	if s.cfg.CSPPServer != "" {
  3374  		csppServer = s.cfg.CSPPServer
  3375  		mixedAccount, err = w.AccountNumber(ctx, s.cfg.MixAccount)
  3376  		if err != nil {
  3377  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
  3378  				"CSPP Server set, but error on mixed account: %v", err)
  3379  		}
  3380  		mixedAccountBranch = s.cfg.MixBranch
  3381  		if mixedAccountBranch != 0 && mixedAccountBranch != 1 {
  3382  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
  3383  				"MixedAccountBranch should be 0 or 1.")
  3384  		}
  3385  		_, err = w.AccountNumber(ctx, s.cfg.TicketSplitAccount)
  3386  		if err != nil {
  3387  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
  3388  				"CSPP Server set, but error on mixedSplitAccount: %v", err)
  3389  		}
  3390  		_, err = w.AccountNumber(ctx, s.cfg.MixChangeAccount)
  3391  		if err != nil {
  3392  			return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
  3393  				"CSPP Server set, but error on changeAccount: %v", err)
  3394  		}
  3395  	}
  3396  
  3397  	var vspClient *vsp.Client
  3398  	if s.cfg.VSPHost != "" {
  3399  		cfg := vsp.Config{
  3400  			URL:    s.cfg.VSPHost,
  3401  			PubKey: s.cfg.VSPPubKey,
  3402  			Dialer: s.cfg.Dial,
  3403  			Wallet: w,
  3404  			Policy: vsp.Policy{
  3405  				MaxFee:     s.cfg.VSPMaxFee,
  3406  				FeeAcct:    account,
  3407  				ChangeAcct: changeAccount,
  3408  			},
  3409  		}
  3410  		vspClient, err = loader.VSP(cfg)
  3411  		if err != nil {
  3412  			return nil, rpcErrorf(dcrjson.ErrRPCMisc,
  3413  				"VSP Server instance failed to start: %v", err)
  3414  		}
  3415  	}
  3416  
  3417  	request := &wallet.PurchaseTicketsRequest{
  3418  		Count:         numTickets,
  3419  		SourceAccount: account,
  3420  		VotingAddress: ticketAddr,
  3421  		MinConf:       minConf,
  3422  		Expiry:        expiry,
  3423  		DontSignTx:    dontSignTx,
  3424  		VSPAddress:    poolAddr,
  3425  		VSPFees:       poolFee,
  3426  
  3427  		// CSPP
  3428  		CSPPServer:         csppServer,
  3429  		DialCSPPServer:     s.cfg.DialCSPPServer,
  3430  		MixedAccount:       mixedAccount,
  3431  		MixedAccountBranch: mixedAccountBranch,
  3432  		MixedSplitAccount:  mixedSplitAccount,
  3433  		ChangeAccount:      changeAccount,
  3434  	}
  3435  
  3436  	if vspClient != nil {
  3437  		request.VSPFeePaymentProcess = vspClient.Process
  3438  		request.VSPFeeProcess = vspClient.FeePercentage
  3439  	}
  3440  
  3441  	ticketsResponse, err := w.PurchaseTickets(ctx, n, request)
  3442  	if err != nil {
  3443  		return nil, err
  3444  	}
  3445  	ticketsTx := ticketsResponse.Tickets
  3446  	splitTx := ticketsResponse.SplitTx
  3447  
  3448  	// If dontSignTx is false, we return the TicketHashes of the published txs.
  3449  	if !dontSignTx {
  3450  		hashes := ticketsResponse.TicketHashes
  3451  		hashStrs := make([]string, len(hashes))
  3452  		for i := range hashes {
  3453  			hashStrs[i] = hashes[i].String()
  3454  		}
  3455  
  3456  		return hashStrs, err
  3457  	}
  3458  
  3459  	// Otherwise we return its unsigned tickets bytes and the splittx, so a
  3460  	// cold wallet can handle it.
  3461  	var stringBuilder strings.Builder
  3462  	unsignedTickets := make([]string, len(ticketsTx))
  3463  	for i, mtx := range ticketsTx {
  3464  		err = mtx.Serialize(hex.NewEncoder(&stringBuilder))
  3465  		if err != nil {
  3466  			return nil, err
  3467  		}
  3468  		unsignedTickets[i] = stringBuilder.String()
  3469  		stringBuilder.Reset()
  3470  	}
  3471  
  3472  	err = splitTx.Serialize(hex.NewEncoder(&stringBuilder))
  3473  	if err != nil {
  3474  		return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  3475  	}
  3476  
  3477  	splitTxString := stringBuilder.String()
  3478  
  3479  	return types.CreateUnsignedTicketResult{
  3480  		UnsignedTickets: unsignedTickets,
  3481  		SplitTx:         splitTxString,
  3482  	}, nil
  3483  }
  3484  
  3485  // processUnmanagedTicket takes a ticket hash as an argument and attempts to
  3486  // start managing it for the set vsp client from the config.
  3487  func (s *Server) processUnmanagedTicket(ctx context.Context, icmd interface{}) (interface{}, error) {
  3488  	cmd := icmd.(*types.ProcessUnmanagedTicketCmd)
  3489  
  3490  	var ticketHash *chainhash.Hash
  3491  	if cmd.TicketHash != nil {
  3492  		hash, err := chainhash.NewHashFromStr(*cmd.TicketHash)
  3493  		if err != nil {
  3494  			return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  3495  		}
  3496  		ticketHash = hash
  3497  	} else {
  3498  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "ticket hash must be provided")
  3499  	}
  3500  	vspHost := s.cfg.VSPHost
  3501  	if vspHost == "" {
  3502  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "vsphost must be set in options")
  3503  	}
  3504  	vspClient, err := loader.LookupVSP(vspHost)
  3505  	if err != nil {
  3506  		return nil, err
  3507  	}
  3508  
  3509  	err = vspClient.ProcessTicket(ctx, ticketHash)
  3510  	if err != nil {
  3511  		return nil, err
  3512  	}
  3513  
  3514  	return nil, nil
  3515  
  3516  }
  3517  
  3518  // makeOutputs creates a slice of transaction outputs from a pair of address
  3519  // strings to amounts.  This is used to create the outputs to include in newly
  3520  // created transactions from a JSON object describing the output destinations
  3521  // and amounts.
  3522  func makeOutputs(pairs map[string]dcrutil.Amount, chainParams *chaincfg.Params) ([]*wire.TxOut, error) {
  3523  	outputs := make([]*wire.TxOut, 0, len(pairs))
  3524  	for addrStr, amt := range pairs {
  3525  		if amt < 0 {
  3526  			return nil, errNeedPositiveAmount
  3527  		}
  3528  		addr, err := decodeAddress(addrStr, chainParams)
  3529  		if err != nil {
  3530  			return nil, err
  3531  		}
  3532  
  3533  		vers, pkScript := addr.PaymentScript()
  3534  
  3535  		outputs = append(outputs, &wire.TxOut{
  3536  			Value:    int64(amt),
  3537  			PkScript: pkScript,
  3538  			Version:  vers,
  3539  		})
  3540  	}
  3541  	return outputs, nil
  3542  }
  3543  
  3544  // sendPairs creates and sends payment transactions.
  3545  // It returns the transaction hash in string format upon success
  3546  // All errors are returned in dcrjson.RPCError format
  3547  func (s *Server) sendPairs(ctx context.Context, w *wallet.Wallet, amounts map[string]dcrutil.Amount, account uint32, minconf int32) (string, error) {
  3548  	changeAccount := account
  3549  	if s.cfg.CSPPServer != "" && s.cfg.MixAccount != "" && s.cfg.MixChangeAccount != "" {
  3550  		mixAccount, err := w.AccountNumber(ctx, s.cfg.MixAccount)
  3551  		if err != nil {
  3552  			return "", err
  3553  		}
  3554  		if account == mixAccount {
  3555  			changeAccount, err = w.AccountNumber(ctx, s.cfg.MixChangeAccount)
  3556  			if err != nil {
  3557  				return "", err
  3558  			}
  3559  		}
  3560  	}
  3561  
  3562  	outputs, err := makeOutputs(amounts, w.ChainParams())
  3563  	if err != nil {
  3564  		return "", err
  3565  	}
  3566  	txSha, err := w.SendOutputs(ctx, outputs, account, changeAccount, minconf)
  3567  	if err != nil {
  3568  		if errors.Is(err, errors.Locked) {
  3569  			return "", errWalletUnlockNeeded
  3570  		}
  3571  		if errors.Is(err, errors.InsufficientBalance) {
  3572  			return "", rpcError(dcrjson.ErrRPCWalletInsufficientFunds, err)
  3573  		}
  3574  		return "", err
  3575  	}
  3576  
  3577  	return txSha.String(), nil
  3578  }
  3579  
  3580  // sendAmountToTreasury creates and sends payment transactions to the treasury.
  3581  // It returns the transaction hash in string format upon success All errors are
  3582  // returned in dcrjson.RPCError format
  3583  func (s *Server) sendAmountToTreasury(ctx context.Context, w *wallet.Wallet, amount dcrutil.Amount, account uint32, minconf int32) (string, error) {
  3584  	changeAccount := account
  3585  	if s.cfg.CSPPServer != "" {
  3586  		mixAccount, err := w.AccountNumber(ctx, s.cfg.MixAccount)
  3587  		if err != nil {
  3588  			return "", err
  3589  		}
  3590  		if account == mixAccount {
  3591  			changeAccount, err = w.AccountNumber(ctx,
  3592  				s.cfg.MixChangeAccount)
  3593  			if err != nil {
  3594  				return "", err
  3595  			}
  3596  		}
  3597  	}
  3598  
  3599  	outputs := []*wire.TxOut{
  3600  		{
  3601  			Value:    int64(amount),
  3602  			PkScript: []byte{txscript.OP_TADD},
  3603  			Version:  wire.DefaultPkScriptVersion,
  3604  		},
  3605  	}
  3606  	txSha, err := w.SendOutputsToTreasury(ctx, outputs, account,
  3607  		changeAccount, minconf)
  3608  	if err != nil {
  3609  		if errors.Is(err, errors.Locked) {
  3610  			return "", errWalletUnlockNeeded
  3611  		}
  3612  		if errors.Is(err, errors.InsufficientBalance) {
  3613  			return "", rpcError(dcrjson.ErrRPCWalletInsufficientFunds,
  3614  				err)
  3615  		}
  3616  		return "", err
  3617  	}
  3618  
  3619  	return txSha.String(), nil
  3620  }
  3621  
  3622  // sendOutputsFromTreasury creates and sends payment transactions from the treasury.
  3623  // It returns the transaction hash in string format upon success All errors are
  3624  // returned in dcrjson.RPCError format
  3625  func (s *Server) sendOutputsFromTreasury(ctx context.Context, w *wallet.Wallet, cmd types.SendFromTreasuryCmd) (string, error) {
  3626  	// Look to see if the we have the private key imported.
  3627  	publicKey, err := decodeAddress(cmd.Key, w.ChainParams())
  3628  	if err != nil {
  3629  		return "", err
  3630  	}
  3631  	privKey, zero, err := w.LoadPrivateKey(ctx, publicKey)
  3632  	if err != nil {
  3633  		return "", err
  3634  	}
  3635  	defer zero()
  3636  
  3637  	_, tipHeight := w.MainChainTip(ctx)
  3638  
  3639  	// OP_RETURN <8 Bytes ValueIn><24 byte random>. The encoded ValueIn is
  3640  	// added at the end of this function.
  3641  	var payload [32]byte
  3642  	_, err = rand.Read(payload[8:])
  3643  	if err != nil {
  3644  		return "", rpcErrorf(dcrjson.ErrRPCInternal.Code,
  3645  			"sendOutputsFromTreasury Read: %v", err)
  3646  	}
  3647  	builder := txscript.NewScriptBuilder()
  3648  	builder.AddOp(txscript.OP_RETURN)
  3649  	builder.AddData(payload[:])
  3650  	opretScript, err := builder.Script()
  3651  	if err != nil {
  3652  		return "", rpcErrorf(dcrjson.ErrRPCInternal.Code,
  3653  			"sendOutputsFromTreasury NewScriptBuilder: %v", err)
  3654  	}
  3655  	msgTx := wire.NewMsgTx()
  3656  	msgTx.Version = wire.TxVersionTreasury
  3657  	msgTx.AddTxOut(wire.NewTxOut(0, opretScript))
  3658  
  3659  	// Calculate expiry.
  3660  	msgTx.Expiry = blockchain.CalcTSpendExpiry(int64(tipHeight+1),
  3661  		w.ChainParams().TreasuryVoteInterval,
  3662  		w.ChainParams().TreasuryVoteIntervalMultiplier)
  3663  
  3664  	// OP_TGEN and calculate totals.
  3665  	var totalPayout dcrutil.Amount
  3666  	for address, amount := range cmd.Amounts {
  3667  		amt, err := dcrutil.NewAmount(amount)
  3668  		if err != nil {
  3669  			return "", rpcError(dcrjson.ErrRPCInvalidParameter, err)
  3670  		}
  3671  
  3672  		// While looping calculate total amount
  3673  		totalPayout += amt
  3674  
  3675  		// Decode address.
  3676  		addr, err := decodeStakeAddress(address, w.ChainParams())
  3677  		if err != nil {
  3678  			return "", err
  3679  		}
  3680  
  3681  		// Create OP_TGEN prefixed script.
  3682  		vers, script := addr.PayFromTreasuryScript()
  3683  
  3684  		// Make sure this is not dust.
  3685  		txOut := &wire.TxOut{
  3686  			Value:    int64(amt),
  3687  			Version:  vers,
  3688  			PkScript: script,
  3689  		}
  3690  		if txrules.IsDustOutput(txOut, w.RelayFee()) {
  3691  			return "", rpcErrorf(dcrjson.ErrRPCInvalidParameter,
  3692  				"Amount is dust: %v %v", addr, amt)
  3693  		}
  3694  
  3695  		// Add to transaction.
  3696  		msgTx.AddTxOut(txOut)
  3697  	}
  3698  
  3699  	// Calculate fee. Inputs are <signature> <compressed key> OP_TSPEND.
  3700  	estimatedFee := txsizes.EstimateSerializeSize([]int{txsizes.TSPENDInputSize},
  3701  		msgTx.TxOut, 0)
  3702  	fee := txrules.FeeForSerializeSize(w.RelayFee(), estimatedFee)
  3703  
  3704  	// Assemble TxIn.
  3705  	msgTx.AddTxIn(&wire.TxIn{
  3706  		// Stakebase transactions have no inputs, so previous outpoint
  3707  		// is zero hash and max index.
  3708  		PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{},
  3709  			wire.MaxPrevOutIndex, wire.TxTreeRegular),
  3710  		Sequence:        wire.MaxTxInSequenceNum,
  3711  		ValueIn:         int64(fee) + int64(totalPayout),
  3712  		BlockHeight:     wire.NullBlockHeight,
  3713  		BlockIndex:      wire.NullBlockIndex,
  3714  		SignatureScript: []byte{}, // Empty for now
  3715  	})
  3716  
  3717  	// Encode total amount in first 8 bytes of TxOut[0] OP_RETURN.
  3718  	binary.LittleEndian.PutUint64(msgTx.TxOut[0].PkScript[2:2+8],
  3719  		uint64(fee)+uint64(totalPayout))
  3720  
  3721  	// Calculate TSpend signature without SigHashType.
  3722  	privKeyBytes := privKey.Serialize()
  3723  	sigscript, err := sign.TSpendSignatureScript(msgTx, privKeyBytes)
  3724  	if err != nil {
  3725  		return "", err
  3726  	}
  3727  	msgTx.TxIn[0].SignatureScript = sigscript
  3728  
  3729  	_, _, err = stake.CheckTSpend(msgTx)
  3730  	if err != nil {
  3731  		return "", err
  3732  	}
  3733  
  3734  	// Send to dcrd.
  3735  	n, ok := s.walletLoader.NetworkBackend()
  3736  	if !ok {
  3737  		return "", errNoNetwork
  3738  	}
  3739  	err = n.PublishTransactions(ctx, msgTx)
  3740  	if err != nil {
  3741  		return "", err
  3742  	}
  3743  
  3744  	return msgTx.TxHash().String(), nil
  3745  }
  3746  
  3747  // treasuryPolicy returns voting policies for treasury spends by a particular
  3748  // key.  If a key is specified, that policy is returned; otherwise the policies
  3749  // for all keys are returned in an array.  If both a key and ticket hash are
  3750  // provided, the per-ticket key policy is returned.
  3751  func (s *Server) treasuryPolicy(ctx context.Context, icmd interface{}) (interface{}, error) {
  3752  	cmd := icmd.(*types.TreasuryPolicyCmd)
  3753  	w, ok := s.walletLoader.LoadedWallet()
  3754  	if !ok {
  3755  		return nil, errUnloadedWallet
  3756  	}
  3757  
  3758  	var ticketHash *chainhash.Hash
  3759  	if cmd.Ticket != nil && *cmd.Ticket != "" {
  3760  		var err error
  3761  		ticketHash, err = chainhash.NewHashFromStr(*cmd.Ticket)
  3762  		if err != nil {
  3763  			return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  3764  		}
  3765  	}
  3766  
  3767  	if cmd.Key != nil && *cmd.Key != "" {
  3768  		pikey, err := hex.DecodeString(*cmd.Key)
  3769  		if err != nil {
  3770  			return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  3771  		}
  3772  		var policy string
  3773  		switch w.TreasuryKeyPolicy(pikey, ticketHash) {
  3774  		case stake.TreasuryVoteYes:
  3775  			policy = "yes"
  3776  		case stake.TreasuryVoteNo:
  3777  			policy = "no"
  3778  		default:
  3779  			policy = "abstain"
  3780  		}
  3781  		res := &types.TreasuryPolicyResult{
  3782  			Key:    *cmd.Key,
  3783  			Policy: policy,
  3784  		}
  3785  		if cmd.Ticket != nil {
  3786  			res.Ticket = *cmd.Ticket
  3787  		}
  3788  		return res, nil
  3789  	}
  3790  
  3791  	policies := w.TreasuryKeyPolicies()
  3792  	res := make([]types.TreasuryPolicyResult, 0, len(policies))
  3793  	for i := range policies {
  3794  		var policy string
  3795  		switch policies[i].Policy {
  3796  		case stake.TreasuryVoteYes:
  3797  			policy = "yes"
  3798  		case stake.TreasuryVoteNo:
  3799  			policy = "no"
  3800  		}
  3801  		r := types.TreasuryPolicyResult{
  3802  			Key:    hex.EncodeToString(policies[i].PiKey),
  3803  			Policy: policy,
  3804  		}
  3805  		if policies[i].Ticket != nil {
  3806  			r.Ticket = policies[i].Ticket.String()
  3807  		}
  3808  		res = append(res, r)
  3809  	}
  3810  	return res, nil
  3811  }
  3812  
  3813  // setDisapprovePercent sets the wallet's disapprove percentage.
  3814  func (s *Server) setDisapprovePercent(ctx context.Context, icmd interface{}) (interface{}, error) {
  3815  	if s.activeNet.Net == wire.MainNet {
  3816  		return nil, dcrjson.ErrInvalidRequest
  3817  	}
  3818  	cmd := icmd.(*types.SetDisapprovePercentCmd)
  3819  	if cmd.Percent > 100 {
  3820  		return nil, rpcError(dcrjson.ErrRPCInvalidParameter,
  3821  			errors.New("percent must be from 0 to 100"))
  3822  	}
  3823  	w, ok := s.walletLoader.LoadedWallet()
  3824  	if !ok {
  3825  		return nil, errUnloadedWallet
  3826  	}
  3827  	w.SetDisapprovePercent(cmd.Percent)
  3828  	return nil, nil
  3829  }
  3830  
  3831  // setTreasuryPolicy saves the voting policy for treasury spends by a particular
  3832  // key, and optionally, setting the key policy used by a specific ticket.
  3833  //
  3834  // If a VSP host is configured in the application settings, the voting
  3835  // preferences will also be set with the VSP.
  3836  func (s *Server) setTreasuryPolicy(ctx context.Context, icmd interface{}) (interface{}, error) {
  3837  	cmd := icmd.(*types.SetTreasuryPolicyCmd)
  3838  	w, ok := s.walletLoader.LoadedWallet()
  3839  	if !ok {
  3840  		return nil, errUnloadedWallet
  3841  	}
  3842  
  3843  	var ticketHash *chainhash.Hash
  3844  	if cmd.Ticket != nil && *cmd.Ticket != "" {
  3845  		if len(*cmd.Ticket) != chainhash.MaxHashStringSize {
  3846  			err := fmt.Errorf("invalid ticket hash length, expected %d got %d",
  3847  				chainhash.MaxHashStringSize, len(*cmd.Ticket))
  3848  			return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  3849  		}
  3850  		var err error
  3851  		ticketHash, err = chainhash.NewHashFromStr(*cmd.Ticket)
  3852  		if err != nil {
  3853  			return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  3854  		}
  3855  	}
  3856  
  3857  	pikey, err := hex.DecodeString(cmd.Key)
  3858  	if err != nil {
  3859  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  3860  	}
  3861  	if len(pikey) != secp256k1.PubKeyBytesLenCompressed {
  3862  		err := errors.New("treasury key must be 33 bytes")
  3863  		return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  3864  	}
  3865  	var policy stake.TreasuryVoteT
  3866  	switch cmd.Policy {
  3867  	case "abstain", "invalid", "":
  3868  		policy = stake.TreasuryVoteInvalid
  3869  	case "yes":
  3870  		policy = stake.TreasuryVoteYes
  3871  	case "no":
  3872  		policy = stake.TreasuryVoteNo
  3873  	default:
  3874  		err := fmt.Errorf("unknown policy %q", cmd.Policy)
  3875  		return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  3876  	}
  3877  
  3878  	err = w.SetTreasuryKeyPolicy(ctx, pikey, policy, ticketHash)
  3879  	if err != nil {
  3880  		return nil, err
  3881  	}
  3882  
  3883  	// Update voting preferences on VSPs if required.
  3884  	policyMap := map[string]string{
  3885  		cmd.Key: cmd.Policy,
  3886  	}
  3887  	err = s.updateVSPVoteChoices(ctx, w, ticketHash, nil, nil, policyMap)
  3888  
  3889  	return nil, err
  3890  }
  3891  
  3892  // tspendPolicy returns voting policies for particular treasury spends
  3893  // transactions.  If a tspend transaction hash is specified, that policy is
  3894  // returned; otherwise the policies for all known tspends are returned in an
  3895  // array.  If both a tspend transaction hash and a ticket hash are provided,
  3896  // the per-ticket tspend policy is returned.
  3897  func (s *Server) tspendPolicy(ctx context.Context, icmd interface{}) (interface{}, error) {
  3898  	cmd := icmd.(*types.TSpendPolicyCmd)
  3899  	w, ok := s.walletLoader.LoadedWallet()
  3900  	if !ok {
  3901  		return nil, errUnloadedWallet
  3902  	}
  3903  
  3904  	var ticketHash *chainhash.Hash
  3905  	if cmd.Ticket != nil && *cmd.Ticket != "" {
  3906  		var err error
  3907  		ticketHash, err = chainhash.NewHashFromStr(*cmd.Ticket)
  3908  		if err != nil {
  3909  			return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  3910  		}
  3911  	}
  3912  
  3913  	if cmd.Hash != nil && *cmd.Hash != "" {
  3914  		hash, err := chainhash.NewHashFromStr(*cmd.Hash)
  3915  		if err != nil {
  3916  			return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  3917  		}
  3918  		var policy string
  3919  		switch w.TSpendPolicy(hash, ticketHash) {
  3920  		case stake.TreasuryVoteYes:
  3921  			policy = "yes"
  3922  		case stake.TreasuryVoteNo:
  3923  			policy = "no"
  3924  		default:
  3925  			policy = "abstain"
  3926  		}
  3927  		res := &types.TSpendPolicyResult{
  3928  			Hash:   *cmd.Hash,
  3929  			Policy: policy,
  3930  		}
  3931  		if cmd.Ticket != nil {
  3932  			res.Ticket = *cmd.Ticket
  3933  		}
  3934  		return res, nil
  3935  	}
  3936  
  3937  	tspends := w.GetAllTSpends(ctx)
  3938  	res := make([]types.TSpendPolicyResult, 0, len(tspends))
  3939  	for i := range tspends {
  3940  		tspendHash := tspends[i].TxHash()
  3941  		p := w.TSpendPolicy(&tspendHash, ticketHash)
  3942  
  3943  		var policy string
  3944  		switch p {
  3945  		case stake.TreasuryVoteYes:
  3946  			policy = "yes"
  3947  		case stake.TreasuryVoteNo:
  3948  			policy = "no"
  3949  		}
  3950  		r := types.TSpendPolicyResult{
  3951  			Hash:   tspendHash.String(),
  3952  			Policy: policy,
  3953  		}
  3954  		if cmd.Ticket != nil {
  3955  			r.Ticket = *cmd.Ticket
  3956  		}
  3957  		res = append(res, r)
  3958  	}
  3959  	return res, nil
  3960  }
  3961  
  3962  // setTSpendPolicy saves the voting policy for a particular tspend transaction
  3963  // hash, and optionally, setting the tspend policy used by a specific ticket.
  3964  //
  3965  // If a VSP host is configured in the application settings, the voting
  3966  // preferences will also be set with the VSP.
  3967  func (s *Server) setTSpendPolicy(ctx context.Context, icmd interface{}) (interface{}, error) {
  3968  	cmd := icmd.(*types.SetTSpendPolicyCmd)
  3969  	w, ok := s.walletLoader.LoadedWallet()
  3970  	if !ok {
  3971  		return nil, errUnloadedWallet
  3972  	}
  3973  
  3974  	if len(cmd.Hash) != chainhash.MaxHashStringSize {
  3975  		err := fmt.Errorf("invalid tspend hash length, expected %d got %d",
  3976  			chainhash.MaxHashStringSize, len(cmd.Hash))
  3977  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  3978  	}
  3979  
  3980  	hash, err := chainhash.NewHashFromStr(cmd.Hash)
  3981  	if err != nil {
  3982  		return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  3983  	}
  3984  
  3985  	var ticketHash *chainhash.Hash
  3986  	if cmd.Ticket != nil && *cmd.Ticket != "" {
  3987  		if len(*cmd.Ticket) != chainhash.MaxHashStringSize {
  3988  			err := fmt.Errorf("invalid ticket hash length, expected %d got %d",
  3989  				chainhash.MaxHashStringSize, len(*cmd.Ticket))
  3990  			return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  3991  		}
  3992  		var err error
  3993  		ticketHash, err = chainhash.NewHashFromStr(*cmd.Ticket)
  3994  		if err != nil {
  3995  			return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  3996  		}
  3997  	}
  3998  
  3999  	var policy stake.TreasuryVoteT
  4000  	switch cmd.Policy {
  4001  	case "abstain", "invalid", "":
  4002  		policy = stake.TreasuryVoteInvalid
  4003  	case "yes":
  4004  		policy = stake.TreasuryVoteYes
  4005  	case "no":
  4006  		policy = stake.TreasuryVoteNo
  4007  	default:
  4008  		err := fmt.Errorf("unknown policy %q", cmd.Policy)
  4009  		return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  4010  	}
  4011  
  4012  	err = w.SetTSpendPolicy(ctx, hash, policy, ticketHash)
  4013  	if err != nil {
  4014  		return nil, err
  4015  	}
  4016  
  4017  	// Update voting preferences on VSPs if required.
  4018  	policyMap := map[string]string{
  4019  		cmd.Hash: cmd.Policy,
  4020  	}
  4021  	err = s.updateVSPVoteChoices(ctx, w, ticketHash, nil, policyMap, nil)
  4022  	return nil, err
  4023  }
  4024  
  4025  // redeemMultiSigOut receives a transaction hash/idx and fetches the first output
  4026  // index or indices with known script hashes from the transaction. It then
  4027  // construct a transaction with a single P2PKH paying to a specified address.
  4028  // It signs any inputs that it can, then provides the raw transaction to
  4029  // the user to export to others to sign.
  4030  func (s *Server) redeemMultiSigOut(ctx context.Context, icmd interface{}) (interface{}, error) {
  4031  	cmd := icmd.(*types.RedeemMultiSigOutCmd)
  4032  	w, ok := s.walletLoader.LoadedWallet()
  4033  	if !ok {
  4034  		return nil, errUnloadedWallet
  4035  	}
  4036  
  4037  	// Convert the address to a useable format. If
  4038  	// we have no address, create a new address in
  4039  	// this wallet to send the output to.
  4040  	var addr stdaddr.Address
  4041  	var err error
  4042  	if cmd.Address != nil {
  4043  		addr, err = decodeAddress(*cmd.Address, w.ChainParams())
  4044  		if err != nil {
  4045  			return nil, err
  4046  		}
  4047  	} else {
  4048  		account := uint32(udb.DefaultAccountNum)
  4049  		addr, err = w.NewInternalAddress(ctx, account, wallet.WithGapPolicyWrap())
  4050  		if err != nil {
  4051  			return nil, err
  4052  		}
  4053  	}
  4054  
  4055  	// Lookup the multisignature output and get the amount
  4056  	// along with the script for that transaction. Then,
  4057  	// begin crafting a MsgTx.
  4058  	hash, err := chainhash.NewHashFromStr(cmd.Hash)
  4059  	if err != nil {
  4060  		return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  4061  	}
  4062  	op := wire.OutPoint{
  4063  		Hash:  *hash,
  4064  		Index: cmd.Index,
  4065  		Tree:  cmd.Tree,
  4066  	}
  4067  	p2shOutput, err := w.FetchP2SHMultiSigOutput(ctx, &op)
  4068  	if err != nil {
  4069  		return nil, err
  4070  	}
  4071  	sc := stdscript.DetermineScriptType(scriptVersionAssumed, p2shOutput.RedeemScript)
  4072  	if sc != stdscript.STMultiSig {
  4073  		return nil, errors.E("P2SH redeem script is not multisig")
  4074  	}
  4075  	msgTx := wire.NewMsgTx()
  4076  	txIn := wire.NewTxIn(&op, int64(p2shOutput.OutputAmount), nil)
  4077  	msgTx.AddTxIn(txIn)
  4078  
  4079  	_, pkScript := addr.PaymentScript()
  4080  
  4081  	err = w.PrepareRedeemMultiSigOutTxOutput(msgTx, p2shOutput, &pkScript)
  4082  	if err != nil {
  4083  		return nil, err
  4084  	}
  4085  
  4086  	// Start creating the SignRawTransactionCmd.
  4087  	_, outpointScript := p2shOutput.P2SHAddress.PaymentScript()
  4088  	outpointScriptStr := hex.EncodeToString(outpointScript)
  4089  
  4090  	rti := types.RawTxInput{
  4091  		Txid:         cmd.Hash,
  4092  		Vout:         cmd.Index,
  4093  		Tree:         cmd.Tree,
  4094  		ScriptPubKey: outpointScriptStr,
  4095  		RedeemScript: "",
  4096  	}
  4097  	rtis := []types.RawTxInput{rti}
  4098  
  4099  	var b strings.Builder
  4100  	b.Grow(2 * msgTx.SerializeSize())
  4101  	err = msgTx.Serialize(hex.NewEncoder(&b))
  4102  	if err != nil {
  4103  		return nil, err
  4104  	}
  4105  	sigHashAll := "ALL"
  4106  
  4107  	srtc := &types.SignRawTransactionCmd{
  4108  		RawTx:    b.String(),
  4109  		Inputs:   &rtis,
  4110  		PrivKeys: &[]string{},
  4111  		Flags:    &sigHashAll,
  4112  	}
  4113  
  4114  	// Sign it and give the results to the user.
  4115  	signedTxResult, err := s.signRawTransaction(ctx, srtc)
  4116  	if signedTxResult == nil || err != nil {
  4117  		return nil, err
  4118  	}
  4119  	srtTyped := signedTxResult.(types.SignRawTransactionResult)
  4120  	return types.RedeemMultiSigOutResult(srtTyped), nil
  4121  }
  4122  
  4123  // redeemMultisigOuts receives a script hash (in the form of a
  4124  // script hash address), looks up all the unspent outpoints associated
  4125  // with that address, then generates a list of partially signed
  4126  // transactions spending to either an address specified or internal
  4127  // addresses in this wallet.
  4128  func (s *Server) redeemMultiSigOuts(ctx context.Context, icmd interface{}) (interface{}, error) {
  4129  	cmd := icmd.(*types.RedeemMultiSigOutsCmd)
  4130  	w, ok := s.walletLoader.LoadedWallet()
  4131  	if !ok {
  4132  		return nil, errUnloadedWallet
  4133  	}
  4134  
  4135  	// Get all the multisignature outpoints that are unspent for this
  4136  	// address.
  4137  	addr, err := decodeAddress(cmd.FromScrAddress, w.ChainParams())
  4138  	if err != nil {
  4139  		return nil, err
  4140  	}
  4141  	p2shAddr, ok := addr.(*stdaddr.AddressScriptHashV0)
  4142  	if !ok {
  4143  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "address is not P2SH")
  4144  	}
  4145  	msos, err := wallet.UnstableAPI(w).UnspentMultisigCreditsForAddress(ctx, p2shAddr)
  4146  	if err != nil {
  4147  		return nil, err
  4148  	}
  4149  	max := uint32(0xffffffff)
  4150  	if cmd.Number != nil {
  4151  		max = uint32(*cmd.Number)
  4152  	}
  4153  
  4154  	itr := uint32(0)
  4155  	rmsoResults := make([]types.RedeemMultiSigOutResult, len(msos))
  4156  	for i, mso := range msos {
  4157  		if itr > max {
  4158  			break
  4159  		}
  4160  
  4161  		rmsoRequest := &types.RedeemMultiSigOutCmd{
  4162  			Hash:    mso.OutPoint.Hash.String(),
  4163  			Index:   mso.OutPoint.Index,
  4164  			Tree:    mso.OutPoint.Tree,
  4165  			Address: cmd.ToAddress,
  4166  		}
  4167  		redeemResult, err := s.redeemMultiSigOut(ctx, rmsoRequest)
  4168  		if err != nil {
  4169  			return nil, err
  4170  		}
  4171  		redeemResultTyped := redeemResult.(types.RedeemMultiSigOutResult)
  4172  		rmsoResults[i] = redeemResultTyped
  4173  
  4174  		itr++
  4175  	}
  4176  
  4177  	return types.RedeemMultiSigOutsResult{Results: rmsoResults}, nil
  4178  }
  4179  
  4180  // rescanWallet initiates a rescan of the block chain for wallet data, blocking
  4181  // until the rescan completes or exits with an error.
  4182  func (s *Server) rescanWallet(ctx context.Context, icmd interface{}) (interface{}, error) {
  4183  	cmd := icmd.(*types.RescanWalletCmd)
  4184  	w, ok := s.walletLoader.LoadedWallet()
  4185  	if !ok {
  4186  		return nil, errUnloadedWallet
  4187  	}
  4188  
  4189  	n, ok := s.walletLoader.NetworkBackend()
  4190  	if !ok {
  4191  		return nil, errNoNetwork
  4192  	}
  4193  
  4194  	err := w.RescanFromHeight(ctx, n, int32(*cmd.BeginHeight))
  4195  	return nil, err
  4196  }
  4197  
  4198  // stakePoolUserInfo returns the ticket information for a given user from the
  4199  // stake pool.
  4200  func (s *Server) stakePoolUserInfo(ctx context.Context, icmd interface{}) (interface{}, error) {
  4201  	cmd := icmd.(*types.StakePoolUserInfoCmd)
  4202  	w, ok := s.walletLoader.LoadedWallet()
  4203  	if !ok {
  4204  		return nil, errUnloadedWallet
  4205  	}
  4206  
  4207  	userAddr, err := decodeStakeAddress(cmd.User, w.ChainParams())
  4208  	if err != nil {
  4209  		return nil, err
  4210  	}
  4211  	spui, err := w.StakePoolUserInfo(ctx, userAddr)
  4212  	if err != nil {
  4213  		return nil, err
  4214  	}
  4215  
  4216  	resp := new(types.StakePoolUserInfoResult)
  4217  	resp.Tickets = make([]types.PoolUserTicket, 0, len(spui.Tickets))
  4218  	resp.InvalidTickets = make([]string, 0, len(spui.InvalidTickets))
  4219  	_, height := w.MainChainTip(ctx)
  4220  	for _, ticket := range spui.Tickets {
  4221  		var ticketRes types.PoolUserTicket
  4222  
  4223  		status := ""
  4224  		switch ticket.Status {
  4225  		case udb.TSImmatureOrLive:
  4226  			maturedHeight := int32(ticket.HeightTicket + uint32(w.ChainParams().TicketMaturity) + 1)
  4227  
  4228  			if height >= maturedHeight {
  4229  				status = "live"
  4230  			} else {
  4231  				status = "immature"
  4232  			}
  4233  		case udb.TSVoted:
  4234  			status = "voted"
  4235  		case udb.TSMissed:
  4236  			status = "missed"
  4237  			if ticket.HeightSpent-ticket.HeightTicket >= w.ChainParams().TicketExpiry {
  4238  				status = "expired"
  4239  			}
  4240  		}
  4241  		ticketRes.Status = status
  4242  
  4243  		ticketRes.Ticket = ticket.Ticket.String()
  4244  		ticketRes.TicketHeight = ticket.HeightTicket
  4245  		ticketRes.SpentBy = ticket.SpentBy.String()
  4246  		ticketRes.SpentByHeight = ticket.HeightSpent
  4247  
  4248  		resp.Tickets = append(resp.Tickets, ticketRes)
  4249  	}
  4250  	for _, invalid := range spui.InvalidTickets {
  4251  		invalidTicket := invalid.String()
  4252  
  4253  		resp.InvalidTickets = append(resp.InvalidTickets, invalidTicket)
  4254  	}
  4255  
  4256  	return resp, nil
  4257  }
  4258  
  4259  func (s *Server) ticketInfo(ctx context.Context, icmd interface{}) (interface{}, error) {
  4260  	cmd := icmd.(*types.TicketInfoCmd)
  4261  	w, ok := s.walletLoader.LoadedWallet()
  4262  	if !ok {
  4263  		return nil, errUnloadedWallet
  4264  	}
  4265  
  4266  	res := make([]types.TicketInfoResult, 0)
  4267  
  4268  	start := wallet.NewBlockIdentifierFromHeight(*cmd.StartHeight)
  4269  	end := wallet.NewBlockIdentifierFromHeight(-1)
  4270  	tmptx := new(wire.MsgTx)
  4271  	err := w.GetTickets(ctx, func(ts []*wallet.TicketSummary, h *wire.BlockHeader) (bool, error) {
  4272  		for _, t := range ts {
  4273  			status := t.Status
  4274  			if status == wallet.TicketStatusUnmined {
  4275  				// Standardize on immature.  An unmined ticket
  4276  				// can be determined by the block height field
  4277  				// and the lack of a block hash.
  4278  				status = wallet.TicketStatusImmature
  4279  			}
  4280  			err := tmptx.Deserialize(bytes.NewReader(t.Ticket.Transaction))
  4281  			if err != nil {
  4282  				return false, err
  4283  			}
  4284  			out := tmptx.TxOut[0]
  4285  			info := types.TicketInfoResult{
  4286  				Hash:        t.Ticket.Hash.String(),
  4287  				Cost:        dcrutil.Amount(out.Value).ToCoin(),
  4288  				BlockHeight: -1,
  4289  				Status:      status.String(),
  4290  			}
  4291  
  4292  			_, addrs := stdscript.ExtractAddrs(out.Version, out.PkScript, w.ChainParams())
  4293  			if len(addrs) == 0 {
  4294  				return false, errors.New("unable to decode ticket pkScript")
  4295  			}
  4296  			info.VotingAddress = addrs[0].String()
  4297  			if h != nil {
  4298  				info.BlockHash = h.BlockHash().String()
  4299  				info.BlockHeight = int32(h.Height)
  4300  			}
  4301  			if t.Spender != nil {
  4302  				hash := t.Spender.Hash.String()
  4303  				if t.Spender.Type == wallet.TransactionTypeRevocation {
  4304  					info.Revocation = hash
  4305  				} else {
  4306  					info.Vote = hash
  4307  				}
  4308  			}
  4309  
  4310  			choices, _, err := w.AgendaChoices(ctx, t.Ticket.Hash)
  4311  			if err != nil {
  4312  				return false, err
  4313  			}
  4314  			info.Choices = make([]types.VoteChoice, len(choices))
  4315  			for i := range choices {
  4316  				info.Choices[i].AgendaID = choices[i].AgendaID
  4317  				info.Choices[i].ChoiceID = choices[i].ChoiceID
  4318  			}
  4319  
  4320  			host, err := w.VSPHostForTicket(ctx, t.Ticket.Hash)
  4321  			if err != nil && !errors.Is(err, errors.NotExist) {
  4322  				return false, err
  4323  			}
  4324  			info.VSPHost = host
  4325  
  4326  			res = append(res, info)
  4327  		}
  4328  		return false, nil
  4329  	}, start, end)
  4330  
  4331  	return res, err
  4332  }
  4333  
  4334  // ticketsForAddress retrieves all ticket hashes that have the passed voting
  4335  // address. It will only return tickets that are in the mempool or blockchain,
  4336  // and should not return pruned tickets.
  4337  func (s *Server) ticketsForAddress(ctx context.Context, icmd interface{}) (interface{}, error) {
  4338  	cmd := icmd.(*types.TicketsForAddressCmd)
  4339  	w, ok := s.walletLoader.LoadedWallet()
  4340  	if !ok {
  4341  		return nil, errUnloadedWallet
  4342  	}
  4343  
  4344  	addr, err := stdaddr.DecodeAddress(cmd.Address, w.ChainParams())
  4345  	if err != nil {
  4346  		return nil, err
  4347  	}
  4348  
  4349  	ticketHashes, err := w.TicketHashesForVotingAddress(ctx, addr)
  4350  	if err != nil {
  4351  		return nil, err
  4352  	}
  4353  
  4354  	ticketHashStrs := make([]string, 0, len(ticketHashes))
  4355  	for _, hash := range ticketHashes {
  4356  		ticketHashStrs = append(ticketHashStrs, hash.String())
  4357  	}
  4358  
  4359  	return dcrdtypes.TicketsForAddressResult{Tickets: ticketHashStrs}, nil
  4360  }
  4361  
  4362  func isNilOrEmpty(s *string) bool {
  4363  	return s == nil || *s == ""
  4364  }
  4365  
  4366  // sendFrom handles a sendfrom RPC request by creating a new transaction
  4367  // spending unspent transaction outputs for a wallet to another payment
  4368  // address.  Leftover inputs not sent to the payment address or a fee for
  4369  // the miner are sent back to a new address in the wallet.  Upon success,
  4370  // the TxID for the created transaction is returned.
  4371  func (s *Server) sendFrom(ctx context.Context, icmd interface{}) (interface{}, error) {
  4372  	cmd := icmd.(*types.SendFromCmd)
  4373  	w, ok := s.walletLoader.LoadedWallet()
  4374  	if !ok {
  4375  		return nil, errUnloadedWallet
  4376  	}
  4377  
  4378  	// Transaction comments are not yet supported.  Error instead of
  4379  	// pretending to save them.
  4380  	if !isNilOrEmpty(cmd.Comment) || !isNilOrEmpty(cmd.CommentTo) {
  4381  		return nil, rpcErrorf(dcrjson.ErrRPCUnimplemented, "transaction comments are unsupported")
  4382  	}
  4383  
  4384  	account, err := w.AccountNumber(ctx, cmd.FromAccount)
  4385  	if err != nil {
  4386  		return nil, err
  4387  	}
  4388  
  4389  	// Check that signed integer parameters are positive.
  4390  	if cmd.Amount < 0 {
  4391  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "negative amount")
  4392  	}
  4393  	minConf := int32(*cmd.MinConf)
  4394  	if minConf < 0 {
  4395  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "negative minconf")
  4396  	}
  4397  	// Create map of address and amount pairs.
  4398  	amt, err := dcrutil.NewAmount(cmd.Amount)
  4399  	if err != nil {
  4400  		return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  4401  	}
  4402  	pairs := map[string]dcrutil.Amount{
  4403  		cmd.ToAddress: amt,
  4404  	}
  4405  
  4406  	return s.sendPairs(ctx, w, pairs, account, minConf)
  4407  }
  4408  
  4409  // sendMany handles a sendmany RPC request by creating a new transaction
  4410  // spending unspent transaction outputs for a wallet to any number of
  4411  // payment addresses.  Leftover inputs not sent to the payment address
  4412  // or a fee for the miner are sent back to a new address in the wallet.
  4413  // Upon success, the TxID for the created transaction is returned.
  4414  func (s *Server) sendMany(ctx context.Context, icmd interface{}) (interface{}, error) {
  4415  	cmd := icmd.(*types.SendManyCmd)
  4416  	w, ok := s.walletLoader.LoadedWallet()
  4417  	if !ok {
  4418  		return nil, errUnloadedWallet
  4419  	}
  4420  
  4421  	// Transaction comments are not yet supported.  Error instead of
  4422  	// pretending to save them.
  4423  	if !isNilOrEmpty(cmd.Comment) {
  4424  		return nil, rpcErrorf(dcrjson.ErrRPCUnimplemented, "transaction comments are unsupported")
  4425  	}
  4426  
  4427  	account, err := w.AccountNumber(ctx, cmd.FromAccount)
  4428  	if err != nil {
  4429  		return nil, err
  4430  	}
  4431  
  4432  	// Check that minconf is positive.
  4433  	minConf := int32(*cmd.MinConf)
  4434  	if minConf < 0 {
  4435  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "negative minconf")
  4436  	}
  4437  
  4438  	// Recreate address/amount pairs, using dcrutil.Amount.
  4439  	pairs := make(map[string]dcrutil.Amount, len(cmd.Amounts))
  4440  	for k, v := range cmd.Amounts {
  4441  		amt, err := dcrutil.NewAmount(v)
  4442  		if err != nil {
  4443  			return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  4444  		}
  4445  		pairs[k] = amt
  4446  	}
  4447  
  4448  	return s.sendPairs(ctx, w, pairs, account, minConf)
  4449  }
  4450  
  4451  // sendToAddress handles a sendtoaddress RPC request by creating a new
  4452  // transaction spending unspent transaction outputs for a wallet to another
  4453  // payment address.  Leftover inputs not sent to the payment address or a fee
  4454  // for the miner are sent back to a new address in the wallet.  Upon success,
  4455  // the TxID for the created transaction is returned.
  4456  func (s *Server) sendToAddress(ctx context.Context, icmd interface{}) (interface{}, error) {
  4457  	cmd := icmd.(*types.SendToAddressCmd)
  4458  	w, ok := s.walletLoader.LoadedWallet()
  4459  	if !ok {
  4460  		return nil, errUnloadedWallet
  4461  	}
  4462  
  4463  	// Transaction comments are not yet supported.  Error instead of
  4464  	// pretending to save them.
  4465  	if !isNilOrEmpty(cmd.Comment) || !isNilOrEmpty(cmd.CommentTo) {
  4466  		return nil, rpcErrorf(dcrjson.ErrRPCUnimplemented, "transaction comments are unsupported")
  4467  	}
  4468  
  4469  	amt, err := dcrutil.NewAmount(cmd.Amount)
  4470  	if err != nil {
  4471  		return nil, err
  4472  	}
  4473  
  4474  	// Check that signed integer parameters are positive.
  4475  	if amt < 0 {
  4476  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "negative amount")
  4477  	}
  4478  
  4479  	// Mock up map of address and amount pairs.
  4480  	pairs := map[string]dcrutil.Amount{
  4481  		cmd.Address: amt,
  4482  	}
  4483  
  4484  	// sendtoaddress always spends from the default account, this matches bitcoind
  4485  	return s.sendPairs(ctx, w, pairs, udb.DefaultAccountNum, 1)
  4486  }
  4487  
  4488  // sendToMultiSig handles a sendtomultisig RPC request by creating a new
  4489  // transaction spending amount many funds to an output containing a multi-
  4490  // signature script hash. The function will fail if there isn't at least one
  4491  // public key in the public key list that corresponds to one that is owned
  4492  // locally.
  4493  // Upon successfully sending the transaction to the daemon, the script hash
  4494  // is stored in the transaction manager and the corresponding address
  4495  // specified to be watched by the daemon.
  4496  // The function returns a tx hash, P2SH address, and a multisig script if
  4497  // successful.
  4498  // TODO Use with non-default accounts as well
  4499  func (s *Server) sendToMultiSig(ctx context.Context, icmd interface{}) (interface{}, error) {
  4500  	cmd := icmd.(*types.SendToMultiSigCmd)
  4501  	w, ok := s.walletLoader.LoadedWallet()
  4502  	if !ok {
  4503  		return nil, errUnloadedWallet
  4504  	}
  4505  
  4506  	account := uint32(udb.DefaultAccountNum)
  4507  	amount, err := dcrutil.NewAmount(cmd.Amount)
  4508  	if err != nil {
  4509  		return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  4510  	}
  4511  	nrequired := int8(*cmd.NRequired)
  4512  	minconf := int32(*cmd.MinConf)
  4513  
  4514  	pubKeys, err := walletPubKeys(ctx, w, cmd.Pubkeys)
  4515  	if err != nil {
  4516  		return nil, err
  4517  	}
  4518  
  4519  	tx, addr, script, err :=
  4520  		w.CreateMultisigTx(ctx, account, amount, pubKeys, nrequired, minconf)
  4521  	if err != nil {
  4522  		return nil, err
  4523  	}
  4524  
  4525  	result := &types.SendToMultiSigResult{
  4526  		TxHash:       tx.MsgTx.TxHash().String(),
  4527  		Address:      addr.String(),
  4528  		RedeemScript: hex.EncodeToString(script),
  4529  	}
  4530  
  4531  	log.Infof("Successfully sent funds to multisignature output in "+
  4532  		"transaction %v", tx.MsgTx.TxHash().String())
  4533  
  4534  	return result, nil
  4535  }
  4536  
  4537  // sendRawTransaction handles a sendrawtransaction RPC request by decoding hex
  4538  // transaction and sending it to the network backend for propagation.
  4539  func (s *Server) sendRawTransaction(ctx context.Context, icmd interface{}) (interface{}, error) {
  4540  	cmd := icmd.(*types.SendRawTransactionCmd)
  4541  	w, ok := s.walletLoader.LoadedWallet()
  4542  	if !ok {
  4543  		return nil, errUnloadedWallet
  4544  	}
  4545  
  4546  	n, err := w.NetworkBackend()
  4547  	if err != nil {
  4548  		return nil, err
  4549  	}
  4550  
  4551  	msgtx := wire.NewMsgTx()
  4552  	err = msgtx.Deserialize(hex.NewDecoder(strings.NewReader(cmd.HexTx)))
  4553  	if err != nil {
  4554  		return nil, rpcError(dcrjson.ErrRPCDeserialization, err)
  4555  	}
  4556  
  4557  	if !*cmd.AllowHighFees {
  4558  		highFees, err := txrules.TxPaysHighFees(msgtx)
  4559  		if err != nil {
  4560  			return nil, err
  4561  		}
  4562  		if highFees {
  4563  			return nil, errors.E(errors.Policy, "high fees")
  4564  		}
  4565  	}
  4566  
  4567  	txHash, err := w.PublishTransaction(ctx, msgtx, n)
  4568  	if err != nil {
  4569  		return nil, err
  4570  	}
  4571  
  4572  	return txHash.String(), nil
  4573  }
  4574  
  4575  // sendToTreasury handles a sendtotreasury RPC request by creating a new
  4576  // transaction spending unspent transaction outputs for a wallet to the
  4577  // treasury.  Leftover inputs not sent to the payment address or a fee for the
  4578  // miner are sent back to a new address in the wallet.  Upon success, the TxID
  4579  // for the created transaction is returned.
  4580  func (s *Server) sendToTreasury(ctx context.Context, icmd interface{}) (interface{}, error) {
  4581  	cmd := icmd.(*types.SendToTreasuryCmd)
  4582  	w, ok := s.walletLoader.LoadedWallet()
  4583  	if !ok {
  4584  		return nil, errUnloadedWallet
  4585  	}
  4586  
  4587  	amt, err := dcrutil.NewAmount(cmd.Amount)
  4588  	if err != nil {
  4589  		return nil, err
  4590  	}
  4591  
  4592  	// Check that signed integer parameters are positive.
  4593  	if amt <= 0 {
  4594  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "negative amount")
  4595  	}
  4596  
  4597  	// sendtotreasury always spends from the default account.
  4598  	return s.sendAmountToTreasury(ctx, w, amt, udb.DefaultAccountNum, 1)
  4599  }
  4600  
  4601  // transaction spending treasury balance.
  4602  // Upon success, the TxID for the created transaction is returned.
  4603  func (s *Server) sendFromTreasury(ctx context.Context, icmd interface{}) (interface{}, error) {
  4604  	cmd := icmd.(*types.SendFromTreasuryCmd)
  4605  	w, ok := s.walletLoader.LoadedWallet()
  4606  	if !ok {
  4607  		return nil, errUnloadedWallet
  4608  	}
  4609  
  4610  	return s.sendOutputsFromTreasury(ctx, w, *cmd)
  4611  }
  4612  
  4613  // setTxFee sets the transaction fee per kilobyte added to transactions.
  4614  func (s *Server) setTxFee(ctx context.Context, icmd interface{}) (interface{}, error) {
  4615  	cmd := icmd.(*types.SetTxFeeCmd)
  4616  	w, ok := s.walletLoader.LoadedWallet()
  4617  	if !ok {
  4618  		return nil, errUnloadedWallet
  4619  	}
  4620  
  4621  	// Check that amount is not negative.
  4622  	if cmd.Amount < 0 {
  4623  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "negative amount")
  4624  	}
  4625  
  4626  	relayFee, err := dcrutil.NewAmount(cmd.Amount)
  4627  	if err != nil {
  4628  		return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  4629  	}
  4630  	w.SetRelayFee(relayFee)
  4631  
  4632  	// A boolean true result is returned upon success.
  4633  	return true, nil
  4634  }
  4635  
  4636  // setVoteChoice handles a setvotechoice request by modifying the preferred
  4637  // choice for a voting agenda.
  4638  //
  4639  // If a VSP host is configured in the application settings, the voting
  4640  // preferences will also be set with the VSP.
  4641  func (s *Server) setVoteChoice(ctx context.Context, icmd interface{}) (interface{}, error) {
  4642  	cmd := icmd.(*types.SetVoteChoiceCmd)
  4643  	w, ok := s.walletLoader.LoadedWallet()
  4644  	if !ok {
  4645  		return nil, errUnloadedWallet
  4646  	}
  4647  
  4648  	var ticketHash *chainhash.Hash
  4649  	if cmd.TicketHash != nil {
  4650  		hash, err := chainhash.NewHashFromStr(*cmd.TicketHash)
  4651  		if err != nil {
  4652  			return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  4653  		}
  4654  		ticketHash = hash
  4655  	}
  4656  
  4657  	choice := wallet.AgendaChoices{
  4658  		{
  4659  			AgendaID: cmd.AgendaID,
  4660  			ChoiceID: cmd.ChoiceID,
  4661  		},
  4662  	}
  4663  	_, err := w.SetAgendaChoices(ctx, ticketHash, choice...)
  4664  	if err != nil {
  4665  		return nil, err
  4666  	}
  4667  
  4668  	// Update voting preferences on VSPs if required.
  4669  	err = s.updateVSPVoteChoices(ctx, w, ticketHash, choice, nil, nil)
  4670  	return nil, err
  4671  }
  4672  
  4673  func (s *Server) updateVSPVoteChoices(ctx context.Context, w *wallet.Wallet, ticketHash *chainhash.Hash,
  4674  	choices wallet.AgendaChoices, tspendPolicy map[string]string, treasuryPolicy map[string]string) error {
  4675  
  4676  	if ticketHash != nil {
  4677  		vspHost, err := w.VSPHostForTicket(ctx, ticketHash)
  4678  		if err != nil {
  4679  			if errors.Is(err, errors.NotExist) {
  4680  				// Ticket is not registered with a VSP, nothing more to do here.
  4681  				return nil
  4682  			}
  4683  			return err
  4684  		}
  4685  		vspClient, err := loader.LookupVSP(vspHost)
  4686  		if err != nil {
  4687  			return err
  4688  		}
  4689  		err = vspClient.SetVoteChoice(ctx, ticketHash, choices.Map(), tspendPolicy, treasuryPolicy)
  4690  		return err
  4691  	}
  4692  	var firstErr error
  4693  	err := w.ForUnspentUnexpiredTickets(ctx, func(hash *chainhash.Hash) error {
  4694  		vspHost, err := w.VSPHostForTicket(ctx, hash)
  4695  		if err != nil && firstErr == nil {
  4696  			if errors.Is(err, errors.NotExist) {
  4697  				// Ticket is not registered with a VSP, nothing more to do here.
  4698  				return nil
  4699  			}
  4700  			firstErr = err
  4701  			return nil
  4702  		}
  4703  		vspClient, err := loader.LookupVSP(vspHost)
  4704  		if err != nil && firstErr == nil {
  4705  			firstErr = err
  4706  			return nil
  4707  		}
  4708  		// Never return errors here, so all tickets are tried.
  4709  		// The first error will be returned to the user.
  4710  		err = vspClient.SetVoteChoice(ctx, hash, choices.Map(), tspendPolicy, treasuryPolicy)
  4711  		if err != nil && firstErr == nil {
  4712  			firstErr = err
  4713  		}
  4714  		return nil
  4715  	})
  4716  	if err != nil {
  4717  		return err
  4718  	}
  4719  	return firstErr
  4720  }
  4721  
  4722  // signMessage signs the given message with the private key for the given
  4723  // address
  4724  func (s *Server) signMessage(ctx context.Context, icmd interface{}) (interface{}, error) {
  4725  	cmd := icmd.(*types.SignMessageCmd)
  4726  	w, ok := s.walletLoader.LoadedWallet()
  4727  	if !ok {
  4728  		return nil, errUnloadedWallet
  4729  	}
  4730  
  4731  	addr, err := decodeAddress(cmd.Address, w.ChainParams())
  4732  	if err != nil {
  4733  		return nil, err
  4734  	}
  4735  	sig, err := w.SignMessage(ctx, cmd.Message, addr)
  4736  	if err != nil {
  4737  		if errors.Is(err, errors.NotExist) {
  4738  			return nil, errAddressNotInWallet
  4739  		}
  4740  		if errors.Is(err, errors.Locked) {
  4741  			return nil, errWalletUnlockNeeded
  4742  		}
  4743  		return nil, err
  4744  	}
  4745  	return base64.StdEncoding.EncodeToString(sig), nil
  4746  }
  4747  
  4748  // signRawTransaction handles the signrawtransaction command.
  4749  //
  4750  // chainClient may be nil, in which case it was called by the NoChainRPC
  4751  // variant.  It must be checked before all usage.
  4752  func (s *Server) signRawTransaction(ctx context.Context, icmd interface{}) (interface{}, error) {
  4753  	cmd := icmd.(*types.SignRawTransactionCmd)
  4754  	w, ok := s.walletLoader.LoadedWallet()
  4755  	if !ok {
  4756  		return nil, errUnloadedWallet
  4757  	}
  4758  
  4759  	tx := wire.NewMsgTx()
  4760  	err := tx.Deserialize(hex.NewDecoder(strings.NewReader(cmd.RawTx)))
  4761  	if err != nil {
  4762  		return nil, rpcError(dcrjson.ErrRPCDeserialization, err)
  4763  	}
  4764  	if len(tx.TxIn) == 0 {
  4765  		err := errors.New("transaction with no inputs cannot be signed")
  4766  		return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  4767  	}
  4768  
  4769  	var hashType txscript.SigHashType
  4770  	switch *cmd.Flags {
  4771  	case "ALL":
  4772  		hashType = txscript.SigHashAll
  4773  	case "NONE":
  4774  		hashType = txscript.SigHashNone
  4775  	case "SINGLE":
  4776  		hashType = txscript.SigHashSingle
  4777  	case "ALL|ANYONECANPAY":
  4778  		hashType = txscript.SigHashAll | txscript.SigHashAnyOneCanPay
  4779  	case "NONE|ANYONECANPAY":
  4780  		hashType = txscript.SigHashNone | txscript.SigHashAnyOneCanPay
  4781  	case "SINGLE|ANYONECANPAY":
  4782  		hashType = txscript.SigHashSingle | txscript.SigHashAnyOneCanPay
  4783  	case "ssgen": // Special case of SigHashAll
  4784  		hashType = txscript.SigHashAll
  4785  	case "ssrtx": // Special case of SigHashAll
  4786  		hashType = txscript.SigHashAll
  4787  	default:
  4788  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "invalid sighash flag")
  4789  	}
  4790  
  4791  	// TODO: really we probably should look these up with dcrd anyway to
  4792  	// make sure that they match the blockchain if present.
  4793  	inputs := make(map[wire.OutPoint][]byte)
  4794  	scripts := make(map[string][]byte)
  4795  	var cmdInputs []types.RawTxInput
  4796  	if cmd.Inputs != nil {
  4797  		cmdInputs = *cmd.Inputs
  4798  	}
  4799  	for _, rti := range cmdInputs {
  4800  		inputSha, err := chainhash.NewHashFromStr(rti.Txid)
  4801  		if err != nil {
  4802  			return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  4803  		}
  4804  
  4805  		script, err := decodeHexStr(rti.ScriptPubKey)
  4806  		if err != nil {
  4807  			return nil, err
  4808  		}
  4809  
  4810  		// redeemScript is only actually used iff the user provided
  4811  		// private keys. In which case, it is used to get the scripts
  4812  		// for signing. If the user did not provide keys then we always
  4813  		// get scripts from the wallet.
  4814  		// Empty strings are ok for this one and hex.DecodeString will
  4815  		// DTRT.
  4816  		// Note that redeemScript is NOT only the redeemscript
  4817  		// required to be appended to the end of a P2SH output
  4818  		// spend, but the entire signature script for spending
  4819  		// *any* outpoint with dummy values inserted into it
  4820  		// that can later be replacing by txscript's sign.
  4821  		if cmd.PrivKeys != nil && len(*cmd.PrivKeys) != 0 {
  4822  			redeemScript, err := decodeHexStr(rti.RedeemScript)
  4823  			if err != nil {
  4824  				return nil, err
  4825  			}
  4826  
  4827  			addr, err := stdaddr.NewAddressScriptHashV0(redeemScript,
  4828  				w.ChainParams())
  4829  			if err != nil {
  4830  				return nil, err
  4831  			}
  4832  			scripts[addr.String()] = redeemScript
  4833  		}
  4834  		inputs[wire.OutPoint{
  4835  			Hash:  *inputSha,
  4836  			Tree:  rti.Tree,
  4837  			Index: rti.Vout,
  4838  		}] = script
  4839  	}
  4840  
  4841  	// Now we go and look for any inputs that we were not provided by
  4842  	// querying dcrd with getrawtransaction. We queue up a bunch of async
  4843  	// requests and will wait for replies after we have checked the rest of
  4844  	// the arguments.
  4845  	requested := make(map[wire.OutPoint]*dcrdtypes.GetTxOutResult)
  4846  	var requestedMu sync.Mutex
  4847  	requestedGroup, gctx := errgroup.WithContext(ctx)
  4848  	n, _ := s.walletLoader.NetworkBackend()
  4849  	if rpc, ok := n.(*dcrd.RPC); ok {
  4850  		for i, txIn := range tx.TxIn {
  4851  			// We don't need the first input of a stakebase tx, as it's garbage
  4852  			// anyway.
  4853  			if i == 0 && *cmd.Flags == "ssgen" {
  4854  				continue
  4855  			}
  4856  
  4857  			// Did we get this outpoint from the arguments?
  4858  			if _, ok := inputs[txIn.PreviousOutPoint]; ok {
  4859  				continue
  4860  			}
  4861  
  4862  			// Asynchronously request the output script.
  4863  			txIn := txIn
  4864  			requestedGroup.Go(func() error {
  4865  				hash := txIn.PreviousOutPoint.Hash.String()
  4866  				index := txIn.PreviousOutPoint.Index
  4867  				tree := txIn.PreviousOutPoint.Tree
  4868  				// gettxout returns null without error if the output exists
  4869  				// but is spent.  A double pointer is used to handle this case.
  4870  				var res *dcrdtypes.GetTxOutResult
  4871  				err := rpc.Call(gctx, "gettxout", &res, hash, index, tree, true)
  4872  				if err != nil {
  4873  					return errors.E(errors.Op("dcrd.jsonrpc.gettxout"), err)
  4874  				}
  4875  				requestedMu.Lock()
  4876  				requested[txIn.PreviousOutPoint] = res
  4877  				requestedMu.Unlock()
  4878  				return nil
  4879  			})
  4880  		}
  4881  	}
  4882  
  4883  	// Parse list of private keys, if present. If there are any keys here
  4884  	// they are the keys that we may use for signing. If empty we will
  4885  	// use any keys known to us already.
  4886  	var keys map[string]*dcrutil.WIF
  4887  	if cmd.PrivKeys != nil {
  4888  		keys = make(map[string]*dcrutil.WIF)
  4889  
  4890  		for _, key := range *cmd.PrivKeys {
  4891  			wif, err := dcrutil.DecodeWIF(key, w.ChainParams().PrivateKeyID)
  4892  			if err != nil {
  4893  				return nil, rpcError(dcrjson.ErrRPCDeserialization, err)
  4894  			}
  4895  
  4896  			var addr stdaddr.Address
  4897  			switch wif.DSA() {
  4898  			case dcrec.STEcdsaSecp256k1:
  4899  				addr, err = stdaddr.NewAddressPubKeyEcdsaSecp256k1V0Raw(
  4900  					wif.PubKey(), w.ChainParams())
  4901  				if err != nil {
  4902  					return nil, err
  4903  				}
  4904  			case dcrec.STEd25519:
  4905  				addr, err = stdaddr.NewAddressPubKeyEd25519V0Raw(
  4906  					wif.PubKey(), w.ChainParams())
  4907  				if err != nil {
  4908  					return nil, err
  4909  				}
  4910  			case dcrec.STSchnorrSecp256k1:
  4911  				addr, err = stdaddr.NewAddressPubKeySchnorrSecp256k1V0Raw(
  4912  					wif.PubKey(), w.ChainParams())
  4913  				if err != nil {
  4914  					return nil, err
  4915  				}
  4916  			}
  4917  			keys[addr.String()] = wif
  4918  
  4919  			// Add the pubkey hash variant for supported addresses as well.
  4920  			if pkH, ok := addr.(stdaddr.AddressPubKeyHasher); ok {
  4921  				keys[pkH.AddressPubKeyHash().String()] = wif
  4922  			}
  4923  		}
  4924  	}
  4925  
  4926  	// We have checked the rest of the args. now we can collect the async
  4927  	// txs.
  4928  	err = requestedGroup.Wait()
  4929  	if err != nil {
  4930  		return nil, err
  4931  	}
  4932  	for outPoint, result := range requested {
  4933  		// gettxout returns JSON null if the output is found, but is spent by
  4934  		// another transaction in the main chain.
  4935  		if result == nil {
  4936  			continue
  4937  		}
  4938  		script, err := hex.DecodeString(result.ScriptPubKey.Hex)
  4939  		if err != nil {
  4940  			return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
  4941  		}
  4942  		inputs[outPoint] = script
  4943  	}
  4944  
  4945  	// All args collected. Now we can sign all the inputs that we can.
  4946  	// `complete' denotes that we successfully signed all outputs and that
  4947  	// all scripts will run to completion. This is returned as part of the
  4948  	// reply.
  4949  	signErrs, err := w.SignTransaction(ctx, tx, hashType, inputs, keys, scripts)
  4950  	if err != nil {
  4951  		return nil, err
  4952  	}
  4953  
  4954  	var b strings.Builder
  4955  	b.Grow(2 * tx.SerializeSize())
  4956  	err = tx.Serialize(hex.NewEncoder(&b))
  4957  	if err != nil {
  4958  		return nil, err
  4959  	}
  4960  
  4961  	signErrors := make([]types.SignRawTransactionError, 0, len(signErrs))
  4962  	for _, e := range signErrs {
  4963  		input := tx.TxIn[e.InputIndex]
  4964  		signErrors = append(signErrors, types.SignRawTransactionError{
  4965  			TxID:      input.PreviousOutPoint.Hash.String(),
  4966  			Vout:      input.PreviousOutPoint.Index,
  4967  			ScriptSig: hex.EncodeToString(input.SignatureScript),
  4968  			Sequence:  input.Sequence,
  4969  			Error:     e.Error.Error(),
  4970  		})
  4971  	}
  4972  
  4973  	return types.SignRawTransactionResult{
  4974  		Hex:      b.String(),
  4975  		Complete: len(signErrors) == 0,
  4976  		Errors:   signErrors,
  4977  	}, nil
  4978  }
  4979  
  4980  // signRawTransactions handles the signrawtransactions command.
  4981  func (s *Server) signRawTransactions(ctx context.Context, icmd interface{}) (interface{}, error) {
  4982  	cmd := icmd.(*types.SignRawTransactionsCmd)
  4983  
  4984  	// Sign each transaction sequentially and record the results.
  4985  	// Error out if we meet some unexpected failure.
  4986  	results := make([]types.SignRawTransactionResult, len(cmd.RawTxs))
  4987  	for i, etx := range cmd.RawTxs {
  4988  		flagAll := "ALL"
  4989  		srtc := &types.SignRawTransactionCmd{
  4990  			RawTx: etx,
  4991  			Flags: &flagAll,
  4992  		}
  4993  		result, err := s.signRawTransaction(ctx, srtc)
  4994  		if err != nil {
  4995  			return nil, err
  4996  		}
  4997  
  4998  		tResult := result.(types.SignRawTransactionResult)
  4999  		results[i] = tResult
  5000  	}
  5001  
  5002  	// If the user wants completed transactions to be automatically send,
  5003  	// do that now. Otherwise, construct the slice and return it.
  5004  	toReturn := make([]types.SignedTransaction, len(cmd.RawTxs))
  5005  
  5006  	if *cmd.Send {
  5007  		n, ok := s.walletLoader.NetworkBackend()
  5008  		if !ok {
  5009  			return nil, errNoNetwork
  5010  		}
  5011  
  5012  		for i, result := range results {
  5013  			if result.Complete {
  5014  				// Slow/mem hungry because of the deserializing.
  5015  				msgTx := wire.NewMsgTx()
  5016  				err := msgTx.Deserialize(hex.NewDecoder(strings.NewReader(result.Hex)))
  5017  				if err != nil {
  5018  					return nil, rpcError(dcrjson.ErrRPCDeserialization, err)
  5019  				}
  5020  				sent := false
  5021  				hashStr := ""
  5022  				err = n.PublishTransactions(ctx, msgTx)
  5023  				// If sendrawtransaction errors out (blockchain rule
  5024  				// issue, etc), continue onto the next transaction.
  5025  				if err == nil {
  5026  					sent = true
  5027  					hashStr = msgTx.TxHash().String()
  5028  				}
  5029  
  5030  				st := types.SignedTransaction{
  5031  					SigningResult: result,
  5032  					Sent:          sent,
  5033  					TxHash:        &hashStr,
  5034  				}
  5035  				toReturn[i] = st
  5036  			} else {
  5037  				st := types.SignedTransaction{
  5038  					SigningResult: result,
  5039  					Sent:          false,
  5040  					TxHash:        nil,
  5041  				}
  5042  				toReturn[i] = st
  5043  			}
  5044  		}
  5045  	} else { // Just return the results.
  5046  		for i, result := range results {
  5047  			st := types.SignedTransaction{
  5048  				SigningResult: result,
  5049  				Sent:          false,
  5050  				TxHash:        nil,
  5051  			}
  5052  			toReturn[i] = st
  5053  		}
  5054  	}
  5055  
  5056  	return &types.SignRawTransactionsResult{Results: toReturn}, nil
  5057  }
  5058  
  5059  // scriptChangeSource is a ChangeSource which is used to
  5060  // receive all correlated previous input value.
  5061  type scriptChangeSource struct {
  5062  	version uint16
  5063  	script  []byte
  5064  }
  5065  
  5066  func (src *scriptChangeSource) Script() ([]byte, uint16, error) {
  5067  	return src.script, src.version, nil
  5068  }
  5069  
  5070  func (src *scriptChangeSource) ScriptSize() int {
  5071  	return len(src.script)
  5072  }
  5073  
  5074  func makeScriptChangeSource(address string, params *chaincfg.Params) (*scriptChangeSource, error) {
  5075  	destinationAddress, err := stdaddr.DecodeAddress(address, params)
  5076  	if err != nil {
  5077  		return nil, err
  5078  	}
  5079  	version, script := destinationAddress.PaymentScript()
  5080  	source := &scriptChangeSource{
  5081  		version: version,
  5082  		script:  script,
  5083  	}
  5084  	return source, nil
  5085  }
  5086  
  5087  func sumOutputValues(outputs []*wire.TxOut) (totalOutput dcrutil.Amount) {
  5088  	for _, txOut := range outputs {
  5089  		totalOutput += dcrutil.Amount(txOut.Value)
  5090  	}
  5091  	return totalOutput
  5092  }
  5093  
  5094  // sweepAccount handles the sweepaccount command.
  5095  func (s *Server) sweepAccount(ctx context.Context, icmd interface{}) (interface{}, error) {
  5096  	cmd := icmd.(*types.SweepAccountCmd)
  5097  	w, ok := s.walletLoader.LoadedWallet()
  5098  	if !ok {
  5099  		return nil, errUnloadedWallet
  5100  	}
  5101  
  5102  	// use provided fee per Kb if specified
  5103  	feePerKb := w.RelayFee()
  5104  	if cmd.FeePerKb != nil {
  5105  		var err error
  5106  		feePerKb, err = dcrutil.NewAmount(*cmd.FeePerKb)
  5107  		if err != nil {
  5108  			return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
  5109  		}
  5110  	}
  5111  
  5112  	// use provided required confirmations if specified
  5113  	requiredConfs := int32(1)
  5114  	if cmd.RequiredConfirmations != nil {
  5115  		requiredConfs = int32(*cmd.RequiredConfirmations)
  5116  		if requiredConfs < 0 {
  5117  			return nil, errNeedPositiveAmount
  5118  		}
  5119  	}
  5120  
  5121  	account, err := w.AccountNumber(ctx, cmd.SourceAccount)
  5122  	if err != nil {
  5123  		if errors.Is(err, errors.NotExist) {
  5124  			return nil, errAccountNotFound
  5125  		}
  5126  		return nil, err
  5127  	}
  5128  
  5129  	changeSource, err := makeScriptChangeSource(cmd.DestinationAddress, w.ChainParams())
  5130  	if err != nil {
  5131  		return nil, err
  5132  	}
  5133  	tx, err := w.NewUnsignedTransaction(ctx, nil, feePerKb, account,
  5134  		requiredConfs, wallet.OutputSelectionAlgorithmAll, changeSource, nil)
  5135  	if err != nil {
  5136  		if errors.Is(err, errors.InsufficientBalance) {
  5137  			return nil, rpcError(dcrjson.ErrRPCWalletInsufficientFunds, err)
  5138  		}
  5139  		return nil, err
  5140  	}
  5141  
  5142  	var b strings.Builder
  5143  	b.Grow(2 * tx.Tx.SerializeSize())
  5144  	err = tx.Tx.Serialize(hex.NewEncoder(&b))
  5145  	if err != nil {
  5146  		return nil, err
  5147  	}
  5148  
  5149  	res := &types.SweepAccountResult{
  5150  		UnsignedTransaction:       b.String(),
  5151  		TotalPreviousOutputAmount: tx.TotalInput.ToCoin(),
  5152  		TotalOutputAmount:         sumOutputValues(tx.Tx.TxOut).ToCoin(),
  5153  		EstimatedSignedSize:       uint32(tx.EstimatedSignedSerializeSize),
  5154  	}
  5155  
  5156  	return res, nil
  5157  }
  5158  
  5159  // validateAddress handles the validateaddress command.
  5160  func (s *Server) validateAddress(ctx context.Context, icmd interface{}) (interface{}, error) {
  5161  	cmd := icmd.(*types.ValidateAddressCmd)
  5162  	w, ok := s.walletLoader.LoadedWallet()
  5163  	if !ok {
  5164  		return nil, errUnloadedWallet
  5165  	}
  5166  
  5167  	result := types.ValidateAddressResult{}
  5168  	addr, err := decodeAddress(cmd.Address, w.ChainParams())
  5169  	if err != nil {
  5170  		result.Script = stdscript.STNonStandard.String()
  5171  		// Use result zero value (IsValid=false).
  5172  		return result, nil
  5173  	}
  5174  
  5175  	result.Address = addr.String()
  5176  	result.IsValid = true
  5177  	ver, scr := addr.PaymentScript()
  5178  	class, _ := stdscript.ExtractAddrs(ver, scr, w.ChainParams())
  5179  	result.Script = class.String()
  5180  	if pker, ok := addr.(stdaddr.SerializedPubKeyer); ok {
  5181  		result.PubKey = hex.EncodeToString(pker.SerializedPubKey())
  5182  		result.PubKeyAddr = addr.String()
  5183  	}
  5184  	if class == stdscript.STScriptHash {
  5185  		result.IsScript = true
  5186  	}
  5187  	if _, ok := addr.(stdaddr.Hash160er); ok {
  5188  		result.IsCompressed = true
  5189  	}
  5190  
  5191  	ka, err := w.KnownAddress(ctx, addr)
  5192  	if err != nil {
  5193  		if errors.Is(err, errors.NotExist) {
  5194  			// No additional information available about the address.
  5195  			return result, nil
  5196  		}
  5197  		return nil, err
  5198  	}
  5199  
  5200  	// The address lookup was successful which means there is further
  5201  	// information about it available and it is "mine".
  5202  	result.IsMine = true
  5203  	result.Account = ka.AccountName()
  5204  
  5205  	switch ka := ka.(type) {
  5206  	case wallet.PubKeyHashAddress:
  5207  		pubKey := ka.PubKey()
  5208  		result.PubKey = hex.EncodeToString(pubKey)
  5209  		pubKeyAddr, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0Raw(pubKey, w.ChainParams())
  5210  		if err != nil {
  5211  			return nil, err
  5212  		}
  5213  		result.PubKeyAddr = pubKeyAddr.String()
  5214  	case wallet.P2SHAddress:
  5215  		version, script := ka.RedeemScript()
  5216  		result.Hex = hex.EncodeToString(script)
  5217  
  5218  		class, addrs := stdscript.ExtractAddrs(version, script, w.ChainParams())
  5219  		addrStrings := make([]string, len(addrs))
  5220  		for i, a := range addrs {
  5221  			addrStrings[i] = a.String()
  5222  		}
  5223  		result.Addresses = addrStrings
  5224  		result.Script = class.String()
  5225  
  5226  		// Multi-signature scripts also provide the number of required
  5227  		// signatures.
  5228  		if class == stdscript.STMultiSig {
  5229  			result.SigsRequired = int32(stdscript.DetermineRequiredSigs(version, script))
  5230  		}
  5231  	}
  5232  
  5233  	if ka, ok := ka.(wallet.BIP0044Address); ok {
  5234  		acct, branch, child := ka.Path()
  5235  		if ka.AccountKind() != wallet.AccountKindImportedXpub {
  5236  			result.AccountN = &acct
  5237  		}
  5238  		result.Branch = &branch
  5239  		result.Index = &child
  5240  	}
  5241  
  5242  	return result, nil
  5243  }
  5244  
  5245  // validatePreDCP0005CF handles the validatepredcp0005cf command.
  5246  func (s *Server) validatePreDCP0005CF(ctx context.Context, icmd interface{}) (interface{}, error) {
  5247  	w, ok := s.walletLoader.LoadedWallet()
  5248  	if !ok {
  5249  		return nil, errUnloadedWallet
  5250  	}
  5251  
  5252  	err := w.ValidatePreDCP0005CFilters(ctx)
  5253  	return err == nil, err
  5254  }
  5255  
  5256  // verifyMessage handles the verifymessage command by verifying the provided
  5257  // compact signature for the given address and message.
  5258  func (s *Server) verifyMessage(ctx context.Context, icmd interface{}) (interface{}, error) {
  5259  	cmd := icmd.(*types.VerifyMessageCmd)
  5260  
  5261  	var valid bool
  5262  
  5263  	// Decode address and base64 signature from the request.
  5264  	addr, err := stdaddr.DecodeAddress(cmd.Address, s.activeNet)
  5265  	if err != nil {
  5266  		return nil, err
  5267  	}
  5268  	sig, err := base64.StdEncoding.DecodeString(cmd.Signature)
  5269  	if err != nil {
  5270  		return nil, err
  5271  	}
  5272  
  5273  	// Addresses must have an associated secp256k1 private key and must be P2PKH
  5274  	// (P2PK and P2SH is not allowed).
  5275  	switch addr.(type) {
  5276  	case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0:
  5277  	default:
  5278  		return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
  5279  			"address must be secp256k1 pay-to-pubkey-hash")
  5280  	}
  5281  
  5282  	valid, err = wallet.VerifyMessage(cmd.Message, addr, sig, s.activeNet)
  5283  	// Mirror Bitcoin Core behavior, which treats all erorrs as an invalid
  5284  	// signature.
  5285  	return err == nil && valid, nil
  5286  }
  5287  
  5288  // version handles the version command by returning the RPC API versions of the
  5289  // wallet and, optionally, the consensus RPC server as well if it is associated
  5290  // with the server.  The chainClient is optional, and this is simply a helper
  5291  // function for the versionWithChainRPC and versionNoChainRPC handlers.
  5292  func (s *Server) version(ctx context.Context, icmd interface{}) (interface{}, error) {
  5293  	resp := make(map[string]dcrdtypes.VersionResult)
  5294  	n, _ := s.walletLoader.NetworkBackend()
  5295  	if rpc, ok := n.(*dcrd.RPC); ok {
  5296  		err := rpc.Call(ctx, "version", &resp)
  5297  		if err != nil {
  5298  			return nil, err
  5299  		}
  5300  	}
  5301  
  5302  	resp["dcrwalletjsonrpcapi"] = dcrdtypes.VersionResult{
  5303  		VersionString: jsonrpcSemverString,
  5304  		Major:         jsonrpcSemverMajor,
  5305  		Minor:         jsonrpcSemverMinor,
  5306  		Patch:         jsonrpcSemverPatch,
  5307  	}
  5308  	return resp, nil
  5309  }
  5310  
  5311  // walletInfo gets the current information about the wallet. If the daemon
  5312  // is connected and fails to ping, the function will still return that the
  5313  // daemon is disconnected.
  5314  func (s *Server) walletInfo(ctx context.Context, icmd interface{}) (interface{}, error) {
  5315  	w, ok := s.walletLoader.LoadedWallet()
  5316  	if !ok {
  5317  		return nil, errUnloadedWallet
  5318  	}
  5319  
  5320  	var connected, spvMode bool
  5321  	switch n, _ := w.NetworkBackend(); rpc := n.(type) {
  5322  	case *spv.Syncer:
  5323  		spvMode = true
  5324  		connected = len(rpc.GetRemotePeers()) > 0
  5325  	case *dcrd.RPC:
  5326  		err := rpc.Call(ctx, "ping", nil)
  5327  		if ctx.Err() != nil {
  5328  			return nil, ctx.Err()
  5329  		}
  5330  		if err != nil {
  5331  			log.Warnf("Ping failed on connected daemon client: %v", err)
  5332  		} else {
  5333  			connected = true
  5334  		}
  5335  	case nil:
  5336  		log.Warnf("walletInfo - no network backend")
  5337  	default:
  5338  		log.Errorf("walletInfo - invalid network backend (%T).", n)
  5339  		return nil, &dcrjson.RPCError{
  5340  			Code:    dcrjson.ErrRPCMisc,
  5341  			Message: "invalid network backend",
  5342  		}
  5343  	}
  5344  
  5345  	coinType, err := w.CoinType(ctx)
  5346  	if errors.Is(err, errors.WatchingOnly) {
  5347  		// This is a watching-only wallet, which does not store the active coin
  5348  		// type. Return CoinTypes default value (0), which will be omitted from
  5349  		// the JSON response, and log a debug message.
  5350  		log.Debug("Watching only wallets do not store the coin type keys.")
  5351  	} else if err != nil {
  5352  		log.Errorf("Failed to retrieve the active coin type: %v", err)
  5353  		coinType = 0
  5354  	}
  5355  
  5356  	unlocked := !(w.Locked())
  5357  	fi := w.RelayFee()
  5358  	voteBits := w.VoteBits()
  5359  	var voteVersion uint32
  5360  	_ = binary.Read(bytes.NewBuffer(voteBits.ExtendedBits[0:4]), binary.LittleEndian, &voteVersion)
  5361  	voting := w.VotingEnabled()
  5362  
  5363  	return &types.WalletInfoResult{
  5364  		DaemonConnected:  connected,
  5365  		SPV:              spvMode,
  5366  		Unlocked:         unlocked,
  5367  		CoinType:         coinType,
  5368  		TxFee:            fi.ToCoin(),
  5369  		VoteBits:         voteBits.Bits,
  5370  		VoteBitsExtended: hex.EncodeToString(voteBits.ExtendedBits),
  5371  		VoteVersion:      voteVersion,
  5372  		Voting:           voting,
  5373  		ManualTickets:    w.ManualTickets(),
  5374  	}, nil
  5375  }
  5376  
  5377  // walletIsLocked handles the walletislocked extension request by
  5378  // returning the current lock state (false for unlocked, true for locked)
  5379  // of an account.
  5380  func (s *Server) walletIsLocked(ctx context.Context, icmd interface{}) (interface{}, error) {
  5381  	w, ok := s.walletLoader.LoadedWallet()
  5382  	if !ok {
  5383  		return nil, errUnloadedWallet
  5384  	}
  5385  
  5386  	return w.Locked(), nil
  5387  }
  5388  
  5389  // walletLock handles a walletlock request by locking the all account
  5390  // wallets, returning an error if any wallet is not encrypted (for example,
  5391  // a watching-only wallet).
  5392  func (s *Server) walletLock(ctx context.Context, icmd interface{}) (interface{}, error) {
  5393  	w, ok := s.walletLoader.LoadedWallet()
  5394  	if !ok {
  5395  		return nil, errUnloadedWallet
  5396  	}
  5397  
  5398  	w.Lock()
  5399  	return nil, nil
  5400  }
  5401  
  5402  // walletPassphrase responds to the walletpassphrase request by unlocking the
  5403  // wallet. The decryption key is saved in the wallet until timeout seconds
  5404  // expires, after which the wallet is locked. A timeout of 0 leaves the wallet
  5405  // unlocked indefinitely.
  5406  func (s *Server) walletPassphrase(ctx context.Context, icmd interface{}) (interface{}, error) {
  5407  	cmd := icmd.(*types.WalletPassphraseCmd)
  5408  	w, ok := s.walletLoader.LoadedWallet()
  5409  	if !ok {
  5410  		return nil, errUnloadedWallet
  5411  	}
  5412  
  5413  	timeout := time.Second * time.Duration(cmd.Timeout)
  5414  	var unlockAfter <-chan time.Time
  5415  	if timeout != 0 {
  5416  		unlockAfter = time.After(timeout)
  5417  	}
  5418  	err := w.Unlock(ctx, []byte(cmd.Passphrase), unlockAfter)
  5419  	return nil, err
  5420  }
  5421  
  5422  // walletPassphraseChange responds to the walletpassphrasechange request
  5423  // by unlocking all accounts with the provided old passphrase, and
  5424  // re-encrypting each private key with an AES key derived from the new
  5425  // passphrase.
  5426  //
  5427  // If the old passphrase is correct and the passphrase is changed, all
  5428  // wallets will be immediately locked.
  5429  func (s *Server) walletPassphraseChange(ctx context.Context, icmd interface{}) (interface{}, error) {
  5430  	cmd := icmd.(*types.WalletPassphraseChangeCmd)
  5431  	w, ok := s.walletLoader.LoadedWallet()
  5432  	if !ok {
  5433  		return nil, errUnloadedWallet
  5434  	}
  5435  
  5436  	err := w.ChangePrivatePassphrase(ctx, []byte(cmd.OldPassphrase),
  5437  		[]byte(cmd.NewPassphrase))
  5438  	if err != nil {
  5439  		if errors.Is(err, errors.Passphrase) {
  5440  			return nil, rpcErrorf(dcrjson.ErrRPCWalletPassphraseIncorrect, "incorrect passphrase")
  5441  		}
  5442  		return nil, err
  5443  	}
  5444  	return nil, nil
  5445  }
  5446  
  5447  func (s *Server) mixOutput(ctx context.Context, icmd interface{}) (interface{}, error) {
  5448  	cmd := icmd.(*types.MixOutputCmd)
  5449  	if s.cfg.CSPPServer == "" {
  5450  		return nil, errors.E("CoinShuffle++ server is not configured")
  5451  	}
  5452  	w, ok := s.walletLoader.LoadedWallet()
  5453  	if !ok {
  5454  		return nil, errUnloadedWallet
  5455  	}
  5456  
  5457  	outpoint, err := parseOutpoint(cmd.Outpoint)
  5458  	if err != nil {
  5459  		return nil, err
  5460  	}
  5461  
  5462  	mixAccount, err := w.AccountNumber(ctx, s.cfg.MixAccount)
  5463  	if err != nil {
  5464  		if errors.Is(err, errors.NotExist) {
  5465  			return nil, errAccountNotFound
  5466  		}
  5467  		return nil, err
  5468  	}
  5469  	changeAccount, err := w.AccountNumber(ctx, s.cfg.MixChangeAccount)
  5470  	if err != nil {
  5471  		if errors.Is(err, errors.NotExist) {
  5472  			return nil, errAccountNotFound
  5473  		}
  5474  		return nil, err
  5475  	}
  5476  
  5477  	dial := s.cfg.DialCSPPServer
  5478  	server := s.cfg.CSPPServer
  5479  	mixBranch := s.cfg.MixBranch
  5480  
  5481  	err = w.MixOutput(ctx, dial, server, outpoint, changeAccount, mixAccount, mixBranch)
  5482  	return nil, err
  5483  }
  5484  
  5485  func (s *Server) mixAccount(ctx context.Context, icmd interface{}) (interface{}, error) {
  5486  	if s.cfg.CSPPServer == "" {
  5487  		return nil, errors.E("CoinShuffle++ server is not configured")
  5488  	}
  5489  	w, ok := s.walletLoader.LoadedWallet()
  5490  	if !ok {
  5491  		return nil, errUnloadedWallet
  5492  	}
  5493  
  5494  	mixAccount, err := w.AccountNumber(ctx, s.cfg.MixAccount)
  5495  	if err != nil {
  5496  		if errors.Is(err, errors.NotExist) {
  5497  			return nil, errAccountNotFound
  5498  		}
  5499  		return nil, err
  5500  	}
  5501  	changeAccount, err := w.AccountNumber(ctx, s.cfg.MixChangeAccount)
  5502  	if err != nil {
  5503  		if errors.Is(err, errors.NotExist) {
  5504  			return nil, errAccountNotFound
  5505  		}
  5506  		return nil, err
  5507  	}
  5508  
  5509  	dial := s.cfg.DialCSPPServer
  5510  	server := s.cfg.CSPPServer
  5511  	mixBranch := s.cfg.MixBranch
  5512  
  5513  	err = w.MixAccount(ctx, dial, server, changeAccount, mixAccount, mixBranch)
  5514  	return nil, err
  5515  }
  5516  
  5517  func parseOutpoint(s string) (*wire.OutPoint, error) {
  5518  	const op errors.Op = "parseOutpoint"
  5519  	if len(s) < 66 {
  5520  		return nil, errors.E(op, "bad len")
  5521  	}
  5522  	if s[64] != ':' { // sep follows 32 bytes of hex
  5523  		return nil, errors.E(op, "bad separator")
  5524  	}
  5525  	hash, err := chainhash.NewHashFromStr(s[:64])
  5526  	if err != nil {
  5527  		return nil, errors.E(op, err)
  5528  	}
  5529  	index, err := strconv.ParseUint(s[65:], 10, 32)
  5530  	if err != nil {
  5531  		return nil, errors.E(op, err)
  5532  	}
  5533  	return &wire.OutPoint{Hash: *hash, Index: uint32(index)}, nil
  5534  }
  5535  
  5536  // walletPubPassphraseChange responds to the walletpubpassphrasechange request
  5537  // by modifying the public passphrase of the wallet.
  5538  func (s *Server) walletPubPassphraseChange(ctx context.Context, icmd interface{}) (interface{}, error) {
  5539  	cmd := icmd.(*types.WalletPubPassphraseChangeCmd)
  5540  	w, ok := s.walletLoader.LoadedWallet()
  5541  	if !ok {
  5542  		return nil, errUnloadedWallet
  5543  	}
  5544  
  5545  	err := w.ChangePublicPassphrase(ctx, []byte(cmd.OldPassphrase),
  5546  		[]byte(cmd.NewPassphrase))
  5547  	if errors.Is(errors.Passphrase, err) {
  5548  		return nil, rpcErrorf(dcrjson.ErrRPCWalletPassphraseIncorrect, "incorrect passphrase")
  5549  	}
  5550  	return nil, err
  5551  }
  5552  
  5553  func (s *Server) setAccountPassphrase(ctx context.Context, icmd interface{}) (interface{}, error) {
  5554  	cmd := icmd.(*types.SetAccountPassphraseCmd)
  5555  	w, ok := s.walletLoader.LoadedWallet()
  5556  	if !ok {
  5557  		return nil, errUnloadedWallet
  5558  	}
  5559  
  5560  	account, err := w.AccountNumber(ctx, cmd.Account)
  5561  	if err != nil {
  5562  		if errors.Is(err, errors.NotExist) {
  5563  			return nil, errAccountNotFound
  5564  		}
  5565  		return nil, err
  5566  	}
  5567  	err = w.SetAccountPassphrase(ctx, account, []byte(cmd.Passphrase))
  5568  	return nil, err
  5569  }
  5570  
  5571  func (s *Server) accountUnlocked(ctx context.Context, icmd interface{}) (interface{}, error) {
  5572  	cmd := icmd.(*types.AccountUnlockedCmd)
  5573  	w, ok := s.walletLoader.LoadedWallet()
  5574  	if !ok {
  5575  		return nil, errUnloadedWallet
  5576  	}
  5577  
  5578  	account, err := w.AccountNumber(ctx, cmd.Account)
  5579  	if err != nil {
  5580  		if errors.Is(err, errors.NotExist) {
  5581  			return nil, errAccountNotFound
  5582  		}
  5583  		return nil, err
  5584  	}
  5585  
  5586  	encrypted, err := w.AccountHasPassphrase(ctx, account)
  5587  	if err != nil {
  5588  		return nil, err
  5589  	}
  5590  	if !encrypted {
  5591  		return &types.AccountUnlockedResult{}, nil
  5592  	}
  5593  
  5594  	unlocked, err := w.AccountUnlocked(ctx, account)
  5595  	if err != nil {
  5596  		return nil, err
  5597  	}
  5598  
  5599  	return &types.AccountUnlockedResult{
  5600  		Encrypted: true,
  5601  		Unlocked:  &unlocked,
  5602  	}, nil
  5603  }
  5604  
  5605  func (s *Server) unlockAccount(ctx context.Context, icmd interface{}) (interface{}, error) {
  5606  	cmd := icmd.(*types.UnlockAccountCmd)
  5607  	w, ok := s.walletLoader.LoadedWallet()
  5608  	if !ok {
  5609  		return nil, errUnloadedWallet
  5610  	}
  5611  
  5612  	account, err := w.AccountNumber(ctx, cmd.Account)
  5613  	if err != nil {
  5614  		if errors.Is(err, errors.NotExist) {
  5615  			return nil, errAccountNotFound
  5616  		}
  5617  		return nil, err
  5618  	}
  5619  	err = w.UnlockAccount(ctx, account, []byte(cmd.Passphrase))
  5620  	return nil, err
  5621  }
  5622  
  5623  func (s *Server) lockAccount(ctx context.Context, icmd interface{}) (interface{}, error) {
  5624  	cmd := icmd.(*types.LockAccountCmd)
  5625  	w, ok := s.walletLoader.LoadedWallet()
  5626  	if !ok {
  5627  		return nil, errUnloadedWallet
  5628  	}
  5629  
  5630  	if cmd.Account == "*" {
  5631  		a, err := w.Accounts(ctx)
  5632  		if err != nil {
  5633  			return nil, err
  5634  		}
  5635  		for _, acct := range a.Accounts {
  5636  			if acct.AccountEncrypted && acct.AccountUnlocked {
  5637  				err = w.LockAccount(ctx, acct.AccountNumber)
  5638  				if err != nil {
  5639  					return nil, err
  5640  				}
  5641  			}
  5642  		}
  5643  		return nil, nil
  5644  	}
  5645  
  5646  	account, err := w.AccountNumber(ctx, cmd.Account)
  5647  	if err != nil {
  5648  		if errors.Is(err, errors.NotExist) {
  5649  			return nil, errAccountNotFound
  5650  		}
  5651  		return nil, err
  5652  	}
  5653  	err = w.LockAccount(ctx, account)
  5654  	return nil, err
  5655  }
  5656  
  5657  // decodeHexStr decodes the hex encoding of a string, possibly prepending a
  5658  // leading '0' character if there is an odd number of bytes in the hex string.
  5659  // This is to prevent an error for an invalid hex string when using an odd
  5660  // number of bytes when calling hex.Decode.
  5661  func decodeHexStr(hexStr string) ([]byte, error) {
  5662  	if len(hexStr)%2 != 0 {
  5663  		hexStr = "0" + hexStr
  5664  	}
  5665  	decoded, err := hex.DecodeString(hexStr)
  5666  	if err != nil {
  5667  		return nil, rpcErrorf(dcrjson.ErrRPCDecodeHexString, "hex string decode failed: %v", err)
  5668  	}
  5669  	return decoded, nil
  5670  }
  5671  
  5672  func (s *Server) getcoinjoinsbyacct(ctx context.Context, icmd interface{}) (interface{}, error) {
  5673  	_ = icmd.(*types.GetCoinjoinsByAcctCmd)
  5674  	w, ok := s.walletLoader.LoadedWallet()
  5675  	if !ok {
  5676  		return nil, errUnloadedWallet
  5677  	}
  5678  
  5679  	acctCoinjoinsSum, err := w.GetCoinjoinTxsSumbByAcct(ctx)
  5680  	if err != nil {
  5681  		if errors.Is(err, errors.Passphrase) {
  5682  			return nil, rpcErrorf(dcrjson.ErrRPCWalletPassphraseIncorrect, "incorrect passphrase")
  5683  		}
  5684  		return nil, err
  5685  	}
  5686  
  5687  	acctNameCoinjoinSum := map[string]int{}
  5688  	for acctIdx, coinjoinSum := range acctCoinjoinsSum {
  5689  		accountName, err := w.AccountName(ctx, acctIdx)
  5690  		if err != nil {
  5691  			// Expect account lookup to succeed
  5692  			if errors.Is(err, errors.NotExist) {
  5693  				return nil, rpcError(dcrjson.ErrRPCInternal.Code, err)
  5694  			}
  5695  			return nil, err
  5696  		}
  5697  		acctNameCoinjoinSum[accountName] = coinjoinSum
  5698  	}
  5699  
  5700  	return acctNameCoinjoinSum, nil
  5701  }