github.com/decred/dcrlnd@v0.7.6/lnwallet/remotedcrwallet/wallet.go (about)

     1  package remotedcrwallet
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"math"
     9  	"strings"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	pb "decred.org/dcrwallet/v4/rpc/walletrpc"
    15  	"decred.org/dcrwallet/v4/wallet"
    16  	base "decred.org/dcrwallet/v4/wallet"
    17  	"google.golang.org/grpc"
    18  
    19  	"decred.org/dcrwallet/v4/wallet/txauthor"
    20  	"github.com/decred/dcrd/chaincfg/chainhash"
    21  	"github.com/decred/dcrd/chaincfg/v3"
    22  	"github.com/decred/dcrd/dcrec"
    23  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    24  	"github.com/decred/dcrd/dcrutil/v4"
    25  	"github.com/decred/dcrd/hdkeychain/v3"
    26  	"github.com/decred/dcrd/txscript/v4"
    27  	"github.com/decred/dcrd/txscript/v4/sign"
    28  	"github.com/decred/dcrd/txscript/v4/stdaddr"
    29  	"github.com/decred/dcrd/txscript/v4/stdscript"
    30  	"github.com/decred/dcrd/wire"
    31  	"github.com/decred/dcrlnd/btcwalletcompat"
    32  	"github.com/decred/dcrlnd/channeldb"
    33  	"github.com/decred/dcrlnd/lnwallet"
    34  	"github.com/decred/dcrlnd/lnwallet/chainfee"
    35  )
    36  
    37  const (
    38  	// dryRunImportAccountNumAddrs represents the number of addresses we'll
    39  	// derive for an imported account's external and internal branch when a
    40  	// dry run is attempted.
    41  	dryRunImportAccountNumAddrs = 5
    42  )
    43  
    44  type DcrWallet struct {
    45  	// syncedChan is a channel that is closed once the wallet has initially synced
    46  	// to the network. It is protected by atomicWalletSynced.
    47  	syncedChan chan struct{}
    48  
    49  	// atomicWalletSynced is an atomic CAS flag (synced = 1) that indicates
    50  	// when syncing has completed.
    51  	atomicWalletSynced uint32
    52  
    53  	// conn is the underlying grpc socket connection.
    54  	conn *grpc.ClientConn
    55  
    56  	cfg Config
    57  
    58  	chainParams *chaincfg.Params
    59  	db          *channeldb.DB
    60  
    61  	*remoteWalletKeyRing
    62  
    63  	branchExtXPriv *hdkeychain.ExtendedKey
    64  	branchIntXPriv *hdkeychain.ExtendedKey
    65  
    66  	// account is the account number which controls onchain funds available
    67  	// for use from dcrlnd.
    68  	account uint32
    69  
    70  	// lockedOutpointsMu controls access to lockedOutpoints.
    71  	lockedOutpointsMu sync.Mutex
    72  
    73  	// lockedOutpoints is a list of outputs that the wallet won't use for
    74  	// other operations. This is necessary in the driver because grpc
    75  	// doesn't provide an entpoint to control this directly in the wallet.
    76  	lockedOutpoints map[wire.OutPoint]struct{}
    77  
    78  	wallet    pb.WalletServiceClient
    79  	network   pb.NetworkServiceClient
    80  	ctx       context.Context
    81  	cancelCtx func()
    82  }
    83  
    84  // A compile time check to ensure that DcrWallet implements the
    85  // WalletController interface.
    86  var _ lnwallet.WalletController = (*DcrWallet)(nil)
    87  
    88  // Compile time check to ensure that Dcrwallet implements the
    89  // onchainAddrSourcer interface.
    90  var _ (onchainAddrSourcer) = (*DcrWallet)(nil)
    91  
    92  func New(cfg Config) (*DcrWallet, error) {
    93  
    94  	if cfg.Conn == nil {
    95  		return nil, fmt.Errorf("conn cannot be empty")
    96  	}
    97  
    98  	// TODO(decred): Check if the node is synced before allowing this to
    99  	// proceed.
   100  	ctxb := context.Background()
   101  	wallet := pb.NewWalletServiceClient(cfg.Conn)
   102  
   103  	// Unlock the account.
   104  	unlockAcctReq := &pb.UnlockAccountRequest{
   105  		AccountNumber: uint32(cfg.AccountNumber),
   106  		Passphrase:    cfg.PrivatePass,
   107  	}
   108  	_, err := wallet.UnlockAccount(ctxb, unlockAcctReq)
   109  	if err != nil {
   110  		return nil, fmt.Errorf("unable to unlock account: %v", err)
   111  	}
   112  
   113  	// Obtain the root master priv key from which all LN-related
   114  	// keys are derived. By convention, this is a special branch in the
   115  	// passed account.
   116  	network := pb.NewNetworkServiceClient(cfg.Conn)
   117  	req := &pb.GetAccountExtendedPrivKeyRequest{
   118  		AccountNumber: uint32(cfg.AccountNumber),
   119  	}
   120  	resp, err := wallet.GetAccountExtendedPrivKey(ctxb, req)
   121  
   122  	// Irrespective of the return of GetAccountExtendedPrivKey, re-lock the
   123  	// account.
   124  	lockAcctReq := &pb.LockAccountRequest{
   125  		AccountNumber: uint32(cfg.AccountNumber),
   126  	}
   127  	_, lockErr := wallet.LockAccount(ctxb, lockAcctReq)
   128  	if lockErr != nil {
   129  		dcrwLog.Errorf("Error while locking account number %d: %v",
   130  			cfg.AccountNumber, lockErr)
   131  	}
   132  
   133  	// And now check if GetAccountExtendedPrivKey returned an error.
   134  	if err != nil {
   135  		return nil, fmt.Errorf("unable to get master LN account "+
   136  			"extended priv key: %v", err)
   137  	}
   138  
   139  	acctXPriv, err := hdkeychain.NewKeyFromString(
   140  		resp.AccExtendedPrivKey, cfg.NetParams,
   141  	)
   142  	if err != nil {
   143  		return nil, fmt.Errorf("unable to create account xpriv: %v", err)
   144  	}
   145  
   146  	// Derive and store the account's external and internal extended priv
   147  	// keys so that we can redeem funds stored in this account's utxos and
   148  	// use them to fund channels, send coins to other nodes, etc.
   149  	branchExtXPriv, err := acctXPriv.Child(0)
   150  	if err != nil {
   151  		return nil, fmt.Errorf("unable to derive the external branch xpriv: %v", err)
   152  	}
   153  	branchIntXPriv, err := acctXPriv.Child(1)
   154  	if err != nil {
   155  		return nil, fmt.Errorf("unable to derive the internal branch xpriv: %v", err)
   156  	}
   157  
   158  	// Ensure we don't attempt to use a keyring derived from a different
   159  	// account than previously used by comparing the first external public
   160  	// key with the one stored in the database.
   161  	firstKey, err := branchExtXPriv.Child(0)
   162  	if err != nil {
   163  		return nil, fmt.Errorf("unable to derive first external key: %v", err)
   164  	}
   165  	firstPubKeyBytes := firstKey.SerializedPubKey()
   166  	if err = cfg.DB.CompareAndStoreAccountID(firstPubKeyBytes); err != nil {
   167  		return nil, fmt.Errorf("account number %d failed to generate "+
   168  			"previously stored account ID: %v", cfg.AccountNumber, err)
   169  	}
   170  
   171  	ctx, cancelCtx := context.WithCancel(ctxb)
   172  	dcrw := &DcrWallet{
   173  		account:         uint32(cfg.AccountNumber),
   174  		syncedChan:      make(chan struct{}),
   175  		chainParams:     cfg.NetParams,
   176  		db:              cfg.DB,
   177  		cfg:             cfg,
   178  		conn:            cfg.Conn,
   179  		wallet:          wallet,
   180  		network:         network,
   181  		branchExtXPriv:  branchExtXPriv,
   182  		branchIntXPriv:  branchIntXPriv,
   183  		lockedOutpoints: make(map[wire.OutPoint]struct{}),
   184  		ctx:             ctx,
   185  		cancelCtx:       cancelCtx,
   186  	}
   187  
   188  	// Finally, create the keyring using the conventions for remote
   189  	// wallets.
   190  	dcrw.remoteWalletKeyRing, err = newRemoteWalletKeyRing(acctXPriv, cfg.DB, dcrw)
   191  	if err != nil {
   192  		// Sign operations will fail, so signal the error and prevent
   193  		// the wallet from considering itself synced (to prevent usage)
   194  		return nil, fmt.Errorf("unable to create wallet key ring: %v", err)
   195  	}
   196  
   197  	return dcrw, nil
   198  }
   199  
   200  // BackEnd returns the underlying ChainService's name as a string.
   201  //
   202  // This is a part of the WalletController interface.
   203  func (b *DcrWallet) BackEnd() string {
   204  	return "remotedcrwallet"
   205  }
   206  
   207  // Start initializes the underlying rpc connection, the wallet itself, and
   208  // begins syncing to the current available blockchain state.
   209  //
   210  // This is a part of the WalletController interface.
   211  func (b *DcrWallet) Start() error {
   212  	b.synced()
   213  	return nil
   214  }
   215  
   216  // Stop signals the wallet for shutdown. Shutdown may entail closing
   217  // any active sockets, database handles, stopping goroutines, etc.
   218  //
   219  // This is a part of the WalletController interface.
   220  func (b *DcrWallet) Stop() error {
   221  	b.cancelCtx()
   222  	return b.conn.Close()
   223  }
   224  
   225  // ConfirmedBalance returns the sum of all the wallet's unspent outputs that
   226  // have at least confs confirmations. If confs is set to zero, then all unspent
   227  // outputs, including those currently in the mempool will be included in the
   228  // final sum.
   229  //
   230  // This is a part of the WalletController interface.
   231  func (b *DcrWallet) ConfirmedBalance(confs int32, accountName string) (dcrutil.Amount, error) {
   232  	var acctNb = b.account
   233  	if accountName != "" && accountName != lnwallet.DefaultAccountName {
   234  		res, err := b.wallet.AccountNumber(context.Background(), &pb.AccountNumberRequest{AccountName: accountName})
   235  		if err != nil {
   236  			return 0, fmt.Errorf("unknown account named %s: %v", accountName, err)
   237  		}
   238  		acctNb = res.AccountNumber
   239  	}
   240  
   241  	req := &pb.BalanceRequest{
   242  		AccountNumber:         acctNb,
   243  		RequiredConfirmations: confs,
   244  	}
   245  	resp, err := b.wallet.Balance(context.Background(), req)
   246  	if err != nil {
   247  		return 0, err
   248  	}
   249  	return dcrutil.Amount(resp.Spendable), nil
   250  }
   251  
   252  // NewAddress returns the next external or internal address for the wallet
   253  // dictated by the value of the `change` parameter. If change is true, then an
   254  // internal address will be returned, otherwise an external address should be
   255  // returned.
   256  //
   257  // This is a part of the WalletController interface.
   258  func (b *DcrWallet) NewAddress(t lnwallet.AddressType, change bool, accountName string) (stdaddr.Address, error) {
   259  
   260  	switch t {
   261  	case lnwallet.PubKeyHash:
   262  		// nop
   263  	default:
   264  		return nil, fmt.Errorf("unknown address type")
   265  	}
   266  
   267  	var acctNb = b.account
   268  	if accountName != lnwallet.DefaultAccountName {
   269  		res, err := b.wallet.AccountNumber(context.Background(), &pb.AccountNumberRequest{AccountName: accountName})
   270  		if err != nil {
   271  			return nil, fmt.Errorf("unknown account named %s: %v", accountName, err)
   272  		}
   273  		acctNb = res.AccountNumber
   274  	}
   275  
   276  	kind := pb.NextAddressRequest_BIP0044_EXTERNAL
   277  	if change {
   278  		kind = pb.NextAddressRequest_BIP0044_INTERNAL
   279  	}
   280  	req := &pb.NextAddressRequest{
   281  		Kind:      kind,
   282  		Account:   acctNb,
   283  		GapPolicy: pb.NextAddressRequest_GAP_POLICY_WRAP,
   284  	}
   285  	resp, err := b.wallet.NextAddress(context.Background(), req)
   286  	if err != nil {
   287  		return nil, err
   288  	}
   289  
   290  	addr, err := stdaddr.DecodeAddress(resp.Address, b.chainParams)
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  	return addr, nil
   295  }
   296  
   297  // LastUnusedAddress returns the last *unused* address known by the wallet. An
   298  // address is unused if it hasn't received any payments. This can be useful in
   299  // UIs in order to continually show the "freshest" address without having to
   300  // worry about "address inflation" caused by continual refreshing. Similar to
   301  // NewAddress it can derive a specified address type, and also optionally a
   302  // change address.
   303  func (b *DcrWallet) LastUnusedAddress(addrType lnwallet.AddressType, accountName string) (
   304  	stdaddr.Address, error) {
   305  
   306  	switch addrType {
   307  	case lnwallet.PubKeyHash:
   308  		// nop
   309  	default:
   310  		return nil, fmt.Errorf("unknown address type")
   311  	}
   312  
   313  	return nil, fmt.Errorf("LastUnusedAddress unimplemented")
   314  }
   315  
   316  // IsOurAddress checks if the passed address belongs to this wallet
   317  //
   318  // This is a part of the WalletController interface.
   319  func (b *DcrWallet) IsOurAddress(a stdaddr.Address) bool {
   320  	validReq := &pb.ValidateAddressRequest{
   321  		Address: a.String(),
   322  	}
   323  	validResp, err := b.wallet.ValidateAddress(context.Background(), validReq)
   324  	if err != nil {
   325  		dcrwLog.Errorf("Error validating address to determine "+
   326  			"ownership: %v", err)
   327  		return false
   328  	}
   329  	return validResp.IsMine
   330  }
   331  
   332  // SendOutputs funds, signs, and broadcasts a Decred transaction paying out to
   333  // the specified outputs. In the case the wallet has insufficient funds, or the
   334  // outputs are non-standard, a non-nil error will be returned.
   335  //
   336  // This is a part of the WalletController interface.
   337  func (b *DcrWallet) SendOutputs(outputs []*wire.TxOut,
   338  	feeRate chainfee.AtomPerKByte, minConfs int32, label, fromAccount string) (*wire.MsgTx, error) {
   339  
   340  	ctxb := context.Background()
   341  
   342  	var acctNb = b.account
   343  	if fromAccount != "" && fromAccount != lnwallet.DefaultAccountName {
   344  		res, err := b.wallet.AccountNumber(context.Background(), &pb.AccountNumberRequest{AccountName: fromAccount})
   345  		if err != nil {
   346  			return nil, fmt.Errorf("unknown account named %s: %v", fromAccount, err)
   347  		}
   348  		acctNb = res.AccountNumber
   349  	}
   350  
   351  	reqOutputs := make([]*pb.ConstructTransactionRequest_Output, len(outputs))
   352  	for i, out := range outputs {
   353  		dest := &pb.ConstructTransactionRequest_OutputDestination{
   354  			Script:        out.PkScript,
   355  			ScriptVersion: uint32(out.Version),
   356  		}
   357  		reqOutputs[i] = &pb.ConstructTransactionRequest_Output{
   358  			Amount:      out.Value,
   359  			Destination: dest,
   360  		}
   361  	}
   362  	req := &pb.ConstructTransactionRequest{
   363  		SourceAccount:         acctNb,
   364  		FeePerKb:              int32(feeRate),
   365  		NonChangeOutputs:      reqOutputs,
   366  		RequiredConfirmations: minConfs,
   367  	}
   368  
   369  	resp, err := b.wallet.ConstructTransaction(ctxb, req)
   370  	if err != nil {
   371  		return nil, err
   372  	}
   373  
   374  	tx := new(wire.MsgTx)
   375  	err = tx.FromBytes(resp.UnsignedTransaction)
   376  	if err != nil {
   377  		return nil, err
   378  	}
   379  
   380  	// We need to manually sign the transaction here (instead of passing it
   381  	// to SignTransaction) because we don't hang onto the wallet password,
   382  	// but we do know the master priv key to the source account and can
   383  	// therefore derive the private keys for the individual addresses of
   384  	// the selected utxos.
   385  	//
   386  	// Additionally, we need to retrieve the index of each utxo address to
   387  	// know how to derive the private key.
   388  	//
   389  	// This ends up being harder (and slower) than this needs to be but
   390  	// allows us to not have to keep a completely unlocked remote wallet.
   391  
   392  	// Loop over the inputs which we need to sign.
   393  	for i, in := range tx.TxIn {
   394  		// Find out the PKScript of the input.
   395  		txReq := &pb.GetTransactionRequest{
   396  			TransactionHash: in.PreviousOutPoint.Hash[:],
   397  		}
   398  		txResp, err := b.wallet.GetTransaction(ctxb, txReq)
   399  		if err != nil {
   400  			return nil, fmt.Errorf("unable to fetch tx: %v", err)
   401  		}
   402  
   403  		var credit *pb.TransactionDetails_Output
   404  		for _, c := range txResp.Transaction.Credits {
   405  			if c.Index == in.PreviousOutPoint.Index {
   406  				credit = c
   407  				break
   408  			}
   409  		}
   410  		if credit == nil {
   411  			return nil, fmt.Errorf("unable to find pkscript of "+
   412  				"prev outpoint %v", in.PreviousOutPoint)
   413  		}
   414  		pkScript := credit.OutputScript
   415  
   416  		// Find out the HD index of the address.
   417  		validReq := &pb.ValidateAddressRequest{
   418  			Address: credit.Address,
   419  		}
   420  		validResp, err := b.wallet.ValidateAddress(ctxb, validReq)
   421  		if err != nil {
   422  			return nil, err
   423  		}
   424  
   425  		// Derive the private key that signs this utxo.
   426  		branchXPriv := b.branchExtXPriv
   427  		if validResp.IsInternal {
   428  			branchXPriv = b.branchIntXPriv
   429  		}
   430  		extPrivKey, err := branchXPriv.Child(validResp.Index)
   431  		if err != nil {
   432  			return nil, err
   433  		}
   434  		privKey, err := extPrivKey.SerializedPrivKey()
   435  		if err != nil {
   436  			return nil, err
   437  		}
   438  
   439  		// Actually sign the input.
   440  		sigScript, err := sign.SignatureScript(
   441  			tx, i, pkScript, txscript.SigHashAll, privKey,
   442  			dcrec.STEcdsaSecp256k1, true,
   443  		)
   444  		if err != nil {
   445  			return nil, err
   446  		}
   447  		in.SignatureScript = sigScript
   448  	}
   449  
   450  	// Now publish the transaction to the network.
   451  	signedTx, err := tx.Bytes()
   452  	if err != nil {
   453  		return nil, err
   454  	}
   455  	publishReq := &pb.PublishTransactionRequest{
   456  		SignedTransaction: signedTx,
   457  	}
   458  	_, err = b.wallet.PublishTransaction(ctxb, publishReq)
   459  	if err != nil {
   460  		return nil, err
   461  	}
   462  
   463  	return tx, nil
   464  }
   465  
   466  // CreateSimpleTx creates a Bitcoin transaction paying to the specified
   467  // outputs. The transaction is not broadcasted to the network, but a new change
   468  // address might be created in the wallet database. In the case the wallet has
   469  // insufficient funds, or the outputs are non-standard, an error should be
   470  // returned. This method also takes the target fee expressed in sat/kw that
   471  // should be used when crafting the transaction.
   472  //
   473  // NOTE: The dryRun argument can be set true to create a tx that doesn't alter
   474  // the database. A tx created with this set to true SHOULD NOT be broadcasted.
   475  //
   476  // This is a part of the WalletController interface.
   477  func (b *DcrWallet) CreateSimpleTx(outputs []*wire.TxOut,
   478  	feeRate chainfee.AtomPerKByte, minConfs int32, dryRun bool) (*txauthor.AuthoredTx, error) {
   479  
   480  	// TODO(decred) Review semantics for btcwallet's CreateSimpleTx.
   481  	return nil, fmt.Errorf("CreateSimpleTx unimplemented for dcrwallet")
   482  }
   483  
   484  // LockOutpoint marks an outpoint as locked meaning it will no longer be deemed
   485  // as eligible for coin selection. Locking outputs are utilized in order to
   486  // avoid race conditions when selecting inputs for usage when funding a
   487  // channel.
   488  //
   489  // This is a part of the WalletController interface.
   490  func (b *DcrWallet) LockOutpoint(o wire.OutPoint) {
   491  	b.lockedOutpointsMu.Lock()
   492  	b.lockedOutpoints[o] = struct{}{}
   493  	b.lockedOutpointsMu.Unlock()
   494  }
   495  
   496  // UnlockOutpoint unlocks a previously locked output, marking it eligible for
   497  // coin selection.
   498  //
   499  // This is a part of the WalletController interface.
   500  func (b *DcrWallet) UnlockOutpoint(o wire.OutPoint) {
   501  	b.lockedOutpointsMu.Lock()
   502  	delete(b.lockedOutpoints, o)
   503  	b.lockedOutpointsMu.Unlock()
   504  }
   505  
   506  // ListUnspentWitness returns a slice of all the unspent outputs the wallet
   507  // controls which pay to witness programs either directly or indirectly.
   508  //
   509  // This is a part of the WalletController interface.
   510  func (b *DcrWallet) ListUnspentWitness(minConfs, maxConfs int32, accountName string) (
   511  	[]*lnwallet.Utxo, error) {
   512  
   513  	var acctNb = b.account
   514  	if accountName != "" && accountName != lnwallet.DefaultAccountName {
   515  		res, err := b.wallet.AccountNumber(context.Background(), &pb.AccountNumberRequest{AccountName: accountName})
   516  		if err != nil {
   517  			return nil, fmt.Errorf("unknown account named %s: %v", accountName, err)
   518  		}
   519  		acctNb = res.AccountNumber
   520  	}
   521  
   522  	if maxConfs != 0 && maxConfs != math.MaxInt32 {
   523  		return nil, fmt.Errorf("maxconfs is not supported")
   524  	}
   525  
   526  	req := &pb.UnspentOutputsRequest{
   527  		Account:               acctNb,
   528  		RequiredConfirmations: minConfs,
   529  	}
   530  	stream, err := b.wallet.UnspentOutputs(context.Background(), req)
   531  	if err != nil {
   532  		return nil, err
   533  	}
   534  
   535  	// Decred only supports p2pkh in its wallets.
   536  	addressType := lnwallet.PubKeyHash
   537  
   538  	// Convert to the appropriate format.
   539  	utxos := make([]*lnwallet.Utxo, 0)
   540  	for {
   541  		msg, err := stream.Recv()
   542  		if err == io.EOF {
   543  			break
   544  		}
   545  		if err != nil {
   546  			return nil, err
   547  		}
   548  
   549  		txid, err := chainhash.NewHash(msg.TransactionHash)
   550  		if err != nil {
   551  			return nil, err
   552  		}
   553  
   554  		// Ensure this utxo hasn't been locked.
   555  		outp := wire.OutPoint{
   556  			Hash:  *txid,
   557  			Index: msg.OutputIndex,
   558  			Tree:  int8(msg.Tree),
   559  		}
   560  		b.lockedOutpointsMu.Lock()
   561  		_, lockedUtxo := b.lockedOutpoints[outp]
   562  		b.lockedOutpointsMu.Unlock()
   563  		if lockedUtxo {
   564  			continue
   565  		}
   566  
   567  		// TODO(decred): Modify UnspentOutputs() grpc call to return
   568  		// the confirmation height so that the confirmation count can
   569  		// be deduced without having to perform a second rpc call.
   570  		txReq := &pb.GetTransactionRequest{
   571  			TransactionHash: txid[:],
   572  		}
   573  		txResp, err := b.wallet.GetTransaction(context.Background(), txReq)
   574  		if err != nil {
   575  			return nil, err
   576  		}
   577  		confs := txResp.Confirmations
   578  		if confs > maxConfs {
   579  			continue
   580  		}
   581  
   582  		utxo := &lnwallet.Utxo{
   583  			AddressType:   addressType,
   584  			Value:         dcrutil.Amount(msg.Amount),
   585  			PkScript:      msg.PkScript,
   586  			OutPoint:      outp,
   587  			Confirmations: int64(confs),
   588  		}
   589  		utxos = append(utxos, utxo)
   590  	}
   591  
   592  	return utxos, nil
   593  }
   594  
   595  // PublishTransaction performs cursory validation (dust checks, etc), then
   596  // finally broadcasts the passed transaction to the Decred network. If
   597  // publishing the transaction fails, an error describing the reason is
   598  // returned (currently ErrDoubleSpend). If the transaction is already
   599  // published to the network (either in the mempool or chain) no error
   600  // will be returned.
   601  //
   602  // This is a part of the WalletController interface.
   603  func (b *DcrWallet) PublishTransaction(tx *wire.MsgTx, label string) error {
   604  	rawTx, err := tx.Bytes()
   605  	if err != nil {
   606  		return err
   607  	}
   608  	publishReq := &pb.PublishTransactionRequest{
   609  		SignedTransaction: rawTx,
   610  	}
   611  	_, err = b.wallet.PublishTransaction(context.Background(), publishReq)
   612  	dcrwLog.Debugf("PublishTransaction(%s): error %v", newLogClosure(
   613  		func() string { return tx.TxHash().String() }), err)
   614  	if err != nil {
   615  		// TODO(decred): review if the string messages are correct.
   616  		// Possible convert from checking the message to checking the
   617  		// op.
   618  		//
   619  		// NOTE(decred): These checks were removed upstream due to
   620  		// changing the underlying btcwallet semantics on
   621  		// PublishTransaction().
   622  		if strings.Contains(err.Error(), "already have") {
   623  			// Transaction was already in the mempool, do
   624  			// not treat as an error. We do this to mimic
   625  			// the behaviour of bitcoind, which will not
   626  			// return an error if a transaction in the
   627  			// mempool is sent again using the
   628  			// sendrawtransaction RPC call.
   629  			return nil
   630  		}
   631  		if strings.Contains(err.Error(), "already exists") {
   632  			// Transaction was already mined, we don't
   633  			// consider this an error.
   634  			return nil
   635  		}
   636  		if strings.Contains(err.Error(), "by double spending") {
   637  			// Output was already spent.
   638  			return lnwallet.ErrDoubleSpend
   639  		}
   640  		if strings.Contains(err.Error(), "already spends the same coins") {
   641  			// Output was already spent.
   642  			return lnwallet.ErrDoubleSpend
   643  		}
   644  		if strings.Contains(err.Error(), "already spent") {
   645  			// Output was already spent.
   646  			return lnwallet.ErrDoubleSpend
   647  		}
   648  		if strings.Contains(err.Error(), "already been spent") {
   649  			// Output was already spent.
   650  			return lnwallet.ErrDoubleSpend
   651  		}
   652  		if strings.Contains(err.Error(), "orphan transaction") {
   653  			// Transaction is spending either output that
   654  			// is missing or already spent.
   655  			return lnwallet.ErrDoubleSpend
   656  		}
   657  		if strings.Contains(err.Error(), "by double spending") {
   658  			// Wallet has a conflicting unmined transaction.
   659  			return lnwallet.ErrDoubleSpend
   660  		}
   661  		return err
   662  	}
   663  
   664  	return nil
   665  }
   666  
   667  // AbandonDoubleSpends abandons any unconfirmed transaction that also spends
   668  // any of the specified outpoints.
   669  //
   670  // This is part of the WalletController interface.
   671  func (b *DcrWallet) AbandonDoubleSpends(spentOutpoints ...*wire.OutPoint) error {
   672  
   673  	// Fetch all unconfirmed transactions.
   674  	req := &pb.GetTransactionsRequest{
   675  		StartingBlockHeight: -1,
   676  		EndingBlockHeight:   0,
   677  	}
   678  
   679  	stream, err := b.wallet.GetTransactions(context.Background(), req)
   680  	if err != nil {
   681  		return err
   682  	}
   683  
   684  	// Make a map of inputs that were spent to speed up lookup.
   685  	spent := make(map[wire.OutPoint]struct{}, len(spentOutpoints))
   686  	for _, outp := range spentOutpoints {
   687  		spent[*outp] = struct{}{}
   688  	}
   689  
   690  	// Now collect all txs that need to be abandoned.
   691  	abandon := make(map[chainhash.Hash]struct{}, len(spentOutpoints))
   692  	for {
   693  		msg, err := stream.Recv()
   694  		if err == io.EOF {
   695  			break
   696  		}
   697  		if err != nil {
   698  			return err
   699  		}
   700  
   701  		for _, tx := range msg.UnminedTransactions {
   702  			wireTx := new(wire.MsgTx)
   703  			err := wireTx.FromBytes(tx.Transaction)
   704  			if err != nil {
   705  				dcrwLog.Warnf("Error decoding wallet-provided "+
   706  					"tx: %v", err)
   707  				continue
   708  			}
   709  			txh := wireTx.TxHash()
   710  
   711  			for _, in := range wireTx.TxIn {
   712  				if _, isSpent := spent[in.PreviousOutPoint]; !isSpent {
   713  					continue
   714  				}
   715  
   716  				// This input was spent. Register this as a tx
   717  				// that needs abandoning.
   718  				abandon[txh] = struct{}{}
   719  				break
   720  			}
   721  		}
   722  	}
   723  
   724  	// Finally, abandon all transactions.
   725  	for txh := range abandon {
   726  		dcrwLog.Infof("Abandoning double spent tx %s", txh)
   727  		req := &pb.AbandonTransactionRequest{
   728  			TransactionHash: txh[:],
   729  		}
   730  		_, err := b.wallet.AbandonTransaction(context.Background(), req)
   731  		if err != nil {
   732  			dcrwLog.Warnf("Error abandoning tx %s: %v", txh, err)
   733  		}
   734  	}
   735  
   736  	return nil
   737  }
   738  
   739  // extractBalanceDelta extracts the net balance delta from the PoV of the
   740  // wallet given a TransactionSummary.
   741  func extractBalanceDelta(
   742  	txSummary *pb.TransactionDetails,
   743  	tx *wire.MsgTx,
   744  ) (dcrutil.Amount, error) {
   745  	// For each input we debit the wallet's outflow for this transaction,
   746  	// and for each output we credit the wallet's inflow for this
   747  	// transaction.
   748  	var balanceDelta dcrutil.Amount
   749  	for _, input := range txSummary.Debits {
   750  		balanceDelta -= dcrutil.Amount(input.PreviousAmount)
   751  	}
   752  	for _, output := range txSummary.Credits {
   753  		balanceDelta += dcrutil.Amount(tx.TxOut[output.Index].Value)
   754  	}
   755  
   756  	return balanceDelta, nil
   757  }
   758  
   759  // minedTransactionsToDetails is a helper function which converts a summary
   760  // information about mined transactions to a TransactionDetail.
   761  func minedTransactionsToDetails(
   762  	currentHeight int32,
   763  	block *pb.BlockDetails,
   764  	chainParams *chaincfg.Params,
   765  	acctNb uint32,
   766  ) ([]*lnwallet.TransactionDetail, error) {
   767  
   768  	headerHeight := block.Height
   769  
   770  	blockHash, err := chainhash.NewHash(block.Hash)
   771  	if err != nil {
   772  		return nil, err
   773  	}
   774  
   775  	details := make([]*lnwallet.TransactionDetail, 0, len(block.Transactions))
   776  	for _, tx := range block.Transactions {
   777  		if acctNb < math.MaxUint32 {
   778  			fromTargetAcct := false
   779  			for _, in := range tx.Debits {
   780  				fromTargetAcct = fromTargetAcct || in.PreviousAccount == acctNb
   781  			}
   782  			for _, out := range tx.Credits {
   783  				fromTargetAcct = fromTargetAcct || out.Account == acctNb
   784  			}
   785  			if !fromTargetAcct {
   786  				continue
   787  			}
   788  		}
   789  		wireTx := &wire.MsgTx{}
   790  		txReader := bytes.NewReader(tx.Transaction)
   791  
   792  		if err := wireTx.Deserialize(txReader); err != nil {
   793  			return nil, err
   794  		}
   795  
   796  		var destAddresses []stdaddr.Address
   797  		for _, txOut := range wireTx.TxOut {
   798  			_, outAddresses := stdscript.ExtractAddrs(
   799  				txOut.Version, txOut.PkScript, chainParams)
   800  			destAddresses = append(destAddresses, outAddresses...)
   801  		}
   802  
   803  		txDetail := &lnwallet.TransactionDetail{
   804  			Hash:             wireTx.TxHash(),
   805  			NumConfirmations: currentHeight - headerHeight + 1,
   806  			BlockHash:        blockHash,
   807  			BlockHeight:      headerHeight,
   808  			Timestamp:        block.Timestamp,
   809  			TotalFees:        tx.Fee,
   810  			DestAddresses:    destAddresses,
   811  			RawTx:            tx.Transaction,
   812  		}
   813  
   814  		balanceDelta, err := extractBalanceDelta(tx, wireTx)
   815  		if err != nil {
   816  			return nil, err
   817  		}
   818  		txDetail.Value = balanceDelta
   819  
   820  		details = append(details, txDetail)
   821  	}
   822  
   823  	return details, nil
   824  }
   825  
   826  // unminedTransactionsToDetail is a helper function which converts a summary
   827  // for an unconfirmed transaction to a transaction detail.
   828  func unminedTransactionsToDetail(
   829  	summary *pb.TransactionDetails,
   830  	chainParams *chaincfg.Params,
   831  ) (*lnwallet.TransactionDetail, error) {
   832  
   833  	wireTx := &wire.MsgTx{}
   834  	txReader := bytes.NewReader(summary.Transaction)
   835  
   836  	if err := wireTx.Deserialize(txReader); err != nil {
   837  		return nil, err
   838  	}
   839  
   840  	var destAddresses []stdaddr.Address
   841  	for _, txOut := range wireTx.TxOut {
   842  		_, outAddresses :=
   843  			stdscript.ExtractAddrs(txOut.Version,
   844  				txOut.PkScript, chainParams)
   845  		destAddresses = append(destAddresses, outAddresses...)
   846  	}
   847  
   848  	txDetail := &lnwallet.TransactionDetail{
   849  		Hash:          wireTx.TxHash(),
   850  		TotalFees:     summary.Fee,
   851  		Timestamp:     summary.Timestamp,
   852  		DestAddresses: destAddresses,
   853  		RawTx:         summary.Transaction,
   854  	}
   855  
   856  	balanceDelta, err := extractBalanceDelta(summary, wireTx)
   857  	if err != nil {
   858  		return nil, err
   859  	}
   860  	txDetail.Value = balanceDelta
   861  
   862  	return txDetail, nil
   863  }
   864  
   865  // ListTransactionDetails returns a list of all transactions which are
   866  // relevant to the wallet.
   867  //
   868  // This is a part of the WalletController interface.
   869  func (b *DcrWallet) ListTransactionDetails(startHeight,
   870  	endHeight int32, accountName string) ([]*lnwallet.TransactionDetail, error) {
   871  
   872  	// Grab the best block the wallet knows of, we'll use this to calculate
   873  	// # of confirmations shortly below.
   874  	bestBlockRes, err := b.wallet.BestBlock(context.Background(), &pb.BestBlockRequest{})
   875  	if err != nil {
   876  		return nil, err
   877  	}
   878  	currentHeight := int32(bestBlockRes.Height)
   879  
   880  	var acctNb = b.account
   881  	if accountName == "" {
   882  		acctNb = math.MaxUint32
   883  	} else if accountName != lnwallet.DefaultAccountName {
   884  		res, err := b.wallet.AccountNumber(context.Background(), &pb.AccountNumberRequest{AccountName: accountName})
   885  		if err != nil {
   886  			return nil, fmt.Errorf("unknown account named %s: %v", accountName, err)
   887  		}
   888  		acctNb = res.AccountNumber
   889  	}
   890  
   891  	req := &pb.GetTransactionsRequest{
   892  		StartingBlockHeight: startHeight,
   893  		EndingBlockHeight:   endHeight,
   894  	}
   895  
   896  	stream, err := b.wallet.GetTransactions(context.Background(), req)
   897  	if err != nil {
   898  		return nil, err
   899  	}
   900  	txs := make([]*lnwallet.TransactionDetail, 0)
   901  	for {
   902  		msg, err := stream.Recv()
   903  		if err == io.EOF {
   904  			break
   905  		}
   906  		if err != nil {
   907  			return nil, err
   908  		}
   909  
   910  		if msg.MinedTransactions != nil {
   911  			minedTxs, err := minedTransactionsToDetails(currentHeight,
   912  				msg.MinedTransactions, b.chainParams, acctNb)
   913  			if err != nil {
   914  				return nil, err
   915  			}
   916  			txs = append(txs, minedTxs...)
   917  		}
   918  
   919  		for _, tx := range msg.UnminedTransactions {
   920  			if acctNb < math.MaxUint32 {
   921  				fromTargetAcct := false
   922  				for _, in := range tx.Debits {
   923  					fromTargetAcct = fromTargetAcct || in.PreviousAccount == acctNb
   924  				}
   925  				for _, out := range tx.Credits {
   926  					fromTargetAcct = fromTargetAcct || out.Account == acctNb
   927  				}
   928  				if !fromTargetAcct {
   929  					continue
   930  				}
   931  			}
   932  
   933  			unminedTx, err := unminedTransactionsToDetail(tx, b.chainParams)
   934  			if err != nil {
   935  				return nil, err
   936  			}
   937  			txs = append(txs, unminedTx)
   938  		}
   939  	}
   940  
   941  	return txs, nil
   942  }
   943  
   944  // txSubscriptionClient encapsulates the transaction notification client from
   945  // the base wallet. Notifications received from the client will be proxied over
   946  // two distinct channels.
   947  type txSubscriptionClient struct {
   948  	txClient pb.WalletService_TransactionNotificationsClient
   949  
   950  	confirmed   chan *lnwallet.TransactionDetail
   951  	unconfirmed chan *lnwallet.TransactionDetail
   952  
   953  	wallet      pb.WalletServiceClient
   954  	chainParams *chaincfg.Params
   955  
   956  	wg     sync.WaitGroup
   957  	ctx    context.Context
   958  	cancel func()
   959  }
   960  
   961  // ConfirmedTransactions returns a channel which will be sent on as new
   962  // relevant transactions are confirmed.
   963  //
   964  // This is part of the TransactionSubscription interface.
   965  func (t *txSubscriptionClient) ConfirmedTransactions() chan *lnwallet.TransactionDetail {
   966  	return t.confirmed
   967  }
   968  
   969  // UnconfirmedTransactions returns a channel which will be sent on as
   970  // new relevant transactions are seen within the network.
   971  //
   972  // This is part of the TransactionSubscription interface.
   973  func (t *txSubscriptionClient) UnconfirmedTransactions() chan *lnwallet.TransactionDetail {
   974  	return t.unconfirmed
   975  }
   976  
   977  // Cancel finalizes the subscription, cleaning up any resources allocated.
   978  //
   979  // This is part of the TransactionSubscription interface.
   980  func (t *txSubscriptionClient) Cancel() {
   981  	t.cancel()
   982  	t.wg.Wait()
   983  }
   984  
   985  // notificationProxier proxies the notifications received by the underlying
   986  // wallet's notification client to a higher-level TransactionSubscription
   987  // client.
   988  func (t *txSubscriptionClient) notificationProxier() {
   989  	defer t.wg.Done()
   990  
   991  	for {
   992  		msg, err := t.txClient.Recv()
   993  		if err == io.EOF {
   994  			// Cancel() was called.
   995  			break
   996  		}
   997  		if err != nil {
   998  			dcrwLog.Errorf("Error during tx subscription: %v", err)
   999  			break
  1000  		}
  1001  
  1002  		// TODO(roasbeef): handle detached blocks
  1003  		ctxb := context.Background()
  1004  		bestBlockResp, err := t.wallet.BestBlock(ctxb, &pb.BestBlockRequest{})
  1005  		if err != nil {
  1006  			dcrwLog.Errorf("Unable to query best block in tx subscription")
  1007  		}
  1008  		currentHeight := int32(bestBlockResp.Height)
  1009  
  1010  		// Launch a goroutine to re-package and send notifications for
  1011  		// any newly confirmed transactions.
  1012  		go func() {
  1013  			for _, block := range msg.AttachedBlocks {
  1014  				details, err := minedTransactionsToDetails(
  1015  					currentHeight, block, t.chainParams,
  1016  					math.MaxUint32,
  1017  				)
  1018  				if err != nil {
  1019  					continue
  1020  				}
  1021  
  1022  				for _, d := range details {
  1023  					select {
  1024  					case t.confirmed <- d:
  1025  					case <-t.ctx.Done():
  1026  						return
  1027  					}
  1028  				}
  1029  			}
  1030  
  1031  		}()
  1032  
  1033  		// Launch a goroutine to re-package and send
  1034  		// notifications for any newly unconfirmed transactions.
  1035  		go func() {
  1036  			for _, tx := range msg.UnminedTransactions {
  1037  				detail, err := unminedTransactionsToDetail(
  1038  					tx, t.chainParams,
  1039  				)
  1040  				if err != nil {
  1041  					continue
  1042  				}
  1043  
  1044  				select {
  1045  				case t.unconfirmed <- detail:
  1046  				case <-t.ctx.Done():
  1047  					return
  1048  				}
  1049  			}
  1050  		}()
  1051  	}
  1052  }
  1053  
  1054  // SubscribeTransactions returns a TransactionSubscription client which
  1055  // is capable of receiving async notifications as new transactions
  1056  // related to the wallet are seen within the network, or found in
  1057  // blocks.
  1058  //
  1059  // This is a part of the WalletController interface.
  1060  func (b *DcrWallet) SubscribeTransactions() (lnwallet.TransactionSubscription, error) {
  1061  	req := &pb.TransactionNotificationsRequest{}
  1062  	ctx, cancel := context.WithCancel(context.Background())
  1063  	stream, err := b.wallet.TransactionNotifications(ctx, req)
  1064  	if err != nil {
  1065  		cancel()
  1066  		return nil, err
  1067  	}
  1068  
  1069  	txClient := &txSubscriptionClient{
  1070  		txClient:    stream,
  1071  		confirmed:   make(chan *lnwallet.TransactionDetail),
  1072  		unconfirmed: make(chan *lnwallet.TransactionDetail),
  1073  		wallet:      b.wallet,
  1074  		chainParams: b.chainParams,
  1075  		ctx:         ctx,
  1076  		cancel:      cancel,
  1077  	}
  1078  	txClient.wg.Add(1)
  1079  	go txClient.notificationProxier()
  1080  
  1081  	return txClient, nil
  1082  }
  1083  
  1084  // IsSynced returns a boolean indicating if from the PoV of the wallet, it has
  1085  // fully synced to the current best block in the main chain.
  1086  //
  1087  // This is a part of the WalletController interface.
  1088  func (b *DcrWallet) IsSynced() (bool, int64, error) {
  1089  	// Grab the best chain state the wallet is currently aware of.
  1090  	ctxb := context.Background()
  1091  	bestBlockResp, err := b.wallet.BestBlock(ctxb, &pb.BestBlockRequest{})
  1092  	if err != nil {
  1093  		return false, 0, err
  1094  	}
  1095  	walletBestHash := bestBlockResp.Hash
  1096  	blockInfoResp, err := b.wallet.BlockInfo(ctxb, &pb.BlockInfoRequest{BlockHash: walletBestHash})
  1097  	if err != nil {
  1098  		return false, 0, err
  1099  	}
  1100  	headerTS := time.Unix(blockInfoResp.Timestamp, 0)
  1101  
  1102  	// TODO(decred) Check if the wallet is still syncing.  This is
  1103  	// currently done by checking the associated chainIO but ideally the
  1104  	// wallet should return the height it's attempting to sync to.
  1105  	if b.cfg.ChainIO != nil {
  1106  		ioHash, _, err := b.cfg.ChainIO.GetBestBlock()
  1107  		if err != nil {
  1108  			return false, 0, err
  1109  		}
  1110  		if !bytes.Equal(walletBestHash, ioHash[:]) {
  1111  			return false, headerTS.Unix(), nil
  1112  		}
  1113  	}
  1114  
  1115  	// If the timestamp on the best header is more than 2 hours in the
  1116  	// past, then we're not yet synced.
  1117  	minus2Hours := time.Now().Add(-2 * time.Hour)
  1118  	if headerTS.Before(minus2Hours) {
  1119  		return false, headerTS.Unix(), nil
  1120  	}
  1121  
  1122  	// Check if the wallet has completed the initial sync procedure (discover
  1123  	// addresses, load tx filter, etc).
  1124  	walletSynced := atomic.LoadUint32(&b.atomicWalletSynced) == 1
  1125  
  1126  	return walletSynced, headerTS.Unix(), nil
  1127  }
  1128  
  1129  func (b *DcrWallet) BestBlock() (int64, chainhash.Hash, int64, error) {
  1130  	ctxb := context.Background()
  1131  	bestBlockResp, err := b.wallet.BestBlock(ctxb, &pb.BestBlockRequest{})
  1132  	if err != nil {
  1133  		return 0, chainhash.Hash{}, 0, err
  1134  	}
  1135  	walletBestHash, err := chainhash.NewHash(bestBlockResp.Hash)
  1136  	if err != nil {
  1137  		return 0, chainhash.Hash{}, 0, err
  1138  	}
  1139  	blockInfoResp, err := b.wallet.BlockInfo(ctxb, &pb.BlockInfoRequest{BlockHash: walletBestHash[:]})
  1140  	if err != nil {
  1141  		return 0, chainhash.Hash{}, 0, err
  1142  	}
  1143  	headerTS := time.Unix(blockInfoResp.Timestamp, 0)
  1144  
  1145  	return int64(bestBlockResp.Height), *walletBestHash, headerTS.Unix(), nil
  1146  }
  1147  
  1148  // InitialSyncChannel returns the channel used to signal that wallet init has
  1149  // finished.
  1150  //
  1151  // This is a part of the WalletController interface.
  1152  func (b *DcrWallet) InitialSyncChannel() <-chan struct{} {
  1153  	return b.syncedChan
  1154  }
  1155  
  1156  // synced is called by the chosen chain syncer (rpc/spv) after the wallet has
  1157  // successfully synced its state to the chain.
  1158  func (b *DcrWallet) synced() {
  1159  	dcrwLog.Debug("Syncer notified wallet is synced")
  1160  
  1161  	// Signal that the wallet is synced by closing the channel.
  1162  	if atomic.CompareAndSwapUint32(&b.atomicWalletSynced, 0, 1) {
  1163  		close(b.syncedChan)
  1164  	}
  1165  }
  1166  
  1167  // Bip44AddressInfo returns the BIP44 relevant (account, branch and index) of
  1168  // the given wallet address.
  1169  func (b *DcrWallet) Bip44AddressInfo(addr stdaddr.Address) (uint32, uint32, uint32, error) {
  1170  	req := &pb.ValidateAddressRequest{
  1171  		Address: addr.String(),
  1172  	}
  1173  	resp, err := b.wallet.ValidateAddress(context.Background(), req)
  1174  	if err != nil {
  1175  		return 0, 0, 0, err
  1176  	}
  1177  
  1178  	if !resp.IsValid {
  1179  		return 0, 0, 0, fmt.Errorf("invalid address")
  1180  	}
  1181  	if !resp.IsMine {
  1182  		return 0, 0, 0, fmt.Errorf("not an owned address")
  1183  	}
  1184  	if resp.IsScript {
  1185  		return 0, 0, 0, fmt.Errorf("not a p2pkh address")
  1186  	}
  1187  
  1188  	branch := uint32(0)
  1189  	if resp.IsInternal {
  1190  		branch = 1
  1191  	}
  1192  	return resp.AccountNumber, branch, resp.Index, nil
  1193  }
  1194  
  1195  // LabelTransaction adds the given external label to the specified transaction.
  1196  //
  1197  // This is a part of the WalletController interface.
  1198  func (b *DcrWallet) LabelTransaction(hash chainhash.Hash, label string, overwrite bool) error {
  1199  	return fmt.Errorf("unimplemented")
  1200  }
  1201  
  1202  // LeaseOutput markes the output as used for some time.
  1203  //
  1204  // This is a part of the WalletController interface.
  1205  func (b *DcrWallet) LeaseOutput(lnwallet.LockID, wire.OutPoint, time.Duration) (time.Time, error) {
  1206  	return time.Time{}, fmt.Errorf("unimplemented")
  1207  }
  1208  
  1209  // ReleaseOutput marks the output as unused.
  1210  //
  1211  // This is a part of the WalletController interface.
  1212  func (b *DcrWallet) ReleaseOutput(lnwallet.LockID, wire.OutPoint) error {
  1213  	return fmt.Errorf("unimplemented")
  1214  }
  1215  
  1216  // ListLeasedOutputs lists leased wallet outputs.
  1217  //
  1218  // This is a part of the WalletController interface.
  1219  func (b *DcrWallet) ListLeasedOutputs() ([]*lnwallet.LockedOutput, error) {
  1220  	return nil, fmt.Errorf("unimplemented")
  1221  }
  1222  
  1223  // GetRecoveryInfo returns the current status of the recovery of the wallet.
  1224  //
  1225  // This is a part of the WalletController interface.
  1226  func (b *DcrWallet) GetRecoveryInfo() (bool, float64, error) {
  1227  	return false, 0, fmt.Errorf("unimplemented")
  1228  }
  1229  
  1230  // FetchTx attempts to fetch a transaction in the wallet's database
  1231  // identified by the passed transaction hash. If the transaction can't
  1232  // be found, then a nil pointer is returned.
  1233  //
  1234  // This is a part of the WalletController interface.
  1235  func (b *DcrWallet) FetchTx(txid chainhash.Hash) (*wire.MsgTx, error) {
  1236  	req := &pb.GetTransactionRequest{TransactionHash: txid[:]}
  1237  	res, err := b.wallet.GetTransaction(b.ctx, req)
  1238  	if err != nil {
  1239  		return nil, err
  1240  	}
  1241  	tx := wire.NewMsgTx()
  1242  	err = tx.Deserialize(bytes.NewBuffer(res.Transaction.Transaction))
  1243  	if err != nil {
  1244  		return nil, err
  1245  	}
  1246  	return tx, err
  1247  }
  1248  
  1249  // RemoveDescendants attempts to remove any transaction from the
  1250  // wallet's tx store (that may be unconfirmed) that spends outputs
  1251  // created by the passed transaction. This remove propagates
  1252  // recursively down the chain of descendent transactions.
  1253  //
  1254  // This is a part of the WalletController interface.
  1255  func (b *DcrWallet) RemoveDescendants(*wire.MsgTx) error {
  1256  	return fmt.Errorf("RemoveDescendants is unimplemented")
  1257  }
  1258  
  1259  // ListAccount lists existing wallet accounts.
  1260  //
  1261  // This is a part of the WalletController interface.
  1262  func (b *DcrWallet) ListAccounts(accountName string) ([]base.AccountProperties, error) {
  1263  	accounts, err := b.wallet.Accounts(context.Background(), &pb.AccountsRequest{})
  1264  	if err != nil {
  1265  		return nil, err
  1266  	}
  1267  
  1268  	szHint := len(accounts.Accounts)
  1269  	if accountName != "" {
  1270  		szHint = 1
  1271  	}
  1272  	res := make([]base.AccountProperties, 0, szHint)
  1273  	for _, acct := range accounts.Accounts {
  1274  		if accountName != "" && acct.AccountName != accountName {
  1275  			continue
  1276  		}
  1277  		res = append(res, base.AccountProperties{
  1278  			AccountNumber:             acct.AccountNumber,
  1279  			AccountName:               acct.AccountName,
  1280  			LastReturnedExternalIndex: acct.ExternalKeyCount - 1,
  1281  			LastReturnedInternalIndex: acct.InternalKeyCount - 1,
  1282  			ImportedKeyCount:          acct.ImportedKeyCount,
  1283  			AccountEncrypted:          acct.AccountEncrypted,
  1284  			AccountUnlocked:           acct.AccountUnlocked,
  1285  
  1286  			// The following are not exposed via gRPC currently.
  1287  			//AccountType:               acct.AccountType,
  1288  			//LastUsedExternalIndex: acct.ExternalKeyCount,
  1289  			//LastUsedInternalIndex: acct.InternalKeyCount,
  1290  		})
  1291  	}
  1292  
  1293  	return res, nil
  1294  }
  1295  
  1296  // ImportAccount imports the specified xpub into the wallet.
  1297  //
  1298  // This is a part of the WalletController interface.
  1299  func (b *DcrWallet) ImportAccount(name string, accountPubKey *hdkeychain.ExtendedKey, dryRun bool) (
  1300  	*wallet.AccountProperties, []stdaddr.Address, []stdaddr.Address, error) {
  1301  
  1302  	fail := func(err error) (*wallet.AccountProperties, []stdaddr.Address, []stdaddr.Address, error) {
  1303  		return nil, nil, nil, err
  1304  	}
  1305  
  1306  	if dryRun {
  1307  		intAddrs, extAddrs, err := lnwallet.DeriveAddrsFromExtPub(accountPubKey,
  1308  			b.chainParams, dryRunImportAccountNumAddrs)
  1309  		if err != nil {
  1310  			return fail(err)
  1311  		}
  1312  
  1313  		acctProps := &wallet.AccountProperties{
  1314  			AccountName: name,
  1315  		}
  1316  		return acctProps, intAddrs, extAddrs, nil
  1317  	}
  1318  
  1319  	req := &pb.ImportExtendedPublicKeyRequest{
  1320  		AccountName: name,
  1321  		Xpub:        accountPubKey.String(),
  1322  	}
  1323  	_, err := b.wallet.ImportExtendedPublicKey(context.Background(), req)
  1324  	if err != nil {
  1325  		return fail(err)
  1326  	}
  1327  
  1328  	accounts, err := b.ListAccounts(name)
  1329  	if err != nil {
  1330  		return fail(err)
  1331  	}
  1332  	if len(accounts) == 0 {
  1333  		return fail(fmt.Errorf("account named %q does not exist", name))
  1334  	}
  1335  
  1336  	return &accounts[0], nil, nil, nil
  1337  }
  1338  
  1339  // ImportPublicKey imports the specified public key into the wallet.
  1340  //
  1341  // This is a part of the WalletController interface.
  1342  func (b *DcrWallet) ImportPublicKey(pubKey *secp256k1.PublicKey) error {
  1343  	return fmt.Errorf("unimplemented in gRPC")
  1344  }
  1345  
  1346  func (b *DcrWallet) ScriptForOutput(*wire.TxOut) (
  1347  	btcwalletcompat.ManagedPubKeyAddress, []byte, []byte, error) {
  1348  	return nil, nil, nil, fmt.Errorf("remotedcrwallet.ScriptForOutput is not implemented")
  1349  }