decred.org/dcrwallet/v3@v3.1.0/internal/rpc/rpcserver/server.go (about)

     1  // Copyright (c) 2015-2016 The btcsuite developers
     2  // Copyright (c) 2016-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 rpcserver implements the RPC API and is used by the main package to
     7  // start gRPC services.
     8  //
     9  // Full documentation of the API implemented by this package is maintained in a
    10  // language-agnostic document:
    11  //
    12  //	https://github.com/decred/dcrwallet/blob/master/rpc/documentation/api.md
    13  //
    14  // Any API changes must be performed according to the steps listed here:
    15  //
    16  //	https://github.com/decred/dcrwallet/blob/master/rpc/documentation/serverchanges.md
    17  package rpcserver
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"crypto/rand"
    23  	"encoding/hex"
    24  	"fmt"
    25  	"net"
    26  	"sort"
    27  	"sync/atomic"
    28  	"time"
    29  
    30  	"google.golang.org/grpc"
    31  	"google.golang.org/grpc/codes"
    32  	"google.golang.org/grpc/status"
    33  
    34  	"decred.org/dcrwallet/v3/chain"
    35  	"decred.org/dcrwallet/v3/errors"
    36  	"decred.org/dcrwallet/v3/internal/cfgutil"
    37  	"decred.org/dcrwallet/v3/internal/loader"
    38  	"decred.org/dcrwallet/v3/internal/netparams"
    39  	"decred.org/dcrwallet/v3/internal/vsp"
    40  	"decred.org/dcrwallet/v3/p2p"
    41  	"decred.org/dcrwallet/v3/rpc/client/dcrd"
    42  	pb "decred.org/dcrwallet/v3/rpc/walletrpc"
    43  	"decred.org/dcrwallet/v3/spv"
    44  	"decred.org/dcrwallet/v3/ticketbuyer"
    45  	"decred.org/dcrwallet/v3/wallet"
    46  	"decred.org/dcrwallet/v3/wallet/txauthor"
    47  	"decred.org/dcrwallet/v3/wallet/txrules"
    48  	"decred.org/dcrwallet/v3/wallet/udb"
    49  	"decred.org/dcrwallet/v3/walletseed"
    50  	"github.com/decred/dcrd/addrmgr/v2"
    51  	"github.com/decred/dcrd/blockchain/stake/v5"
    52  	"github.com/decred/dcrd/chaincfg/chainhash"
    53  	"github.com/decred/dcrd/chaincfg/v3"
    54  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    55  	"github.com/decred/dcrd/dcrutil/v4"
    56  	"github.com/decred/dcrd/gcs/v4"
    57  	"github.com/decred/dcrd/hdkeychain/v3"
    58  	"github.com/decred/dcrd/txscript/v4"
    59  	"github.com/decred/dcrd/txscript/v4/stdaddr"
    60  	"github.com/decred/dcrd/txscript/v4/stdscript"
    61  	"github.com/decred/dcrd/wire"
    62  )
    63  
    64  // Public API version constants
    65  const (
    66  	semverString = "7.17.0"
    67  	semverMajor  = 7
    68  	semverMinor  = 17
    69  	semverPatch  = 0
    70  )
    71  
    72  // The assumed output script version is defined to assist with refactoring to
    73  // use actual script versions.
    74  const scriptVersionAssumed = 0
    75  
    76  // translateError creates a new gRPC error with an appropriate error code for
    77  // recognized errors.
    78  //
    79  // This function is by no means complete and should be expanded based on other
    80  // known errors.  Any RPC handler not returning a gRPC error (with status.Errorf)
    81  // should return this result instead.
    82  func translateError(err error) error {
    83  	code := errorCode(err)
    84  	return status.Errorf(code, "%s", err.Error())
    85  }
    86  
    87  func errorCode(err error) codes.Code {
    88  	var kind errors.Kind
    89  	if errors.As(err, &kind) {
    90  		switch kind {
    91  		case errors.Bug:
    92  		case errors.Invalid:
    93  			return codes.InvalidArgument
    94  		case errors.Permission:
    95  			return codes.PermissionDenied
    96  		case errors.IO:
    97  		case errors.Exist:
    98  			return codes.AlreadyExists
    99  		case errors.NotExist:
   100  			return codes.NotFound
   101  		case errors.Encoding:
   102  		case errors.Crypto:
   103  			return codes.DataLoss
   104  		case errors.Locked:
   105  			return codes.FailedPrecondition
   106  		case errors.Passphrase:
   107  			return codes.InvalidArgument
   108  		case errors.Seed:
   109  			return codes.InvalidArgument
   110  		case errors.WatchingOnly:
   111  			return codes.Unimplemented
   112  		case errors.InsufficientBalance:
   113  			return codes.ResourceExhausted
   114  		case errors.ScriptFailure:
   115  		case errors.Policy:
   116  		case errors.DoubleSpend:
   117  		case errors.Protocol:
   118  		case errors.NoPeers:
   119  			return codes.Unavailable
   120  		}
   121  	}
   122  	if errors.Is(err, hdkeychain.ErrInvalidSeedLen) {
   123  		return codes.InvalidArgument
   124  	}
   125  	if errors.Is(errors.Cause(err), context.Canceled) {
   126  		return codes.Canceled
   127  	}
   128  	if code := status.Code(errors.Cause(err)); code != codes.OK && code != codes.Unknown {
   129  		return code
   130  	}
   131  	return codes.Unknown
   132  }
   133  
   134  // decodeAddress decodes an address and verifies it is intended for the active
   135  // network.  This should be used preferred to direct usage of
   136  // dcrutil.DecodeAddress, which does not perform the network check.
   137  func decodeAddress(a string, params *chaincfg.Params) (stdaddr.Address, error) {
   138  	addr, err := stdaddr.DecodeAddress(a, params)
   139  	if err != nil {
   140  		return nil, status.Errorf(codes.InvalidArgument, "invalid address %v: %v", a, err)
   141  	}
   142  	return addr, nil
   143  }
   144  
   145  func decodeStakeAddress(s string, params *chaincfg.Params) (stdaddr.StakeAddress, error) {
   146  	a, err := decodeAddress(s, params)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	if sa, ok := a.(stdaddr.StakeAddress); ok {
   151  		return sa, nil
   152  	}
   153  	return nil, status.Errorf(codes.InvalidArgument, "invalid stake address %q", s)
   154  }
   155  
   156  func decodeHashes(in [][]byte) ([]*chainhash.Hash, error) {
   157  	out := make([]*chainhash.Hash, len(in))
   158  	var err error
   159  	for i, h := range in {
   160  		out[i], err = chainhash.NewHash(h)
   161  		if err != nil {
   162  			return nil, status.Errorf(codes.InvalidArgument, "hash (hex %x): %v", h, err)
   163  		}
   164  	}
   165  	return out, nil
   166  }
   167  
   168  // versionServer provides RPC clients with the ability to query the RPC server
   169  // version.
   170  type versionServer struct {
   171  	pb.UnimplementedVersionServiceServer
   172  }
   173  
   174  // walletServer provides wallet services for RPC clients.
   175  type walletServer struct {
   176  	ready          uint32 // atomic
   177  	wallet         *wallet.Wallet
   178  	dialCSPPServer func(ctx context.Context, network, addr string) (net.Conn, error)
   179  	pb.UnimplementedWalletServiceServer
   180  }
   181  
   182  // loaderServer provides RPC clients with the ability to load and close wallets,
   183  // as well as establishing a RPC connection to a dcrd consensus server.
   184  type loaderServer struct {
   185  	ready     uint32 // atomic
   186  	loader    *loader.Loader
   187  	activeNet *netparams.Params
   188  	pb.UnimplementedWalletLoaderServiceServer
   189  }
   190  
   191  // seedServer provides RPC clients with the ability to generate secure random
   192  // seeds encoded in both binary and human-readable formats, and decode any
   193  // human-readable input back to binary.
   194  type seedServer struct {
   195  	pb.UnimplementedSeedServiceServer
   196  }
   197  
   198  // accountMixerServer provides RPC clients with the ability to start/stop the
   199  // account mixing privacy service.
   200  type accountMixerServer struct {
   201  	ready  uint32 // atomic
   202  	loader *loader.Loader
   203  	pb.UnimplementedAccountMixerServiceServer
   204  }
   205  
   206  // ticketbuyerServer provides RPC clients with the ability to start/stop the
   207  // automatic ticket buyer service.
   208  type ticketbuyerV2Server struct {
   209  	ready  uint32 // atomic
   210  	loader *loader.Loader
   211  	pb.UnimplementedTicketBuyerV2ServiceServer
   212  }
   213  
   214  type agendaServer struct {
   215  	ready     uint32 // atomic
   216  	activeNet *chaincfg.Params
   217  	pb.UnimplementedAgendaServiceServer
   218  }
   219  
   220  type votingServer struct {
   221  	ready  uint32 // atomic
   222  	wallet *wallet.Wallet
   223  	pb.UnimplementedVotingServiceServer
   224  }
   225  
   226  // messageVerificationServer provides RPC clients with the ability to verify
   227  // that a message was signed using the private key of a particular address.
   228  type messageVerificationServer struct {
   229  	chainParams *chaincfg.Params
   230  	pb.UnimplementedMessageVerificationServiceServer
   231  }
   232  
   233  type decodeMessageServer struct {
   234  	chainParams *chaincfg.Params
   235  	pb.UnimplementedDecodeMessageServiceServer
   236  }
   237  
   238  // networkServer provices RPC clients with the ability to perform network
   239  // related calls that are not necessarily used or backed by the wallet itself.
   240  type networkServer struct {
   241  	ready  uint32 // atomic
   242  	wallet *wallet.Wallet
   243  	pb.UnimplementedNetworkServiceServer
   244  }
   245  
   246  // Singleton implementations of each service.  Not all services are immediately
   247  // usable.
   248  var (
   249  	versionService             versionServer
   250  	walletService              walletServer
   251  	loaderService              loaderServer
   252  	seedService                seedServer
   253  	accountMixerService        accountMixerServer
   254  	ticketBuyerV2Service       ticketbuyerV2Server
   255  	agendaService              agendaServer
   256  	votingService              votingServer
   257  	messageVerificationService messageVerificationServer
   258  	decodeMessageService       decodeMessageServer
   259  	networkService             networkServer
   260  )
   261  
   262  // RegisterServices registers implementations of each gRPC service and registers
   263  // it with the server.  Not all service are ready to be used after registration.
   264  func RegisterServices(server *grpc.Server) {
   265  	pb.RegisterVersionServiceServer(server, &versionService)
   266  	pb.RegisterWalletServiceServer(server, &walletService)
   267  	pb.RegisterWalletLoaderServiceServer(server, &loaderService)
   268  	pb.RegisterSeedServiceServer(server, &seedService)
   269  	pb.RegisterAccountMixerServiceServer(server, &accountMixerService)
   270  	pb.RegisterTicketBuyerV2ServiceServer(server, &ticketBuyerV2Service)
   271  	pb.RegisterAgendaServiceServer(server, &agendaService)
   272  	pb.RegisterVotingServiceServer(server, &votingService)
   273  	pb.RegisterMessageVerificationServiceServer(server, &messageVerificationService)
   274  	pb.RegisterDecodeMessageServiceServer(server, &decodeMessageService)
   275  	pb.RegisterNetworkServiceServer(server, &networkService)
   276  }
   277  
   278  var serviceMap = map[string]interface{}{
   279  	"walletrpc.VersionService":             &versionService,
   280  	"walletrpc.WalletService":              &walletService,
   281  	"walletrpc.WalletLoaderService":        &loaderService,
   282  	"walletrpc.SeedService":                &seedService,
   283  	"walletrpc.AccountMixerService":        &accountMixerService,
   284  	"walletrpc.TicketBuyerV2Service":       &ticketBuyerV2Service,
   285  	"walletrpc.AgendaService":              &agendaService,
   286  	"walletrpc.VotingService":              &votingService,
   287  	"walletrpc.MessageVerificationService": &messageVerificationService,
   288  	"walletrpc.DecodeMessageService":       &decodeMessageService,
   289  	"walletrpc.NetworkService":             &networkService,
   290  }
   291  
   292  // ServiceReady returns nil when the service is ready and a gRPC error when not.
   293  func ServiceReady(service string) error {
   294  	s, ok := serviceMap[service]
   295  	if !ok {
   296  		return status.Errorf(codes.Unimplemented, "service %s not found", service)
   297  	}
   298  	type readyChecker interface {
   299  		checkReady() bool
   300  	}
   301  	ready := true
   302  	r, ok := s.(readyChecker)
   303  	if ok {
   304  		ready = r.checkReady()
   305  	}
   306  	if !ready {
   307  		return status.Errorf(codes.FailedPrecondition, "service %v is not ready", service)
   308  	}
   309  	return nil
   310  }
   311  
   312  func (*versionServer) Version(ctx context.Context, req *pb.VersionRequest) (*pb.VersionResponse, error) {
   313  	return &pb.VersionResponse{
   314  		VersionString: semverString,
   315  		Major:         semverMajor,
   316  		Minor:         semverMinor,
   317  		Patch:         semverPatch,
   318  	}, nil
   319  }
   320  
   321  type dialFunc func(ctx context.Context, network, addr string) (net.Conn, error)
   322  
   323  // StartWalletService starts the WalletService.
   324  func StartWalletService(server *grpc.Server, wallet *wallet.Wallet, dialCSPP dialFunc) {
   325  	if atomic.SwapUint32(&walletService.ready, 1) != 0 {
   326  		panic("service already started")
   327  	}
   328  	walletService.wallet = wallet
   329  	walletService.dialCSPPServer = dialCSPP
   330  }
   331  
   332  func (s *walletServer) checkReady() bool {
   333  	return atomic.LoadUint32(&s.ready) != 0
   334  }
   335  
   336  // requireNetworkBackend checks whether the wallet has been associated with the
   337  // consensus server RPC client, returning a gRPC error when it is not.
   338  func (s *walletServer) requireNetworkBackend() (wallet.NetworkBackend, error) {
   339  	n, err := s.wallet.NetworkBackend()
   340  	if err != nil {
   341  		return nil, status.Errorf(codes.FailedPrecondition,
   342  			"wallet is not associated with a consensus server RPC client")
   343  	}
   344  	return n, nil
   345  }
   346  
   347  func (s *walletServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PingResponse, error) {
   348  	return &pb.PingResponse{}, nil
   349  }
   350  
   351  func (s *walletServer) Network(ctx context.Context, req *pb.NetworkRequest) (
   352  	*pb.NetworkResponse, error) {
   353  
   354  	return &pb.NetworkResponse{ActiveNetwork: uint32(s.wallet.ChainParams().Net)}, nil
   355  }
   356  
   357  func (s *walletServer) CoinType(ctx context.Context, req *pb.CoinTypeRequest) (*pb.CoinTypeResponse, error) {
   358  	coinType, err := s.wallet.CoinType(ctx)
   359  	if err != nil {
   360  		return nil, translateError(err)
   361  	}
   362  	return &pb.CoinTypeResponse{CoinType: coinType}, nil
   363  }
   364  
   365  func (s *walletServer) AccountNumber(ctx context.Context, req *pb.AccountNumberRequest) (
   366  	*pb.AccountNumberResponse, error) {
   367  
   368  	accountNum, err := s.wallet.AccountNumber(ctx, req.AccountName)
   369  	if err != nil {
   370  		return nil, translateError(err)
   371  	}
   372  
   373  	return &pb.AccountNumberResponse{AccountNumber: accountNum}, nil
   374  }
   375  
   376  func (s *walletServer) Accounts(ctx context.Context, req *pb.AccountsRequest) (*pb.AccountsResponse, error) {
   377  	resp, err := s.wallet.Accounts(ctx)
   378  	if err != nil {
   379  		return nil, translateError(err)
   380  	}
   381  	accounts := make([]*pb.AccountsResponse_Account, len(resp.Accounts))
   382  	for i := range resp.Accounts {
   383  		a := &resp.Accounts[i]
   384  		accounts[i] = &pb.AccountsResponse_Account{
   385  			AccountNumber: a.AccountNumber,
   386  			AccountName:   a.AccountName,
   387  			TotalBalance:  int64(a.TotalBalance),
   388  			// indexes are zero based, add 1 for the count
   389  			ExternalKeyCount: a.LastReturnedExternalIndex + 1,
   390  			InternalKeyCount: a.LastReturnedInternalIndex + 1,
   391  			ImportedKeyCount: a.ImportedKeyCount,
   392  			AccountEncrypted: a.AccountEncrypted,
   393  			AccountUnlocked:  a.AccountUnlocked,
   394  		}
   395  	}
   396  	return &pb.AccountsResponse{
   397  		Accounts:           accounts,
   398  		CurrentBlockHash:   resp.CurrentBlockHash[:],
   399  		CurrentBlockHeight: resp.CurrentBlockHeight,
   400  	}, nil
   401  }
   402  
   403  func (s *walletServer) RenameAccount(ctx context.Context, req *pb.RenameAccountRequest) (
   404  	*pb.RenameAccountResponse, error) {
   405  
   406  	err := s.wallet.RenameAccount(ctx, req.AccountNumber, req.NewName)
   407  	if err != nil {
   408  		return nil, translateError(err)
   409  	}
   410  
   411  	return &pb.RenameAccountResponse{}, nil
   412  }
   413  
   414  func (s *walletServer) PublishUnminedTransactions(ctx context.Context, req *pb.PublishUnminedTransactionsRequest) (
   415  	*pb.PublishUnminedTransactionsResponse, error) {
   416  	n, err := s.requireNetworkBackend()
   417  	if err != nil {
   418  		return nil, err
   419  	}
   420  	err = s.wallet.PublishUnminedTransactions(ctx, n)
   421  	if err != nil {
   422  		return nil, translateError(err)
   423  	}
   424  
   425  	return &pb.PublishUnminedTransactionsResponse{}, nil
   426  }
   427  
   428  func (s *walletServer) Rescan(req *pb.RescanRequest, svr pb.WalletService_RescanServer) error {
   429  	n, err := s.requireNetworkBackend()
   430  	if err != nil {
   431  		return err
   432  	}
   433  
   434  	var blockID *wallet.BlockIdentifier
   435  	switch {
   436  	case req.BeginHash != nil && req.BeginHeight != 0:
   437  		return status.Errorf(codes.InvalidArgument, "begin hash and height must not be set together")
   438  	case req.BeginHeight < 0:
   439  		return status.Errorf(codes.InvalidArgument, "begin height must be non-negative")
   440  	case req.BeginHash != nil:
   441  		blockHash, err := chainhash.NewHash(req.BeginHash)
   442  		if err != nil {
   443  			return status.Errorf(codes.InvalidArgument, "block hash has invalid length")
   444  		}
   445  		blockID = wallet.NewBlockIdentifierFromHash(blockHash)
   446  	default:
   447  		blockID = wallet.NewBlockIdentifierFromHeight(req.BeginHeight)
   448  	}
   449  
   450  	b, err := s.wallet.BlockInfo(svr.Context(), blockID)
   451  	if err != nil {
   452  		return translateError(err)
   453  	}
   454  
   455  	progress := make(chan wallet.RescanProgress, 1)
   456  	go s.wallet.RescanProgressFromHeight(svr.Context(), n, b.Height, progress)
   457  
   458  	for p := range progress {
   459  		if p.Err != nil {
   460  			return translateError(p.Err)
   461  		}
   462  		resp := &pb.RescanResponse{RescannedThrough: p.ScannedThrough}
   463  		err := svr.Send(resp)
   464  		if err != nil {
   465  			return translateError(err)
   466  		}
   467  	}
   468  	// finished or cancelled rescan without error
   469  	select {
   470  	case <-svr.Context().Done():
   471  		return status.Errorf(codes.Canceled, "rescan canceled")
   472  	default:
   473  		return nil
   474  	}
   475  }
   476  
   477  func zero(b []byte) {
   478  	for i := range b {
   479  		b[i] = 0
   480  	}
   481  }
   482  
   483  func (s *walletServer) NextAccount(ctx context.Context, req *pb.NextAccountRequest) (
   484  	*pb.NextAccountResponse, error) {
   485  
   486  	defer zero(req.Passphrase)
   487  
   488  	if req.AccountName == "" {
   489  		return nil, status.Errorf(codes.InvalidArgument, "account name may not be empty")
   490  	}
   491  
   492  	if len(req.Passphrase) > 0 {
   493  		lock := make(chan time.Time, 1)
   494  		defer func() {
   495  			lock <- time.Time{} // send matters, not the value
   496  		}()
   497  		err := s.wallet.Unlock(ctx, req.Passphrase, lock)
   498  		if err != nil {
   499  			return nil, translateError(err)
   500  		}
   501  	}
   502  
   503  	account, err := s.wallet.NextAccount(ctx, req.AccountName)
   504  	if err != nil {
   505  		return nil, translateError(err)
   506  	}
   507  
   508  	return &pb.NextAccountResponse{AccountNumber: account}, nil
   509  }
   510  
   511  func (s *walletServer) NextAddress(ctx context.Context, req *pb.NextAddressRequest) (
   512  	*pb.NextAddressResponse, error) {
   513  
   514  	var callOpts []wallet.NextAddressCallOption
   515  	switch req.GapPolicy {
   516  	case pb.NextAddressRequest_GAP_POLICY_UNSPECIFIED:
   517  	case pb.NextAddressRequest_GAP_POLICY_ERROR:
   518  		callOpts = append(callOpts, wallet.WithGapPolicyError())
   519  	case pb.NextAddressRequest_GAP_POLICY_IGNORE:
   520  		callOpts = append(callOpts, wallet.WithGapPolicyIgnore())
   521  	case pb.NextAddressRequest_GAP_POLICY_WRAP:
   522  		callOpts = append(callOpts, wallet.WithGapPolicyWrap())
   523  	default:
   524  		return nil, status.Errorf(codes.InvalidArgument, "gap_policy=%v", req.GapPolicy)
   525  	}
   526  
   527  	var (
   528  		addr stdaddr.Address
   529  		err  error
   530  	)
   531  	switch req.Kind {
   532  	case pb.NextAddressRequest_BIP0044_EXTERNAL:
   533  		addr, err = s.wallet.NewExternalAddress(ctx, req.Account, callOpts...)
   534  		if err != nil {
   535  			return nil, translateError(err)
   536  		}
   537  	case pb.NextAddressRequest_BIP0044_INTERNAL:
   538  		addr, err = s.wallet.NewInternalAddress(ctx, req.Account, callOpts...)
   539  		if err != nil {
   540  			return nil, translateError(err)
   541  		}
   542  	default:
   543  		return nil, status.Errorf(codes.InvalidArgument, "kind=%v", req.Kind)
   544  	}
   545  	if err != nil {
   546  		return nil, translateError(err)
   547  	}
   548  
   549  	var pubKeyAddrString string
   550  	switch addr := addr.(type) {
   551  	case wallet.PubKeyHashAddress:
   552  		pubKey := addr.PubKey()
   553  		pubKeyAddr, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0Raw(
   554  			pubKey, s.wallet.ChainParams())
   555  		if err != nil {
   556  			return nil, translateError(err)
   557  		}
   558  		pubKeyAddrString = pubKeyAddr.String()
   559  	}
   560  
   561  	return &pb.NextAddressResponse{
   562  		Address:   addr.String(),
   563  		PublicKey: pubKeyAddrString,
   564  	}, nil
   565  }
   566  
   567  func (s *walletServer) Address(ctx context.Context, req *pb.AddressRequest) (
   568  	*pb.AddressResponse, error) {
   569  	var branch uint32
   570  	switch req.Kind {
   571  	case pb.AddressRequest_BIP0044_EXTERNAL:
   572  	case pb.AddressRequest_BIP0044_INTERNAL:
   573  		branch = 1
   574  	default:
   575  		return nil, status.Errorf(codes.InvalidArgument, "kind=%v", req.Kind)
   576  	}
   577  	addr, err := s.wallet.AddressAtIdx(ctx, req.Account, branch, req.Index)
   578  	if err != nil {
   579  		return nil, translateError(err)
   580  	}
   581  	var pubKeyAddrString string
   582  	switch addr := addr.(type) {
   583  	case wallet.PubKeyHashAddress:
   584  		pubKey := addr.PubKey()
   585  		pubKeyAddr, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0Raw(
   586  			pubKey, s.wallet.ChainParams())
   587  		if err != nil {
   588  			return nil, translateError(err)
   589  		}
   590  		pubKeyAddrString = pubKeyAddr.String()
   591  	}
   592  
   593  	return &pb.AddressResponse{
   594  		Address:   addr.String(),
   595  		PublicKey: pubKeyAddrString,
   596  	}, nil
   597  }
   598  
   599  func (s *walletServer) DumpPrivateKey(ctx context.Context, req *pb.DumpPrivateKeyRequest) (
   600  	*pb.DumpPrivateKeyResponse, error) {
   601  
   602  	addr, err := decodeAddress(req.Address, s.wallet.ChainParams())
   603  	if err != nil {
   604  		return nil, err
   605  	}
   606  
   607  	key, err := s.wallet.DumpWIFPrivateKey(ctx, addr)
   608  	if err != nil {
   609  		return nil, translateError(err)
   610  	}
   611  	return &pb.DumpPrivateKeyResponse{PrivateKeyWif: key}, nil
   612  }
   613  
   614  func (s *walletServer) ImportPrivateKey(ctx context.Context, req *pb.ImportPrivateKeyRequest) (
   615  	*pb.ImportPrivateKeyResponse, error) {
   616  
   617  	defer zero(req.Passphrase)
   618  
   619  	wif, err := dcrutil.DecodeWIF(req.PrivateKeyWif, s.wallet.ChainParams().PrivateKeyID)
   620  	if err != nil {
   621  		return nil, status.Errorf(codes.InvalidArgument,
   622  			"Invalid WIF-encoded private key: %v", err)
   623  	}
   624  
   625  	if len(req.Passphrase) > 0 {
   626  		lock := make(chan time.Time, 1)
   627  		defer func() {
   628  			lock <- time.Time{} // send matters, not the value
   629  		}()
   630  		err = s.wallet.Unlock(ctx, req.Passphrase, lock)
   631  		if err != nil {
   632  			return nil, translateError(err)
   633  		}
   634  	}
   635  
   636  	// At the moment, only the special-cased import account can be used to
   637  	// import keys.
   638  	if req.Account != udb.ImportedAddrAccount {
   639  		return nil, status.Errorf(codes.InvalidArgument,
   640  			"Only the imported account accepts private key imports")
   641  	}
   642  
   643  	if req.ScanFrom < 0 {
   644  		return nil, status.Errorf(codes.InvalidArgument,
   645  			"Attempted to scan from a negative block height")
   646  	}
   647  
   648  	if req.ScanFrom > 0 && req.Rescan {
   649  		return nil, status.Errorf(codes.InvalidArgument,
   650  			"Passed a rescan height without rescan set")
   651  	}
   652  
   653  	n, err := s.requireNetworkBackend()
   654  	if err != nil {
   655  		return nil, err
   656  	}
   657  
   658  	_, err = s.wallet.ImportPrivateKey(ctx, wif)
   659  	if err != nil {
   660  		return nil, translateError(err)
   661  	}
   662  
   663  	if req.Rescan {
   664  		go s.wallet.RescanFromHeight(context.Background(), n, req.ScanFrom)
   665  	}
   666  
   667  	return &pb.ImportPrivateKeyResponse{}, nil
   668  }
   669  
   670  func (s *walletServer) ImportExtendedPublicKey(ctx context.Context, req *pb.ImportExtendedPublicKeyRequest) (
   671  	*pb.ImportExtendedPublicKeyResponse, error) {
   672  
   673  	xpub, err := hdkeychain.NewKeyFromString(req.Xpub, s.wallet.ChainParams())
   674  	if err != nil {
   675  		return nil, err
   676  	}
   677  
   678  	n, err := s.requireNetworkBackend()
   679  	if err != nil {
   680  		return nil, err
   681  	}
   682  
   683  	err = s.wallet.ImportXpubAccount(ctx, req.AccountName, xpub)
   684  	if err != nil {
   685  		return nil, translateError(err)
   686  	}
   687  
   688  	if req.Rescan {
   689  		go s.wallet.RescanFromHeight(context.Background(), n, req.ScanFrom)
   690  	}
   691  
   692  	return &pb.ImportExtendedPublicKeyResponse{}, nil
   693  }
   694  
   695  func (s *walletServer) ImportScript(ctx context.Context,
   696  	req *pb.ImportScriptRequest) (*pb.ImportScriptResponse, error) {
   697  
   698  	// TODO: Rather than assuming a script version, it must become a parameter
   699  	// to the request.
   700  	sc, addrs := stdscript.ExtractAddrs(scriptVersionAssumed, req.Script, s.wallet.ChainParams())
   701  	requiredSigs := stdscript.DetermineRequiredSigs(scriptVersionAssumed, req.Script)
   702  	// NOTE: not explicitly disallowing len(addrs) == 0 && requiredSigs == 0
   703  	var ownAddrs uint16
   704  	for _, a := range addrs {
   705  		haveAddr, err := s.wallet.HaveAddress(ctx, a)
   706  		if err != nil {
   707  			return nil, translateError(err)
   708  		}
   709  		if haveAddr {
   710  			ownAddrs++
   711  		}
   712  	}
   713  	redeemable := sc == stdscript.STMultiSig && ownAddrs >= requiredSigs
   714  	if !redeemable && req.RequireRedeemable {
   715  		return nil, status.Errorf(codes.FailedPrecondition,
   716  			"The script is not redeemable by the wallet")
   717  	}
   718  
   719  	if req.ScanFrom < 0 {
   720  		return nil, status.Errorf(codes.InvalidArgument,
   721  			"Attempted to scan from a negative block height")
   722  	}
   723  
   724  	if req.ScanFrom > 0 && req.Rescan {
   725  		return nil, status.Errorf(codes.InvalidArgument,
   726  			"Passed a rescan height without rescan set")
   727  	}
   728  
   729  	n, err := s.requireNetworkBackend()
   730  	if err != nil {
   731  		return nil, err
   732  	}
   733  
   734  	err = s.wallet.ImportScript(ctx, req.Script)
   735  	if err != nil && !errors.Is(err, errors.Exist) {
   736  		return nil, translateError(err)
   737  	}
   738  	if err == nil && req.Rescan {
   739  		go s.wallet.RescanFromHeight(context.Background(), n, req.ScanFrom)
   740  	}
   741  
   742  	p2sh, err := stdaddr.NewAddressScriptHashV0(req.Script, s.wallet.ChainParams())
   743  	if err != nil {
   744  		return nil, translateError(err)
   745  	}
   746  
   747  	return &pb.ImportScriptResponse{P2ShAddress: p2sh.String(), Redeemable: redeemable}, nil
   748  }
   749  
   750  func (s *walletServer) ImportVotingAccountFromSeed(ctx context.Context, req *pb.ImportVotingAccountFromSeedRequest) (
   751  	*pb.ImportVotingAccountFromSeedResponse, error) {
   752  
   753  	defer func() {
   754  		zero(req.Passphrase)
   755  		zero(req.Seed)
   756  	}()
   757  
   758  	seedSize := len(req.Seed)
   759  	if seedSize < hdkeychain.MinSeedBytes || seedSize > hdkeychain.MaxSeedBytes {
   760  		return nil, status.Errorf(codes.InvalidArgument, "invalid seed length")
   761  	}
   762  
   763  	if req.ScanFrom < 0 {
   764  		return nil, status.Errorf(codes.InvalidArgument,
   765  			"Attempted to scan from a negative block height")
   766  	}
   767  
   768  	if req.ScanFrom > 0 && req.Rescan {
   769  		return nil, status.Errorf(codes.InvalidArgument,
   770  			"Passed a rescan height without rescan set")
   771  	}
   772  
   773  	if req.DiscoverFrom < 0 {
   774  		return nil, status.Errorf(codes.InvalidArgument,
   775  			"Attempted to discover usage from a negative block height")
   776  	}
   777  
   778  	if req.DiscoverFrom > 0 && req.DiscoverUsage {
   779  		return nil, status.Errorf(codes.InvalidArgument,
   780  			"Passed a discover usage height without discover usage set")
   781  	}
   782  
   783  	var (
   784  		n   wallet.NetworkBackend
   785  		err error
   786  	)
   787  	if req.DiscoverUsage || req.Rescan {
   788  		n, err = s.requireNetworkBackend()
   789  		if err != nil {
   790  			return nil, status.Errorf(codes.Unknown, "Unable to retrieve network backend. Error: %v", err)
   791  		}
   792  	}
   793  
   794  	acctKeyPriv, err := s.wallet.VotingXprivFromSeed(req.Seed)
   795  	if err != nil {
   796  		return nil, translateError(err)
   797  	}
   798  
   799  	accountN, err := s.wallet.ImportVotingAccount(ctx, acctKeyPriv, req.Passphrase, req.Name)
   800  	if err != nil {
   801  		return nil, translateError(err)
   802  	}
   803  
   804  	if req.DiscoverUsage {
   805  		startBlock := s.wallet.ChainParams().GenesisHash
   806  		if req.DiscoverFrom != 0 {
   807  			blockID := wallet.NewBlockIdentifierFromHeight(req.DiscoverFrom)
   808  			b, err := s.wallet.BlockInfo(ctx, blockID)
   809  			if err != nil {
   810  				return nil, translateError(err)
   811  			}
   812  			startBlock = b.Hash
   813  		}
   814  
   815  		gapLimit := s.wallet.GapLimit()
   816  		if req.DiscoverGapLimit != 0 {
   817  			gapLimit = uint32(req.DiscoverGapLimit)
   818  		}
   819  
   820  		err = s.wallet.DiscoverActiveAddresses(ctx, n, &startBlock, false, gapLimit)
   821  		if err != nil {
   822  			return nil, err
   823  		}
   824  	}
   825  
   826  	if req.Rescan {
   827  		go s.wallet.RescanFromHeight(context.Background(), n, req.ScanFrom)
   828  	}
   829  
   830  	return &pb.ImportVotingAccountFromSeedResponse{Account: accountN}, nil
   831  }
   832  
   833  func (s *walletServer) Balance(ctx context.Context, req *pb.BalanceRequest) (
   834  	*pb.BalanceResponse, error) {
   835  
   836  	account := req.AccountNumber
   837  	reqConfs := req.RequiredConfirmations
   838  	bals, err := s.wallet.AccountBalance(ctx, account, reqConfs)
   839  	if err != nil {
   840  		return nil, translateError(err)
   841  	}
   842  
   843  	// TODO: Spendable currently includes multisig outputs that may not
   844  	// actually be spendable without additional keys.
   845  	resp := &pb.BalanceResponse{
   846  		Total:                   int64(bals.Total),
   847  		Spendable:               int64(bals.Spendable),
   848  		ImmatureReward:          int64(bals.ImmatureCoinbaseRewards),
   849  		ImmatureStakeGeneration: int64(bals.ImmatureStakeGeneration),
   850  		LockedByTickets:         int64(bals.LockedByTickets),
   851  		VotingAuthority:         int64(bals.VotingAuthority),
   852  		Unconfirmed:             int64(bals.Unconfirmed),
   853  	}
   854  	return resp, nil
   855  }
   856  
   857  func (s *walletServer) TicketPrice(ctx context.Context, req *pb.TicketPriceRequest) (*pb.TicketPriceResponse, error) {
   858  	sdiff, err := s.wallet.NextStakeDifficulty(ctx)
   859  	if err != nil {
   860  		return nil, translateError(err)
   861  	}
   862  	_, tipHeight := s.wallet.MainChainTip(ctx)
   863  	resp := &pb.TicketPriceResponse{
   864  		TicketPrice: int64(sdiff),
   865  		Height:      tipHeight,
   866  	}
   867  	return resp, nil
   868  }
   869  
   870  func (s *walletServer) StakeInfo(ctx context.Context, req *pb.StakeInfoRequest) (*pb.StakeInfoResponse, error) {
   871  	var rpc *dcrd.RPC
   872  	n, _ := s.wallet.NetworkBackend()
   873  	if client, ok := n.(*dcrd.RPC); ok {
   874  		rpc = client
   875  	}
   876  	var si *wallet.StakeInfoData
   877  	var err error
   878  	if rpc != nil {
   879  		si, err = s.wallet.StakeInfoPrecise(ctx, rpc)
   880  	} else {
   881  		si, err = s.wallet.StakeInfo(ctx)
   882  	}
   883  	if err != nil {
   884  		return nil, translateError(err)
   885  	}
   886  
   887  	return &pb.StakeInfoResponse{
   888  		PoolSize:      si.PoolSize,
   889  		AllMempoolTix: si.AllMempoolTix,
   890  		OwnMempoolTix: si.OwnMempoolTix,
   891  		Immature:      si.Immature,
   892  		Live:          si.Live,
   893  		Voted:         si.Voted,
   894  		Missed:        si.Missed,
   895  		Revoked:       si.Revoked,
   896  		Expired:       si.Expired,
   897  		TotalSubsidy:  int64(si.TotalSubsidy),
   898  		Unspent:       si.Unspent,
   899  	}, nil
   900  }
   901  
   902  // scriptChangeSource is a ChangeSource which is used to
   903  // receive all correlated previous input value.
   904  type scriptChangeSource struct {
   905  	version uint16
   906  	script  []byte
   907  }
   908  
   909  func (src *scriptChangeSource) Script() ([]byte, uint16, error) {
   910  	return src.script, src.version, nil
   911  }
   912  
   913  func (src *scriptChangeSource) ScriptSize() int {
   914  	return len(src.script)
   915  }
   916  
   917  func makeScriptChangeSource(address string, params *chaincfg.Params) (*scriptChangeSource, error) {
   918  	destinationAddress, err := stdaddr.DecodeAddress(address, params)
   919  	if err != nil {
   920  		return nil, err
   921  	}
   922  	version, script := destinationAddress.PaymentScript()
   923  	source := &scriptChangeSource{
   924  		version: version,
   925  		script:  script,
   926  	}
   927  	return source, nil
   928  }
   929  
   930  func sumOutputValues(outputs []*wire.TxOut) (totalOutput dcrutil.Amount) {
   931  	for _, txOut := range outputs {
   932  		totalOutput += dcrutil.Amount(txOut.Value)
   933  	}
   934  	return totalOutput
   935  }
   936  
   937  func (s *walletServer) SweepAccount(ctx context.Context, req *pb.SweepAccountRequest) (*pb.SweepAccountResponse, error) {
   938  	feePerKb := s.wallet.RelayFee()
   939  
   940  	// Use provided fee per Kb if specified.
   941  	if req.FeePerKb < 0 {
   942  		return nil, status.Errorf(codes.InvalidArgument, "%s",
   943  			"fee per kb argument cannot be negative")
   944  	}
   945  
   946  	if req.FeePerKb > 0 {
   947  		var err error
   948  		feePerKb, err = dcrutil.NewAmount(req.FeePerKb)
   949  		if err != nil {
   950  			return nil, status.Errorf(codes.InvalidArgument, "%v", err)
   951  		}
   952  	}
   953  
   954  	account, err := s.wallet.AccountNumber(ctx, req.SourceAccount)
   955  	if err != nil {
   956  		return nil, translateError(err)
   957  	}
   958  
   959  	changeSource, err := makeScriptChangeSource(req.DestinationAddress, s.wallet.ChainParams())
   960  	if err != nil {
   961  		return nil, translateError(err)
   962  	}
   963  
   964  	tx, err := s.wallet.NewUnsignedTransaction(ctx, nil, feePerKb, account,
   965  		int32(req.RequiredConfirmations), wallet.OutputSelectionAlgorithmAll,
   966  		changeSource, nil)
   967  	if err != nil {
   968  		return nil, translateError(err)
   969  	}
   970  
   971  	var txBuf bytes.Buffer
   972  	txBuf.Grow(tx.Tx.SerializeSize())
   973  	err = tx.Tx.Serialize(&txBuf)
   974  	if err != nil {
   975  		return nil, translateError(err)
   976  	}
   977  
   978  	res := &pb.SweepAccountResponse{
   979  		UnsignedTransaction:       txBuf.Bytes(),
   980  		TotalPreviousOutputAmount: int64(tx.TotalInput),
   981  		TotalOutputAmount:         int64(sumOutputValues(tx.Tx.TxOut)),
   982  		EstimatedSignedSize:       uint32(tx.EstimatedSignedSerializeSize),
   983  	}
   984  
   985  	return res, nil
   986  }
   987  
   988  func (s *walletServer) BlockInfo(ctx context.Context, req *pb.BlockInfoRequest) (*pb.BlockInfoResponse, error) {
   989  	var blockID *wallet.BlockIdentifier
   990  	switch {
   991  	case req.BlockHash != nil && req.BlockHeight != 0:
   992  		return nil, status.Errorf(codes.InvalidArgument, "block hash and height must not be set together")
   993  	case req.BlockHash != nil:
   994  		blockHash, err := chainhash.NewHash(req.BlockHash)
   995  		if err != nil {
   996  			return nil, status.Errorf(codes.InvalidArgument, "block hash has invalid length")
   997  		}
   998  		blockID = wallet.NewBlockIdentifierFromHash(blockHash)
   999  	default:
  1000  		blockID = wallet.NewBlockIdentifierFromHeight(req.BlockHeight)
  1001  	}
  1002  
  1003  	b, err := s.wallet.BlockInfo(ctx, blockID)
  1004  	if err != nil {
  1005  		return nil, translateError(err)
  1006  	}
  1007  
  1008  	header := new(wire.BlockHeader)
  1009  	err = header.Deserialize(bytes.NewReader(b.Header))
  1010  	if err != nil {
  1011  		return nil, status.Errorf(codes.Internal, "failed to deserialize saved block header: %v", err)
  1012  	}
  1013  
  1014  	return &pb.BlockInfoResponse{
  1015  		BlockHash:        b.Hash[:],
  1016  		BlockHeight:      b.Height,
  1017  		Confirmations:    b.Confirmations,
  1018  		Timestamp:        b.Timestamp,
  1019  		BlockHeader:      b.Header,
  1020  		StakeInvalidated: b.StakeInvalidated,
  1021  		ApprovesParent:   header.VoteBits&dcrutil.BlockValid != 0,
  1022  	}, nil
  1023  }
  1024  
  1025  func (s *walletServer) UnspentOutputs(req *pb.UnspentOutputsRequest, svr pb.WalletService_UnspentOutputsServer) error {
  1026  	policy := wallet.OutputSelectionPolicy{
  1027  		Account:               req.Account,
  1028  		RequiredConfirmations: req.RequiredConfirmations,
  1029  	}
  1030  	inputDetail, err := s.wallet.SelectInputs(svr.Context(), dcrutil.Amount(req.TargetAmount), policy)
  1031  	// Do not return errors to caller when there was insufficient spendable
  1032  	// outputs available for the target amount.
  1033  	if err != nil && !errors.Is(err, errors.InsufficientBalance) {
  1034  		return translateError(err)
  1035  	}
  1036  
  1037  	var sum int64
  1038  	for i, input := range inputDetail.Inputs {
  1039  		select {
  1040  		case <-svr.Context().Done():
  1041  			return status.Errorf(codes.Canceled, "unspentoutputs cancelled")
  1042  		default:
  1043  			outputInfo, err := s.wallet.OutputInfo(svr.Context(), &input.PreviousOutPoint)
  1044  			if err != nil {
  1045  				return translateError(err)
  1046  			}
  1047  			unspentOutput := &pb.UnspentOutputResponse{
  1048  				TransactionHash: input.PreviousOutPoint.Hash[:],
  1049  				OutputIndex:     input.PreviousOutPoint.Index,
  1050  				Tree:            int32(input.PreviousOutPoint.Tree),
  1051  				Amount:          int64(outputInfo.Amount),
  1052  				PkScript:        inputDetail.Scripts[i],
  1053  				ReceiveTime:     outputInfo.Received.Unix(),
  1054  				FromCoinbase:    outputInfo.FromCoinbase,
  1055  			}
  1056  
  1057  			sum += unspentOutput.Amount
  1058  			unspentOutput.AmountSum = sum
  1059  
  1060  			err = svr.Send(unspentOutput)
  1061  			if err != nil {
  1062  				return translateError(err)
  1063  			}
  1064  		}
  1065  	}
  1066  	return nil
  1067  }
  1068  
  1069  func (s *walletServer) FundTransaction(ctx context.Context, req *pb.FundTransactionRequest) (
  1070  	*pb.FundTransactionResponse, error) {
  1071  
  1072  	policy := wallet.OutputSelectionPolicy{
  1073  		Account:               req.Account,
  1074  		RequiredConfirmations: req.RequiredConfirmations,
  1075  	}
  1076  	inputDetail, err := s.wallet.SelectInputs(ctx, dcrutil.Amount(req.TargetAmount), policy)
  1077  	// Do not return errors to caller when there was insufficient spendable
  1078  	// outputs available for the target amount.
  1079  	if err != nil && !errors.Is(err, errors.InsufficientBalance) {
  1080  		return nil, translateError(err)
  1081  	}
  1082  
  1083  	selectedOutputs := make([]*pb.FundTransactionResponse_PreviousOutput, len(inputDetail.Inputs))
  1084  	for i, input := range inputDetail.Inputs {
  1085  		outputInfo, err := s.wallet.OutputInfo(ctx, &input.PreviousOutPoint)
  1086  		if err != nil {
  1087  			return nil, translateError(err)
  1088  		}
  1089  		selectedOutputs[i] = &pb.FundTransactionResponse_PreviousOutput{
  1090  			TransactionHash: input.PreviousOutPoint.Hash[:],
  1091  			OutputIndex:     input.PreviousOutPoint.Index,
  1092  			Tree:            int32(input.PreviousOutPoint.Tree),
  1093  			Amount:          int64(outputInfo.Amount),
  1094  			PkScript:        inputDetail.Scripts[i],
  1095  			ReceiveTime:     outputInfo.Received.Unix(),
  1096  			FromCoinbase:    outputInfo.FromCoinbase,
  1097  		}
  1098  	}
  1099  
  1100  	var changeScript []byte
  1101  	if req.IncludeChangeScript && inputDetail.Amount > dcrutil.Amount(req.TargetAmount) {
  1102  		changeAddr, err := s.wallet.NewChangeAddress(ctx, req.Account)
  1103  		if err != nil {
  1104  			return nil, translateError(err)
  1105  		}
  1106  		_, changeScript = changeAddr.PaymentScript()
  1107  	}
  1108  
  1109  	return &pb.FundTransactionResponse{
  1110  		SelectedOutputs: selectedOutputs,
  1111  		TotalAmount:     int64(inputDetail.Amount),
  1112  		ChangePkScript:  changeScript,
  1113  	}, nil
  1114  }
  1115  
  1116  func decodeDestination(dest *pb.ConstructTransactionRequest_OutputDestination,
  1117  	chainParams *chaincfg.Params) (pkScript []byte, version uint16, err error) {
  1118  
  1119  	switch {
  1120  	case dest == nil:
  1121  		fallthrough
  1122  	default:
  1123  		return nil, 0, status.Errorf(codes.InvalidArgument, "unknown or missing output destination")
  1124  
  1125  	case dest.Address != "":
  1126  		addr, err := decodeAddress(dest.Address, chainParams)
  1127  		if err != nil {
  1128  			return nil, 0, err
  1129  		}
  1130  		version, pkScript = addr.PaymentScript()
  1131  		return pkScript, version, nil
  1132  	case dest.Script != nil:
  1133  		if dest.ScriptVersion > uint32(^uint16(0)) {
  1134  			return nil, 0, status.Errorf(codes.InvalidArgument, "script_version overflows uint16")
  1135  		}
  1136  		return dest.Script, uint16(dest.ScriptVersion), nil
  1137  	}
  1138  }
  1139  
  1140  type txChangeSource struct {
  1141  	version uint16
  1142  	script  []byte
  1143  }
  1144  
  1145  func (src *txChangeSource) Script() ([]byte, uint16, error) {
  1146  	return src.script, src.version, nil
  1147  }
  1148  
  1149  func (src *txChangeSource) ScriptSize() int {
  1150  	return len(src.script)
  1151  }
  1152  
  1153  func makeTxChangeSource(destination *pb.ConstructTransactionRequest_OutputDestination,
  1154  	chainParams *chaincfg.Params) (*txChangeSource, error) {
  1155  	script, version, err := decodeDestination(destination, chainParams)
  1156  	if err != nil {
  1157  		return nil, err
  1158  	}
  1159  	changeSource := &txChangeSource{
  1160  		script:  script,
  1161  		version: version,
  1162  	}
  1163  	return changeSource, nil
  1164  }
  1165  
  1166  func (s *walletServer) ConstructTransaction(ctx context.Context, req *pb.ConstructTransactionRequest) (
  1167  	*pb.ConstructTransactionResponse, error) {
  1168  
  1169  	chainParams := s.wallet.ChainParams()
  1170  
  1171  	if len(req.NonChangeOutputs) == 0 && req.ChangeDestination == nil {
  1172  		return nil, status.Errorf(codes.InvalidArgument,
  1173  			"non_change_outputs and change_destination may not both be empty or null")
  1174  	}
  1175  
  1176  	outputs := make([]*wire.TxOut, 0, len(req.NonChangeOutputs))
  1177  	for _, o := range req.NonChangeOutputs {
  1178  		script, version, err := decodeDestination(o.Destination, chainParams)
  1179  		if err != nil {
  1180  			return nil, err
  1181  		}
  1182  		output := &wire.TxOut{
  1183  			Value:    o.Amount,
  1184  			Version:  version,
  1185  			PkScript: script,
  1186  		}
  1187  		outputs = append(outputs, output)
  1188  	}
  1189  
  1190  	var algo wallet.OutputSelectionAlgorithm
  1191  	switch req.OutputSelectionAlgorithm {
  1192  	case pb.ConstructTransactionRequest_UNSPECIFIED:
  1193  		algo = wallet.OutputSelectionAlgorithmDefault
  1194  	case pb.ConstructTransactionRequest_ALL:
  1195  		algo = wallet.OutputSelectionAlgorithmAll
  1196  	default:
  1197  		return nil, status.Errorf(codes.InvalidArgument, "unknown output selection algorithm")
  1198  	}
  1199  
  1200  	feePerKb := txrules.DefaultRelayFeePerKb
  1201  	if req.FeePerKb != 0 {
  1202  		feePerKb = dcrutil.Amount(req.FeePerKb)
  1203  	}
  1204  
  1205  	var changeSource txauthor.ChangeSource
  1206  	var err error
  1207  	if req.ChangeDestination != nil {
  1208  		changeSource, err = makeTxChangeSource(req.ChangeDestination, chainParams)
  1209  		if err != nil {
  1210  			return nil, translateError(err)
  1211  		}
  1212  	}
  1213  
  1214  	tx, err := s.wallet.NewUnsignedTransaction(ctx, outputs, feePerKb, req.SourceAccount,
  1215  		req.RequiredConfirmations, algo, changeSource, nil)
  1216  	if err != nil {
  1217  		return nil, translateError(err)
  1218  	}
  1219  
  1220  	if tx.ChangeIndex >= 0 {
  1221  		tx.RandomizeChangePosition()
  1222  	}
  1223  
  1224  	var txBuf bytes.Buffer
  1225  	txBuf.Grow(tx.Tx.SerializeSize())
  1226  	err = tx.Tx.Serialize(&txBuf)
  1227  	if err != nil {
  1228  		return nil, translateError(err)
  1229  	}
  1230  
  1231  	res := &pb.ConstructTransactionResponse{
  1232  		UnsignedTransaction:       txBuf.Bytes(),
  1233  		TotalPreviousOutputAmount: int64(tx.TotalInput),
  1234  		TotalOutputAmount:         int64(sumOutputValues(tx.Tx.TxOut)),
  1235  		EstimatedSignedSize:       uint32(tx.EstimatedSignedSerializeSize),
  1236  		ChangeIndex:               int32(tx.ChangeIndex),
  1237  	}
  1238  	return res, nil
  1239  }
  1240  
  1241  func (s *walletServer) GetAccountExtendedPubKey(ctx context.Context, req *pb.GetAccountExtendedPubKeyRequest) (*pb.GetAccountExtendedPubKeyResponse, error) {
  1242  	accExtendedPubKey, err := s.wallet.AccountXpub(ctx, req.AccountNumber)
  1243  	if err != nil {
  1244  		return nil, err
  1245  	}
  1246  	res := &pb.GetAccountExtendedPubKeyResponse{
  1247  		AccExtendedPubKey: accExtendedPubKey.String(),
  1248  	}
  1249  	return res, nil
  1250  }
  1251  
  1252  func (s *walletServer) GetAccountExtendedPrivKey(ctx context.Context, req *pb.GetAccountExtendedPrivKeyRequest) (*pb.GetAccountExtendedPrivKeyResponse, error) {
  1253  	if len(req.Passphrase) > 0 {
  1254  		lock := make(chan time.Time, 1)
  1255  		lockWallet := func() {
  1256  			lock <- time.Time{}
  1257  			zero(req.Passphrase)
  1258  		}
  1259  
  1260  		err := s.wallet.Unlock(ctx, req.Passphrase, lock)
  1261  		if err != nil {
  1262  			return nil, translateError(err)
  1263  		}
  1264  		defer lockWallet()
  1265  	}
  1266  
  1267  	accExtendedPrivKey, err := s.wallet.AccountXpriv(ctx, req.AccountNumber)
  1268  	if err != nil {
  1269  		return nil, translateError(err)
  1270  	}
  1271  	res := &pb.GetAccountExtendedPrivKeyResponse{
  1272  		AccExtendedPrivKey: accExtendedPrivKey.String(),
  1273  	}
  1274  	return res, nil
  1275  }
  1276  
  1277  func (s *walletServer) GetTransaction(ctx context.Context, req *pb.GetTransactionRequest) (*pb.GetTransactionResponse, error) {
  1278  	txHash, err := chainhash.NewHash(req.TransactionHash)
  1279  	if err != nil {
  1280  		return nil, status.Errorf(codes.InvalidArgument, "transaction_hash has invalid length")
  1281  	}
  1282  
  1283  	txSummary, confs, blockHash, err := s.wallet.TransactionSummary(ctx, txHash)
  1284  	if err != nil {
  1285  		return nil, translateError(err)
  1286  	}
  1287  	resp := &pb.GetTransactionResponse{
  1288  		Transaction:   marshalTransactionDetails(txSummary),
  1289  		Confirmations: confs,
  1290  	}
  1291  	if blockHash != nil {
  1292  		resp.BlockHash = blockHash[:]
  1293  	}
  1294  	return resp, nil
  1295  }
  1296  
  1297  type blockRangeReq interface {
  1298  	GetStartingBlockHash() []byte
  1299  	GetStartingBlockHeight() int32
  1300  	GetEndingBlockHash() []byte
  1301  	GetEndingBlockHeight() int32
  1302  }
  1303  
  1304  // decodeBlockRange converts a request that has the standard set of
  1305  // StartingBlockHash/StartingBlockHeight, EndingBlockHash/EndingBlockHeight
  1306  // parameters into corresponding *wallet.BlockIdentifier for use in functions
  1307  // that range over blocks.
  1308  //
  1309  // For each (hash,height) pair of each (start,end) set, the following
  1310  // conversion is made:
  1311  //
  1312  // - Specifying both hash and height is an error
  1313  // - Non-nil hash is used
  1314  // - Non-zero height is used
  1315  // - Otherwise a nil identifier is returned
  1316  //
  1317  // This behavior makes the wallet's internal `blockRange` function match blocks
  1318  // over the entire range of mainchain blocks, including unmined transactions
  1319  // (when applicable).
  1320  func decodeBlockRange(req blockRangeReq) (*wallet.BlockIdentifier, *wallet.BlockIdentifier, error) {
  1321  	var startBlock, endBlock *wallet.BlockIdentifier
  1322  	sbh := req.GetStartingBlockHash()
  1323  	sh := req.GetStartingBlockHeight()
  1324  	ebh := req.GetEndingBlockHash()
  1325  	eh := req.GetEndingBlockHeight()
  1326  
  1327  	// Determine start block identifier.
  1328  	switch {
  1329  	case sbh != nil && sh != 0:
  1330  		return nil, nil, status.Errorf(codes.InvalidArgument,
  1331  			"starting block hash and height may not be specified simultaneously")
  1332  	case sbh != nil:
  1333  		hash, err := chainhash.NewHash(sbh)
  1334  		if err != nil {
  1335  			return nil, nil, status.Errorf(codes.InvalidArgument,
  1336  				"invalid starting block hash: %s", err.Error())
  1337  		}
  1338  		startBlock = wallet.NewBlockIdentifierFromHash(hash)
  1339  	case sh != 0:
  1340  		startBlock = wallet.NewBlockIdentifierFromHeight(sh)
  1341  	}
  1342  
  1343  	// Determine end block identifier.
  1344  	switch {
  1345  	case ebh != nil && eh != 0:
  1346  		return nil, nil, status.Errorf(codes.InvalidArgument,
  1347  			"ending block hash and height may not be specified simultaneously")
  1348  	case ebh != nil:
  1349  		hash, err := chainhash.NewHash(ebh)
  1350  		if err != nil {
  1351  			return nil, nil, status.Errorf(codes.InvalidArgument,
  1352  				"invalid ending block hash: %s", err.Error())
  1353  		}
  1354  		endBlock = wallet.NewBlockIdentifierFromHash(hash)
  1355  	case eh != 0:
  1356  		endBlock = wallet.NewBlockIdentifierFromHeight(eh)
  1357  	}
  1358  
  1359  	return startBlock, endBlock, nil
  1360  }
  1361  
  1362  // BUGS:
  1363  // - MinimumRecentTransactions is ignored.
  1364  // - Wrong error codes when a block height or hash is not recognized
  1365  func (s *walletServer) GetTransactions(req *pb.GetTransactionsRequest,
  1366  	server pb.WalletService_GetTransactionsServer) error {
  1367  
  1368  	startBlock, endBlock, err := decodeBlockRange(req)
  1369  	if err != nil {
  1370  		return err
  1371  	}
  1372  
  1373  	var minRecentTxs int
  1374  	if req.MinimumRecentTransactions != 0 {
  1375  		if endBlock != nil {
  1376  			return status.Errorf(codes.InvalidArgument,
  1377  				"ending block and minimum number of recent transactions "+
  1378  					"may not be specified simultaneously")
  1379  		}
  1380  		minRecentTxs = int(req.MinimumRecentTransactions)
  1381  		if minRecentTxs < 0 {
  1382  			return status.Errorf(codes.InvalidArgument,
  1383  				"minimum number of recent transactions may not be negative")
  1384  		}
  1385  	}
  1386  	_ = minRecentTxs
  1387  
  1388  	targetTxCount := int(req.TargetTransactionCount)
  1389  	if targetTxCount < 0 {
  1390  		return status.Errorf(codes.InvalidArgument,
  1391  			"maximum transaction count may not be negative")
  1392  	}
  1393  
  1394  	ctx := server.Context()
  1395  	txCount := 0
  1396  
  1397  	rangeFn := func(block *wallet.Block) (bool, error) {
  1398  		var resp *pb.GetTransactionsResponse
  1399  		if block.Header != nil {
  1400  			resp = &pb.GetTransactionsResponse{
  1401  				MinedTransactions: marshalBlock(block),
  1402  			}
  1403  		} else {
  1404  			resp = &pb.GetTransactionsResponse{
  1405  				UnminedTransactions: marshalTransactionDetailsSlice(block.Transactions),
  1406  			}
  1407  		}
  1408  		txCount += len(block.Transactions)
  1409  
  1410  		select {
  1411  		case <-ctx.Done():
  1412  			return true, ctx.Err()
  1413  		default:
  1414  			err := server.Send(resp)
  1415  			return (err != nil) || ((targetTxCount > 0) && (txCount >= targetTxCount)), err
  1416  		}
  1417  	}
  1418  
  1419  	err = s.wallet.GetTransactions(ctx, rangeFn, startBlock, endBlock)
  1420  	if err != nil {
  1421  		return translateError(err)
  1422  	}
  1423  
  1424  	return nil
  1425  }
  1426  
  1427  func (s *walletServer) GetTicket(ctx context.Context, req *pb.GetTicketRequest) (*pb.GetTicketsResponse, error) {
  1428  	ticketHash, err := chainhash.NewHash(req.TicketHash)
  1429  	if err != nil {
  1430  		return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  1431  	}
  1432  
  1433  	// The dcrd client could be nil here if the network backend is not
  1434  	// the consensus rpc client.  This is fine since the chain client is
  1435  	// optional.
  1436  	n, _ := s.wallet.NetworkBackend()
  1437  	rpc, _ := n.(*dcrd.RPC)
  1438  
  1439  	var ticketSummary *wallet.TicketSummary
  1440  	var blockHeader *wire.BlockHeader
  1441  	if rpc == nil {
  1442  		ticketSummary, blockHeader, err = s.wallet.GetTicketInfo(ctx, ticketHash)
  1443  	} else {
  1444  		ticketSummary, blockHeader, err =
  1445  			s.wallet.GetTicketInfoPrecise(ctx, rpc, ticketHash)
  1446  	}
  1447  	if err != nil {
  1448  		return nil, translateError(err)
  1449  	}
  1450  
  1451  	host, err := s.wallet.VSPHostForTicket(ctx, ticketHash)
  1452  	if err != nil && !errors.Is(err, errors.NotExist) {
  1453  		return nil, err
  1454  	}
  1455  
  1456  	resp := &pb.GetTicketsResponse{
  1457  		Ticket:  marshalTicketDetails(ticketSummary),
  1458  		Block:   marshalGetTicketBlockDetails(blockHeader),
  1459  		VspHost: host,
  1460  	}
  1461  	return resp, nil
  1462  }
  1463  
  1464  func (s *walletServer) GetTickets(req *pb.GetTicketsRequest,
  1465  	server pb.WalletService_GetTicketsServer) error {
  1466  
  1467  	startBlock, endBlock, err := decodeBlockRange(req)
  1468  	if err != nil {
  1469  		return err
  1470  	}
  1471  
  1472  	targetTicketCount := int(req.TargetTicketCount)
  1473  	if targetTicketCount < 0 {
  1474  		return status.Errorf(codes.InvalidArgument,
  1475  			"target ticket count may not be negative")
  1476  	}
  1477  
  1478  	ticketCount := 0
  1479  	ctx := server.Context()
  1480  
  1481  	rangeFn := func(tickets []*wallet.TicketSummary, block *wire.BlockHeader) (bool, error) {
  1482  		resp := &pb.GetTicketsResponse{
  1483  			Block: marshalGetTicketBlockDetails(block),
  1484  		}
  1485  
  1486  		// current contract for grpc GetTickets is for one ticket per response.
  1487  		// To make sure we don't miss any while paginating, we only check for
  1488  		// the targetTicketCount after sending all from this block.
  1489  		for _, t := range tickets {
  1490  			resp.Ticket = marshalTicketDetails(t)
  1491  
  1492  			host, err := s.wallet.VSPHostForTicket(ctx, t.Ticket.Hash)
  1493  			if err != nil && !errors.Is(err, errors.NotExist) {
  1494  				return true, err
  1495  			}
  1496  			resp.VspHost = host
  1497  
  1498  			err = server.Send(resp)
  1499  			if err != nil {
  1500  				return true, err
  1501  			}
  1502  		}
  1503  		ticketCount += len(tickets)
  1504  
  1505  		select {
  1506  		case <-ctx.Done():
  1507  			return true, ctx.Err()
  1508  		default:
  1509  			return ((targetTicketCount > 0) && (ticketCount >= targetTicketCount)), nil
  1510  		}
  1511  	}
  1512  	n, _ := s.wallet.NetworkBackend()
  1513  	if rpc, ok := n.(*dcrd.RPC); ok {
  1514  		err = s.wallet.GetTicketsPrecise(ctx, rpc, rangeFn, startBlock, endBlock)
  1515  	} else {
  1516  		err = s.wallet.GetTickets(ctx, rangeFn, startBlock, endBlock)
  1517  	}
  1518  	if err != nil {
  1519  		return translateError(err)
  1520  	}
  1521  
  1522  	return nil
  1523  }
  1524  
  1525  func (s *walletServer) ChangePassphrase(ctx context.Context, req *pb.ChangePassphraseRequest) (
  1526  	*pb.ChangePassphraseResponse, error) {
  1527  
  1528  	defer func() {
  1529  		zero(req.OldPassphrase)
  1530  		zero(req.NewPassphrase)
  1531  	}()
  1532  
  1533  	var (
  1534  		oldPass = req.OldPassphrase
  1535  		newPass = req.NewPassphrase
  1536  	)
  1537  
  1538  	var err error
  1539  	switch req.Key {
  1540  	case pb.ChangePassphraseRequest_PRIVATE:
  1541  		err = s.wallet.ChangePrivatePassphrase(ctx, oldPass, newPass)
  1542  	case pb.ChangePassphraseRequest_PUBLIC:
  1543  		if len(oldPass) == 0 {
  1544  			oldPass = []byte(wallet.InsecurePubPassphrase)
  1545  		}
  1546  		if len(newPass) == 0 {
  1547  			newPass = []byte(wallet.InsecurePubPassphrase)
  1548  		}
  1549  		err = s.wallet.ChangePublicPassphrase(ctx, oldPass, newPass)
  1550  	default:
  1551  		return nil, status.Errorf(codes.InvalidArgument, "Unknown key type (%d)", req.Key)
  1552  	}
  1553  	if err != nil {
  1554  		return nil, translateError(err)
  1555  	}
  1556  	return &pb.ChangePassphraseResponse{}, nil
  1557  }
  1558  
  1559  func (s *walletServer) SignTransaction(ctx context.Context, req *pb.SignTransactionRequest) (
  1560  	*pb.SignTransactionResponse, error) {
  1561  
  1562  	var tx wire.MsgTx
  1563  	err := tx.Deserialize(bytes.NewReader(req.SerializedTransaction))
  1564  	if err != nil {
  1565  		return nil, status.Errorf(codes.InvalidArgument,
  1566  			"Bytes do not represent a valid raw transaction: %v", err)
  1567  	}
  1568  
  1569  	if len(req.Passphrase) > 0 {
  1570  		lock := make(chan time.Time, 1)
  1571  		defer func() {
  1572  			lock <- time.Time{} // send matters, not the value
  1573  			zero(req.Passphrase)
  1574  		}()
  1575  		err = s.wallet.Unlock(ctx, req.Passphrase, lock)
  1576  		if err != nil {
  1577  			return nil, translateError(err)
  1578  		}
  1579  	}
  1580  
  1581  	var additionalPkScripts map[wire.OutPoint][]byte
  1582  	if len(req.AdditionalScripts) > 0 {
  1583  		additionalPkScripts = make(map[wire.OutPoint][]byte, len(req.AdditionalScripts))
  1584  		for _, script := range req.AdditionalScripts {
  1585  			op := wire.OutPoint{Index: script.OutputIndex, Tree: int8(script.Tree)}
  1586  			if len(script.TransactionHash) != chainhash.HashSize {
  1587  				return nil, status.Errorf(codes.InvalidArgument,
  1588  					"Invalid transaction hash length for script %v, expected %v got %v",
  1589  					script, chainhash.HashSize, len(script.TransactionHash))
  1590  			}
  1591  
  1592  			copy(op.Hash[:], script.TransactionHash)
  1593  			additionalPkScripts[op] = script.PkScript
  1594  		}
  1595  	}
  1596  
  1597  	invalidSigs, err := s.wallet.SignTransaction(ctx, &tx, txscript.SigHashAll, additionalPkScripts, nil, nil)
  1598  	if err != nil {
  1599  		return nil, translateError(err)
  1600  	}
  1601  
  1602  	invalidInputIndexes := make([]uint32, len(invalidSigs))
  1603  	for i, e := range invalidSigs {
  1604  		invalidInputIndexes[i] = e.InputIndex
  1605  	}
  1606  
  1607  	var serializedTransaction bytes.Buffer
  1608  	serializedTransaction.Grow(tx.SerializeSize())
  1609  	err = tx.Serialize(&serializedTransaction)
  1610  	if err != nil {
  1611  		return nil, translateError(err)
  1612  	}
  1613  
  1614  	resp := &pb.SignTransactionResponse{
  1615  		Transaction:          serializedTransaction.Bytes(),
  1616  		UnsignedInputIndexes: invalidInputIndexes,
  1617  	}
  1618  	return resp, nil
  1619  }
  1620  
  1621  func (s *walletServer) SignTransactions(ctx context.Context, req *pb.SignTransactionsRequest) (
  1622  	*pb.SignTransactionsResponse, error) {
  1623  	defer zero(req.Passphrase)
  1624  
  1625  	if len(req.Passphrase) > 0 {
  1626  		lock := make(chan time.Time, 1)
  1627  		defer func() {
  1628  			lock <- time.Time{} // send matters, not the value
  1629  		}()
  1630  		err := s.wallet.Unlock(ctx, req.Passphrase, lock)
  1631  		if err != nil {
  1632  			return nil, translateError(err)
  1633  		}
  1634  	}
  1635  
  1636  	var additionalPkScripts map[wire.OutPoint][]byte
  1637  	if len(req.AdditionalScripts) > 0 {
  1638  		additionalPkScripts = make(map[wire.OutPoint][]byte, len(req.AdditionalScripts))
  1639  		for _, script := range req.AdditionalScripts {
  1640  			op := wire.OutPoint{Index: script.OutputIndex, Tree: int8(script.Tree)}
  1641  			if len(script.TransactionHash) != chainhash.HashSize {
  1642  				return nil, status.Errorf(codes.InvalidArgument,
  1643  					"Invalid transaction hash length for script %v, expected %v got %v",
  1644  					script, chainhash.HashSize, len(script.TransactionHash))
  1645  			}
  1646  
  1647  			copy(op.Hash[:], script.TransactionHash)
  1648  			additionalPkScripts[op] = script.PkScript
  1649  		}
  1650  	}
  1651  
  1652  	resp := pb.SignTransactionsResponse{}
  1653  	resp.Transactions = make([]*pb.SignTransactionsResponse_SignedTransaction, 0, len(req.Transactions))
  1654  	for _, unsignedTx := range req.Transactions {
  1655  		var tx wire.MsgTx
  1656  		err := tx.Deserialize(bytes.NewReader(unsignedTx.SerializedTransaction))
  1657  		if err != nil {
  1658  			return nil, status.Errorf(codes.InvalidArgument,
  1659  				"Bytes do not represent a valid raw transaction: %v", err)
  1660  		}
  1661  
  1662  		invalidSigs, err := s.wallet.SignTransaction(ctx, &tx, txscript.SigHashAll, additionalPkScripts, nil, nil)
  1663  		if err != nil {
  1664  			return nil, translateError(err)
  1665  		}
  1666  
  1667  		invalidInputIndexes := make([]uint32, len(invalidSigs))
  1668  		for i, e := range invalidSigs {
  1669  			invalidInputIndexes[i] = e.InputIndex
  1670  		}
  1671  
  1672  		var serializedTransaction bytes.Buffer
  1673  		serializedTransaction.Grow(tx.SerializeSize())
  1674  		err = tx.Serialize(&serializedTransaction)
  1675  		if err != nil {
  1676  			return nil, translateError(err)
  1677  		}
  1678  
  1679  		resp.Transactions = append(resp.Transactions, &pb.SignTransactionsResponse_SignedTransaction{
  1680  			Transaction:          serializedTransaction.Bytes(),
  1681  			UnsignedInputIndexes: invalidInputIndexes,
  1682  		})
  1683  	}
  1684  
  1685  	return &resp, nil
  1686  }
  1687  
  1688  func (s *walletServer) CreateSignature(ctx context.Context, req *pb.CreateSignatureRequest) (
  1689  	*pb.CreateSignatureResponse, error) {
  1690  
  1691  	defer zero(req.Passphrase)
  1692  
  1693  	var tx wire.MsgTx
  1694  	err := tx.Deserialize(bytes.NewReader(req.SerializedTransaction))
  1695  	if err != nil {
  1696  		return nil, status.Errorf(codes.InvalidArgument,
  1697  			"Bytes do not represent a valid raw transaction: %v", err)
  1698  	}
  1699  
  1700  	if req.InputIndex >= uint32(len(tx.TxIn)) {
  1701  		return nil, status.Errorf(codes.InvalidArgument,
  1702  			"transaction input %d does not exist", req.InputIndex)
  1703  	}
  1704  
  1705  	if len(req.Passphrase) > 0 {
  1706  		lock := make(chan time.Time, 1)
  1707  		defer func() {
  1708  			lock <- time.Time{} // send matters, not the value
  1709  		}()
  1710  		err = s.wallet.Unlock(ctx, req.Passphrase, lock)
  1711  		if err != nil {
  1712  			return nil, translateError(err)
  1713  		}
  1714  	}
  1715  
  1716  	addr, err := decodeAddress(req.Address, s.wallet.ChainParams())
  1717  	if err != nil {
  1718  		return nil, err
  1719  	}
  1720  
  1721  	hashType := txscript.SigHashType(req.HashType)
  1722  	sig, pubkey, err := s.wallet.CreateSignature(ctx, &tx, req.InputIndex, addr, hashType, req.PreviousPkScript)
  1723  	if err != nil {
  1724  		return nil, translateError(err)
  1725  	}
  1726  
  1727  	return &pb.CreateSignatureResponse{Signature: sig, PublicKey: pubkey}, nil
  1728  }
  1729  
  1730  func (s *walletServer) PublishTransaction(ctx context.Context, req *pb.PublishTransactionRequest) (
  1731  	*pb.PublishTransactionResponse, error) {
  1732  
  1733  	n, err := s.requireNetworkBackend()
  1734  	if err != nil {
  1735  		return nil, err
  1736  	}
  1737  
  1738  	msgTx := new(wire.MsgTx)
  1739  	err = msgTx.Deserialize(bytes.NewReader(req.SignedTransaction))
  1740  	if err != nil {
  1741  		return nil, status.Errorf(codes.InvalidArgument,
  1742  			"Bytes do not represent a valid raw transaction: %v", err)
  1743  	}
  1744  
  1745  	if !s.wallet.AllowsHighFees() {
  1746  		highFees, err := txrules.TxPaysHighFees(msgTx)
  1747  		if err != nil {
  1748  			return nil, translateError(err)
  1749  		}
  1750  		if highFees {
  1751  			err := errors.E(errors.Policy, "high fees")
  1752  			return nil, translateError(err)
  1753  		}
  1754  	}
  1755  
  1756  	txHash, err := s.wallet.PublishTransaction(ctx, msgTx, n)
  1757  	if err != nil {
  1758  		return nil, translateError(err)
  1759  	}
  1760  
  1761  	return &pb.PublishTransactionResponse{TransactionHash: txHash[:]}, nil
  1762  }
  1763  
  1764  // PurchaseTickets purchases tickets from the wallet.
  1765  func (s *walletServer) PurchaseTickets(ctx context.Context,
  1766  	req *pb.PurchaseTicketsRequest) (*pb.PurchaseTicketsResponse, error) {
  1767  	// Unmarshall the received data and prepare it as input for the ticket
  1768  	// purchase request.
  1769  	spendLimit := dcrutil.Amount(req.SpendLimit)
  1770  	if spendLimit < 0 {
  1771  		return nil, status.Errorf(codes.InvalidArgument,
  1772  			"Negative spend limit given")
  1773  	}
  1774  
  1775  	minConf := int32(req.RequiredConfirmations)
  1776  	params := s.wallet.ChainParams()
  1777  
  1778  	var ticketAddr stdaddr.StakeAddress
  1779  	var err error
  1780  
  1781  	n, err := s.wallet.NetworkBackend()
  1782  	if err != nil {
  1783  		return nil, err
  1784  	}
  1785  
  1786  	if req.TicketAddress != "" {
  1787  		ticketAddr, err = decodeStakeAddress(req.TicketAddress, params)
  1788  		if err != nil {
  1789  			return nil, err
  1790  		}
  1791  	}
  1792  
  1793  	var poolAddr stdaddr.StakeAddress
  1794  	var poolFees float64
  1795  	if req.PoolAddress != "" {
  1796  		if req.VspHost != "" || req.VspPubkey != "" {
  1797  			return nil, status.Errorf(codes.InvalidArgument,
  1798  				"request contains both legacy stakepoold and vspd options.")
  1799  		}
  1800  		poolAddr, err = decodeStakeAddress(req.PoolAddress, params)
  1801  		if err != nil {
  1802  			return nil, err
  1803  		}
  1804  	}
  1805  
  1806  	// new vspd request
  1807  	var vspHost string
  1808  	var vspPubKey string
  1809  	var vspClient *vsp.Client
  1810  	if req.VspHost != "" || req.VspPubkey != "" {
  1811  		vspHost = req.VspHost
  1812  		vspPubKey = req.VspPubkey
  1813  		if vspPubKey == "" {
  1814  			return nil, status.Errorf(codes.InvalidArgument, "vsp pubkey can not be null")
  1815  		}
  1816  		if vspHost == "" {
  1817  			return nil, status.Errorf(codes.InvalidArgument, "vsp host can not be null")
  1818  		}
  1819  		cfg := vsp.Config{
  1820  			URL:    vspHost,
  1821  			PubKey: vspPubKey,
  1822  			Dialer: nil,
  1823  			Wallet: s.wallet,
  1824  			Policy: vsp.Policy{
  1825  				MaxFee:     0.1e8,
  1826  				FeeAcct:    req.Account,
  1827  				ChangeAcct: req.ChangeAccount,
  1828  			},
  1829  		}
  1830  		vspClient, err = loader.VSP(cfg)
  1831  		if err != nil {
  1832  			return nil, status.Errorf(codes.Unknown, "VSP Server instance failed to start: %v", err)
  1833  		}
  1834  	}
  1835  
  1836  	if req.PoolFees > 0 {
  1837  		poolFees = req.PoolFees
  1838  		if !txrules.ValidPoolFeeRate(req.PoolFees) {
  1839  			return nil, status.Errorf(codes.InvalidArgument, "Invalid pool fees percentage")
  1840  		}
  1841  	}
  1842  
  1843  	if poolFees > 0 && poolAddr == nil {
  1844  		return nil, status.Errorf(codes.InvalidArgument,
  1845  			"Pool fees set but no pool address given")
  1846  	}
  1847  
  1848  	if poolFees <= 0 && poolAddr != nil {
  1849  		return nil, status.Errorf(codes.InvalidArgument,
  1850  			"Pool fees negative or unset but pool address given")
  1851  	}
  1852  
  1853  	numTickets := int(req.NumTickets)
  1854  	if numTickets < 1 {
  1855  		return nil, status.Errorf(codes.InvalidArgument,
  1856  			"Zero or negative number of tickets given")
  1857  	}
  1858  
  1859  	expiry := int32(req.Expiry)
  1860  	txFee := dcrutil.Amount(req.TxFee)
  1861  
  1862  	if txFee < 0 {
  1863  		return nil, status.Errorf(codes.InvalidArgument,
  1864  			"Negative fees per KB given")
  1865  	}
  1866  
  1867  	dontSignTx := req.DontSignTx
  1868  
  1869  	var csppServer string
  1870  	var mixedAccount uint32
  1871  	var mixedAccountBranch uint32
  1872  	var mixedSplitAccount uint32
  1873  	var changeAccount = req.ChangeAccount
  1874  
  1875  	if req.CsppServer != "" {
  1876  		csppServer = req.CsppServer
  1877  		mixedAccount = req.MixedAccount
  1878  		_, err = s.wallet.AccountName(ctx, mixedAccount)
  1879  		if err != nil {
  1880  			return nil, status.Errorf(codes.InvalidArgument,
  1881  				"CSPP Server set, but error on mixed account: %v", err)
  1882  		}
  1883  		mixedAccountBranch = req.MixedAccountBranch
  1884  		if mixedAccountBranch != 0 && mixedAccountBranch != 1 {
  1885  			return nil, status.Errorf(codes.InvalidArgument,
  1886  				"MixedAccountBranch should be 0 or 1.")
  1887  		}
  1888  		mixedSplitAccount = req.MixedSplitAccount
  1889  		_, err = s.wallet.AccountName(ctx, mixedSplitAccount)
  1890  		if err != nil {
  1891  			return nil, status.Errorf(codes.InvalidArgument,
  1892  				"CSPP Server set, but error on mixedSplitAccount: %v", err)
  1893  		}
  1894  		_, err = s.wallet.AccountName(ctx, changeAccount)
  1895  		if err != nil {
  1896  			return nil, status.Errorf(codes.InvalidArgument,
  1897  				"CSPP Server set, but error on changeAccount: %v", err)
  1898  		}
  1899  	}
  1900  
  1901  	request := &wallet.PurchaseTicketsRequest{
  1902  		Count:            numTickets,
  1903  		SourceAccount:    req.Account,
  1904  		VotingAddress:    ticketAddr,
  1905  		MinConf:          minConf,
  1906  		Expiry:           expiry,
  1907  		DontSignTx:       dontSignTx,
  1908  		VSPAddress:       poolAddr,
  1909  		VSPFees:          poolFees,
  1910  		UseVotingAccount: req.UseVotingAccount,
  1911  		VotingAccount:    req.VotingAccount,
  1912  
  1913  		// CSPP
  1914  		CSPPServer:         csppServer,
  1915  		DialCSPPServer:     s.dialCSPPServer,
  1916  		MixedAccount:       mixedAccount,
  1917  		MixedAccountBranch: mixedAccountBranch,
  1918  		MixedSplitAccount:  mixedSplitAccount,
  1919  		ChangeAccount:      changeAccount,
  1920  	}
  1921  
  1922  	if vspClient != nil {
  1923  		request.VSPFeePaymentProcess = vspClient.Process
  1924  		request.VSPFeeProcess = vspClient.FeePercentage
  1925  	}
  1926  
  1927  	// If dontSignTx is false we unlock the wallet so we can sign the tx.
  1928  	if !dontSignTx && len(req.Passphrase) > 0 {
  1929  		lock := make(chan time.Time, 1)
  1930  		defer func() {
  1931  			lock <- time.Time{} // send matters, not the value
  1932  		}()
  1933  		err = s.wallet.Unlock(ctx, req.Passphrase, lock)
  1934  		if err != nil {
  1935  			return nil, translateError(err)
  1936  		}
  1937  	}
  1938  
  1939  	ticketsResponse, err := s.wallet.PurchaseTickets(ctx, n, request)
  1940  	if err != nil {
  1941  		return nil, err
  1942  	}
  1943  	ticketsTx := ticketsResponse.Tickets
  1944  	splitTx := ticketsResponse.SplitTx
  1945  
  1946  	unsignedTickets := make([][]byte, len(ticketsTx))
  1947  	for i, mtx := range ticketsTx {
  1948  		var buf bytes.Buffer
  1949  
  1950  		err = mtx.Serialize(&buf)
  1951  		if err != nil {
  1952  			return nil, err
  1953  		}
  1954  		unsignedTickets[i] = buf.Bytes()
  1955  	}
  1956  
  1957  	var splitTxBuf bytes.Buffer
  1958  	splitTxBuf.Grow(splitTx.SerializeSize())
  1959  	err = splitTx.Serialize(&splitTxBuf)
  1960  	if err != nil {
  1961  		return nil, status.Errorf(codes.Unknown, "Error Serializing split tx: %v", err)
  1962  	}
  1963  	splitTxBytes := splitTxBuf.Bytes()
  1964  	hashesBytes := marshalHashes(ticketsResponse.TicketHashes)
  1965  
  1966  	return &pb.PurchaseTicketsResponse{
  1967  		TicketHashes: hashesBytes,
  1968  		Tickets:      unsignedTickets,
  1969  		SplitTx:      splitTxBytes,
  1970  	}, nil
  1971  }
  1972  
  1973  // deprecated
  1974  func (s *walletServer) RevokeTickets(ctx context.Context, req *pb.RevokeTicketsRequest) (*pb.RevokeTicketsResponse, error) {
  1975  	return &pb.RevokeTicketsResponse{}, nil
  1976  }
  1977  
  1978  // deprecated
  1979  func (s *walletServer) RevokeTicket(ctx context.Context, req *pb.RevokeTicketRequest) (*pb.RevokeTicketResponse, error) {
  1980  	return &pb.RevokeTicketResponse{}, nil
  1981  }
  1982  
  1983  func (s *walletServer) LoadActiveDataFilters(ctx context.Context, req *pb.LoadActiveDataFiltersRequest) (
  1984  	*pb.LoadActiveDataFiltersResponse, error) {
  1985  
  1986  	n, err := s.requireNetworkBackend()
  1987  	if err != nil {
  1988  		return nil, err
  1989  	}
  1990  
  1991  	err = s.wallet.LoadActiveDataFilters(ctx, n, false)
  1992  	if err != nil {
  1993  		return nil, translateError(err)
  1994  	}
  1995  
  1996  	return &pb.LoadActiveDataFiltersResponse{}, nil
  1997  }
  1998  
  1999  func (s *walletServer) CommittedTickets(ctx context.Context, req *pb.CommittedTicketsRequest) (
  2000  	*pb.CommittedTicketsResponse, error) {
  2001  
  2002  	// Translate [][]byte to []*chainhash.Hash
  2003  	in := make([]*chainhash.Hash, 0, len(req.Tickets))
  2004  	for _, v := range req.Tickets {
  2005  		hash, err := chainhash.NewHash(v)
  2006  		if err != nil {
  2007  			return &pb.CommittedTicketsResponse{},
  2008  				status.Error(codes.InvalidArgument,
  2009  					"invalid hash "+hex.EncodeToString(v))
  2010  		}
  2011  		in = append(in, hash)
  2012  	}
  2013  
  2014  	// Figure out which tickets we own
  2015  	out, outAddr, err := s.wallet.CommittedTickets(ctx, in)
  2016  	if err != nil {
  2017  		return nil, translateError(err)
  2018  	}
  2019  	if len(out) != len(outAddr) {
  2020  		// Sanity check
  2021  		return nil, status.Error(codes.Internal,
  2022  			"impossible condition: ticket and address count unequal")
  2023  	}
  2024  
  2025  	// Translate []*chainhash.Hash to [][]byte
  2026  	ctr := &pb.CommittedTicketsResponse{
  2027  		TicketAddresses: make([]*pb.CommittedTicketsResponse_TicketAddress,
  2028  			0, len(out)),
  2029  	}
  2030  	for k, v := range out {
  2031  		ctr.TicketAddresses = append(ctr.TicketAddresses,
  2032  			&pb.CommittedTicketsResponse_TicketAddress{
  2033  				Ticket:  v[:],
  2034  				Address: outAddr[k].String(),
  2035  			})
  2036  	}
  2037  
  2038  	return ctr, nil
  2039  }
  2040  
  2041  func (s *walletServer) signMessage(ctx context.Context, address, message string) ([]byte, error) {
  2042  	addr, err := decodeAddress(address, s.wallet.ChainParams())
  2043  	if err != nil {
  2044  		return nil, err
  2045  	}
  2046  
  2047  	// Addresses must have an associated secp256k1 private key and therefore
  2048  	// must be P2PK or P2PKH (P2SH is not allowed).
  2049  	var sig []byte
  2050  	switch addr.(type) {
  2051  	case *stdaddr.AddressPubKeyEcdsaSecp256k1V0:
  2052  	case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0:
  2053  	default:
  2054  		return nil, status.Error(codes.InvalidArgument,
  2055  			"address must be secp256k1 P2PK or P2PKH")
  2056  	}
  2057  
  2058  	sig, err = s.wallet.SignMessage(ctx, message, addr)
  2059  	if err != nil {
  2060  		return nil, translateError(err)
  2061  	}
  2062  	return sig, nil
  2063  }
  2064  
  2065  func (s *walletServer) SignMessage(ctx context.Context, req *pb.SignMessageRequest) (*pb.SignMessageResponse, error) {
  2066  	if len(req.Passphrase) > 0 {
  2067  		lock := make(chan time.Time, 1)
  2068  		defer func() {
  2069  			lock <- time.Time{} // send matters, not the value
  2070  		}()
  2071  		err := s.wallet.Unlock(ctx, req.Passphrase, lock)
  2072  		if err != nil {
  2073  			return nil, translateError(err)
  2074  		}
  2075  	}
  2076  
  2077  	sig, err := s.signMessage(ctx, req.Address, req.Message)
  2078  	if err != nil {
  2079  		return nil, err
  2080  	}
  2081  
  2082  	return &pb.SignMessageResponse{Signature: sig}, nil
  2083  }
  2084  
  2085  func (s *walletServer) SignMessages(ctx context.Context, req *pb.SignMessagesRequest) (*pb.SignMessagesResponse, error) {
  2086  	if len(req.Passphrase) > 0 {
  2087  		lock := make(chan time.Time, 1)
  2088  		defer func() {
  2089  			lock <- time.Time{} // send matters, not the value
  2090  		}()
  2091  		err := s.wallet.Unlock(ctx, req.Passphrase, lock)
  2092  		if err != nil {
  2093  			return nil, translateError(err)
  2094  		}
  2095  	}
  2096  
  2097  	smr := pb.SignMessagesResponse{
  2098  		Replies: make([]*pb.SignMessagesResponse_SignReply, 0,
  2099  			len(req.Messages)),
  2100  	}
  2101  	for _, v := range req.Messages {
  2102  		e := ""
  2103  		sig, err := s.signMessage(ctx, v.Address, v.Message)
  2104  		if err != nil {
  2105  			e = err.Error()
  2106  		}
  2107  		smr.Replies = append(smr.Replies,
  2108  			&pb.SignMessagesResponse_SignReply{
  2109  				Signature: sig,
  2110  				Error:     e,
  2111  			})
  2112  	}
  2113  
  2114  	return &smr, nil
  2115  }
  2116  
  2117  func (s *walletServer) ValidateAddress(ctx context.Context, req *pb.ValidateAddressRequest) (*pb.ValidateAddressResponse, error) {
  2118  	result := &pb.ValidateAddressResponse{}
  2119  	addr, err := decodeAddress(req.GetAddress(), s.wallet.ChainParams())
  2120  	if err != nil {
  2121  		return result, nil
  2122  	}
  2123  
  2124  	result.IsValid = true
  2125  
  2126  	ver, scr := addr.PaymentScript()
  2127  	class, _ := stdscript.ExtractAddrs(ver, scr, s.wallet.ChainParams())
  2128  	result.ScriptType = pb.ValidateAddressResponse_ScriptType(scProto(class))
  2129  	result.PayToAddrScript = scr
  2130  	if pker, ok := addr.(stdaddr.SerializedPubKeyer); ok {
  2131  		result.PubKey = pker.SerializedPubKey()
  2132  		result.PubKeyAddr = addr.String()
  2133  	}
  2134  	if class == stdscript.STScriptHash {
  2135  		result.IsScript = true
  2136  	}
  2137  
  2138  	ka, err := s.wallet.KnownAddress(ctx, addr)
  2139  	if err != nil {
  2140  		if errors.Is(err, errors.NotExist) {
  2141  			// No additional information available about the address.
  2142  			return result, nil
  2143  		}
  2144  		return nil, err
  2145  	}
  2146  	acct, err := s.wallet.AccountNumber(ctx, ka.AccountName())
  2147  	if err != nil {
  2148  		return nil, err
  2149  	}
  2150  
  2151  	// The address lookup was successful which means there is further
  2152  	// information about it available and it is "mine".
  2153  	result.IsMine = true
  2154  	result.AccountNumber = acct
  2155  
  2156  	switch ka := ka.(type) {
  2157  	case wallet.PubKeyHashAddress:
  2158  		result.PubKey = ka.PubKey()
  2159  		pubKeyAddr, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0Raw(result.PubKey,
  2160  			s.wallet.ChainParams())
  2161  		if err != nil {
  2162  			return nil, err
  2163  		}
  2164  		result.PubKeyAddr = pubKeyAddr.String()
  2165  	case wallet.P2SHAddress:
  2166  		version, redeem := ka.RedeemScript()
  2167  		class, addrs := stdscript.ExtractAddrs(version, redeem, s.wallet.ChainParams())
  2168  		reqSigs := stdscript.DetermineRequiredSigs(version, redeem)
  2169  		addrStrings := make([]string, len(addrs))
  2170  		for i, a := range addrs {
  2171  			addrStrings[i] = a.String()
  2172  		}
  2173  		result.PkScriptAddrs = addrStrings
  2174  
  2175  		// Multi-signature scripts also provide the number of required
  2176  		// signatures.
  2177  		result.ScriptType = pb.ValidateAddressResponse_ScriptType(scProto(class))
  2178  		if class == stdscript.STMultiSig {
  2179  			result.SigsRequired = uint32(reqSigs)
  2180  		}
  2181  	}
  2182  
  2183  	switch ka := ka.(type) {
  2184  	case wallet.BIP0044Address:
  2185  		_, branch, child := ka.Path()
  2186  		result.IsInternal = branch == udb.InternalBranch
  2187  		result.Index = child
  2188  	}
  2189  
  2190  	return result, nil
  2191  }
  2192  
  2193  func (s *walletServer) Spender(ctx context.Context, req *pb.SpenderRequest) (*pb.SpenderResponse, error) {
  2194  	txHash, err := chainhash.NewHash(req.TransactionHash)
  2195  	if err != nil {
  2196  		return nil, status.Errorf(codes.InvalidArgument, "invalid transaction hash: %v", err)
  2197  	}
  2198  	out := wire.OutPoint{Hash: *txHash, Index: req.Index}
  2199  
  2200  	spender, spenderIndex, err := s.wallet.Spender(ctx, &out)
  2201  	if err != nil {
  2202  		if errors.Is(errors.NotExist, err) {
  2203  			return nil, status.Errorf(codes.NotFound, "output is unspent")
  2204  		}
  2205  		if errors.Is(errors.Invalid, err) {
  2206  			return nil, status.Errorf(codes.InvalidArgument, "output is not relevant to the wallet")
  2207  		}
  2208  		return nil, translateError(err)
  2209  	}
  2210  
  2211  	var buf bytes.Buffer
  2212  	buf.Grow(spender.SerializeSize())
  2213  	err = spender.Serialize(&buf)
  2214  	if err != nil {
  2215  		return nil, translateError(err)
  2216  	}
  2217  
  2218  	resp := &pb.SpenderResponse{
  2219  		SpenderTransaction: buf.Bytes(),
  2220  		InputIndex:         spenderIndex,
  2221  	}
  2222  	return resp, nil
  2223  }
  2224  
  2225  func (s *walletServer) GetCFilters(req *pb.GetCFiltersRequest, server pb.WalletService_GetCFiltersServer) error {
  2226  	startBlock, endBlock, err := decodeBlockRange(req)
  2227  	if err != nil {
  2228  		return err
  2229  	}
  2230  
  2231  	ctx := server.Context()
  2232  	rangeFn := func(bh chainhash.Hash, key [gcs.KeySize]byte, cf *gcs.FilterV2) (bool, error) {
  2233  		resp := &pb.GetCFiltersResponse{
  2234  			Key:       key[:],
  2235  			Filter:    cf.Bytes(),
  2236  			BlockHash: bh[:],
  2237  		}
  2238  		err := server.Send(resp)
  2239  		if err != nil {
  2240  			return true, err
  2241  		}
  2242  
  2243  		select {
  2244  		case <-ctx.Done():
  2245  			return true, ctx.Err()
  2246  		default:
  2247  			return false, nil
  2248  		}
  2249  	}
  2250  	err = s.wallet.RangeCFiltersV2(ctx, startBlock, endBlock, rangeFn)
  2251  	if err != nil {
  2252  		return translateError(err)
  2253  	}
  2254  
  2255  	return nil
  2256  }
  2257  
  2258  func marshalTransactionInputs(v []wallet.TransactionSummaryInput) []*pb.TransactionDetails_Input {
  2259  	inputs := make([]*pb.TransactionDetails_Input, len(v))
  2260  	for i := range v {
  2261  		input := &v[i]
  2262  		inputs[i] = &pb.TransactionDetails_Input{
  2263  			Index:           input.Index,
  2264  			PreviousAccount: input.PreviousAccount,
  2265  			PreviousAmount:  int64(input.PreviousAmount),
  2266  		}
  2267  	}
  2268  	return inputs
  2269  }
  2270  
  2271  func marshalTransactionOutputs(v []wallet.TransactionSummaryOutput) []*pb.TransactionDetails_Output {
  2272  	outputs := make([]*pb.TransactionDetails_Output, len(v))
  2273  	for i := range v {
  2274  		output := &v[i]
  2275  		address := ""
  2276  		if output.Address != nil {
  2277  			address = output.Address.String()
  2278  		}
  2279  		outputs[i] = &pb.TransactionDetails_Output{
  2280  			Index:        output.Index,
  2281  			Account:      output.Account,
  2282  			Internal:     output.Internal,
  2283  			Amount:       int64(output.Amount),
  2284  			Address:      address,
  2285  			OutputScript: output.OutputScript,
  2286  		}
  2287  	}
  2288  	return outputs
  2289  }
  2290  
  2291  func marshalTxType(walletTxType wallet.TransactionType) pb.TransactionDetails_TransactionType {
  2292  	switch walletTxType {
  2293  	case wallet.TransactionTypeCoinbase:
  2294  		return pb.TransactionDetails_COINBASE
  2295  	case wallet.TransactionTypeTicketPurchase:
  2296  		return pb.TransactionDetails_TICKET_PURCHASE
  2297  	case wallet.TransactionTypeVote:
  2298  		return pb.TransactionDetails_VOTE
  2299  	case wallet.TransactionTypeRevocation:
  2300  		return pb.TransactionDetails_REVOCATION
  2301  	default:
  2302  		return pb.TransactionDetails_REGULAR
  2303  	}
  2304  }
  2305  
  2306  func marshalTransactionDetails(tx *wallet.TransactionSummary) *pb.TransactionDetails {
  2307  
  2308  	return &pb.TransactionDetails{
  2309  		Hash:            tx.Hash[:],
  2310  		Transaction:     tx.Transaction,
  2311  		Debits:          marshalTransactionInputs(tx.MyInputs),
  2312  		Credits:         marshalTransactionOutputs(tx.MyOutputs),
  2313  		Fee:             int64(tx.Fee),
  2314  		Timestamp:       tx.Timestamp,
  2315  		TransactionType: marshalTxType(tx.Type),
  2316  	}
  2317  }
  2318  
  2319  func marshalTransactionDetailsSlice(v []wallet.TransactionSummary) []*pb.TransactionDetails {
  2320  	txs := make([]*pb.TransactionDetails, len(v))
  2321  	for i := range v {
  2322  		txs[i] = marshalTransactionDetails(&v[i])
  2323  	}
  2324  	return txs
  2325  }
  2326  
  2327  func marshalTicketDetails(ticket *wallet.TicketSummary) *pb.GetTicketsResponse_TicketDetails {
  2328  	var ticketStatus = pb.GetTicketsResponse_TicketDetails_LIVE
  2329  	switch ticket.Status {
  2330  	case wallet.TicketStatusExpired:
  2331  		ticketStatus = pb.GetTicketsResponse_TicketDetails_EXPIRED
  2332  	case wallet.TicketStatusImmature:
  2333  		ticketStatus = pb.GetTicketsResponse_TicketDetails_IMMATURE
  2334  	case wallet.TicketStatusVoted:
  2335  		ticketStatus = pb.GetTicketsResponse_TicketDetails_VOTED
  2336  	case wallet.TicketStatusRevoked:
  2337  		ticketStatus = pb.GetTicketsResponse_TicketDetails_REVOKED
  2338  	case wallet.TicketStatusUnmined:
  2339  		ticketStatus = pb.GetTicketsResponse_TicketDetails_UNMINED
  2340  	case wallet.TicketStatusMissed:
  2341  		ticketStatus = pb.GetTicketsResponse_TicketDetails_MISSED
  2342  	case wallet.TicketStatusUnknown:
  2343  		ticketStatus = pb.GetTicketsResponse_TicketDetails_UNKNOWN
  2344  	}
  2345  	spender := &pb.TransactionDetails{}
  2346  	if ticket.Spender != nil {
  2347  		spender = marshalTransactionDetails(ticket.Spender)
  2348  	}
  2349  	return &pb.GetTicketsResponse_TicketDetails{
  2350  		Ticket:       marshalTransactionDetails(ticket.Ticket),
  2351  		Spender:      spender,
  2352  		TicketStatus: ticketStatus,
  2353  	}
  2354  }
  2355  
  2356  func marshalGetTicketBlockDetails(v *wire.BlockHeader) *pb.GetTicketsResponse_BlockDetails {
  2357  	if v == nil {
  2358  		return nil
  2359  	}
  2360  
  2361  	blockHash := v.BlockHash()
  2362  	return &pb.GetTicketsResponse_BlockDetails{
  2363  		Hash:      blockHash[:],
  2364  		Height:    int32(v.Height),
  2365  		Timestamp: v.Timestamp.Unix(),
  2366  	}
  2367  }
  2368  
  2369  func marshalBlock(v *wallet.Block) *pb.BlockDetails {
  2370  	txs := marshalTransactionDetailsSlice(v.Transactions)
  2371  
  2372  	if v.Header == nil {
  2373  		return &pb.BlockDetails{
  2374  			Hash:         nil,
  2375  			Height:       -1,
  2376  			Transactions: txs,
  2377  		}
  2378  	}
  2379  
  2380  	hash := v.Header.BlockHash()
  2381  	return &pb.BlockDetails{
  2382  		Hash:           hash[:],
  2383  		Height:         int32(v.Header.Height),
  2384  		Timestamp:      v.Header.Timestamp.Unix(),
  2385  		ApprovesParent: v.Header.VoteBits&dcrutil.BlockValid != 0,
  2386  		Transactions:   txs,
  2387  		PrevBlock:      v.Header.PrevBlock[:],
  2388  	}
  2389  }
  2390  
  2391  func marshalBlocks(v []wallet.Block) []*pb.BlockDetails {
  2392  	blocks := make([]*pb.BlockDetails, len(v))
  2393  	for i := range v {
  2394  		blocks[i] = marshalBlock(&v[i])
  2395  	}
  2396  	return blocks
  2397  }
  2398  
  2399  func marshalDetachedBlock(v *wire.BlockHeader) *pb.DetachedBlockDetails {
  2400  	hash := v.BlockHash()
  2401  	return &pb.DetachedBlockDetails{
  2402  		Hash:      hash[:],
  2403  		Height:    int32(v.Height),
  2404  		Timestamp: v.Timestamp.Unix(),
  2405  		PrevBlock: v.PrevBlock[:],
  2406  	}
  2407  }
  2408  
  2409  func marshalDetachedBlocks(v []*wire.BlockHeader) []*pb.DetachedBlockDetails {
  2410  	blocks := make([]*pb.DetachedBlockDetails, len(v))
  2411  	for i := range v {
  2412  		blocks[i] = marshalDetachedBlock(v[i])
  2413  	}
  2414  	return blocks
  2415  }
  2416  
  2417  func marshalHeaderHashes(v []*wire.BlockHeader) [][]byte {
  2418  	hashes := make([][]byte, len(v))
  2419  	for i := range v {
  2420  		hash := v[i].BlockHash()
  2421  		hashes[i] = hash[:]
  2422  	}
  2423  	return hashes
  2424  }
  2425  
  2426  func marshalHashes(v []*chainhash.Hash) [][]byte {
  2427  	hashes := make([][]byte, len(v))
  2428  	for i, hash := range v {
  2429  		hashes[i] = hash[:]
  2430  	}
  2431  	return hashes
  2432  }
  2433  
  2434  func (s *walletServer) TransactionNotifications(req *pb.TransactionNotificationsRequest,
  2435  	svr pb.WalletService_TransactionNotificationsServer) error {
  2436  
  2437  	n := s.wallet.NtfnServer.TransactionNotifications()
  2438  	defer n.Done()
  2439  
  2440  	ctxDone := svr.Context().Done()
  2441  	for {
  2442  		select {
  2443  		case v := <-n.C:
  2444  			resp := pb.TransactionNotificationsResponse{
  2445  				AttachedBlocks:           marshalBlocks(v.AttachedBlocks),
  2446  				DetachedBlocks:           marshalHeaderHashes(v.DetachedBlocks),
  2447  				UnminedTransactions:      marshalTransactionDetailsSlice(v.UnminedTransactions),
  2448  				UnminedTransactionHashes: marshalHashes(v.UnminedTransactionHashes),
  2449  				DetachedBlockHeaders:     marshalDetachedBlocks(v.DetachedBlocks),
  2450  			}
  2451  			err := svr.Send(&resp)
  2452  			if err != nil {
  2453  				return translateError(err)
  2454  			}
  2455  
  2456  		case <-ctxDone:
  2457  			return nil
  2458  		}
  2459  	}
  2460  }
  2461  
  2462  func (s *walletServer) AccountNotifications(req *pb.AccountNotificationsRequest,
  2463  	svr pb.WalletService_AccountNotificationsServer) error {
  2464  
  2465  	n := s.wallet.NtfnServer.AccountNotifications()
  2466  	defer n.Done()
  2467  
  2468  	ctxDone := svr.Context().Done()
  2469  	for {
  2470  		select {
  2471  		case v := <-n.C:
  2472  			resp := pb.AccountNotificationsResponse{
  2473  				AccountNumber:    v.AccountNumber,
  2474  				AccountName:      v.AccountName,
  2475  				ExternalKeyCount: v.ExternalKeyCount,
  2476  				InternalKeyCount: v.InternalKeyCount,
  2477  				ImportedKeyCount: v.ImportedKeyCount,
  2478  			}
  2479  			err := svr.Send(&resp)
  2480  			if err != nil {
  2481  				return translateError(err)
  2482  			}
  2483  
  2484  		case <-ctxDone:
  2485  			return nil
  2486  		}
  2487  	}
  2488  }
  2489  
  2490  func (s *walletServer) ConfirmationNotifications(svr pb.WalletService_ConfirmationNotificationsServer) error {
  2491  	c := s.wallet.NtfnServer.ConfirmationNotifications(svr.Context())
  2492  	errOut := make(chan error, 2)
  2493  	go func() {
  2494  		for {
  2495  			req, err := svr.Recv()
  2496  			if err != nil {
  2497  				errOut <- err
  2498  				return
  2499  			}
  2500  			txHashes, err := decodeHashes(req.TxHashes)
  2501  			if err != nil {
  2502  				errOut <- err
  2503  				return
  2504  			}
  2505  			if req.StopAfter < 0 {
  2506  				errOut <- status.Errorf(codes.InvalidArgument, "stop_after must be non-negative")
  2507  				return
  2508  			}
  2509  			c.Watch(txHashes, req.StopAfter)
  2510  		}
  2511  	}()
  2512  	go func() {
  2513  		for {
  2514  			n, err := c.Recv()
  2515  			if err != nil {
  2516  				errOut <- err
  2517  				return
  2518  			}
  2519  			results := make([]*pb.ConfirmationNotificationsResponse_TransactionConfirmations, len(n))
  2520  			for i, r := range n {
  2521  				var blockHash []byte
  2522  				if r.BlockHash != nil {
  2523  					blockHash = r.BlockHash[:]
  2524  				}
  2525  				results[i] = &pb.ConfirmationNotificationsResponse_TransactionConfirmations{
  2526  					TxHash:        r.TxHash[:],
  2527  					Confirmations: r.Confirmations,
  2528  					BlockHash:     blockHash,
  2529  					BlockHeight:   r.BlockHeight,
  2530  				}
  2531  			}
  2532  			r := &pb.ConfirmationNotificationsResponse{
  2533  				Confirmations: results,
  2534  			}
  2535  			err = svr.Send(r)
  2536  			if err != nil {
  2537  				errOut <- err
  2538  				return
  2539  			}
  2540  		}
  2541  	}()
  2542  
  2543  	select {
  2544  	case <-svr.Context().Done():
  2545  		return nil
  2546  	case err := <-errOut:
  2547  		if errors.Is(err, context.Canceled) {
  2548  			return nil
  2549  		}
  2550  		if _, ok := status.FromError(err); ok {
  2551  			return err
  2552  		}
  2553  		return translateError(err)
  2554  	}
  2555  }
  2556  
  2557  // StartWalletLoaderService starts the WalletLoaderService.
  2558  func StartWalletLoaderService(server *grpc.Server, loader *loader.Loader, activeNet *netparams.Params) {
  2559  	loaderService.loader = loader
  2560  	loaderService.activeNet = activeNet
  2561  	if atomic.SwapUint32(&loaderService.ready, 1) != 0 {
  2562  		panic("service already started")
  2563  	}
  2564  }
  2565  
  2566  func (s *loaderServer) checkReady() bool {
  2567  	return atomic.LoadUint32(&s.ready) != 0
  2568  }
  2569  
  2570  // StartAccountMixerService starts the AccountMixerService.
  2571  func StartAccountMixerService(server *grpc.Server, loader *loader.Loader) {
  2572  	accountMixerService.loader = loader
  2573  	if atomic.SwapUint32(&accountMixerService.ready, 1) != 0 {
  2574  		panic("service already started")
  2575  	}
  2576  }
  2577  
  2578  // RunAccountMixer starts the automatic account mixer for the service.
  2579  func (t *accountMixerServer) RunAccountMixer(req *pb.RunAccountMixerRequest, svr pb.AccountMixerService_RunAccountMixerServer) error {
  2580  	wallet, ok := t.loader.LoadedWallet()
  2581  	if !ok {
  2582  		return status.Errorf(codes.FailedPrecondition, "Wallet has not been loaded")
  2583  	}
  2584  
  2585  	tb := ticketbuyer.New(wallet)
  2586  
  2587  	// Set ticketbuyerV2 config
  2588  	tb.AccessConfig(func(c *ticketbuyer.Config) {
  2589  		c.MixedAccountBranch = req.MixedAccountBranch
  2590  		c.MixedAccount = req.MixedAccount
  2591  		c.ChangeAccount = req.ChangeAccount
  2592  		c.CSPPServer = req.CsppServer
  2593  		c.DialCSPPServer = t.loader.DialCSPPServer
  2594  		c.BuyTickets = false
  2595  		c.MixChange = true
  2596  	})
  2597  
  2598  	if len(req.Passphrase) > 0 {
  2599  		lock := make(chan time.Time, 1)
  2600  
  2601  		lockWallet := func() {
  2602  			lock <- time.Time{}
  2603  			zero(req.Passphrase)
  2604  		}
  2605  
  2606  		err := wallet.Unlock(svr.Context(), req.Passphrase, lock)
  2607  		if err != nil {
  2608  			return translateError(err)
  2609  		}
  2610  		defer lockWallet()
  2611  	}
  2612  
  2613  	err := tb.Run(svr.Context(), req.Passphrase)
  2614  	if err != nil {
  2615  		if svr.Context().Err() != nil {
  2616  			return status.Errorf(codes.Canceled, "AccountMixer instance canceled, account number: %v", req.MixedAccount)
  2617  		}
  2618  		return status.Errorf(codes.Unknown, "AccountMixer instance errored: %v", err)
  2619  	}
  2620  
  2621  	return nil
  2622  }
  2623  
  2624  func (t *accountMixerServer) checkReady() bool {
  2625  	return atomic.LoadUint32(&t.ready) != 0
  2626  }
  2627  
  2628  // StartTicketBuyerV2Service starts the TicketBuyerV2Service.
  2629  func StartTicketBuyerV2Service(server *grpc.Server, loader *loader.Loader) {
  2630  	ticketBuyerV2Service.loader = loader
  2631  	if atomic.SwapUint32(&ticketBuyerV2Service.ready, 1) != 0 {
  2632  		panic("service already started")
  2633  	}
  2634  }
  2635  
  2636  // StartTicketBuyer starts the automatic ticket buyer for the v2 service.
  2637  func (t *ticketbuyerV2Server) RunTicketBuyer(req *pb.RunTicketBuyerRequest, svr pb.TicketBuyerV2Service_RunTicketBuyerServer) error {
  2638  	wallet, ok := t.loader.LoadedWallet()
  2639  	if !ok {
  2640  		return status.Errorf(codes.FailedPrecondition, "Wallet has not been loaded")
  2641  	}
  2642  	params := wallet.ChainParams()
  2643  
  2644  	ctx := svr.Context()
  2645  	// Legacy vsp request. After stopping supporting the old vsp version, this
  2646  	// code can be removed.
  2647  	// Confirm validity of provided voting addresses and pool addresses.
  2648  	var votingAddress stdaddr.StakeAddress
  2649  	var err error
  2650  	if req.VotingAddress != "" {
  2651  		votingAddress, err = decodeStakeAddress(req.VotingAddress, params)
  2652  		if err != nil {
  2653  			return err
  2654  		}
  2655  	}
  2656  	var poolAddress stdaddr.StakeAddress
  2657  	if req.PoolAddress != "" {
  2658  		if req.VspHost != "" || req.VspPubkey != "" {
  2659  			return status.Errorf(codes.InvalidArgument,
  2660  				"request contains both legacy stakepoold and vspd options.")
  2661  		}
  2662  		poolAddress, err = decodeStakeAddress(req.PoolAddress, params)
  2663  		if err != nil {
  2664  			return err
  2665  		}
  2666  	}
  2667  
  2668  	// new vspd request
  2669  	var vspHost string
  2670  	var vspPubKey string
  2671  	var vspClient *vsp.Client
  2672  	if req.VspHost != "" || req.VspPubkey != "" {
  2673  		vspHost = req.VspHost
  2674  		vspPubKey = req.VspPubkey
  2675  		if vspPubKey == "" {
  2676  			return status.Errorf(codes.InvalidArgument, "vsp pubkey can not be null")
  2677  		}
  2678  		if vspHost == "" {
  2679  			return status.Errorf(codes.InvalidArgument, "vsp host can not be null")
  2680  		}
  2681  		cfg := vsp.Config{
  2682  			URL:    vspHost,
  2683  			PubKey: vspPubKey,
  2684  			Dialer: nil,
  2685  			Wallet: wallet,
  2686  			Policy: vsp.Policy{
  2687  				MaxFee:     0.1e8,
  2688  				FeeAcct:    req.Account,
  2689  				ChangeAcct: req.Account,
  2690  			},
  2691  		}
  2692  		vspClient, err = loader.VSP(cfg)
  2693  		if err != nil {
  2694  			return status.Errorf(codes.Unknown, "TicketBuyerV3 instance failed to start. Error: %v", err)
  2695  		}
  2696  	}
  2697  	if req.BalanceToMaintain < 0 {
  2698  		return status.Errorf(codes.InvalidArgument, "Negative balance to maintain given")
  2699  	}
  2700  
  2701  	var csppServer string
  2702  	var mixedAccount uint32
  2703  	var mixedAccountBranch uint32
  2704  	var mixedSplitAccount uint32
  2705  	var changeAccount = req.ChangeAccount
  2706  	var mixedChange = false
  2707  
  2708  	if req.CsppServer != "" {
  2709  		mixedChange = true
  2710  		csppServer = req.CsppServer
  2711  		mixedAccount = req.MixedAccount
  2712  		_, err = wallet.AccountName(ctx, mixedAccount)
  2713  		if err != nil {
  2714  			return status.Errorf(codes.InvalidArgument,
  2715  				"CSPP Server set, but error on mixed account: %v", err)
  2716  		}
  2717  		mixedAccountBranch = req.MixedAccountBranch
  2718  		if mixedAccountBranch != 0 && mixedAccountBranch != 1 {
  2719  			return status.Errorf(codes.InvalidArgument,
  2720  				"MixedAccountBranch should be 0 or 1.")
  2721  		}
  2722  		mixedSplitAccount = req.MixedSplitAccount
  2723  		_, err = wallet.AccountName(ctx, mixedSplitAccount)
  2724  		if err != nil {
  2725  			return status.Errorf(codes.InvalidArgument,
  2726  				"CSPP Server set, but error on mixedSplitAccount: %v", err)
  2727  		}
  2728  		_, err = wallet.AccountName(ctx, changeAccount)
  2729  		if err != nil {
  2730  			return status.Errorf(codes.InvalidArgument,
  2731  				"CSPP Server set, but error on changeAccount: %v", err)
  2732  		}
  2733  	}
  2734  
  2735  	// set limit. If it is not informed by the request it is used 0, which
  2736  	// is defaulted to 20.
  2737  	limit := int(req.Limit)
  2738  
  2739  	tb := ticketbuyer.New(wallet)
  2740  	// Set ticketbuyerV2 config
  2741  	tb.AccessConfig(func(c *ticketbuyer.Config) {
  2742  		c.BuyTickets = true
  2743  		c.Account = req.Account
  2744  		c.VotingAccount = req.VotingAccount
  2745  		c.Maintain = dcrutil.Amount(req.BalanceToMaintain)
  2746  		c.VotingAddr = votingAddress
  2747  		c.PoolFeeAddr = poolAddress
  2748  		c.PoolFees = req.PoolFees
  2749  		c.VSP = vspClient
  2750  		c.MixedAccount = mixedAccount
  2751  		c.MixChange = mixedChange
  2752  		c.CSPPServer = csppServer
  2753  		c.DialCSPPServer = t.loader.DialCSPPServer
  2754  		c.ChangeAccount = changeAccount
  2755  		c.MixedAccountBranch = mixedAccountBranch
  2756  		c.TicketSplitAccount = mixedSplitAccount
  2757  		c.Limit = limit
  2758  	})
  2759  
  2760  	if len(req.Passphrase) > 0 {
  2761  		lock := make(chan time.Time, 1)
  2762  
  2763  		lockWallet := func() {
  2764  			lock <- time.Time{}
  2765  			zero(req.Passphrase)
  2766  		}
  2767  
  2768  		err = wallet.Unlock(svr.Context(), req.Passphrase, lock)
  2769  		if err != nil {
  2770  			return translateError(err)
  2771  		}
  2772  		defer lockWallet()
  2773  	}
  2774  
  2775  	err = tb.Run(svr.Context(), req.Passphrase)
  2776  	if err != nil {
  2777  		if svr.Context().Err() != nil {
  2778  			return status.Errorf(codes.Canceled, "TicketBuyerV2 instance canceled, account number: %v", req.Account)
  2779  		}
  2780  		return status.Errorf(codes.Unknown, "TicketBuyerV2 instance errored: %v", err)
  2781  	}
  2782  
  2783  	return nil
  2784  }
  2785  
  2786  func (t *ticketbuyerV2Server) checkReady() bool {
  2787  	return atomic.LoadUint32(&t.ready) != 0
  2788  }
  2789  
  2790  func (s *loaderServer) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest) (
  2791  	*pb.CreateWalletResponse, error) {
  2792  
  2793  	defer func() {
  2794  		zero(req.PrivatePassphrase)
  2795  		zero(req.Seed)
  2796  	}()
  2797  
  2798  	// Use an insecure public passphrase when the request's is empty.
  2799  	pubPassphrase := req.PublicPassphrase
  2800  	if len(pubPassphrase) == 0 {
  2801  		pubPassphrase = []byte(wallet.InsecurePubPassphrase)
  2802  	}
  2803  
  2804  	// Seed is required.
  2805  	if len(req.Seed) == 0 {
  2806  		return nil, status.Errorf(codes.InvalidArgument, "seed is a required parameter")
  2807  	}
  2808  
  2809  	_, err := s.loader.CreateNewWallet(ctx, pubPassphrase, req.PrivatePassphrase, req.Seed)
  2810  	if err != nil {
  2811  		return nil, translateError(err)
  2812  	}
  2813  
  2814  	return &pb.CreateWalletResponse{}, nil
  2815  }
  2816  
  2817  func (s *loaderServer) CreateWatchingOnlyWallet(ctx context.Context, req *pb.CreateWatchingOnlyWalletRequest) (
  2818  	*pb.CreateWatchingOnlyWalletResponse, error) {
  2819  
  2820  	// Use an insecure public passphrase when the request's is empty.
  2821  	pubPassphrase := req.PublicPassphrase
  2822  	if len(pubPassphrase) == 0 {
  2823  		pubPassphrase = []byte(wallet.InsecurePubPassphrase)
  2824  	}
  2825  
  2826  	_, err := s.loader.CreateWatchingOnlyWallet(ctx, req.ExtendedPubKey, pubPassphrase)
  2827  	if err != nil {
  2828  		return nil, translateError(err)
  2829  	}
  2830  
  2831  	return &pb.CreateWatchingOnlyWalletResponse{}, nil
  2832  }
  2833  
  2834  func (s *loaderServer) OpenWallet(ctx context.Context, req *pb.OpenWalletRequest) (
  2835  	*pb.OpenWalletResponse, error) {
  2836  
  2837  	// Use an insecure public passphrase when the request's is empty.
  2838  	pubPassphrase := req.PublicPassphrase
  2839  	if len(pubPassphrase) == 0 {
  2840  		pubPassphrase = []byte(wallet.InsecurePubPassphrase)
  2841  	}
  2842  
  2843  	w, err := s.loader.OpenExistingWallet(ctx, pubPassphrase)
  2844  	if err != nil {
  2845  		return nil, translateError(err)
  2846  	}
  2847  
  2848  	return &pb.OpenWalletResponse{
  2849  		WatchingOnly: w.WatchingOnly(),
  2850  	}, nil
  2851  }
  2852  
  2853  func (s *loaderServer) WalletExists(ctx context.Context, req *pb.WalletExistsRequest) (
  2854  	*pb.WalletExistsResponse, error) {
  2855  
  2856  	exists, err := s.loader.WalletExists()
  2857  	if err != nil {
  2858  		return nil, translateError(err)
  2859  	}
  2860  	return &pb.WalletExistsResponse{Exists: exists}, nil
  2861  }
  2862  
  2863  func (s *loaderServer) CloseWallet(ctx context.Context, req *pb.CloseWalletRequest) (
  2864  	*pb.CloseWalletResponse, error) {
  2865  
  2866  	err := s.loader.UnloadWallet()
  2867  	if errors.Is(err, errors.Invalid) {
  2868  		return nil, status.Errorf(codes.FailedPrecondition, "Wallet is not loaded")
  2869  	}
  2870  	if err != nil {
  2871  		return nil, translateError(err)
  2872  	}
  2873  
  2874  	return &pb.CloseWalletResponse{}, nil
  2875  }
  2876  
  2877  func isLoopback(addr string) bool {
  2878  	host, _, err := net.SplitHostPort(addr)
  2879  	if err == nil {
  2880  		addr = host
  2881  	}
  2882  	if addr == "localhost" {
  2883  		return true
  2884  	}
  2885  	ip := net.ParseIP(addr)
  2886  	if ip == nil {
  2887  		return false
  2888  	}
  2889  	return ip.IsLoopback()
  2890  }
  2891  
  2892  func (s *loaderServer) RpcSync(req *pb.RpcSyncRequest, svr pb.WalletLoaderService_RpcSyncServer) error {
  2893  	defer zero(req.Password)
  2894  
  2895  	// Error if the wallet is already syncing with the network.
  2896  	wallet, walletLoaded := s.loader.LoadedWallet()
  2897  	if walletLoaded {
  2898  		_, err := wallet.NetworkBackend()
  2899  		if err == nil {
  2900  			return status.Errorf(codes.FailedPrecondition, "wallet is loaded and already synchronizing")
  2901  		}
  2902  	}
  2903  
  2904  	if req.DiscoverAccounts && len(req.PrivatePassphrase) == 0 {
  2905  		return status.Errorf(codes.InvalidArgument, "private passphrase is required for discovering accounts")
  2906  	}
  2907  	var lockWallet func()
  2908  	if req.DiscoverAccounts {
  2909  		lock := make(chan time.Time, 1)
  2910  		lockWallet = func() {
  2911  			lock <- time.Time{}
  2912  			zero(req.PrivatePassphrase)
  2913  		}
  2914  		defer lockWallet()
  2915  		err := wallet.Unlock(svr.Context(), req.PrivatePassphrase, lock)
  2916  		if err != nil {
  2917  			return translateError(err)
  2918  		}
  2919  	}
  2920  
  2921  	syncer := chain.NewSyncer(wallet, &chain.RPCOptions{
  2922  		Address:     req.NetworkAddress,
  2923  		DefaultPort: s.activeNet.JSONRPCClientPort,
  2924  		User:        req.Username,
  2925  		Pass:        string(req.Password),
  2926  		CA:          req.Certificate,
  2927  		Insecure:    isLoopback(req.NetworkAddress) && len(req.Certificate) == 0,
  2928  	})
  2929  
  2930  	cbs := &chain.Callbacks{
  2931  		Synced: func(sync bool) {
  2932  			resp := &pb.RpcSyncResponse{}
  2933  			resp.Synced = sync
  2934  			if sync {
  2935  				resp.NotificationType = pb.SyncNotificationType_SYNCED
  2936  			} else {
  2937  				resp.NotificationType = pb.SyncNotificationType_UNSYNCED
  2938  			}
  2939  			_ = svr.Send(resp)
  2940  		},
  2941  		FetchMissingCFiltersStarted: func() {
  2942  			resp := &pb.RpcSyncResponse{
  2943  				NotificationType: pb.SyncNotificationType_FETCHED_MISSING_CFILTERS_STARTED,
  2944  			}
  2945  			_ = svr.Send(resp)
  2946  		},
  2947  		FetchMissingCFiltersProgress: func(missingCFitlersStart, missingCFitlersEnd int32) {
  2948  			resp := &pb.RpcSyncResponse{
  2949  				NotificationType: pb.SyncNotificationType_FETCHED_MISSING_CFILTERS_PROGRESS,
  2950  				FetchMissingCfilters: &pb.FetchMissingCFiltersNotification{
  2951  					FetchedCfiltersStartHeight: missingCFitlersStart,
  2952  					FetchedCfiltersEndHeight:   missingCFitlersEnd,
  2953  				},
  2954  			}
  2955  			_ = svr.Send(resp)
  2956  		},
  2957  		FetchMissingCFiltersFinished: func() {
  2958  			resp := &pb.RpcSyncResponse{
  2959  				NotificationType: pb.SyncNotificationType_FETCHED_MISSING_CFILTERS_FINISHED,
  2960  			}
  2961  			_ = svr.Send(resp)
  2962  		},
  2963  		FetchHeadersStarted: func() {
  2964  			resp := &pb.RpcSyncResponse{
  2965  				NotificationType: pb.SyncNotificationType_FETCHED_HEADERS_STARTED,
  2966  			}
  2967  			_ = svr.Send(resp)
  2968  		},
  2969  		FetchHeadersProgress: func(fetchedHeadersCount int32, lastHeaderTime int64) {
  2970  			resp := &pb.RpcSyncResponse{
  2971  				NotificationType: pb.SyncNotificationType_FETCHED_HEADERS_PROGRESS,
  2972  				FetchHeaders: &pb.FetchHeadersNotification{
  2973  					FetchedHeadersCount: fetchedHeadersCount,
  2974  					LastHeaderTime:      lastHeaderTime,
  2975  				},
  2976  			}
  2977  			_ = svr.Send(resp)
  2978  		},
  2979  		FetchHeadersFinished: func() {
  2980  			resp := &pb.RpcSyncResponse{
  2981  				NotificationType: pb.SyncNotificationType_FETCHED_HEADERS_FINISHED,
  2982  			}
  2983  			_ = svr.Send(resp)
  2984  		},
  2985  		DiscoverAddressesStarted: func() {
  2986  			resp := &pb.RpcSyncResponse{
  2987  				NotificationType: pb.SyncNotificationType_DISCOVER_ADDRESSES_STARTED,
  2988  			}
  2989  			_ = svr.Send(resp)
  2990  		},
  2991  		DiscoverAddressesFinished: func() {
  2992  			resp := &pb.RpcSyncResponse{
  2993  				NotificationType: pb.SyncNotificationType_DISCOVER_ADDRESSES_FINISHED,
  2994  			}
  2995  
  2996  			// Lock the wallet after the first time discovered while also
  2997  			// discovering accounts.
  2998  			if lockWallet != nil {
  2999  				lockWallet()
  3000  				lockWallet = nil
  3001  			}
  3002  			_ = svr.Send(resp)
  3003  		},
  3004  		RescanStarted: func() {
  3005  			resp := &pb.RpcSyncResponse{
  3006  				NotificationType: pb.SyncNotificationType_RESCAN_STARTED,
  3007  			}
  3008  			_ = svr.Send(resp)
  3009  		},
  3010  		RescanProgress: func(rescannedThrough int32) {
  3011  			resp := &pb.RpcSyncResponse{
  3012  				NotificationType: pb.SyncNotificationType_RESCAN_PROGRESS,
  3013  				RescanProgress: &pb.RescanProgressNotification{
  3014  					RescannedThrough: rescannedThrough,
  3015  				},
  3016  			}
  3017  			_ = svr.Send(resp)
  3018  		},
  3019  		RescanFinished: func() {
  3020  			resp := &pb.RpcSyncResponse{
  3021  				NotificationType: pb.SyncNotificationType_RESCAN_FINISHED,
  3022  			}
  3023  			_ = svr.Send(resp)
  3024  		},
  3025  	}
  3026  	syncer.SetCallbacks(cbs)
  3027  
  3028  	// Synchronize until error or RPC cancelation.
  3029  	err := syncer.Run(svr.Context())
  3030  	if err != nil {
  3031  		if svr.Context().Err() != nil {
  3032  			return status.Errorf(codes.Canceled, "Wallet synchronization canceled: %v", err)
  3033  		}
  3034  		return status.Errorf(codes.Unknown, "Wallet synchronization stopped: %v", err)
  3035  	}
  3036  
  3037  	return nil
  3038  }
  3039  
  3040  func (s *loaderServer) SpvSync(req *pb.SpvSyncRequest, svr pb.WalletLoaderService_SpvSyncServer) error {
  3041  	wallet, ok := s.loader.LoadedWallet()
  3042  	if !ok {
  3043  		return status.Errorf(codes.FailedPrecondition, "Wallet has not been loaded")
  3044  	}
  3045  
  3046  	if req.DiscoverAccounts && len(req.PrivatePassphrase) == 0 {
  3047  		return status.Errorf(codes.InvalidArgument, "private passphrase is required for discovering accounts")
  3048  	}
  3049  	var lockWallet func()
  3050  	if req.DiscoverAccounts {
  3051  		lock := make(chan time.Time, 1)
  3052  		lockWallet = func() {
  3053  			lock <- time.Time{}
  3054  			zero(req.PrivatePassphrase)
  3055  		}
  3056  		defer lockWallet()
  3057  		err := wallet.Unlock(svr.Context(), req.PrivatePassphrase, lock)
  3058  		if err != nil {
  3059  			return translateError(err)
  3060  		}
  3061  	}
  3062  	addr := &net.TCPAddr{IP: net.ParseIP("::1"), Port: 0}
  3063  	amgr := addrmgr.New(s.loader.DbDirPath(), net.LookupIP) // TODO: be mindful of tor
  3064  	lp := p2p.NewLocalPeer(wallet.ChainParams(), addr, amgr)
  3065  
  3066  	ntfns := &spv.Notifications{
  3067  		Synced: func(sync bool) {
  3068  			resp := &pb.SpvSyncResponse{}
  3069  			resp.Synced = sync
  3070  			if sync {
  3071  				resp.NotificationType = pb.SyncNotificationType_SYNCED
  3072  			} else {
  3073  				resp.NotificationType = pb.SyncNotificationType_UNSYNCED
  3074  			}
  3075  			_ = svr.Send(resp)
  3076  		},
  3077  		PeerConnected: func(peerCount int32, addr string) {
  3078  			resp := &pb.SpvSyncResponse{
  3079  				NotificationType: pb.SyncNotificationType_PEER_CONNECTED,
  3080  				PeerInformation: &pb.PeerNotification{
  3081  					PeerCount: peerCount,
  3082  					Address:   addr,
  3083  				},
  3084  			}
  3085  			_ = svr.Send(resp)
  3086  		},
  3087  		PeerDisconnected: func(peerCount int32, addr string) {
  3088  			resp := &pb.SpvSyncResponse{
  3089  				NotificationType: pb.SyncNotificationType_PEER_DISCONNECTED,
  3090  				PeerInformation: &pb.PeerNotification{
  3091  					PeerCount: peerCount,
  3092  					Address:   addr,
  3093  				},
  3094  			}
  3095  			_ = svr.Send(resp)
  3096  		},
  3097  		FetchMissingCFiltersStarted: func() {
  3098  			resp := &pb.SpvSyncResponse{
  3099  				NotificationType: pb.SyncNotificationType_FETCHED_MISSING_CFILTERS_STARTED,
  3100  			}
  3101  			_ = svr.Send(resp)
  3102  		},
  3103  		FetchMissingCFiltersProgress: func(missingCFitlersStart, missingCFitlersEnd int32) {
  3104  			resp := &pb.SpvSyncResponse{
  3105  				NotificationType: pb.SyncNotificationType_FETCHED_MISSING_CFILTERS_PROGRESS,
  3106  				FetchMissingCfilters: &pb.FetchMissingCFiltersNotification{
  3107  					FetchedCfiltersStartHeight: missingCFitlersStart,
  3108  					FetchedCfiltersEndHeight:   missingCFitlersEnd,
  3109  				},
  3110  			}
  3111  			_ = svr.Send(resp)
  3112  		},
  3113  		FetchMissingCFiltersFinished: func() {
  3114  			resp := &pb.SpvSyncResponse{
  3115  				NotificationType: pb.SyncNotificationType_FETCHED_MISSING_CFILTERS_FINISHED,
  3116  			}
  3117  			_ = svr.Send(resp)
  3118  		},
  3119  		FetchHeadersStarted: func() {
  3120  			resp := &pb.SpvSyncResponse{
  3121  				NotificationType: pb.SyncNotificationType_FETCHED_HEADERS_STARTED,
  3122  			}
  3123  			_ = svr.Send(resp)
  3124  		},
  3125  		FetchHeadersProgress: func(fetchedHeadersCount int32, lastHeaderTime int64) {
  3126  			resp := &pb.SpvSyncResponse{
  3127  				NotificationType: pb.SyncNotificationType_FETCHED_HEADERS_PROGRESS,
  3128  				FetchHeaders: &pb.FetchHeadersNotification{
  3129  					FetchedHeadersCount: fetchedHeadersCount,
  3130  					LastHeaderTime:      lastHeaderTime,
  3131  				},
  3132  			}
  3133  			_ = svr.Send(resp)
  3134  		},
  3135  		FetchHeadersFinished: func() {
  3136  			resp := &pb.SpvSyncResponse{
  3137  				NotificationType: pb.SyncNotificationType_FETCHED_HEADERS_FINISHED,
  3138  			}
  3139  			_ = svr.Send(resp)
  3140  		},
  3141  		DiscoverAddressesStarted: func() {
  3142  			resp := &pb.SpvSyncResponse{
  3143  				NotificationType: pb.SyncNotificationType_DISCOVER_ADDRESSES_STARTED,
  3144  			}
  3145  			_ = svr.Send(resp)
  3146  		},
  3147  		DiscoverAddressesFinished: func() {
  3148  			resp := &pb.SpvSyncResponse{
  3149  				NotificationType: pb.SyncNotificationType_DISCOVER_ADDRESSES_FINISHED,
  3150  			}
  3151  
  3152  			// Lock the wallet after the first time discovered while also
  3153  			// discovering accounts.
  3154  			if lockWallet != nil {
  3155  				lockWallet()
  3156  				lockWallet = nil
  3157  			}
  3158  			_ = svr.Send(resp)
  3159  		},
  3160  		RescanStarted: func() {
  3161  			resp := &pb.SpvSyncResponse{
  3162  				NotificationType: pb.SyncNotificationType_RESCAN_STARTED,
  3163  			}
  3164  			_ = svr.Send(resp)
  3165  		},
  3166  		RescanProgress: func(rescannedThrough int32) {
  3167  			resp := &pb.SpvSyncResponse{
  3168  				NotificationType: pb.SyncNotificationType_RESCAN_PROGRESS,
  3169  				RescanProgress: &pb.RescanProgressNotification{
  3170  					RescannedThrough: rescannedThrough,
  3171  				},
  3172  			}
  3173  			_ = svr.Send(resp)
  3174  		},
  3175  		RescanFinished: func() {
  3176  			resp := &pb.SpvSyncResponse{
  3177  				NotificationType: pb.SyncNotificationType_RESCAN_FINISHED,
  3178  			}
  3179  			_ = svr.Send(resp)
  3180  		},
  3181  	}
  3182  	syncer := spv.NewSyncer(wallet, lp)
  3183  	syncer.SetNotifications(ntfns)
  3184  	if len(req.SpvConnect) > 0 {
  3185  		spvConnects := make([]string, len(req.SpvConnect))
  3186  		for i := 0; i < len(req.SpvConnect); i++ {
  3187  			spvConnect, err := cfgutil.NormalizeAddress(req.SpvConnect[i], s.activeNet.Params.DefaultPort)
  3188  			if err != nil {
  3189  				return status.Errorf(codes.FailedPrecondition, "SPV Connect address invalid: %v", err)
  3190  			}
  3191  			spvConnects[i] = spvConnect
  3192  		}
  3193  		syncer.SetPersistentPeers(spvConnects)
  3194  	}
  3195  
  3196  	err := syncer.Run(svr.Context())
  3197  	if err != nil {
  3198  		if errors.Is(err, context.Canceled) {
  3199  			return status.Errorf(codes.Canceled, "SPV synchronization canceled: %v", err)
  3200  		} else if errors.Is(err, context.DeadlineExceeded) {
  3201  			return status.Errorf(codes.DeadlineExceeded, "SPV synchronization deadline exceeded: %v", err)
  3202  		}
  3203  		return translateError(err)
  3204  	}
  3205  	return nil
  3206  }
  3207  
  3208  func (s *loaderServer) RescanPoint(ctx context.Context, req *pb.RescanPointRequest) (*pb.RescanPointResponse, error) {
  3209  	wallet, ok := s.loader.LoadedWallet()
  3210  	if !ok {
  3211  		return nil, status.Errorf(codes.FailedPrecondition, "Wallet has not been loaded")
  3212  	}
  3213  	rescanPoint, err := wallet.RescanPoint(ctx)
  3214  	if err != nil {
  3215  		return nil, status.Errorf(codes.FailedPrecondition, "Rescan point failed to be requested %v", err)
  3216  	}
  3217  	if rescanPoint != nil {
  3218  		return &pb.RescanPointResponse{
  3219  			RescanPointHash: rescanPoint[:],
  3220  		}, nil
  3221  	}
  3222  	return &pb.RescanPointResponse{RescanPointHash: nil}, nil
  3223  }
  3224  
  3225  func (s *seedServer) GenerateRandomSeed(ctx context.Context, req *pb.GenerateRandomSeedRequest) (
  3226  	*pb.GenerateRandomSeedResponse, error) {
  3227  
  3228  	seedSize := req.SeedLength
  3229  	if seedSize == 0 {
  3230  		seedSize = hdkeychain.RecommendedSeedLen
  3231  	}
  3232  	if seedSize < hdkeychain.MinSeedBytes || seedSize > hdkeychain.MaxSeedBytes {
  3233  		return nil, status.Errorf(codes.InvalidArgument, "invalid seed length")
  3234  	}
  3235  
  3236  	seed := make([]byte, seedSize)
  3237  	_, err := rand.Read(seed)
  3238  	if err != nil {
  3239  		return nil, status.Errorf(codes.Unavailable, "failed to read cryptographically-random data for seed: %v", err)
  3240  	}
  3241  
  3242  	res := &pb.GenerateRandomSeedResponse{
  3243  		SeedBytes:    seed,
  3244  		SeedHex:      hex.EncodeToString(seed),
  3245  		SeedMnemonic: walletseed.EncodeMnemonic(seed),
  3246  	}
  3247  	return res, nil
  3248  }
  3249  
  3250  func (s *seedServer) DecodeSeed(ctx context.Context, req *pb.DecodeSeedRequest) (*pb.DecodeSeedResponse, error) {
  3251  	seed, err := walletseed.DecodeUserInput(req.UserInput)
  3252  	if err != nil {
  3253  		return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  3254  	}
  3255  	return &pb.DecodeSeedResponse{DecodedSeed: seed}, nil
  3256  }
  3257  
  3258  func StartAgendaService(server *grpc.Server, activeNet *chaincfg.Params) {
  3259  	agendaService.activeNet = activeNet
  3260  	if atomic.SwapUint32(&agendaService.ready, 1) != 0 {
  3261  		panic("service already started")
  3262  	}
  3263  }
  3264  
  3265  func (s *agendaServer) checkReady() bool {
  3266  	return atomic.LoadUint32(&s.ready) != 0
  3267  }
  3268  
  3269  func (s *agendaServer) Agendas(ctx context.Context, req *pb.AgendasRequest) (*pb.AgendasResponse, error) {
  3270  	version, deployments := wallet.CurrentAgendas(s.activeNet)
  3271  	resp := &pb.AgendasResponse{
  3272  		Version: version,
  3273  		Agendas: make([]*pb.AgendasResponse_Agenda, len(deployments)),
  3274  	}
  3275  	for i := range deployments {
  3276  		d := &deployments[i]
  3277  		resp.Agendas[i] = &pb.AgendasResponse_Agenda{
  3278  			Id:          d.Vote.Id,
  3279  			Description: d.Vote.Description,
  3280  			Mask:        uint32(d.Vote.Mask),
  3281  			Choices:     make([]*pb.AgendasResponse_Choice, len(d.Vote.Choices)),
  3282  			StartTime:   int64(d.StartTime),
  3283  			ExpireTime:  int64(d.ExpireTime),
  3284  		}
  3285  		for j := range d.Vote.Choices {
  3286  			choice := &d.Vote.Choices[j]
  3287  			resp.Agendas[i].Choices[j] = &pb.AgendasResponse_Choice{
  3288  				Id:          choice.Id,
  3289  				Description: choice.Description,
  3290  				Bits:        uint32(choice.Bits),
  3291  				IsAbstain:   choice.IsAbstain,
  3292  				IsNo:        choice.IsNo,
  3293  			}
  3294  		}
  3295  	}
  3296  	return resp, nil
  3297  }
  3298  
  3299  // StartVotingService starts the VotingService.
  3300  func StartVotingService(server *grpc.Server, wallet *wallet.Wallet) {
  3301  	votingService.wallet = wallet
  3302  	if atomic.SwapUint32(&votingService.ready, 1) != 0 {
  3303  		panic("service already started")
  3304  	}
  3305  }
  3306  
  3307  func (s *votingServer) checkReady() bool {
  3308  	return atomic.LoadUint32(&s.ready) != 0
  3309  }
  3310  
  3311  func (s *votingServer) VoteChoices(ctx context.Context, req *pb.VoteChoicesRequest) (*pb.VoteChoicesResponse, error) {
  3312  	var ticketHash *chainhash.Hash
  3313  	var err error
  3314  	if len(req.TicketHash) != 0 {
  3315  		ticketHash, err = chainhash.NewHash(req.TicketHash)
  3316  		if err != nil {
  3317  			return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  3318  		}
  3319  	}
  3320  	version, agendas := wallet.CurrentAgendas(s.wallet.ChainParams())
  3321  	choices, voteBits, err := s.wallet.AgendaChoices(ctx, ticketHash)
  3322  	if err != nil {
  3323  		return nil, translateError(err)
  3324  	}
  3325  	resp := &pb.VoteChoicesResponse{
  3326  		Version:  version,
  3327  		Choices:  make([]*pb.VoteChoicesResponse_Choice, len(agendas)),
  3328  		Votebits: uint32(voteBits),
  3329  	}
  3330  
  3331  	for i := range choices {
  3332  		resp.Choices[i] = &pb.VoteChoicesResponse_Choice{
  3333  			AgendaId:          choices[i].AgendaID,
  3334  			AgendaDescription: agendas[i].Vote.Description,
  3335  			ChoiceId:          choices[i].ChoiceID,
  3336  			ChoiceDescription: "", // Set below
  3337  		}
  3338  		for j := range agendas[i].Vote.Choices {
  3339  			if choices[i].ChoiceID == agendas[i].Vote.Choices[j].Id {
  3340  				resp.Choices[i].ChoiceDescription = agendas[i].Vote.Choices[j].Description
  3341  				break
  3342  			}
  3343  		}
  3344  	}
  3345  	return resp, nil
  3346  }
  3347  
  3348  func (s *votingServer) SetVoteChoices(ctx context.Context, req *pb.SetVoteChoicesRequest) (*pb.SetVoteChoicesResponse, error) {
  3349  	var ticketHash *chainhash.Hash
  3350  	var err error
  3351  	if len(req.TicketHash) != 0 {
  3352  		ticketHash, err = chainhash.NewHash(req.TicketHash)
  3353  		if err != nil {
  3354  			return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  3355  		}
  3356  	}
  3357  	choices := make(wallet.AgendaChoices, len(req.Choices))
  3358  	for i, c := range req.Choices {
  3359  		choices[i] = wallet.AgendaChoice{
  3360  			AgendaID: c.AgendaId,
  3361  			ChoiceID: c.ChoiceId,
  3362  		}
  3363  	}
  3364  	voteBits, err := s.wallet.SetAgendaChoices(ctx, ticketHash, choices...)
  3365  	if err != nil {
  3366  		return nil, translateError(err)
  3367  	}
  3368  	resp := &pb.SetVoteChoicesResponse{
  3369  		Votebits: uint32(voteBits),
  3370  	}
  3371  	return resp, nil
  3372  }
  3373  
  3374  // TSpendPolicies returns voting policies for particular treasury spends
  3375  // transactions. If a tspend transaction hash is specified, that policy is
  3376  // returned; otherwise the policies for all known tspends are returned in an
  3377  // array.  If both a tspend transaction hash and a ticket hash are provided,
  3378  // the per-ticket tspend policy is returned.
  3379  func (s *votingServer) TSpendPolicies(ctx context.Context, req *pb.TSpendPoliciesRequest) (*pb.TSpendPoliciesResponse, error) {
  3380  	var ticketHash *chainhash.Hash
  3381  	var hash *chainhash.Hash
  3382  	var err error
  3383  	if len(req.TicketHash) != 0 {
  3384  		ticketHash, err = chainhash.NewHash(req.TicketHash)
  3385  		if err != nil {
  3386  			return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  3387  		}
  3388  	}
  3389  
  3390  	if len(req.Hash) != 0 {
  3391  		hash, err = chainhash.NewHash(req.Hash)
  3392  		if err != nil {
  3393  			return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  3394  		}
  3395  		var policy string
  3396  		switch s.wallet.TSpendPolicy(hash, ticketHash) {
  3397  		case stake.TreasuryVoteYes:
  3398  			policy = "yes"
  3399  		case stake.TreasuryVoteNo:
  3400  			policy = "no"
  3401  		default:
  3402  			policy = "abstain"
  3403  		}
  3404  		resp := &pb.TSpendPoliciesResponse{
  3405  			Policies: make([]*pb.TSpendPoliciesResponse_Policy, 0, 1),
  3406  		}
  3407  		r := &pb.TSpendPoliciesResponse_Policy{
  3408  			Hash:       req.Hash,
  3409  			Policy:     policy,
  3410  			TicketHash: []byte{},
  3411  		}
  3412  		if req.TicketHash != nil {
  3413  			r.TicketHash = req.TicketHash
  3414  		}
  3415  		resp.Policies = append(resp.Policies, r)
  3416  		return resp, nil
  3417  	}
  3418  
  3419  	tspends := s.wallet.GetAllTSpends(ctx)
  3420  	resp := &pb.TSpendPoliciesResponse{
  3421  		Policies: make([]*pb.TSpendPoliciesResponse_Policy, 0, len(tspends)),
  3422  	}
  3423  	for i := range tspends {
  3424  		tspendHash := tspends[i].TxHash()
  3425  		p := s.wallet.TSpendPolicy(&tspendHash, ticketHash)
  3426  
  3427  		var policy string
  3428  		switch p {
  3429  		case stake.TreasuryVoteYes:
  3430  			policy = "yes"
  3431  		case stake.TreasuryVoteNo:
  3432  			policy = "no"
  3433  		}
  3434  		r := &pb.TSpendPoliciesResponse_Policy{
  3435  			Hash:       tspendHash[:],
  3436  			Policy:     policy,
  3437  			TicketHash: []byte{},
  3438  		}
  3439  		if req.TicketHash != nil {
  3440  			r.TicketHash = req.TicketHash
  3441  		}
  3442  		resp.Policies = append(resp.Policies, r)
  3443  	}
  3444  	return resp, nil
  3445  }
  3446  
  3447  // SetTSpendPolicy saves the voting policy for a particular tspend transaction
  3448  // hash, and optionally, setting the tspend policy used by a specific ticket.
  3449  func (s *votingServer) SetTSpendPolicy(ctx context.Context, req *pb.SetTSpendPolicyRequest) (*pb.SetTSpendPolicyResponse, error) {
  3450  	if len(req.Hash) != chainhash.HashSize {
  3451  		err := fmt.Errorf("invalid tspend hash length, expected %d got %d",
  3452  			chainhash.HashSize, len(req.Hash))
  3453  		return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  3454  	}
  3455  
  3456  	hash, err := chainhash.NewHash(req.Hash)
  3457  	if err != nil {
  3458  		return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  3459  	}
  3460  
  3461  	var ticketHash *chainhash.Hash
  3462  	if req.TicketHash != nil {
  3463  		if len(req.TicketHash) != chainhash.HashSize {
  3464  			err := fmt.Errorf("invalid ticket hash length, expected %d got %d",
  3465  				chainhash.HashSize, len(req.TicketHash))
  3466  			return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  3467  		}
  3468  		var err error
  3469  		ticketHash, err = chainhash.NewHash(req.TicketHash)
  3470  		if err != nil {
  3471  			return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  3472  		}
  3473  	}
  3474  
  3475  	var policy stake.TreasuryVoteT
  3476  	switch req.Policy {
  3477  	case "abstain", "invalid", "":
  3478  		policy = stake.TreasuryVoteInvalid
  3479  	case "yes":
  3480  		policy = stake.TreasuryVoteYes
  3481  	case "no":
  3482  		policy = stake.TreasuryVoteNo
  3483  	default:
  3484  		err = fmt.Errorf("unknown policy %q", req.Policy)
  3485  		return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  3486  	}
  3487  
  3488  	err = s.wallet.SetTSpendPolicy(ctx, hash, policy, ticketHash)
  3489  	if err != nil {
  3490  		return nil, err
  3491  	}
  3492  
  3493  	resp := &pb.SetTSpendPolicyResponse{}
  3494  	return resp, err
  3495  }
  3496  
  3497  // treasuryPolicies returns voting policies for treasury spends for all keys in an array
  3498  func (s *votingServer) TreasuryPolicies(ctx context.Context, req *pb.TreasuryPoliciesRequest) (*pb.TreasuryPoliciesResponse, error) {
  3499  	policies := s.wallet.TreasuryKeyPolicies()
  3500  	resp := &pb.TreasuryPoliciesResponse{
  3501  		Policies: make([]*pb.TreasuryPoliciesResponse_Policy, 0, len(policies)),
  3502  	}
  3503  	for i := range policies {
  3504  		var policy string
  3505  		switch policies[i].Policy {
  3506  		case stake.TreasuryVoteYes:
  3507  			policy = "yes"
  3508  		case stake.TreasuryVoteNo:
  3509  			policy = "no"
  3510  		}
  3511  		r := &pb.TreasuryPoliciesResponse_Policy{
  3512  			Key:        policies[i].PiKey,
  3513  			Policy:     policy,
  3514  			TicketHash: []byte{},
  3515  		}
  3516  
  3517  		if policies[i].Ticket != nil {
  3518  			r.TicketHash = policies[i].Ticket[:]
  3519  		}
  3520  
  3521  		resp.Policies = append(resp.Policies, r)
  3522  	}
  3523  	return resp, nil
  3524  }
  3525  
  3526  // setTreasuryPolicy saves the voting policy for treasury spends by a particular
  3527  // key, and optionally, setting the key policy used by a specific ticket.
  3528  //
  3529  // If a VSP host is configured in the application settings, the voting
  3530  // preferences will also be set with the VSP.
  3531  func (s *votingServer) SetTreasuryPolicy(ctx context.Context, req *pb.SetTreasuryPolicyRequest) (*pb.SetTreasuryPolicyResponse, error) {
  3532  	var ticketHash *chainhash.Hash
  3533  	var err error
  3534  	if len(req.TicketHash) != 0 {
  3535  		ticketHash, err = chainhash.NewHash(req.TicketHash)
  3536  		if err != nil {
  3537  			return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  3538  		}
  3539  	}
  3540  
  3541  	if len(req.Key) != secp256k1.PubKeyBytesLenCompressed {
  3542  		err = errors.New("treasury key must be 33 bytes")
  3543  		return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  3544  	}
  3545  	var policy stake.TreasuryVoteT
  3546  	switch req.Policy {
  3547  	case "abstain", "invalid", "":
  3548  		policy = stake.TreasuryVoteInvalid
  3549  	case "yes":
  3550  		policy = stake.TreasuryVoteYes
  3551  	case "no":
  3552  		policy = stake.TreasuryVoteNo
  3553  	default:
  3554  		err = fmt.Errorf("unknown policy %q", req.Policy)
  3555  		return nil, status.Errorf(codes.InvalidArgument, "%v", err)
  3556  	}
  3557  
  3558  	err = s.wallet.SetTreasuryKeyPolicy(ctx, req.Key, policy, ticketHash)
  3559  	if err != nil {
  3560  		return nil, translateError(err)
  3561  	}
  3562  
  3563  	resp := &pb.SetTreasuryPolicyResponse{}
  3564  	return resp, err
  3565  }
  3566  
  3567  // StartMessageVerificationService starts the MessageVerification service
  3568  func StartMessageVerificationService(server *grpc.Server, chainParams *chaincfg.Params) {
  3569  	messageVerificationService.chainParams = chainParams
  3570  }
  3571  
  3572  func (s *messageVerificationServer) VerifyMessage(ctx context.Context, req *pb.VerifyMessageRequest) (
  3573  	*pb.VerifyMessageResponse, error) {
  3574  
  3575  	var valid bool
  3576  
  3577  	addr, err := stdaddr.DecodeAddress(req.Address, s.chainParams)
  3578  	if err != nil {
  3579  		return nil, translateError(err)
  3580  	}
  3581  
  3582  	// Addresses must have an associated secp256k1 private key and must be P2PKH
  3583  	// (P2PK and P2SH is not allowed).
  3584  	switch addr.(type) {
  3585  	case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0:
  3586  	default:
  3587  		return nil, status.Error(codes.InvalidArgument,
  3588  			"address must be secp256k1 pay-to-pubkey-hash")
  3589  	}
  3590  
  3591  	valid, err = wallet.VerifyMessage(req.Message, addr, req.Signature, s.chainParams)
  3592  	if err != nil {
  3593  		return nil, translateError(err)
  3594  	}
  3595  	return &pb.VerifyMessageResponse{Valid: valid}, nil
  3596  }
  3597  
  3598  // StartDecodeMessageService starts the MessageDecode service
  3599  func StartDecodeMessageService(server *grpc.Server, chainParams *chaincfg.Params) {
  3600  	decodeMessageService.chainParams = chainParams
  3601  }
  3602  
  3603  func marshalDecodedTxInputs(mtx *wire.MsgTx) []*pb.DecodedTransaction_Input {
  3604  	inputs := make([]*pb.DecodedTransaction_Input, len(mtx.TxIn))
  3605  
  3606  	for i, txIn := range mtx.TxIn {
  3607  		// The disassembled string will contain [error] inline
  3608  		// if the script doesn't fully parse, so ignore the
  3609  		// error here.
  3610  		disbuf, _ := txscript.DisasmString(txIn.SignatureScript)
  3611  
  3612  		inputs[i] = &pb.DecodedTransaction_Input{
  3613  			PreviousTransactionHash:  txIn.PreviousOutPoint.Hash[:],
  3614  			PreviousTransactionIndex: txIn.PreviousOutPoint.Index,
  3615  			Tree:                     pb.DecodedTransaction_Input_TreeType(txIn.PreviousOutPoint.Tree),
  3616  			Sequence:                 txIn.Sequence,
  3617  			AmountIn:                 txIn.ValueIn,
  3618  			BlockHeight:              txIn.BlockHeight,
  3619  			BlockIndex:               txIn.BlockIndex,
  3620  			SignatureScript:          txIn.SignatureScript,
  3621  			SignatureScriptAsm:       disbuf,
  3622  		}
  3623  	}
  3624  
  3625  	return inputs
  3626  }
  3627  
  3628  func marshalDecodedTxOutputs(mtx *wire.MsgTx, chainParams *chaincfg.Params) []*pb.DecodedTransaction_Output {
  3629  	outputs := make([]*pb.DecodedTransaction_Output, len(mtx.TxOut))
  3630  	txType := stake.DetermineTxType(mtx)
  3631  
  3632  	for i, v := range mtx.TxOut {
  3633  		// The disassembled string will contain [error] inline if the
  3634  		// script doesn't fully parse, so ignore the error here.
  3635  		disbuf, _ := txscript.DisasmString(v.PkScript)
  3636  
  3637  		// Attempt to extract addresses from the public key script.  In
  3638  		// the case of stake submission transactions, the odd outputs
  3639  		// contain a commitment address, so detect that case
  3640  		// accordingly.
  3641  		var addrs []stdaddr.Address
  3642  		var encodedAddrs []string
  3643  		var scriptClass stdscript.ScriptType
  3644  		var reqSigs uint16
  3645  		var commitAmt *dcrutil.Amount
  3646  		if (txType == stake.TxTypeSStx) && (stake.IsStakeCommitmentTxOut(i)) {
  3647  			// Questionable! This is either "nulldata" or the pseudo-type
  3648  			// "sstxcommitment", not "stakesubmission", which is only the 0th
  3649  			// output of a ticket.
  3650  			scriptClass = stdscript.STStakeSubmissionPubKeyHash
  3651  			addr, err := stake.AddrFromSStxPkScrCommitment(v.PkScript,
  3652  				chainParams)
  3653  			if err != nil {
  3654  				encodedAddrs = []string{fmt.Sprintf(
  3655  					"[error] failed to decode ticket "+
  3656  						"commitment addr output for tx hash "+
  3657  						"%v, output idx %v", mtx.TxHash(), i)}
  3658  			} else {
  3659  				encodedAddrs = []string{addr.String()}
  3660  			}
  3661  			amt, err := stake.AmountFromSStxPkScrCommitment(v.PkScript)
  3662  			if err != nil {
  3663  				commitAmt = &amt
  3664  			}
  3665  		} else {
  3666  			// Ignore the error here since an error means the script
  3667  			// couldn't parse and there is no additional information
  3668  			// about it anyways.
  3669  			scriptClass, addrs = stdscript.ExtractAddrs(v.Version, v.PkScript, chainParams)
  3670  			reqSigs = stdscript.DetermineRequiredSigs(v.Version, v.PkScript)
  3671  			encodedAddrs = make([]string, len(addrs))
  3672  			for j, addr := range addrs {
  3673  				encodedAddrs[j] = addr.String()
  3674  			}
  3675  		}
  3676  
  3677  		outputs[i] = &pb.DecodedTransaction_Output{
  3678  			Index:              uint32(i),
  3679  			Value:              v.Value,
  3680  			Version:            int32(v.Version),
  3681  			Addresses:          encodedAddrs,
  3682  			Script:             v.PkScript,
  3683  			ScriptAsm:          disbuf,
  3684  			ScriptClass:        pb.DecodedTransaction_Output_ScriptClass(scProto(scriptClass)),
  3685  			RequiredSignatures: int32(reqSigs),
  3686  		}
  3687  		if commitAmt != nil {
  3688  			outputs[i].CommitmentAmount = int64(*commitAmt)
  3689  		}
  3690  	}
  3691  
  3692  	return outputs
  3693  }
  3694  
  3695  // api.proto:
  3696  //
  3697  // enum ScriptClass {
  3698  // 	NON_STANDARD = 0;
  3699  // 	PUB_KEY = 1;
  3700  // 	PUB_KEY_HASH = 2;
  3701  // 	SCRIPT_HASH = 3;
  3702  // 	MULTI_SIG = 4;
  3703  // 	NULL_DATA = 5;
  3704  // 	STAKE_SUBMISSION = 6;
  3705  // 	STAKE_GEN = 7;
  3706  // 	STAKE_REVOCATION = 8;
  3707  // 	STAKE_SUB_CHANGE = 9;
  3708  // 	PUB_KEY_ALT = 10;
  3709  // 	PUB_KEY_HASH_ALT = 11;
  3710  // 	TGEN = 12;
  3711  // 	TADD = 13;
  3712  
  3713  func scProto(class stdscript.ScriptType) int32 {
  3714  	switch class {
  3715  	case stdscript.STNonStandard:
  3716  		return 0
  3717  	case stdscript.STPubKeyEcdsaSecp256k1:
  3718  		return 1
  3719  	case stdscript.STPubKeyHashEcdsaSecp256k1:
  3720  		return 2
  3721  	case stdscript.STScriptHash:
  3722  		return 3
  3723  	case stdscript.STMultiSig:
  3724  		return 4
  3725  	case stdscript.STNullData:
  3726  		return 5
  3727  	case stdscript.STStakeSubmissionPubKeyHash, stdscript.STStakeSubmissionScriptHash:
  3728  		return 6
  3729  	case stdscript.STStakeGenPubKeyHash, stdscript.STStakeGenScriptHash:
  3730  		return 7
  3731  	case stdscript.STStakeRevocationPubKeyHash, stdscript.STStakeRevocationScriptHash:
  3732  		return 8
  3733  	case stdscript.STStakeChangePubKeyHash, stdscript.STStakeChangeScriptHash:
  3734  		return 9
  3735  	case stdscript.STPubKeyEd25519, stdscript.STPubKeySchnorrSecp256k1:
  3736  		return 10
  3737  	case stdscript.STPubKeyHashEd25519, stdscript.STPubKeyHashSchnorrSecp256k1:
  3738  		return 11
  3739  	case stdscript.STTreasuryGenPubKeyHash, stdscript.STTreasuryGenScriptHash:
  3740  		return 12
  3741  	case stdscript.STTreasuryAdd:
  3742  		return 13
  3743  	}
  3744  
  3745  	return 0
  3746  }
  3747  
  3748  func (s *decodeMessageServer) DecodeRawTransaction(ctx context.Context, req *pb.DecodeRawTransactionRequest) (
  3749  	*pb.DecodeRawTransactionResponse, error) {
  3750  
  3751  	serializedTx := req.SerializedTransaction
  3752  
  3753  	var mtx wire.MsgTx
  3754  	err := mtx.Deserialize(bytes.NewReader(serializedTx))
  3755  	if err != nil {
  3756  		return nil, status.Errorf(codes.InvalidArgument, "Could not decode Tx: %v",
  3757  			err)
  3758  	}
  3759  
  3760  	txHash := mtx.TxHash()
  3761  	resp := &pb.DecodeRawTransactionResponse{
  3762  		Transaction: &pb.DecodedTransaction{
  3763  			TransactionHash: txHash[:],
  3764  			TransactionType: marshalTxType(wallet.TxTransactionType(&mtx)),
  3765  			Version:         int32(mtx.Version),
  3766  			LockTime:        mtx.LockTime,
  3767  			Expiry:          mtx.Expiry,
  3768  			Inputs:          marshalDecodedTxInputs(&mtx),
  3769  			Outputs:         marshalDecodedTxOutputs(&mtx, s.chainParams),
  3770  		},
  3771  	}
  3772  
  3773  	return resp, nil
  3774  }
  3775  
  3776  func (s *walletServer) BestBlock(ctx context.Context, req *pb.BestBlockRequest) (*pb.BestBlockResponse, error) {
  3777  	hash, height := s.wallet.MainChainTip(ctx)
  3778  	resp := &pb.BestBlockResponse{
  3779  		Hash:   hash[:],
  3780  		Height: uint32(height),
  3781  	}
  3782  	return resp, nil
  3783  }
  3784  
  3785  func (s *walletServer) SignHashes(ctx context.Context, req *pb.SignHashesRequest) (*pb.SignHashesResponse, error) {
  3786  	if len(req.Passphrase) > 0 {
  3787  		lock := make(chan time.Time, 1)
  3788  		defer func() {
  3789  			lock <- time.Time{} // send matters, not the value
  3790  		}()
  3791  		err := s.wallet.Unlock(ctx, req.Passphrase, lock)
  3792  		if err != nil {
  3793  			return nil, translateError(err)
  3794  		}
  3795  	}
  3796  
  3797  	addr, err := decodeAddress(req.Address, s.wallet.ChainParams())
  3798  	if err != nil {
  3799  		return nil, err
  3800  	}
  3801  
  3802  	// Addresses must have an associated secp256k1 private key and therefore
  3803  	// must be P2PK or P2PKH (P2SH is not allowed).
  3804  	switch addr.(type) {
  3805  	case *stdaddr.AddressPubKeyEcdsaSecp256k1V0:
  3806  	case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0:
  3807  	default:
  3808  		return nil, status.Error(codes.InvalidArgument,
  3809  			"address must be secp256k1 P2PK or P2PKH")
  3810  	}
  3811  
  3812  	signatures, pubKey, err := s.wallet.SignHashes(ctx, req.Hashes, addr)
  3813  	if err != nil {
  3814  		return nil, translateError(err)
  3815  	}
  3816  
  3817  	return &pb.SignHashesResponse{
  3818  		PublicKey:  pubKey,
  3819  		Signatures: signatures,
  3820  	}, nil
  3821  }
  3822  
  3823  func (s *walletServer) AbandonTransaction(ctx context.Context, req *pb.AbandonTransactionRequest) (
  3824  	*pb.AbandonTransactionResponse, error) {
  3825  
  3826  	txHash, err := chainhash.NewHash(req.TransactionHash)
  3827  	if err != nil {
  3828  		return nil, status.Errorf(codes.InvalidArgument, "invalid transaction hash: %v", err)
  3829  	}
  3830  	err = s.wallet.AbandonTransaction(ctx, txHash)
  3831  	if err != nil {
  3832  		return nil, translateError(err)
  3833  	}
  3834  	return &pb.AbandonTransactionResponse{}, nil
  3835  }
  3836  
  3837  // StartNetworkService starts the NetworkService.
  3838  func StartNetworkService(server *grpc.Server, wallet *wallet.Wallet) {
  3839  	networkService.wallet = wallet
  3840  	if atomic.SwapUint32(&networkService.ready, 1) != 0 {
  3841  		panic("service already started")
  3842  	}
  3843  }
  3844  
  3845  func (s *networkServer) checkReady() bool {
  3846  	return atomic.LoadUint32(&s.ready) != 0
  3847  }
  3848  
  3849  func (s *networkServer) GetRawBlock(ctx context.Context, req *pb.GetRawBlockRequest) (*pb.GetRawBlockResponse, error) {
  3850  	n, err := s.wallet.NetworkBackend()
  3851  	if err != nil {
  3852  		return nil, translateError(err)
  3853  	}
  3854  
  3855  	var bh *chainhash.Hash
  3856  	if req.BlockHash == nil {
  3857  		id := wallet.NewBlockIdentifierFromHeight(req.BlockHeight)
  3858  		info, err := s.wallet.BlockInfo(ctx, id)
  3859  		if err != nil {
  3860  			return nil, translateError(err)
  3861  		}
  3862  
  3863  		bh = &info.Hash
  3864  	} else {
  3865  		bh, err = chainhash.NewHash(req.BlockHash)
  3866  		if err != nil {
  3867  			return nil, status.Errorf(codes.InvalidArgument, "invalid blockhash: %s", err.Error())
  3868  		}
  3869  	}
  3870  
  3871  	blocks, err := n.Blocks(ctx, []*chainhash.Hash{bh})
  3872  	if err != nil {
  3873  		return nil, translateError(err)
  3874  	}
  3875  	if len(blocks) == 0 {
  3876  		// Should never happen but protects against a possible panic on
  3877  		// the following code.
  3878  		return nil, status.Errorf(codes.Internal, "network returned 0 blocks")
  3879  	}
  3880  
  3881  	rawBlock, err := blocks[0].Bytes()
  3882  	if err != nil {
  3883  		return nil, translateError(err)
  3884  	}
  3885  	return &pb.GetRawBlockResponse{
  3886  		Block: rawBlock,
  3887  	}, nil
  3888  }
  3889  
  3890  func (s *walletServer) GetCoinjoinOutputspByAcct(ctx context.Context, req *pb.GetCoinjoinOutputspByAcctRequest) (
  3891  	*pb.GetCoinjoinOutputspByAcctResponse, error) {
  3892  	coinjumSumByAcct, err := s.wallet.GetCoinjoinTxsSumbByAcct(ctx)
  3893  	if err != nil {
  3894  		return nil, translateError(err)
  3895  	}
  3896  	var resp []*pb.CoinjoinTxsSumByAcct
  3897  	for acctIdx, sum := range coinjumSumByAcct {
  3898  		s := &pb.CoinjoinTxsSumByAcct{
  3899  			AccountNumber:  acctIdx,
  3900  			CoinjoinTxsSum: int32(sum),
  3901  		}
  3902  		resp = append(resp, s)
  3903  	}
  3904  
  3905  	return &pb.GetCoinjoinOutputspByAcctResponse{
  3906  		Data: resp,
  3907  	}, nil
  3908  }
  3909  
  3910  func (s *walletServer) SetAccountPassphrase(ctx context.Context, req *pb.SetAccountPassphraseRequest) (
  3911  	*pb.SetAccountPassphraseResponse, error) {
  3912  	_, err := s.wallet.AccountName(ctx, req.AccountNumber)
  3913  	if err != nil {
  3914  		if errors.Is(err, errors.NotExist) {
  3915  			return nil, status.Errorf(codes.NotFound, "account not found")
  3916  		}
  3917  		return nil, err
  3918  	}
  3919  	encryptedAcct, err := s.wallet.AccountHasPassphrase(ctx, req.AccountNumber)
  3920  	if err != nil {
  3921  		return nil, err
  3922  	}
  3923  
  3924  	// if account is not encrypted we need to unlock the wallet. Otherwise it is
  3925  	// used the account passphrase for it.
  3926  	if encryptedAcct && len(req.AccountPassphrase) > 0 {
  3927  		err = s.wallet.UnlockAccount(ctx, req.AccountNumber, req.AccountPassphrase)
  3928  		if err != nil {
  3929  			return nil, translateError(err)
  3930  		}
  3931  		defer func() {
  3932  			zero(req.AccountPassphrase)
  3933  			err = s.wallet.LockAccount(ctx, req.AccountNumber)
  3934  		}()
  3935  	} else if len(req.WalletPassphrase) > 0 {
  3936  		lock := make(chan time.Time, 1)
  3937  		defer func() {
  3938  			zero(req.WalletPassphrase)
  3939  			lock <- time.Time{} // send matters, not the value
  3940  		}()
  3941  		err = s.wallet.Unlock(ctx, req.WalletPassphrase, lock)
  3942  		if err != nil {
  3943  			return nil, translateError(err)
  3944  		}
  3945  	}
  3946  
  3947  	err = s.wallet.SetAccountPassphrase(ctx, req.AccountNumber, req.NewAccountPassphrase)
  3948  	if err != nil {
  3949  		return nil, translateError(err)
  3950  	}
  3951  	return &pb.SetAccountPassphraseResponse{}, nil
  3952  }
  3953  
  3954  func (s *walletServer) UnlockAccount(ctx context.Context, req *pb.UnlockAccountRequest) (
  3955  	*pb.UnlockAccountResponse, error) {
  3956  	_, err := s.wallet.AccountName(ctx, req.AccountNumber)
  3957  	if err != nil {
  3958  		if errors.Is(err, errors.NotExist) {
  3959  			return nil, status.Errorf(codes.NotFound, "account not found")
  3960  		}
  3961  		return nil, err
  3962  	}
  3963  	err = s.wallet.UnlockAccount(ctx, req.AccountNumber, req.Passphrase)
  3964  	if err != nil {
  3965  		return nil, translateError(err)
  3966  	}
  3967  	return &pb.UnlockAccountResponse{}, nil
  3968  }
  3969  
  3970  func (s *walletServer) LockAccount(ctx context.Context, req *pb.LockAccountRequest) (
  3971  	*pb.LockAccountResponse, error) {
  3972  	_, err := s.wallet.AccountName(ctx, req.AccountNumber)
  3973  	if err != nil {
  3974  		if errors.Is(err, errors.NotExist) {
  3975  			return nil, status.Errorf(codes.NotFound, "account not found")
  3976  		}
  3977  		return nil, translateError(err)
  3978  	}
  3979  	err = s.wallet.LockAccount(ctx, req.AccountNumber)
  3980  	if err != nil {
  3981  		return nil, translateError(err)
  3982  	}
  3983  	return &pb.LockAccountResponse{}, nil
  3984  }
  3985  
  3986  func (s *walletServer) AccountUnlocked(ctx context.Context, req *pb.AccountUnlockedRequest) (
  3987  	*pb.AccountUnlockedResponse, error) {
  3988  	_, err := s.wallet.AccountName(ctx, req.AccountNumber)
  3989  	if err != nil {
  3990  		if errors.Is(err, errors.NotExist) {
  3991  			return nil, status.Errorf(codes.NotFound, "account not found")
  3992  		}
  3993  		return nil, translateError(err)
  3994  	}
  3995  	unlocked, err := s.wallet.AccountUnlocked(ctx, req.AccountNumber)
  3996  	if err != nil {
  3997  		return nil, translateError(err)
  3998  	}
  3999  	return &pb.AccountUnlockedResponse{
  4000  		Unlocked: unlocked,
  4001  	}, nil
  4002  }
  4003  
  4004  func (s *walletServer) UnlockWallet(ctx context.Context, req *pb.UnlockWalletRequest) (
  4005  	*pb.UnlockWalletResponse, error) {
  4006  
  4007  	defer zero(req.Passphrase)
  4008  	err := s.wallet.Unlock(ctx, req.Passphrase, nil)
  4009  	if err != nil {
  4010  		return nil, translateError(err)
  4011  	}
  4012  	return &pb.UnlockWalletResponse{}, nil
  4013  }
  4014  
  4015  func (s *walletServer) LockWallet(ctx context.Context, req *pb.LockWalletRequest) (
  4016  	*pb.LockWalletResponse, error) {
  4017  
  4018  	s.wallet.Lock()
  4019  	return &pb.LockWalletResponse{}, nil
  4020  }
  4021  
  4022  // getPeerInfo responds to the getpeerinfo request.
  4023  // It gets the network backend and views the data on remote peers when in spv mode
  4024  func (s *walletServer) GetPeerInfo(ctx context.Context, req *pb.GetPeerInfoRequest) (*pb.GetPeerInfoResponse, error) {
  4025  	n, err := s.wallet.NetworkBackend()
  4026  	if err != nil {
  4027  		return nil, err
  4028  	}
  4029  	syncer, ok := n.(*spv.Syncer)
  4030  	if !ok {
  4031  		var resp []*struct {
  4032  			ID             int32  `json:"id"`
  4033  			Addr           string `json:"addr"`
  4034  			AddrLocal      string `json:"addrlocal"`
  4035  			Services       string `json:"services"`
  4036  			Version        uint32 `json:"version"`
  4037  			SubVer         string `json:"subver"`
  4038  			StartingHeight int64  `json:"startingheight"`
  4039  			BanScore       int32  `json:"banscore"`
  4040  		}
  4041  		if rpc, ok := n.(*dcrd.RPC); ok {
  4042  			err := rpc.Call(ctx, "getpeerinfo", &resp)
  4043  			if err != nil {
  4044  				return nil, err
  4045  			}
  4046  		}
  4047  		grpcResp := []*pb.GetPeerInfoResponse_PeerInfo{}
  4048  		for _, peerInfo := range resp {
  4049  			peerInfo := &pb.GetPeerInfoResponse_PeerInfo{
  4050  				Id:             peerInfo.ID,
  4051  				Addr:           peerInfo.Addr,
  4052  				AddrLocal:      peerInfo.AddrLocal,
  4053  				Services:       peerInfo.Services,
  4054  				Version:        peerInfo.Version,
  4055  				SubVer:         peerInfo.SubVer,
  4056  				StartingHeight: peerInfo.StartingHeight,
  4057  				BanScore:       peerInfo.BanScore,
  4058  			}
  4059  			grpcResp = append(grpcResp, peerInfo)
  4060  		}
  4061  
  4062  		return &pb.GetPeerInfoResponse{
  4063  			PeerInfo: grpcResp,
  4064  		}, nil
  4065  	}
  4066  
  4067  	rps := syncer.GetRemotePeers()
  4068  	infos := make([]*pb.GetPeerInfoResponse_PeerInfo, 0, len(rps))
  4069  
  4070  	for _, rp := range rps {
  4071  		info := &pb.GetPeerInfoResponse_PeerInfo{
  4072  			Id:             int32(rp.ID()),
  4073  			Addr:           rp.RemoteAddr().String(),
  4074  			AddrLocal:      rp.LocalAddr().String(),
  4075  			Services:       fmt.Sprintf("%08d", uint64(rp.Services())),
  4076  			Version:        rp.Pver(),
  4077  			SubVer:         rp.UA(),
  4078  			StartingHeight: int64(rp.InitialHeight()),
  4079  			BanScore:       int32(rp.BanScore()),
  4080  		}
  4081  		infos = append(infos, info)
  4082  	}
  4083  	sort.Slice(infos, func(i, j int) bool {
  4084  		return infos[i].Id < infos[j].Id
  4085  	})
  4086  	return &pb.GetPeerInfoResponse{
  4087  		PeerInfo: infos,
  4088  	}, nil
  4089  }
  4090  
  4091  func (s *walletServer) GetVSPTicketsByFeeStatus(ctx context.Context, req *pb.GetVSPTicketsByFeeStatusRequest) (
  4092  	*pb.GetVSPTicketsByFeeStatusResponse, error) {
  4093  	var feeStatus int
  4094  	switch req.FeeStatus {
  4095  	case pb.GetVSPTicketsByFeeStatusRequest_VSP_FEE_PROCESS_STARTED:
  4096  		feeStatus = int(udb.VSPFeeProcessStarted)
  4097  	case pb.GetVSPTicketsByFeeStatusRequest_VSP_FEE_PROCESS_PAID:
  4098  		feeStatus = int(udb.VSPFeeProcessPaid)
  4099  	case pb.GetVSPTicketsByFeeStatusRequest_VSP_FEE_PROCESS_ERRORED:
  4100  		feeStatus = int(udb.VSPFeeProcessErrored)
  4101  	case pb.GetVSPTicketsByFeeStatusRequest_VSP_FEE_PROCESS_CONFIRMED:
  4102  		feeStatus = int(udb.VSPFeeProcessConfirmed)
  4103  	default:
  4104  		return nil, status.Errorf(codes.InvalidArgument, "fee status=%v", req.FeeStatus)
  4105  	}
  4106  
  4107  	failedTicketsFee, err := s.wallet.GetVSPTicketsByFeeStatus(ctx, feeStatus)
  4108  	if err != nil {
  4109  		return nil, err
  4110  	}
  4111  
  4112  	hashes := make([][]byte, len(failedTicketsFee))
  4113  	for i := range failedTicketsFee {
  4114  		hashes[i] = failedTicketsFee[i][:]
  4115  	}
  4116  	return &pb.GetVSPTicketsByFeeStatusResponse{
  4117  		TicketsHashes: hashes,
  4118  	}, nil
  4119  }
  4120  
  4121  func (s *walletServer) SyncVSPFailedTickets(ctx context.Context, req *pb.SyncVSPTicketsRequest) (
  4122  	*pb.SyncVSPTicketsResponse, error) {
  4123  	failedTicketsFee, err := s.wallet.GetVSPTicketsByFeeStatus(ctx, int(udb.VSPFeeProcessErrored))
  4124  	if err != nil {
  4125  		return nil, err
  4126  	}
  4127  
  4128  	vspHost := req.VspHost
  4129  	vspPubKey := req.VspPubkey
  4130  	if vspPubKey == "" {
  4131  		return nil, status.Errorf(codes.InvalidArgument, "vsp pubkey can not be null")
  4132  	}
  4133  	if vspHost == "" {
  4134  		return nil, status.Errorf(codes.InvalidArgument, "vsp host can not be null")
  4135  	}
  4136  	policy := vsp.Policy{
  4137  		MaxFee:     0.1e8,
  4138  		FeeAcct:    req.Account,
  4139  		ChangeAcct: req.ChangeAccount,
  4140  	}
  4141  	cfg := vsp.Config{
  4142  		URL:    vspHost,
  4143  		PubKey: vspPubKey,
  4144  		Dialer: nil,
  4145  		Wallet: s.wallet,
  4146  		Policy: policy,
  4147  	}
  4148  	vspClient, err := loader.VSP(cfg)
  4149  	if err != nil {
  4150  		return nil, status.Errorf(codes.Unknown, "TicketBuyerV3 instance failed to start. Error: %v", err)
  4151  	}
  4152  
  4153  	// process tickets fee if needed.
  4154  	for _, ticketHash := range failedTicketsFee {
  4155  		feeTx := new(wire.MsgTx)
  4156  		err := vspClient.Process(ctx, &ticketHash, feeTx)
  4157  		if err != nil {
  4158  			// if it fails to process again, we log it and continue with
  4159  			// the wallet start.
  4160  			// Not sure we need to log here since it's already warned elsewhere
  4161  		}
  4162  	}
  4163  	return &pb.SyncVSPTicketsResponse{}, nil
  4164  }
  4165  
  4166  func (s *walletServer) ProcessManagedTickets(ctx context.Context, req *pb.ProcessManagedTicketsRequest) (
  4167  	*pb.ProcessManagedTicketsResponse, error) {
  4168  
  4169  	vspHost := req.VspHost
  4170  	vspPubKey := req.VspPubkey
  4171  	if vspPubKey == "" {
  4172  		return nil, status.Errorf(codes.InvalidArgument, "vsp pubkey can not be null")
  4173  	}
  4174  	if vspHost == "" {
  4175  		return nil, status.Errorf(codes.InvalidArgument, "vsp host can not be null")
  4176  	}
  4177  	policy := vsp.Policy{
  4178  		MaxFee:     0.1e8,
  4179  		FeeAcct:    req.FeeAccount,
  4180  		ChangeAcct: req.ChangeAccount,
  4181  	}
  4182  	cfg := vsp.Config{
  4183  		URL:    vspHost,
  4184  		PubKey: vspPubKey,
  4185  		Dialer: nil,
  4186  		Wallet: s.wallet,
  4187  		Policy: policy,
  4188  	}
  4189  	vspClient, err := loader.VSP(cfg)
  4190  	if err != nil {
  4191  		return nil, status.Errorf(codes.Unknown, "VSPClient instance failed to start. Error: %v", err)
  4192  	}
  4193  
  4194  	err = vspClient.ProcessManagedTickets(ctx)
  4195  	if err != nil {
  4196  		return nil, status.Errorf(codes.Unknown, "ProcessManagedTickets failed. Error: %v", err)
  4197  	}
  4198  
  4199  	return &pb.ProcessManagedTicketsResponse{}, nil
  4200  }
  4201  
  4202  func (s *walletServer) ProcessUnmanagedTickets(ctx context.Context, req *pb.ProcessUnmanagedTicketsRequest) (
  4203  	*pb.ProcessUnmanagedTicketsResponse, error) {
  4204  
  4205  	vspHost := req.VspHost
  4206  	vspPubKey := req.VspPubkey
  4207  	if vspPubKey == "" {
  4208  		return nil, status.Errorf(codes.InvalidArgument, "vsp pubkey can not be null")
  4209  	}
  4210  	if vspHost == "" {
  4211  		return nil, status.Errorf(codes.InvalidArgument, "vsp host can not be null")
  4212  	}
  4213  	policy := vsp.Policy{
  4214  		MaxFee:     0.1e8,
  4215  		FeeAcct:    req.FeeAccount,
  4216  		ChangeAcct: req.ChangeAccount,
  4217  	}
  4218  	cfg := vsp.Config{
  4219  		URL:    vspHost,
  4220  		PubKey: vspPubKey,
  4221  		Dialer: nil,
  4222  		Wallet: s.wallet,
  4223  		Policy: policy,
  4224  	}
  4225  	vspClient, err := loader.VSP(cfg)
  4226  	if err != nil {
  4227  		return nil, status.Errorf(codes.Unknown, "VSPClient instance failed to start. Error: %v", err)
  4228  	}
  4229  
  4230  	errUnmanagedTickets := errors.New("unmanaged tickets")
  4231  	err = s.wallet.ForUnspentUnexpiredTickets(ctx, func(hash *chainhash.Hash) error {
  4232  		_, err := s.wallet.VSPFeeHashForTicket(ctx, hash)
  4233  		if errors.Is(err, errors.NotExist) {
  4234  			return errUnmanagedTickets
  4235  		}
  4236  		return nil
  4237  	})
  4238  	if errors.Is(err, errUnmanagedTickets) {
  4239  		vspClient.ProcessUnprocessedTickets(ctx)
  4240  	}
  4241  
  4242  	return &pb.ProcessUnmanagedTicketsResponse{}, nil
  4243  }
  4244  
  4245  func (s *walletServer) SetVspdVoteChoices(ctx context.Context, req *pb.SetVspdVoteChoicesRequest) (
  4246  	*pb.SetVspdVoteChoicesResponse, error) {
  4247  
  4248  	vspHost := req.VspHost
  4249  	vspPubKey := req.VspPubkey
  4250  	if vspPubKey == "" {
  4251  		return nil, status.Errorf(codes.InvalidArgument, "vsp pubkey can not be null")
  4252  	}
  4253  	if vspHost == "" {
  4254  		return nil, status.Errorf(codes.InvalidArgument, "vsp host can not be null")
  4255  	}
  4256  	policy := vsp.Policy{
  4257  		MaxFee:     0.1e8,
  4258  		FeeAcct:    req.FeeAccount,
  4259  		ChangeAcct: req.ChangeAccount,
  4260  	}
  4261  	cfg := vsp.Config{
  4262  		URL:    vspHost,
  4263  		PubKey: vspPubKey,
  4264  		Dialer: nil,
  4265  		Wallet: s.wallet,
  4266  		Policy: policy,
  4267  	}
  4268  	vspClient, err := loader.VSP(cfg)
  4269  	if err != nil {
  4270  		return nil, status.Errorf(codes.Unknown, "VSPClient instance failed to start. Error: %v", err)
  4271  	}
  4272  
  4273  	treasuryChoices := make(map[string]string)
  4274  	treasuryPolicies := s.wallet.TreasuryKeyPolicies()
  4275  	for _, value := range treasuryPolicies {
  4276  		var choice string
  4277  		switch value.Policy {
  4278  		case stake.TreasuryVoteYes:
  4279  			choice = "yes"
  4280  		case stake.TreasuryVoteNo:
  4281  			choice = "no"
  4282  		default:
  4283  			choice = "abstain"
  4284  		}
  4285  		treasuryChoices[hex.EncodeToString(value.PiKey)] = choice
  4286  	}
  4287  
  4288  	tSpendChoices := make(map[string]string)
  4289  	tspendPolicies := s.wallet.GetAllTSpends(ctx)
  4290  	for i := range tspendPolicies {
  4291  		tspendHash := tspendPolicies[i].TxHash()
  4292  		p := s.wallet.TSpendPolicy(&tspendHash, nil)
  4293  
  4294  		var policy string
  4295  		switch p {
  4296  		case stake.TreasuryVoteYes:
  4297  			policy = "yes"
  4298  		case stake.TreasuryVoteNo:
  4299  			policy = "no"
  4300  		}
  4301  		tSpendChoices[tspendHash.String()] = policy
  4302  	}
  4303  
  4304  	err = s.wallet.ForUnspentUnexpiredTickets(ctx, func(hash *chainhash.Hash) error {
  4305  		// Skip errors here, but should we log at least?
  4306  		choices, _, err := s.wallet.AgendaChoices(ctx, hash)
  4307  		if err != nil {
  4308  			return nil
  4309  		}
  4310  		ticketHost, err := s.wallet.VSPHostForTicket(ctx, hash)
  4311  		if err != nil {
  4312  			return err
  4313  		}
  4314  		if ticketHost == vspHost {
  4315  			_ = vspClient.SetVoteChoice(ctx, hash, choices.Map(), tSpendChoices, treasuryChoices)
  4316  		}
  4317  		return nil
  4318  	})
  4319  	if err != nil {
  4320  		return nil, status.Errorf(codes.Unknown, "ForUnspentUnexpiredTickets failed. Error: %v", err)
  4321  	}
  4322  
  4323  	return &pb.SetVspdVoteChoicesResponse{}, nil
  4324  }
  4325  
  4326  func marshalVSPTrackedTickets(tickets []*vsp.TicketInfo) []*pb.GetTrackedVSPTicketsResponse_Ticket {
  4327  	res := make([]*pb.GetTrackedVSPTicketsResponse_Ticket, len(tickets))
  4328  	for i, ticket := range tickets {
  4329  		res[i] = &pb.GetTrackedVSPTicketsResponse_Ticket{
  4330  			TicketHash:        ticket.TicketHash[:],
  4331  			CommitmentAddress: ticket.CommitmentAddr.String(),
  4332  			VotingAddress:     ticket.VotingAddr.String(),
  4333  			State:             ticket.State,
  4334  			Fee:               int64(ticket.Fee),
  4335  			FeeHash:           ticket.FeeHash[:],
  4336  		}
  4337  	}
  4338  	return res
  4339  }
  4340  
  4341  func (s *walletServer) GetTrackedVSPTickets(ctx context.Context, req *pb.GetTrackedVSPTicketsRequest) (*pb.GetTrackedVSPTicketsResponse, error) {
  4342  	vspClients := loader.AllVSPs()
  4343  	res := &pb.GetTrackedVSPTicketsResponse{
  4344  		Vsps: make([]*pb.GetTrackedVSPTicketsResponse_VSP, 0, len(vspClients)),
  4345  	}
  4346  	for host, vspClient := range vspClients {
  4347  		tickets := vspClient.TrackedTickets()
  4348  		vspInfo := &pb.GetTrackedVSPTicketsResponse_VSP{
  4349  			Host:    host,
  4350  			Tickets: marshalVSPTrackedTickets(tickets),
  4351  		}
  4352  		res.Vsps = append(res.Vsps, vspInfo)
  4353  	}
  4354  
  4355  	return res, nil
  4356  }
  4357  
  4358  func (s *walletServer) DiscoverUsage(ctx context.Context, req *pb.DiscoverUsageRequest) (*pb.DiscoverUsageResponse, error) {
  4359  	n, err := s.requireNetworkBackend()
  4360  	if err != nil {
  4361  		return nil, status.Errorf(codes.Unknown, "Unable to retrieve network backend. Error: %v", err)
  4362  	}
  4363  
  4364  	startBlock := s.wallet.ChainParams().GenesisHash
  4365  	if req.StartingBlockHash != nil {
  4366  		h, err := chainhash.NewHash(req.StartingBlockHash)
  4367  		if err != nil {
  4368  			return nil, status.Errorf(codes.Unknown, "Invalid starting block hash provided. Error: %v", err)
  4369  		}
  4370  		startBlock = *h
  4371  	}
  4372  
  4373  	gapLimit := s.wallet.GapLimit()
  4374  	if req.GapLimit != 0 {
  4375  		gapLimit = req.GapLimit
  4376  	}
  4377  
  4378  	err = s.wallet.DiscoverActiveAddresses(ctx, n, &startBlock, req.DiscoverAccounts, gapLimit)
  4379  	if err != nil {
  4380  		return nil, err
  4381  	}
  4382  
  4383  	return &pb.DiscoverUsageResponse{}, nil
  4384  }