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

     1  package dcrwallet
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/hex"
     7  	"fmt"
     8  	"strings"
     9  	"sync"
    10  	"sync/atomic"
    11  	"time"
    12  
    13  	"github.com/decred/dcrd/chaincfg/chainhash"
    14  	"github.com/decred/dcrd/chaincfg/v3"
    15  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    16  	"github.com/decred/dcrd/dcrutil/v4"
    17  	"github.com/decred/dcrd/hdkeychain/v3"
    18  	"github.com/decred/dcrd/txscript/v4/stdaddr"
    19  	"github.com/decred/dcrd/txscript/v4/stdscript"
    20  	"github.com/decred/dcrd/wire"
    21  
    22  	"github.com/decred/dcrlnd/btcwalletcompat"
    23  	"github.com/decred/dcrlnd/lnwallet"
    24  	"github.com/decred/dcrlnd/lnwallet/chainfee"
    25  
    26  	"decred.org/dcrwallet/v4/wallet"
    27  	base "decred.org/dcrwallet/v4/wallet"
    28  	"decred.org/dcrwallet/v4/wallet/txauthor"
    29  	"decred.org/dcrwallet/v4/wallet/udb"
    30  	walletloader "github.com/decred/dcrlnd/lnwallet/dcrwallet/loader"
    31  )
    32  
    33  const (
    34  	defaultAccount = uint32(udb.DefaultAccountNum)
    35  	scriptVersion  = uint16(0)
    36  
    37  	// UnconfirmedHeight is the special case end height that is used to
    38  	// obtain unconfirmed transactions from ListTransactionDetails.
    39  	UnconfirmedHeight int32 = -1
    40  
    41  	// dryRunImportAccountNumAddrs represents the number of addresses we'll
    42  	// derive for an imported account's external and internal branch when a
    43  	// dry run is attempted.
    44  	dryRunImportAccountNumAddrs = 5
    45  )
    46  
    47  const (
    48  	// The following values are used by atomicWalletSync. Their
    49  	// interpretation is the following:
    50  	//
    51  	// - Unsynced: wallet just started and hasn't performed the first sync
    52  	// yet.
    53  	// - Synced: wallet is currently synced.
    54  	// - LostSync: wallet was synced in the past but lost the connection to
    55  	// the network and it's unknown whether it's synced or not.
    56  	syncStatusUnsynced uint32 = 0
    57  	syncStatusSynced          = 1
    58  	syncStatusLostSync        = 2
    59  )
    60  
    61  // DcrWallet is an implementation of the lnwallet.WalletController interface
    62  // backed by an active instance of dcrwallet. At the time of the writing of
    63  // this documentation, this implementation requires a full dcrd node to
    64  // operate.
    65  //
    66  // This struct implements the input.input.Signer, lnWallet.Messageinput.Signer,
    67  // keychain.SecretKeyRing and keychain.KeyRing interfaces.
    68  //
    69  // Note that most of its functions might produce errors or panics until the
    70  // wallet has been fully synced.
    71  type DcrWallet struct {
    72  	// wallet is an active instance of dcrwallet.
    73  	wallet *base.Wallet
    74  	loader *walletloader.Loader
    75  
    76  	// atomicWalletSync controls the current sync status of the wallet. It
    77  	// MUST be used atomically.
    78  	atomicWalletSynced uint32
    79  
    80  	// syncedChan is a channel that is closed once the wallet has initially
    81  	// synced to the network. It is protected by atomicWalletSynced.
    82  	syncedChan chan struct{}
    83  
    84  	cfg *Config
    85  
    86  	netParams *chaincfg.Params
    87  
    88  	syncer WalletSyncer
    89  
    90  	ctx       context.Context
    91  	cancelCtx func()
    92  
    93  	*walletKeyRing
    94  }
    95  
    96  // A compile time check to ensure that DcrWallet implements the
    97  // WalletController interface.
    98  var _ lnwallet.WalletController = (*DcrWallet)(nil)
    99  
   100  // New returns a new fully initialized instance of DcrWallet given a valid
   101  // configuration struct.
   102  func New(cfg Config) (*DcrWallet, error) {
   103  
   104  	wallet := cfg.Wallet
   105  	loader := cfg.Loader
   106  	syncer := cfg.Syncer
   107  
   108  	if cfg.Syncer == nil {
   109  		return nil, fmt.Errorf("cfg.Syncer needs to be specified")
   110  	}
   111  
   112  	if cfg.Wallet == nil {
   113  		// Ensure the wallet exists or create it when the create flag
   114  		// is specified
   115  		netDir := NetworkDir(cfg.DataDir, cfg.NetParams)
   116  		loader = walletloader.NewLoader(cfg.NetParams, netDir, base.DefaultGapLimit)
   117  		walletExists, err := loader.WalletExists()
   118  		if err != nil {
   119  			return nil, err
   120  		}
   121  
   122  		if !walletExists {
   123  			// Wallet has never been created, perform initial set up.
   124  			wallet, err = loader.CreateNewWallet(context.TODO(), cfg.PublicPass, cfg.PrivatePass,
   125  				cfg.HdSeed, time.Now().Add(-time.Hour*24))
   126  			if err != nil {
   127  				return nil, err
   128  			}
   129  		} else {
   130  			// Wallet has been created and been initialized at this point,
   131  			// open it along with all the required DB namepsaces, and the
   132  			// DB itself.
   133  			wallet, err = loader.OpenExistingWallet(context.TODO(), cfg.PublicPass)
   134  			if err != nil {
   135  				return nil, err
   136  			}
   137  		}
   138  	}
   139  
   140  	ctx, cancelCtx := context.WithCancel(context.Background())
   141  
   142  	return &DcrWallet{
   143  		cfg:                &cfg,
   144  		wallet:             wallet,
   145  		loader:             loader,
   146  		syncer:             syncer,
   147  		syncedChan:         make(chan struct{}),
   148  		atomicWalletSynced: syncStatusUnsynced,
   149  		netParams:          cfg.NetParams,
   150  		ctx:                ctx,
   151  		cancelCtx:          cancelCtx,
   152  	}, nil
   153  }
   154  
   155  // BackEnd returns the underlying ChainService's name as a string.
   156  //
   157  // This is a part of the WalletController interface.
   158  func (b *DcrWallet) BackEnd() string {
   159  	if _, is := b.syncer.(*RPCSyncer); is {
   160  		// This package only supports full node backends for the moment
   161  		return "dcrd"
   162  	}
   163  
   164  	if _, is := b.syncer.(*SPVSyncer); is {
   165  		// This package only supports full node backends for the moment
   166  		return "dcrw-spv"
   167  	}
   168  
   169  	return ""
   170  }
   171  
   172  // InternalWallet returns a pointer to the internal base wallet which is the
   173  // core of dcrwallet.
   174  func (b *DcrWallet) InternalWallet() *base.Wallet {
   175  	return b.wallet
   176  }
   177  
   178  // Start initializes the underlying rpc connection, the wallet itself, and
   179  // begins syncing to the current available blockchain state.
   180  //
   181  // This is a part of the WalletController interface.
   182  func (b *DcrWallet) Start() error {
   183  	// We'll start by unlocking the wallet and ensuring that the KeyScope:
   184  	// (1017, 1) exists within the internal waddrmgr. We'll need this in
   185  	// order to properly generate the keys required for signing various
   186  	// contracts.
   187  	if err := b.wallet.Unlock(context.TODO(), b.cfg.PrivatePass, nil); err != nil {
   188  		return err
   189  	}
   190  
   191  	// And then start the syncer backend for this wallet.
   192  	if err := b.syncer.start(b); err != nil {
   193  		return err
   194  	}
   195  
   196  	return nil
   197  }
   198  
   199  // Stop signals the wallet for shutdown. Shutdown may entail closing
   200  // any active sockets, database handles, stopping goroutines, etc.
   201  //
   202  // This is a part of the WalletController interface.
   203  func (b *DcrWallet) Stop() error {
   204  	dcrwLog.Debug("Requesting wallet shutdown")
   205  	b.cancelCtx()
   206  	b.syncer.stop()
   207  	b.syncer.waitForShutdown()
   208  
   209  	dcrwLog.Debugf("Wallet has shut down")
   210  
   211  	return nil
   212  }
   213  
   214  // ConfirmedBalance returns the sum of all the wallet's unspent outputs that
   215  // have at least confs confirmations. If confs is set to zero, then all unspent
   216  // outputs, including those currently in the mempool will be included in the
   217  // final sum.
   218  //
   219  // This is a part of the WalletController interface.
   220  //
   221  // TODO(matheusd) Remove witness argument, given that's not applicable to decred
   222  func (b *DcrWallet) ConfirmedBalance(confs int32, accountName string) (dcrutil.Amount, error) {
   223  	acctNb := defaultAccount
   224  	if accountName != "" && accountName != lnwallet.DefaultAccountName {
   225  		var err error
   226  		acctNb, err = b.wallet.AccountNumber(context.TODO(), accountName)
   227  		if err != nil {
   228  			return 0, fmt.Errorf("unknown account named %s: %v", accountName, err)
   229  		}
   230  	}
   231  
   232  	balances, err := b.wallet.AccountBalance(context.TODO(), acctNb, confs)
   233  	if err != nil {
   234  		return 0, err
   235  	}
   236  
   237  	return balances.Spendable, nil
   238  }
   239  
   240  // NewAddress returns the next external or internal address for the wallet
   241  // dictated by the value of the `change` parameter. If change is true, then an
   242  // internal address will be returned, otherwise an external address should be
   243  // returned.
   244  //
   245  // This is a part of the WalletController interface.
   246  func (b *DcrWallet) NewAddress(t lnwallet.AddressType, change bool, accountName string) (stdaddr.Address, error) {
   247  
   248  	switch t {
   249  	case lnwallet.PubKeyHash:
   250  		// nop
   251  	default:
   252  		return nil, fmt.Errorf("unknown address type")
   253  	}
   254  
   255  	acctNb, err := b.wallet.AccountNumber(context.TODO(), accountName)
   256  	if err != nil {
   257  		return nil, fmt.Errorf("unknown account named %s: %v", accountName, err)
   258  	}
   259  
   260  	var addr stdaddr.Address
   261  	if change {
   262  		addr, err = b.wallet.NewInternalAddress(context.TODO(),
   263  			acctNb, base.WithGapPolicyWrap())
   264  	} else {
   265  		addr, err = b.wallet.NewExternalAddress(context.TODO(),
   266  			acctNb, base.WithGapPolicyWrap())
   267  	}
   268  
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  
   273  	// Convert to a regular p2pkh address, since the addresses returned are
   274  	// used as paramaters to PayT.(stdaddr.Hash160er).Hash160()[:] which doesn't understand
   275  	// the native wallet types.
   276  	return stdaddr.DecodeAddress(addr.String(), b.netParams)
   277  }
   278  
   279  // LastUnusedAddress returns the last *unused* address known by the wallet. An
   280  // address is unused if it hasn't received any payments. This can be useful in
   281  // UIs in order to continually show the "freshest" address without having to
   282  // worry about "address inflation" caused by continual refreshing. Similar to
   283  // NewAddress it can derive a specified address type, and also optionally a
   284  // change address.
   285  func (b *DcrWallet) LastUnusedAddress(addrType lnwallet.AddressType, accountName string) (
   286  	stdaddr.Address, error) {
   287  
   288  	acctNb, err := b.wallet.AccountNumber(context.TODO(), accountName)
   289  	if err != nil {
   290  		return nil, fmt.Errorf("unknown account named %s: %v", accountName, err)
   291  	}
   292  
   293  	switch addrType {
   294  	case lnwallet.PubKeyHash:
   295  		// nop
   296  	default:
   297  		return nil, fmt.Errorf("unknown address type")
   298  	}
   299  	a, err := b.wallet.CurrentAddress(acctNb)
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  
   304  	return a, nil
   305  }
   306  
   307  // IsOurAddress checks if the passed address belongs to this wallet
   308  //
   309  // This is a part of the WalletController interface.
   310  func (b *DcrWallet) IsOurAddress(a stdaddr.Address) bool {
   311  	result, err := b.wallet.HaveAddress(context.TODO(), a)
   312  	return result && (err == nil)
   313  }
   314  
   315  // SendOutputs funds, signs, and broadcasts a Decred transaction paying out to
   316  // the specified outputs. In the case the wallet has insufficient funds, or the
   317  // outputs are non-standard, a non-nil error will be returned.
   318  //
   319  // This is a part of the WalletController interface.
   320  func (b *DcrWallet) SendOutputs(outputs []*wire.TxOut,
   321  	feeRate chainfee.AtomPerKByte, minConfs int32, label, fromAccount string) (*wire.MsgTx, error) {
   322  
   323  	// Sanity check outputs.
   324  	if len(outputs) < 1 {
   325  		return nil, lnwallet.ErrNoOutputs
   326  	}
   327  
   328  	acctNb := defaultAccount
   329  	if fromAccount != "" && fromAccount != lnwallet.DefaultAccountName {
   330  		var err error
   331  		acctNb, err = b.wallet.AccountNumber(context.TODO(), fromAccount)
   332  		if err != nil {
   333  			return nil, fmt.Errorf("unknown account named %s: %v", fromAccount, err)
   334  		}
   335  	}
   336  
   337  	// Ensure we haven't changed the default relay fee.
   338  	// TODO(decred) Potentially change to a construct/sign/publish cycle or
   339  	// add the fee as a parameter so that we don't risk changing the default
   340  	// fee rate.
   341  	oldRelayFee := b.wallet.RelayFee()
   342  	b.wallet.SetRelayFee(dcrutil.Amount(feeRate))
   343  	defer b.wallet.SetRelayFee(oldRelayFee)
   344  
   345  	txHash, err := b.wallet.SendOutputs(context.TODO(), outputs,
   346  		acctNb, acctNb, minConfs)
   347  	if err != nil {
   348  		return nil, err
   349  	}
   350  
   351  	txs, _, err := b.wallet.GetTransactionsByHashes(context.TODO(), []*chainhash.Hash{txHash})
   352  	if err != nil {
   353  		return nil, err
   354  	}
   355  
   356  	return txs[0], nil
   357  }
   358  
   359  // CreateSimpleTx creates a Bitcoin transaction paying to the specified
   360  // outputs. The transaction is not broadcasted to the network, but a new change
   361  // address might be created in the wallet database. In the case the wallet has
   362  // insufficient funds, or the outputs are non-standard, an error should be
   363  // returned. This method also takes the target fee expressed in sat/kw that
   364  // should be used when crafting the transaction.
   365  //
   366  // NOTE: The dryRun argument can be set true to create a tx that doesn't alter
   367  // the database. A tx created with this set to true SHOULD NOT be broadcasted.
   368  //
   369  // This is a part of the WalletController interface.
   370  func (b *DcrWallet) CreateSimpleTx(outputs []*wire.TxOut,
   371  	feeRate chainfee.AtomPerKByte, minConfs int32, dryRun bool) (*txauthor.AuthoredTx, error) {
   372  
   373  	// Sanity check outputs.
   374  	if len(outputs) < 1 {
   375  		return nil, lnwallet.ErrNoOutputs
   376  	}
   377  
   378  	// TODO(decred) Review semantics for btcwallet's CreateSimpleTx.
   379  	return nil, fmt.Errorf("CreateSimpleTx unimplemented for dcrwallet")
   380  }
   381  
   382  // LockOutpoint marks an outpoint as locked meaning it will no longer be deemed
   383  // as eligible for coin selection. Locking outputs are utilized in order to
   384  // avoid race conditions when selecting inputs for usage when funding a
   385  // channel.
   386  //
   387  // This is a part of the WalletController interface.
   388  func (b *DcrWallet) LockOutpoint(o wire.OutPoint) {
   389  	b.wallet.LockOutpoint(&o.Hash, o.Index)
   390  }
   391  
   392  // UnlockOutpoint unlocks a previously locked output, marking it eligible for
   393  // coin selection.
   394  //
   395  // This is a part of the WalletController interface.
   396  func (b *DcrWallet) UnlockOutpoint(o wire.OutPoint) {
   397  	b.wallet.UnlockOutpoint(&o.Hash, o.Index)
   398  }
   399  
   400  // ListUnspentWitness returns a slice of all the unspent outputs the wallet
   401  // controls which pay to witness programs either directly or indirectly.
   402  //
   403  // This is a part of the WalletController interface.
   404  func (b *DcrWallet) ListUnspentWitness(minConfs, maxConfs int32, accountName string) (
   405  	[]*lnwallet.Utxo, error) {
   406  
   407  	// First, grab all the unfiltered currently unspent outputs.
   408  	unspentOutputs, err := b.wallet.ListUnspent(context.TODO(), minConfs,
   409  		maxConfs, nil, "")
   410  	if err != nil {
   411  		return nil, err
   412  	}
   413  
   414  	// Convert the dcrjson formatted unspents into lnwallet.Utxo's
   415  	witnessOutputs := make([]*lnwallet.Utxo, 0, len(unspentOutputs))
   416  	for _, output := range unspentOutputs {
   417  		if accountName != "" && accountName != output.Account {
   418  			continue
   419  		}
   420  
   421  		pkScript, err := hex.DecodeString(output.ScriptPubKey)
   422  		if err != nil {
   423  			return nil, err
   424  		}
   425  
   426  		scriptClass := stdscript.DetermineScriptType(scriptVersion, pkScript)
   427  		if scriptClass != stdscript.STPubKeyHashEcdsaSecp256k1 {
   428  			continue
   429  		}
   430  
   431  		addressType := lnwallet.PubKeyHash
   432  		txid, err := chainhash.NewHashFromStr(output.TxID)
   433  		if err != nil {
   434  			return nil, err
   435  		}
   436  
   437  		// We'll ensure we properly convert the amount given in
   438  		// DCR to atoms.
   439  		amt, err := dcrutil.NewAmount(output.Amount)
   440  		if err != nil {
   441  			return nil, err
   442  		}
   443  
   444  		utxo := &lnwallet.Utxo{
   445  			AddressType: addressType,
   446  			Value:       amt,
   447  			PkScript:    pkScript,
   448  			OutPoint: wire.OutPoint{
   449  				Hash:  *txid,
   450  				Index: output.Vout,
   451  				Tree:  output.Tree,
   452  			},
   453  			Confirmations: output.Confirmations,
   454  		}
   455  		witnessOutputs = append(witnessOutputs, utxo)
   456  	}
   457  
   458  	return witnessOutputs, nil
   459  }
   460  
   461  // PublishTransaction performs cursory validation (dust checks, etc), then
   462  // finally broadcasts the passed transaction to the Decred network. If
   463  // publishing the transaction fails, an error describing the reason is
   464  // returned (currently ErrDoubleSpend). If the transaction is already
   465  // published to the network (either in the mempool or chain) no error
   466  // will be returned.
   467  func (b *DcrWallet) PublishTransaction(tx *wire.MsgTx, label string) error {
   468  	n, err := b.wallet.NetworkBackend()
   469  	if err != nil {
   470  		return err
   471  	}
   472  	if n == nil {
   473  		return fmt.Errorf("wallet does not have an active backend")
   474  	}
   475  	_, err = b.wallet.PublishTransaction(context.TODO(), tx, n)
   476  	if err != nil {
   477  		// TODO(decred): review if the string messages are correct.
   478  		// Possible convert from checking the message to checking the
   479  		// op.
   480  		//
   481  		// NOTE(decred): These checks were removed upstream due to
   482  		// changing the underlying btcwallet semantics on
   483  		// PublishTransaction().
   484  		if strings.Contains(err.Error(), "already have") {
   485  			// Transaction was already in the mempool, do
   486  			// not treat as an error. We do this to mimic
   487  			// the behaviour of bitcoind, which will not
   488  			// return an error if a transaction in the
   489  			// mempool is sent again using the
   490  			// sendrawtransaction RPC call.
   491  			return nil
   492  		}
   493  		if strings.Contains(err.Error(), "already exists") {
   494  			// Transaction was already mined, we don't
   495  			// consider this an error.
   496  			return nil
   497  		}
   498  		if strings.Contains(err.Error(), "by double spending") {
   499  			// Output was already spent.
   500  			return lnwallet.ErrDoubleSpend
   501  		}
   502  		if strings.Contains(err.Error(), "already spent") {
   503  			// Output was already spent.
   504  			return lnwallet.ErrDoubleSpend
   505  		}
   506  		if strings.Contains(err.Error(), "already been spent") {
   507  			// Output was already spent.
   508  			return lnwallet.ErrDoubleSpend
   509  		}
   510  		if strings.Contains(err.Error(), "orphan transaction") {
   511  			// Transaction is spending either output that
   512  			// is missing or already spent.
   513  			return lnwallet.ErrDoubleSpend
   514  		}
   515  		if strings.Contains(err.Error(), "by double spending") {
   516  			// Wallet has a conflicting unmined transaction.
   517  			return lnwallet.ErrDoubleSpend
   518  		}
   519  		return err
   520  	}
   521  	return nil
   522  }
   523  
   524  // AbandonDoubleSpends abandons any unconfirmed transaction that also spends
   525  // any of the specified outpoints.
   526  //
   527  // This is part of the WalletController interface.
   528  func (b *DcrWallet) AbandonDoubleSpends(spentOutpoints ...*wire.OutPoint) error {
   529  	// Make a map of inputs that were spent to speed up lookup.
   530  	spent := make(map[wire.OutPoint]struct{}, len(spentOutpoints))
   531  	for _, outp := range spentOutpoints {
   532  		spent[*outp] = struct{}{}
   533  	}
   534  
   535  	// Fetch only unmined txs.
   536  	start := base.NewBlockIdentifierFromHeight(-1)
   537  	stop := base.NewBlockIdentifierFromHeight(-1)
   538  
   539  	// Collect txs that need to be abandoned.
   540  	abandon := make(map[chainhash.Hash]struct{}, len(spentOutpoints))
   541  	rangeFn := func(block *base.Block) (bool, error) {
   542  		if block.Header != nil {
   543  			// Shouldn't happen, but play it safe.
   544  			return false, nil
   545  		}
   546  
   547  		for _, tx := range block.Transactions {
   548  			wireTx := new(wire.MsgTx)
   549  			err := wireTx.FromBytes(tx.Transaction)
   550  			if err != nil {
   551  				dcrwLog.Warnf("Error decoding wallet-provided "+
   552  					"tx: %v", err)
   553  				continue
   554  			}
   555  			txh := wireTx.TxHash()
   556  
   557  			for _, in := range wireTx.TxIn {
   558  				if _, isSpent := spent[in.PreviousOutPoint]; !isSpent {
   559  					continue
   560  				}
   561  
   562  				// This input was spent. Register this as a tx
   563  				// that needs abandoning.
   564  				abandon[txh] = struct{}{}
   565  				break
   566  			}
   567  
   568  		}
   569  
   570  		return false, nil
   571  	}
   572  
   573  	err := b.wallet.GetTransactions(context.TODO(), rangeFn, start, stop)
   574  	if err != nil {
   575  		return err
   576  	}
   577  
   578  	// Finally, abandon all transactions.
   579  	for txh := range abandon {
   580  		dcrwLog.Infof("Abandoning double spent tx %s", txh)
   581  		err := b.wallet.AbandonTransaction(context.Background(), &txh)
   582  		if err != nil {
   583  			dcrwLog.Warnf("Error abandoning tx %s: %v", txh, err)
   584  		}
   585  	}
   586  
   587  	return nil
   588  }
   589  
   590  // extractBalanceDelta extracts the net balance delta from the PoV of the
   591  // wallet given a TransactionSummary.
   592  func extractBalanceDelta(
   593  	txSummary base.TransactionSummary,
   594  	tx *wire.MsgTx,
   595  ) (dcrutil.Amount, error) {
   596  	// For each input we debit the wallet's outflow for this transaction,
   597  	// and for each output we credit the wallet's inflow for this
   598  	// transaction.
   599  	var balanceDelta dcrutil.Amount
   600  	for _, input := range txSummary.MyInputs {
   601  		balanceDelta -= input.PreviousAmount
   602  	}
   603  	for _, output := range txSummary.MyOutputs {
   604  		balanceDelta += dcrutil.Amount(tx.TxOut[output.Index].Value)
   605  	}
   606  
   607  	return balanceDelta, nil
   608  }
   609  
   610  // minedTransactionsToDetails is a helper function which converts a summary
   611  // information about mined transactions to a TransactionDetail.
   612  func minedTransactionsToDetails(
   613  	currentHeight int32,
   614  	block *base.Block,
   615  	chainParams *chaincfg.Params,
   616  ) ([]*lnwallet.TransactionDetail, error) {
   617  
   618  	if block.Header == nil {
   619  		return nil, fmt.Errorf("cannot use minedTransactionsToDetails with mempoool")
   620  	}
   621  
   622  	details := make([]*lnwallet.TransactionDetail, 0, len(block.Transactions))
   623  	for _, tx := range block.Transactions {
   624  		wireTx := &wire.MsgTx{}
   625  		txReader := bytes.NewReader(tx.Transaction)
   626  
   627  		if err := wireTx.Deserialize(txReader); err != nil {
   628  			return nil, err
   629  		}
   630  
   631  		var destAddresses []stdaddr.Address
   632  		for _, txOut := range wireTx.TxOut {
   633  			_, outAddresses := stdscript.ExtractAddrs(
   634  				txOut.Version, txOut.PkScript, chainParams)
   635  			destAddresses = append(destAddresses, outAddresses...)
   636  		}
   637  
   638  		blockHash := block.Header.BlockHash()
   639  		txDetail := &lnwallet.TransactionDetail{
   640  			Hash:             *tx.Hash,
   641  			NumConfirmations: currentHeight - int32(block.Header.Height) + 1,
   642  			BlockHash:        &blockHash,
   643  			BlockHeight:      int32(block.Header.Height),
   644  			Timestamp:        block.Header.Timestamp.Unix(),
   645  			TotalFees:        int64(tx.Fee),
   646  			DestAddresses:    destAddresses,
   647  			RawTx:            tx.Transaction,
   648  			Label:            "",
   649  		}
   650  
   651  		balanceDelta, err := extractBalanceDelta(tx, wireTx)
   652  		if err != nil {
   653  			return nil, err
   654  		}
   655  		txDetail.Value = balanceDelta
   656  
   657  		details = append(details, txDetail)
   658  	}
   659  
   660  	return details, nil
   661  }
   662  
   663  // unminedTransactionsToDetail is a helper function which converts a summary
   664  // for an unconfirmed transaction to a transaction detail.
   665  func unminedTransactionsToDetail(
   666  	summary base.TransactionSummary,
   667  	chainParams *chaincfg.Params,
   668  ) (*lnwallet.TransactionDetail, error) {
   669  
   670  	wireTx := &wire.MsgTx{}
   671  	txReader := bytes.NewReader(summary.Transaction)
   672  
   673  	if err := wireTx.Deserialize(txReader); err != nil {
   674  		return nil, err
   675  	}
   676  
   677  	var destAddresses []stdaddr.Address
   678  	for _, txOut := range wireTx.TxOut {
   679  		_, outAddresses :=
   680  			stdscript.ExtractAddrs(txOut.Version,
   681  				txOut.PkScript, chainParams)
   682  		destAddresses = append(destAddresses, outAddresses...)
   683  	}
   684  
   685  	txDetail := &lnwallet.TransactionDetail{
   686  		Hash:          *summary.Hash,
   687  		TotalFees:     int64(summary.Fee),
   688  		Timestamp:     summary.Timestamp,
   689  		DestAddresses: destAddresses,
   690  		RawTx:         summary.Transaction,
   691  		Label:         "",
   692  	}
   693  
   694  	balanceDelta, err := extractBalanceDelta(summary, wireTx)
   695  	if err != nil {
   696  		return nil, err
   697  	}
   698  	txDetail.Value = balanceDelta
   699  
   700  	return txDetail, nil
   701  }
   702  
   703  // ListTransactionDetails returns a list of all transactions which are
   704  // relevant to the wallet.
   705  //
   706  // This is a part of the WalletController interface.
   707  func (b *DcrWallet) ListTransactionDetails(startHeight,
   708  	endHeight int32, accountName string) ([]*lnwallet.TransactionDetail, error) {
   709  
   710  	var acctNb uint32
   711  	if accountName != "" {
   712  		var err error
   713  		acctNb, err = b.wallet.AccountNumber(context.TODO(), accountName)
   714  		if err != nil {
   715  			return nil, fmt.Errorf("unknown account named %s: %v", accountName, err)
   716  		}
   717  	}
   718  
   719  	// Grab the best block the wallet knows of, we'll use this to calculate
   720  	// # of confirmations shortly below.
   721  	_, currentHeight := b.wallet.MainChainTip(context.TODO())
   722  
   723  	// We'll attempt to find all transactions from start to end.
   724  	start := base.NewBlockIdentifierFromHeight(startHeight)
   725  	stop := base.NewBlockIdentifierFromHeight(endHeight)
   726  
   727  	txDetails := make([]*lnwallet.TransactionDetail, 0)
   728  
   729  	rangeFn := func(block *base.Block) (bool, error) {
   730  		isMined := block.Header != nil
   731  		for _, tx := range block.Transactions {
   732  			if accountName != "" {
   733  				fromTargetAcct := false
   734  				for _, in := range tx.MyInputs {
   735  					if in.PreviousAccount == acctNb {
   736  						fromTargetAcct = true
   737  						break
   738  					}
   739  				}
   740  				for _, out := range tx.MyOutputs {
   741  					if out.Account == acctNb {
   742  						fromTargetAcct = true
   743  						break
   744  					}
   745  				}
   746  				if !fromTargetAcct {
   747  					continue
   748  				}
   749  			}
   750  
   751  			if isMined {
   752  				details, err := minedTransactionsToDetails(currentHeight, block,
   753  					b.netParams)
   754  				if err != nil {
   755  					return true, err
   756  				}
   757  
   758  				txDetails = append(txDetails, details...)
   759  			} else {
   760  				detail, err := unminedTransactionsToDetail(tx,
   761  					b.netParams)
   762  				if err != nil {
   763  					return true, err
   764  				}
   765  
   766  				txDetails = append(txDetails, detail)
   767  			}
   768  		}
   769  
   770  		return false, nil
   771  	}
   772  
   773  	err := b.wallet.GetTransactions(context.TODO(), rangeFn, start, stop)
   774  	if err != nil {
   775  		return nil, err
   776  	}
   777  
   778  	return txDetails, nil
   779  }
   780  
   781  // txSubscriptionClient encapsulates the transaction notification client from
   782  // the base wallet. Notifications received from the client will be proxied over
   783  // two distinct channels.
   784  type txSubscriptionClient struct {
   785  	txClient base.TransactionNotificationsClient
   786  
   787  	confirmed   chan *lnwallet.TransactionDetail
   788  	unconfirmed chan *lnwallet.TransactionDetail
   789  
   790  	w *base.Wallet
   791  
   792  	wg   sync.WaitGroup
   793  	quit chan struct{}
   794  }
   795  
   796  // ConfirmedTransactions returns a channel which will be sent on as new
   797  // relevant transactions are confirmed.
   798  //
   799  // This is part of the TransactionSubscription interface.
   800  func (t *txSubscriptionClient) ConfirmedTransactions() chan *lnwallet.TransactionDetail {
   801  	return t.confirmed
   802  }
   803  
   804  // UnconfirmedTransactions returns a channel which will be sent on as
   805  // new relevant transactions are seen within the network.
   806  //
   807  // This is part of the TransactionSubscription interface.
   808  func (t *txSubscriptionClient) UnconfirmedTransactions() chan *lnwallet.TransactionDetail {
   809  	return t.unconfirmed
   810  }
   811  
   812  // Cancel finalizes the subscription, cleaning up any resources allocated.
   813  //
   814  // This is part of the TransactionSubscription interface.
   815  func (t *txSubscriptionClient) Cancel() {
   816  	close(t.quit)
   817  	t.wg.Wait()
   818  
   819  	t.txClient.Done()
   820  }
   821  
   822  // notificationProxier proxies the notifications received by the underlying
   823  // wallet's notification client to a higher-level TransactionSubscription
   824  // client.
   825  func (t *txSubscriptionClient) notificationProxier() {
   826  	defer t.wg.Done()
   827  
   828  out:
   829  	for {
   830  		select {
   831  		case txNtfn := <-t.txClient.C:
   832  			// TODO(roasbeef): handle detached blocks
   833  			_, currentHeight := t.w.MainChainTip(context.TODO())
   834  
   835  			// Launch a goroutine to re-package and send
   836  			// notifications for any newly confirmed transactions.
   837  			go func() {
   838  				chainParams := t.w.ChainParams()
   839  				for _, block := range txNtfn.AttachedBlocks {
   840  					details, err := minedTransactionsToDetails(currentHeight, &block, chainParams)
   841  					if err != nil {
   842  						continue
   843  					}
   844  
   845  					for _, d := range details {
   846  						select {
   847  						case t.confirmed <- d:
   848  						case <-t.quit:
   849  							return
   850  						}
   851  					}
   852  				}
   853  
   854  			}()
   855  
   856  			// Launch a goroutine to re-package and send
   857  			// notifications for any newly unconfirmed transactions.
   858  			go func() {
   859  				chainParams := t.w.ChainParams()
   860  				for _, tx := range txNtfn.UnminedTransactions {
   861  					detail, err := unminedTransactionsToDetail(
   862  						tx, chainParams,
   863  					)
   864  					if err != nil {
   865  						continue
   866  					}
   867  
   868  					select {
   869  					case t.unconfirmed <- detail:
   870  					case <-t.quit:
   871  						return
   872  					}
   873  				}
   874  			}()
   875  		case <-t.quit:
   876  			break out
   877  		}
   878  	}
   879  }
   880  
   881  // SubscribeTransactions returns a TransactionSubscription client which
   882  // is capable of receiving async notifications as new transactions
   883  // related to the wallet are seen within the network, or found in
   884  // blocks.
   885  //
   886  // This is a part of the WalletController interface.
   887  func (b *DcrWallet) SubscribeTransactions() (lnwallet.TransactionSubscription, error) {
   888  	walletClient := b.wallet.NtfnServer.TransactionNotifications()
   889  
   890  	txClient := &txSubscriptionClient{
   891  		txClient:    walletClient,
   892  		confirmed:   make(chan *lnwallet.TransactionDetail),
   893  		unconfirmed: make(chan *lnwallet.TransactionDetail),
   894  		w:           b.wallet,
   895  		quit:        make(chan struct{}),
   896  	}
   897  	txClient.wg.Add(1)
   898  	go txClient.notificationProxier()
   899  
   900  	return txClient, nil
   901  }
   902  
   903  // IsSynced returns a boolean indicating if from the PoV of the wallet, it has
   904  // fully synced to the current best block in the main chain.
   905  //
   906  // This is a part of the WalletController interface.
   907  func (b *DcrWallet) IsSynced() (bool, int64, error) {
   908  	// Grab the best chain state the wallet is currently aware of.
   909  	walletBestHash, _ := b.wallet.MainChainTip(context.TODO())
   910  	walletBestHeader, err := b.wallet.BlockHeader(context.TODO(), &walletBestHash)
   911  	if err != nil {
   912  		return false, 0, err
   913  	}
   914  
   915  	// TODO(decred) Check if the wallet is still syncing.  This is
   916  	// currently done by checking the associated chainIO but ideally the
   917  	// wallet should return the height it's attempting to sync to.
   918  	if b.cfg.ChainIO != nil {
   919  		ioHash, _, err := b.cfg.ChainIO.GetBestBlock()
   920  		if err != nil {
   921  			return false, 0, err
   922  		}
   923  		if !bytes.Equal(walletBestHash[:], ioHash[:]) {
   924  			return false, walletBestHeader.Timestamp.Unix(), nil
   925  		}
   926  	}
   927  
   928  	// If the timestamp on the best header is more than 2 hours in the
   929  	// past, then we're not yet synced.
   930  	minus2Hours := time.Now().Add(-2 * time.Hour)
   931  	if walletBestHeader.Timestamp.Before(minus2Hours) {
   932  		return false, walletBestHeader.Timestamp.Unix(), nil
   933  	}
   934  
   935  	// Check if the wallet has completed the initial sync procedure (discover
   936  	// addresses, load tx filter, etc).
   937  	walletSynced := atomic.LoadUint32(&b.atomicWalletSynced) == 1
   938  
   939  	return walletSynced, walletBestHeader.Timestamp.Unix(), nil
   940  }
   941  
   942  func (b *DcrWallet) BestBlock() (int64, chainhash.Hash, int64, error) {
   943  	// Grab the best chain state the wallet is currently aware of.
   944  	walletBestHash, walletBestHeight := b.wallet.MainChainTip(context.TODO())
   945  	walletBestHeader, err := b.wallet.BlockHeader(context.TODO(), &walletBestHash)
   946  	if err != nil {
   947  		return 0, chainhash.Hash{}, 0, err
   948  	}
   949  
   950  	timestamp := walletBestHeader.Timestamp.Unix()
   951  	return int64(walletBestHeight), walletBestHash, timestamp, nil
   952  }
   953  
   954  // InitialSyncChannel returns the channel used to signal that wallet init has
   955  // finished.
   956  //
   957  // This is a part of the WalletController interface.
   958  func (b *DcrWallet) InitialSyncChannel() <-chan struct{} {
   959  	return b.syncedChan
   960  }
   961  
   962  func (b *DcrWallet) onSyncerSynced(synced bool) {
   963  	dcrwLog.Debug("RPC syncer notified wallet is synced")
   964  
   965  	if atomic.CompareAndSwapUint32(&b.atomicWalletSynced, syncStatusLostSync, syncStatusSynced) {
   966  		// No need to recreate the keyring or close the initial sync
   967  		// channel, so just return.
   968  		return
   969  	}
   970  
   971  	// Record in the DB that the wallet doesn't need account discovery
   972  	// anymore.
   973  	err := b.cfg.DB.DisableAccountDiscovery()
   974  	if err != nil {
   975  		dcrwLog.Errorf("Unable to disable future account discoveries: %v", err)
   976  	}
   977  
   978  	// Now that the wallet is synced and address discovery has ended, we
   979  	// can create the keyring. We can only do this here (after sync)
   980  	// because address discovery might upgrade the underlying dcrwallet
   981  	// coin type.
   982  	b.walletKeyRing, err = newWalletKeyRing(b.wallet, b.cfg.DB)
   983  	if err != nil {
   984  		// Sign operations will fail, so signal the error and prevent
   985  		// the wallet from considering itself synced (to prevent usage)
   986  		dcrwLog.Errorf("Unable to create wallet key ring: %v", err)
   987  		return
   988  	}
   989  
   990  	// Signal that the wallet is synced by closing the channel.
   991  	if atomic.CompareAndSwapUint32(&b.atomicWalletSynced, syncStatusUnsynced, syncStatusSynced) {
   992  		close(b.syncedChan)
   993  	}
   994  }
   995  
   996  func (b *DcrWallet) rpcSyncerFinished() {
   997  	// The RPC syncer stopped, so if we were previously synced we need to
   998  	// signal that we aren't anymore.
   999  	atomic.CompareAndSwapUint32(&b.atomicWalletSynced, syncStatusSynced, syncStatusLostSync)
  1000  }
  1001  
  1002  // LabelTransaction adds the given external label to the specified transaction.
  1003  //
  1004  // This is a part of the WalletController interface.
  1005  func (b *DcrWallet) LabelTransaction(hash chainhash.Hash, label string, overwrite bool) error {
  1006  	return fmt.Errorf("unimplemented")
  1007  }
  1008  
  1009  // LeaseOutput markes the output as used for some time.
  1010  //
  1011  // This is a part of the WalletController interface.
  1012  func (b *DcrWallet) LeaseOutput(lnwallet.LockID, wire.OutPoint, time.Duration) (time.Time, error) {
  1013  	return time.Time{}, fmt.Errorf("unimplemented")
  1014  }
  1015  
  1016  // ReleaseOutput marks the output as unused.
  1017  //
  1018  // This is a part of the WalletController interface.
  1019  func (b *DcrWallet) ReleaseOutput(lnwallet.LockID, wire.OutPoint) error {
  1020  	return fmt.Errorf("unimplemented")
  1021  }
  1022  
  1023  // ListLeasedOutputs lists leased wallet outputs.
  1024  //
  1025  // This is a part of the WalletController interface.
  1026  func (b *DcrWallet) ListLeasedOutputs() ([]*lnwallet.LockedOutput, error) {
  1027  	return nil, fmt.Errorf("unimplemented")
  1028  }
  1029  
  1030  // GetRecoveryInfo returns the current status of the recovery of the wallet.
  1031  //
  1032  // This is a part of the WalletController interface.
  1033  func (b *DcrWallet) GetRecoveryInfo() (bool, float64, error) {
  1034  	return false, 0, fmt.Errorf("unimplemented")
  1035  }
  1036  
  1037  // FetchTx attempts to fetch a transaction in the wallet's database
  1038  // identified by the passed transaction hash. If the transaction can't
  1039  // be found, then a nil pointer is returned.
  1040  //
  1041  // This is a part of the WalletController interface.
  1042  func (b *DcrWallet) FetchTx(txid chainhash.Hash) (*wire.MsgTx, error) {
  1043  	txs, _, err := b.wallet.GetTransactionsByHashes(b.ctx, []*chainhash.Hash{&txid})
  1044  	if err != nil {
  1045  		return nil, err
  1046  	}
  1047  
  1048  	if len(txs) < 1 {
  1049  		return nil, fmt.Errorf("tx %s not found", txid)
  1050  	}
  1051  
  1052  	return txs[0], nil
  1053  }
  1054  
  1055  // RemoveDescendants attempts to remove any transaction from the
  1056  // wallet's tx store (that may be unconfirmed) that spends outputs
  1057  // created by the passed transaction. This remove propagates
  1058  // recursively down the chain of descendent transactions.
  1059  //
  1060  // This is a part of the WalletController interface.
  1061  func (b *DcrWallet) RemoveDescendants(*wire.MsgTx) error {
  1062  	return fmt.Errorf("RemoveDescendants is unimplemented")
  1063  }
  1064  
  1065  // ListAccount lists existing wallet accounts.
  1066  //
  1067  // This is a part of the WalletController interface.
  1068  func (b *DcrWallet) ListAccounts(accountName string) ([]base.AccountProperties, error) {
  1069  	accounts, err := b.wallet.Accounts(context.Background())
  1070  	if err != nil {
  1071  		return nil, err
  1072  	}
  1073  
  1074  	szHint := len(accounts.Accounts)
  1075  	if accountName != "" {
  1076  		szHint = 1
  1077  	}
  1078  	res := make([]base.AccountProperties, 0, szHint)
  1079  	for _, acct := range accounts.Accounts {
  1080  		if accountName != "" && acct.AccountName != accountName {
  1081  			continue
  1082  		}
  1083  		res = append(res, acct.AccountProperties)
  1084  	}
  1085  
  1086  	return res, nil
  1087  }
  1088  
  1089  // ImportAccount imports the specified xpub into the wallet.
  1090  //
  1091  // This is a part of the WalletController interface.
  1092  func (b *DcrWallet) ImportAccount(name string, accountPubKey *hdkeychain.ExtendedKey, dryRun bool) (
  1093  	*wallet.AccountProperties, []stdaddr.Address, []stdaddr.Address, error) {
  1094  
  1095  	fail := func(err error) (*wallet.AccountProperties, []stdaddr.Address, []stdaddr.Address, error) {
  1096  		return nil, nil, nil, err
  1097  	}
  1098  
  1099  	if dryRun {
  1100  		chainParams := b.wallet.ChainParams()
  1101  		intAddrs, extAddrs, err := lnwallet.DeriveAddrsFromExtPub(accountPubKey,
  1102  			chainParams, dryRunImportAccountNumAddrs)
  1103  		if err != nil {
  1104  			return fail(err)
  1105  		}
  1106  
  1107  		acctProps := &wallet.AccountProperties{
  1108  			AccountName: name,
  1109  		}
  1110  		return acctProps, intAddrs, extAddrs, nil
  1111  	}
  1112  
  1113  	err := b.wallet.ImportXpubAccount(context.Background(), name, accountPubKey)
  1114  	if err != nil {
  1115  		return fail(err)
  1116  	}
  1117  
  1118  	accounts, err := b.ListAccounts(name)
  1119  	if err != nil {
  1120  		return fail(err)
  1121  	}
  1122  	if len(accounts) == 0 {
  1123  		return fail(fmt.Errorf("account named %q does not exist", name))
  1124  	}
  1125  
  1126  	return &accounts[0], nil, nil, nil
  1127  }
  1128  
  1129  // ImportPublicKey imports the specified public key into the wallet.
  1130  //
  1131  // This is a part of the WalletController interface.
  1132  func (b *DcrWallet) ImportPublicKey(pubKey *secp256k1.PublicKey) error {
  1133  	_, err := b.wallet.ImportPublicKey(context.Background(), pubKey.SerializeCompressed())
  1134  	return err
  1135  }
  1136  
  1137  func (b *DcrWallet) ScriptForOutput(*wire.TxOut) (
  1138  	btcwalletcompat.ManagedPubKeyAddress, []byte, []byte, error) {
  1139  	return nil, nil, nil, fmt.Errorf("dcrwallet.ScriptForOutput is not implemented")
  1140  }