github.com/decred/dcrlnd@v0.7.6/walletunlocker/service.go (about)

     1  package walletunlocker
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"sync/atomic"
    10  	"time"
    11  
    12  	"github.com/decred/dcrd/chaincfg/v3"
    13  	"github.com/decred/dcrd/hdkeychain/v3"
    14  	"github.com/decred/dcrlnd/aezeed"
    15  	"github.com/decred/dcrlnd/chanbackup"
    16  	"github.com/decred/dcrlnd/channeldb"
    17  	"github.com/decred/dcrlnd/keychain"
    18  	"github.com/decred/dcrlnd/kvdb"
    19  	"github.com/decred/dcrlnd/lnrpc"
    20  	"github.com/decred/dcrlnd/lnwallet"
    21  	"github.com/decred/dcrlnd/macaroons"
    22  
    23  	"decred.org/dcrwallet/v4/wallet"
    24  	"github.com/decred/dcrlnd/lnwallet/dcrwallet"
    25  	walletloader "github.com/decred/dcrlnd/lnwallet/dcrwallet/loader"
    26  	"google.golang.org/grpc"
    27  )
    28  
    29  var (
    30  	// ErrUnlockTimeout signals that we did not get the expected unlock
    31  	// message before the timeout occurred.
    32  	ErrUnlockTimeout = errors.New("got no unlock message before timeout")
    33  )
    34  
    35  // WalletUnlockParams holds the variables used to parameterize the unlocking of
    36  // lnd's wallet after it has already been created.
    37  type WalletUnlockParams struct {
    38  	// Password is the public and private wallet passphrase.
    39  	Password []byte
    40  
    41  	// Birthday specifies the approximate time that this wallet was created.
    42  	// This is used to bound any rescans on startup.
    43  	Birthday time.Time
    44  
    45  	// RecoveryWindow specifies the address lookahead when entering recovery
    46  	// mode. A recovery will be attempted if this value is non-zero.
    47  	RecoveryWindow uint32
    48  
    49  	// Wallet is the loaded and unlocked Wallet. This is returned
    50  	// from the unlocker service to avoid it being unlocked twice (once in
    51  	// the unlocker service to check if the password is correct and again
    52  	// later when lnd actually uses it). Because unlocking involves scrypt
    53  	// which is resource intensive, we want to avoid doing it twice.
    54  	Wallet *wallet.Wallet
    55  
    56  	// Loader is the wallet loader used to create or open the corresponding
    57  	// wallet.
    58  	Loader *walletloader.Loader
    59  
    60  	// Conn is the connection to the remote wallet when that is used
    61  	// instead of an embedded dcrwallet instance.
    62  	Conn *grpc.ClientConn
    63  
    64  	// ChansToRestore a set of static channel backups that should be
    65  	// restored before the main server instance starts up.
    66  	ChansToRestore ChannelsToRecover
    67  
    68  	// UnloadWallet is a function for unloading the wallet, which should
    69  	// be called on shutdown.
    70  	UnloadWallet func() error
    71  
    72  	// StatelessInit signals that the user requested the daemon to be
    73  	// initialized stateless, which means no unencrypted macaroons should be
    74  	// written to disk.
    75  	StatelessInit bool
    76  
    77  	// MacResponseChan is the channel for sending back the admin macaroon to
    78  	// the WalletUnlocker service.
    79  	MacResponseChan chan []byte
    80  }
    81  
    82  // ChannelsToRecover wraps any set of packed (serialized+encrypted) channel
    83  // back ups together. These can be passed in when unlocking the wallet, or
    84  // creating a new wallet for the first time with an existing seed.
    85  type ChannelsToRecover struct {
    86  	// PackedMultiChanBackup is an encrypted and serialized multi-channel
    87  	// backup.
    88  	PackedMultiChanBackup chanbackup.PackedMulti
    89  
    90  	// PackedSingleChanBackups is a series of encrypted and serialized
    91  	// single-channel backup for one or more channels.
    92  	PackedSingleChanBackups chanbackup.PackedSingles
    93  }
    94  
    95  // WalletInitMsg is a message sent by the UnlockerService when a user wishes to
    96  // set up the internal wallet for the first time. The user MUST provide a
    97  // passphrase, but is also able to provide their own source of entropy. If
    98  // provided, then this source of entropy will be used to generate the wallet's
    99  // HD seed. Otherwise, the wallet will generate one itself.
   100  type WalletInitMsg struct {
   101  	// Passphrase is the passphrase that will be used to encrypt the wallet
   102  	// itself. This MUST be at least 8 characters.
   103  	Passphrase []byte
   104  
   105  	// WalletSeed is the deciphered cipher seed that the wallet should use
   106  	// to initialize itself. The seed might be nil if the wallet should be
   107  	// created from an extended master root key instead.
   108  	WalletSeed *aezeed.CipherSeed
   109  
   110  	// WalletExtendedKey is the wallet's extended master root key that
   111  	// should be used instead of the seed, if non-nil. The extended key is
   112  	// mutually exclusive to the wallet seed, but one of both is always set.
   113  	WalletExtendedKey *hdkeychain.ExtendedKey
   114  
   115  	// ExtendedKeyBirthday is the birthday of a wallet that's being restored
   116  	// through an extended key instead of an aezeed.
   117  	ExtendedKeyBirthday time.Time
   118  
   119  	// WatchOnlyBirthday is the birthday of the master root key the above
   120  	// watch-only account xpubs were derived from.
   121  	WatchOnlyBirthday time.Time
   122  
   123  	// WatchOnlyMasterFingerprint is the fingerprint of the master root key
   124  	// the above watch-only account xpubs were derived from.
   125  	WatchOnlyMasterFingerprint uint32
   126  
   127  	// RecoveryWindow is the address look-ahead used when restoring a seed
   128  	// with existing funds. A recovery window zero indicates that no
   129  	// recovery should be attempted, such as after the wallet's initial
   130  	// creation.
   131  	RecoveryWindow uint32
   132  
   133  	// ChanBackups a set of static channel backups that should be received
   134  	// after the wallet has been initialized.
   135  	ChanBackups ChannelsToRecover
   136  
   137  	// StatelessInit signals that the user requested the daemon to be
   138  	// initialized stateless, which means no unencrypted macaroons should be
   139  	// written to disk.
   140  	StatelessInit bool
   141  }
   142  
   143  // WalletUnlockMsg is a message sent by the UnlockerService when a user wishes
   144  // to unlock the internal wallet after initial setup. The user can optionally
   145  // specify a recovery window, which will resume an interrupted rescan for used
   146  // addresses.
   147  type WalletUnlockMsg struct {
   148  	// Passphrase is the passphrase that will be used to encrypt the wallet
   149  	// itself. This MUST be at least 8 characters.
   150  	Passphrase []byte
   151  
   152  	// RecoveryWindow is the address look-ahead used when restoring a seed
   153  	// with existing funds. A recovery window zero indicates that no
   154  	// recovery should be attempted, such as after the wallet's initial
   155  	// creation, but before any addresses have been created.
   156  	RecoveryWindow uint32
   157  
   158  	// Wallet is the loaded and unlocked Wallet. This is returned through
   159  	// the channel to avoid it being unlocked twice (once to check if the
   160  	// password is correct, here in the WalletUnlocker and again later when
   161  	// lnd actually uses it). Because unlocking involves scrypt which is
   162  	// resource intensive, we want to avoid doing it twice.
   163  	Wallet *wallet.Wallet
   164  
   165  	Loader *walletloader.Loader
   166  
   167  	// Conn is the connection to a remote wallet when the daemon has been
   168  	// configured to connect to a wallet instead of using the embedded one.
   169  	Conn *grpc.ClientConn
   170  
   171  	// ChanBackups a set of static channel backups that should be received
   172  	// after the wallet has been unlocked.
   173  	ChanBackups ChannelsToRecover
   174  
   175  	// UnloadWallet is a function for unloading the wallet, which should
   176  	// be called on shutdown.
   177  	UnloadWallet func() error
   178  
   179  	// StatelessInit signals that the user requested the daemon to be
   180  	// initialized stateless, which means no unencrypted macaroons should be
   181  	// written to disk.
   182  	StatelessInit bool
   183  }
   184  
   185  // UnlockerService implements the WalletUnlocker service used to provide lnd
   186  // with a password for wallet encryption at startup. Additionally, during
   187  // initial setup, users can provide their own source of entropy which will be
   188  // used to generate the seed that's ultimately used within the wallet.
   189  type UnlockerService struct {
   190  	// InitMsgs is a channel that carries all wallet init messages.
   191  	InitMsgs chan *WalletInitMsg
   192  
   193  	// UnlockMsgs is a channel where unlock parameters provided by the rpc
   194  	// client to be used to unlock and decrypt an existing wallet will be
   195  	// sent.
   196  	UnlockMsgs chan *WalletUnlockMsg
   197  
   198  	// MacResponseChan is the channel for sending back the admin macaroon to
   199  	// the WalletUnlocker service.
   200  	MacResponseChan chan []byte
   201  
   202  	chainDir  string
   203  	netParams *chaincfg.Params
   204  
   205  	// db is the db used for checking remote wallet unlocks.
   206  	db atomic.Pointer[channeldb.DB]
   207  
   208  	dcrwHost       string
   209  	dcrwCert       string
   210  	dcrwClientKey  string
   211  	dcrwClientCert string
   212  	dcrwAccount    int32
   213  
   214  	// macaroonFiles is the path to the three generated macaroons with
   215  	// different access permissions. These might not exist in a stateless
   216  	// initialization of lnd.
   217  	macaroonFiles []string
   218  
   219  	// resetWalletTransactions indicates that the wallet state should be
   220  	// reset on unlock to force a full chain rescan.
   221  	resetWalletTransactions bool
   222  
   223  	// LoaderOpts holds the functional options for the wallet loader.
   224  	loaderOpts []walletloader.LoaderOption
   225  
   226  	// macaroonDB is an instance of a database backend that stores all
   227  	// macaroon root keys. This will be nil on initialization and must be
   228  	// set using the SetMacaroonDB method as soon as it's available.
   229  	macaroonDB kvdb.Backend
   230  }
   231  
   232  // New creates and returns a new UnlockerService.
   233  func New(chainDir string, params *chaincfg.Params,
   234  	macaroonFiles []string, dbTimeout time.Duration,
   235  	dcrwHost, dcrwCert, dcrwClientKey,
   236  	dcrwClientCert string, dcrwAccount int32) *UnlockerService {
   237  
   238  	return &UnlockerService{
   239  		InitMsgs:       make(chan *WalletInitMsg, 1),
   240  		UnlockMsgs:     make(chan *WalletUnlockMsg, 1),
   241  		dcrwHost:       dcrwHost,
   242  		dcrwCert:       dcrwCert,
   243  		dcrwClientKey:  dcrwClientKey,
   244  		dcrwClientCert: dcrwClientCert,
   245  		dcrwAccount:    dcrwAccount,
   246  
   247  		// Make sure we buffer the channel is buffered so the main lnd
   248  		// goroutine isn't blocking on writing to it.
   249  		MacResponseChan:         make(chan []byte, 1),
   250  		chainDir:                chainDir,
   251  		netParams:               params,
   252  		macaroonFiles:           macaroonFiles,
   253  		resetWalletTransactions: false,
   254  	}
   255  }
   256  
   257  // SetDB sets the DB used for checking that unlocking remote wallets works. This
   258  // must be called before the service is
   259  func (u *UnlockerService) SetDB(db *channeldb.DB) {
   260  	u.db.Store(db)
   261  }
   262  
   263  // SetLoaderOpts can be used to inject wallet loader options after the unlocker
   264  // service has been hooked to the main RPC server.
   265  func (u *UnlockerService) SetLoaderOpts(loaderOpts []walletloader.LoaderOption) {
   266  	u.loaderOpts = loaderOpts
   267  }
   268  
   269  // SetMacaroonDB can be used to inject the macaroon database after the unlocker
   270  // service has been hooked to the main RPC server.
   271  func (u *UnlockerService) SetMacaroonDB(macaroonDB kvdb.Backend) {
   272  	u.macaroonDB = macaroonDB
   273  }
   274  
   275  func (u *UnlockerService) newLoader(recoveryWindow uint32) (*walletloader.Loader,
   276  	error) {
   277  
   278  	netDir := dcrwallet.NetworkDir(u.chainDir, u.netParams)
   279  	return walletloader.NewLoader(
   280  		u.netParams, netDir, recoveryWindow,
   281  	), nil
   282  }
   283  
   284  // WalletExists returns whether a wallet exists on the file path the
   285  // UnlockerService is using.
   286  func (u *UnlockerService) WalletExists() (bool, error) {
   287  	loader, err := u.newLoader(wallet.DefaultGapLimit)
   288  	if err != nil {
   289  		return false, err
   290  	}
   291  	return loader.WalletExists()
   292  }
   293  
   294  // GenSeed is the first method that should be used to instantiate a new lnd
   295  // instance. This method allows a caller to generate a new aezeed cipher seed
   296  // given an optional passphrase. If provided, the passphrase will be necessary
   297  // to decrypt the cipherseed to expose the internal wallet seed.
   298  //
   299  // Once the cipherseed is obtained and verified by the user, the InitWallet
   300  // method should be used to commit the newly generated seed, and create the
   301  // wallet.
   302  func (u *UnlockerService) GenSeed(_ context.Context,
   303  	in *lnrpc.GenSeedRequest) (*lnrpc.GenSeedResponse, error) {
   304  
   305  	// Before we start, we'll ensure that the wallet hasn't already created
   306  	// so we don't show a *new* seed to the user if one already exists.
   307  	loader, err := u.newLoader(wallet.DefaultGapLimit)
   308  	if err != nil {
   309  		return nil, err
   310  	}
   311  
   312  	walletExists, err := loader.WalletExists()
   313  	if err != nil {
   314  		return nil, err
   315  	}
   316  	if walletExists {
   317  		return nil, fmt.Errorf("wallet already exists")
   318  	}
   319  
   320  	var entropy [aezeed.EntropySize]byte
   321  
   322  	switch {
   323  	// If the user provided any entropy, then we'll make sure it's sized
   324  	// properly.
   325  	case len(in.SeedEntropy) != 0 && len(in.SeedEntropy) != aezeed.EntropySize:
   326  		return nil, fmt.Errorf("incorrect entropy length: expected "+
   327  			"16 bytes, instead got %v bytes", len(in.SeedEntropy))
   328  
   329  	// If the user provided the correct number of bytes, then we'll copy it
   330  	// over into our buffer for usage.
   331  	case len(in.SeedEntropy) == aezeed.EntropySize:
   332  		copy(entropy[:], in.SeedEntropy)
   333  
   334  	// Otherwise, we'll generate a fresh new set of bytes to use as entropy
   335  	// to generate the seed.
   336  	default:
   337  		if _, err := rand.Read(entropy[:]); err != nil {
   338  			return nil, err
   339  		}
   340  	}
   341  
   342  	// Subtract 24h from the birthday, to ensure no reorgs could possibly
   343  	// make us miss some transactions.
   344  	birthday := time.Now().Add(-time.Hour * 24)
   345  
   346  	// Now that we have our set of entropy, we'll create a new cipher seed
   347  	// instance.
   348  	//
   349  	cipherSeed, err := aezeed.New(
   350  		keychain.KeyDerivationVersion, &entropy, birthday,
   351  	)
   352  	if err != nil {
   353  		return nil, err
   354  	}
   355  
   356  	// With our raw cipher seed obtained, we'll convert it into an encoded
   357  	// mnemonic using the user specified pass phrase.
   358  	mnemonic, err := cipherSeed.ToMnemonic(in.AezeedPassphrase)
   359  	if err != nil {
   360  		return nil, err
   361  	}
   362  
   363  	// Additionally, we'll also obtain the raw enciphered cipher seed as
   364  	// well to return to the user.
   365  	encipheredSeed, err := cipherSeed.Encipher(in.AezeedPassphrase)
   366  	if err != nil {
   367  		return nil, err
   368  	}
   369  
   370  	return &lnrpc.GenSeedResponse{
   371  		CipherSeedMnemonic: mnemonic[:],
   372  		EncipheredSeed:     encipheredSeed[:],
   373  	}, nil
   374  }
   375  
   376  // extractChanBackups is a helper function that extracts the set of channel
   377  // backups from the proto into a format that we'll pass to higher level
   378  // sub-systems.
   379  func extractChanBackups(chanBackups *lnrpc.ChanBackupSnapshot) *ChannelsToRecover {
   380  	// If there aren't any populated channel backups, then we can exit
   381  	// early as there's nothing to extract.
   382  	if chanBackups == nil || (chanBackups.SingleChanBackups == nil &&
   383  		chanBackups.MultiChanBackup == nil) {
   384  		return nil
   385  	}
   386  
   387  	// Now that we know there's at least a single back up populated, we'll
   388  	// extract the multi-chan backup (if it's there).
   389  	var backups ChannelsToRecover
   390  	if chanBackups.MultiChanBackup != nil {
   391  		multiBackup := chanBackups.MultiChanBackup
   392  		backups.PackedMultiChanBackup = multiBackup.MultiChanBackup
   393  	}
   394  
   395  	if chanBackups.SingleChanBackups == nil {
   396  		return &backups
   397  	}
   398  
   399  	// Finally, we can extract all the single chan backups as well.
   400  	for _, backup := range chanBackups.SingleChanBackups.ChanBackups {
   401  		singleChanBackup := backup.ChanBackup
   402  
   403  		backups.PackedSingleChanBackups = append(
   404  			backups.PackedSingleChanBackups, singleChanBackup,
   405  		)
   406  	}
   407  
   408  	return &backups
   409  }
   410  
   411  // InitWallet is used when lnd is starting up for the first time to fully
   412  // initialize the daemon and its internal wallet. At the very least a wallet
   413  // password must be provided. This will be used to encrypt sensitive material
   414  // on disk.
   415  //
   416  // In the case of a recovery scenario, the user can also specify their aezeed
   417  // mnemonic and passphrase. If set, then the daemon will use this prior state
   418  // to initialize its internal wallet.
   419  //
   420  // Alternatively, this can be used along with the GenSeed RPC to obtain a
   421  // seed, then present it to the user. Once it has been verified by the user,
   422  // the seed can be fed into this RPC in order to commit the new wallet.
   423  func (u *UnlockerService) InitWallet(ctx context.Context,
   424  	in *lnrpc.InitWalletRequest) (*lnrpc.InitWalletResponse, error) {
   425  
   426  	// Make sure the password meets our constraints.
   427  	password := in.WalletPassword
   428  	if err := ValidatePassword(password); err != nil {
   429  		return nil, err
   430  	}
   431  
   432  	// Require that the recovery window be non-negative.
   433  	recoveryWindow := in.RecoveryWindow
   434  	if recoveryWindow < 0 {
   435  		return nil, fmt.Errorf("recovery window %d must be "+
   436  			"non-negative", recoveryWindow)
   437  	}
   438  	if recoveryWindow == 0 {
   439  		recoveryWindow = int32(wallet.DefaultGapLimit)
   440  	}
   441  
   442  	// We'll then open up the directory that will be used to store the
   443  	// wallet's files so we can check if the wallet already exists.
   444  	loader, err := u.newLoader(uint32(recoveryWindow))
   445  	if err != nil {
   446  		return nil, err
   447  	}
   448  
   449  	walletExists, err := loader.WalletExists()
   450  	if err != nil {
   451  		return nil, err
   452  	}
   453  
   454  	// If the wallet already exists, then we'll exit early as we can't
   455  	// create the wallet if it already exists!
   456  	if walletExists {
   457  		return nil, fmt.Errorf("wallet already exists")
   458  	}
   459  
   460  	// At this point, we know the wallet doesn't already exist so we can
   461  	// prepare the message that we'll send over the channel later.
   462  	initMsg := &WalletInitMsg{
   463  		Passphrase:     password,
   464  		RecoveryWindow: uint32(recoveryWindow),
   465  		StatelessInit:  in.StatelessInit,
   466  	}
   467  
   468  	// There are two supported ways to initialize the wallet. Either from
   469  	// the aezeed or the final extended master key directly.
   470  	switch {
   471  	// Don't allow the user to specify both as that would be ambiguous.
   472  	case len(in.CipherSeedMnemonic) > 0 && len(in.ExtendedMasterKey) > 0:
   473  		return nil, fmt.Errorf("cannot specify both the cipher " +
   474  			"seed mnemonic and the extended master key")
   475  
   476  	// The aezeed is the preferred and default way of initializing a wallet.
   477  	case len(in.CipherSeedMnemonic) > 0:
   478  		// We'll map the user provided aezeed and passphrase into a
   479  		// decoded cipher seed instance.
   480  		var mnemonic aezeed.Mnemonic
   481  		copy(mnemonic[:], in.CipherSeedMnemonic)
   482  
   483  		// If we're unable to map it back into the ciphertext, then
   484  		// either the mnemonic is wrong, or the passphrase is wrong.
   485  		cipherSeed, err := mnemonic.ToCipherSeed(in.AezeedPassphrase)
   486  		if err != nil {
   487  			return nil, err
   488  		}
   489  
   490  		initMsg.WalletSeed = cipherSeed
   491  
   492  	// To support restoring a wallet where the seed isn't known or a wallet
   493  	// created externally to lnd, we also allow the extended master key
   494  	// (xprv) to be imported directly. This is what'll be stored in the
   495  	// dcrwallet database anyway.
   496  	case len(in.ExtendedMasterKey) > 0:
   497  		extendedKey, err := hdkeychain.NewKeyFromString(
   498  			in.ExtendedMasterKey, u.netParams,
   499  		)
   500  		if err != nil {
   501  			return nil, err
   502  		}
   503  
   504  		// The on-chain wallet of lnd is going to derive keys based on
   505  		// the BIP49/84 key derivation paths from this root key. To make
   506  		// sure we use default derivation paths, we want to avoid
   507  		// deriving keys from something other than the master key (at
   508  		// depth 0, denoted with "m/" in BIP32 notation).
   509  		if extendedKey.Depth() != 0 {
   510  			return nil, fmt.Errorf("extended master key must " +
   511  				"be at depth 0 not a child key")
   512  		}
   513  
   514  		// Because we need the master key (at depth 0), it must be an
   515  		// extended private key as the first levels of BIP49/84
   516  		// derivation paths are hardened, which isn't possible with
   517  		// extended public keys.
   518  		if !extendedKey.IsPrivate() {
   519  			return nil, fmt.Errorf("extended master key must " +
   520  				"contain private keys")
   521  		}
   522  
   523  		// When importing a wallet from its extended private key we
   524  		// don't know the birthday as that information is not encoded in
   525  		// that format. We therefore must set an arbitrary date to start
   526  		// rescanning at if the user doesn't provide an explicit value
   527  		// for it. Since lnd only uses SegWit addresses, we pick the
   528  		// date of the first block that contained SegWit transactions
   529  		// (481824).
   530  		initMsg.ExtendedKeyBirthday = time.Date(
   531  			2017, time.August, 24, 1, 57, 37, 0, time.UTC,
   532  		)
   533  		if in.ExtendedMasterKeyBirthdayTimestamp != 0 {
   534  			initMsg.ExtendedKeyBirthday = time.Unix(
   535  				int64(in.ExtendedMasterKeyBirthdayTimestamp), 0,
   536  			)
   537  		}
   538  
   539  		initMsg.WalletExtendedKey = extendedKey
   540  
   541  	// The third option for creating a wallet is the watch-only mode:
   542  	// Instead of providing the master root key directly, each individual
   543  	// account is passed as an extended public key only. Because of the
   544  	// hardened derivation path up to the account (depth 3), it is not
   545  	// possible to create a master root extended _public_ key. Therefore, an
   546  	// xpub must be derived and passed into the unlocker for _every_ account
   547  	// lnd expects.
   548  	case in.WatchOnly != nil && len(in.WatchOnly.Accounts) > 0:
   549  		return nil, fmt.Errorf("watch-only wallets are not supported in dcrlnd")
   550  
   551  	// No key material was set, no wallet can be created.
   552  	default:
   553  		return nil, fmt.Errorf("must either specify cipher seed " +
   554  			"mnemonic or the extended master key")
   555  	}
   556  
   557  	// Before we return the unlock payload, we'll check if we can extract
   558  	// any channel backups to pass up to the higher level sub-system.
   559  	chansToRestore := extractChanBackups(in.ChannelBackups)
   560  	if chansToRestore != nil {
   561  		initMsg.ChanBackups = *chansToRestore
   562  	}
   563  
   564  	// Deliver the initialization message back to the main daemon.
   565  	select {
   566  	case u.InitMsgs <- initMsg:
   567  		// We need to read from the channel to let the daemon continue
   568  		// its work and to get the admin macaroon. Once the response
   569  		// arrives, we directly forward it to the client.
   570  		select {
   571  		case adminMac := <-u.MacResponseChan:
   572  			return &lnrpc.InitWalletResponse{
   573  				AdminMacaroon: adminMac,
   574  			}, nil
   575  
   576  		case <-ctx.Done():
   577  			return nil, ErrUnlockTimeout
   578  		}
   579  
   580  	case <-ctx.Done():
   581  		return nil, ErrUnlockTimeout
   582  	}
   583  }
   584  
   585  // LoadAndUnlock creates a loader for the wallet and tries to unlock the wallet
   586  // with the given password and recovery window. If the drop wallet transactions
   587  // flag is set, the history state drop is performed before unlocking the wallet
   588  // yet again.
   589  func (u *UnlockerService) LoadAndUnlock(ctx context.Context, password []byte,
   590  	recoveryWindow uint32) (*wallet.Wallet, func() error, error) {
   591  
   592  	loader, err := u.newLoader(recoveryWindow)
   593  	if err != nil {
   594  		return nil, nil, err
   595  	}
   596  
   597  	// Check if wallet already exists.
   598  	walletExists, err := loader.WalletExists()
   599  	if err != nil {
   600  		return nil, nil, err
   601  	}
   602  
   603  	if !walletExists {
   604  		// Cannot unlock a wallet that does not exist!
   605  		return nil, nil, fmt.Errorf("wallet not found")
   606  	}
   607  
   608  	// Try opening the existing wallet with the provided password.
   609  	unlockedWallet, err := loader.OpenExistingWallet(ctx, password)
   610  	if err != nil {
   611  		// Could not open wallet, most likely this means that provided
   612  		// password was incorrect.
   613  		return nil, nil, err
   614  	}
   615  
   616  	// The user requested to drop their whole wallet transaction state to
   617  	// force a full chain rescan for wallet addresses. Dropping the state
   618  	// only properly takes effect after opening the wallet. That's why we
   619  	// start, drop, stop and start again.
   620  	if u.resetWalletTransactions {
   621  		return nil, nil, fmt.Errorf("dropping wallet txs is not supported in dcrlnd")
   622  	}
   623  
   624  	return unlockedWallet, loader.UnloadWallet, nil
   625  }
   626  
   627  // UnlockWallet sends the password provided by the incoming UnlockWalletRequest
   628  // over the UnlockMsgs channel in case it successfully decrypts an existing
   629  // wallet found in the chain's wallet database directory.
   630  func (u *UnlockerService) UnlockWallet(ctx context.Context,
   631  	in *lnrpc.UnlockWalletRequest) (*lnrpc.UnlockWalletResponse, error) {
   632  
   633  	if u.dcrwHost != "" && u.dcrwCert != "" {
   634  		// Using a remote wallet.
   635  		return u.unlockRemoteWallet(ctx, in)
   636  	}
   637  
   638  	password := in.WalletPassword
   639  	recoveryWindow := uint32(in.RecoveryWindow)
   640  	if recoveryWindow < wallet.DefaultGapLimit {
   641  		recoveryWindow = wallet.DefaultGapLimit
   642  	}
   643  
   644  	unlockedWallet, unloadFn, err := u.LoadAndUnlock(
   645  		ctx, password, recoveryWindow,
   646  	)
   647  	if err != nil {
   648  		return nil, err
   649  	}
   650  
   651  	// We successfully opened the wallet and pass the instance back to
   652  	// avoid it needing to be unlocked again.
   653  	walletUnlockMsg := &WalletUnlockMsg{
   654  		Passphrase:     password,
   655  		RecoveryWindow: recoveryWindow,
   656  		Wallet:         unlockedWallet,
   657  		UnloadWallet:   unloadFn,
   658  		StatelessInit:  in.StatelessInit,
   659  	}
   660  
   661  	// Before we return the unlock payload, we'll check if we can extract
   662  	// any channel backups to pass up to the higher level sub-system.
   663  	chansToRestore := extractChanBackups(in.ChannelBackups)
   664  	if chansToRestore != nil {
   665  		walletUnlockMsg.ChanBackups = *chansToRestore
   666  	}
   667  
   668  	// At this point we were able to open the existing wallet with the
   669  	// provided password. We send the password over the UnlockMsgs
   670  	// channel, such that it can be used by lnd to open the wallet.
   671  	select {
   672  	case u.UnlockMsgs <- walletUnlockMsg:
   673  		// We need to read from the channel to let the daemon continue
   674  		// its work. But we don't need the returned macaroon for this
   675  		// operation, so we read it but then discard it.
   676  		select {
   677  		case <-u.MacResponseChan:
   678  			return &lnrpc.UnlockWalletResponse{}, nil
   679  
   680  		case <-ctx.Done():
   681  			return nil, ErrUnlockTimeout
   682  		}
   683  
   684  	case <-ctx.Done():
   685  		return nil, ErrUnlockTimeout
   686  	}
   687  }
   688  
   689  // ChangePassword changes the password of the wallet and sends the new password
   690  // across the UnlockPasswords channel to automatically unlock the wallet if
   691  // successful.
   692  func (u *UnlockerService) ChangePassword(ctx context.Context,
   693  	in *lnrpc.ChangePasswordRequest) (*lnrpc.ChangePasswordResponse, error) {
   694  
   695  	loader, err := u.newLoader(wallet.DefaultGapLimit)
   696  	if err != nil {
   697  		return nil, err
   698  	}
   699  
   700  	// First, we'll make sure the wallet exists for the specific chain and
   701  	// network.
   702  	walletExists, err := loader.WalletExists()
   703  	if err != nil {
   704  		return nil, err
   705  	}
   706  
   707  	if !walletExists {
   708  		return nil, errors.New("wallet not found")
   709  	}
   710  
   711  	publicPw := in.CurrentPassword
   712  	privatePw := in.CurrentPassword
   713  
   714  	// If the current password is blank, we'll assume the user is coming
   715  	// from a --noseedbackup state, so we'll use the default passwords.
   716  	if len(in.CurrentPassword) == 0 {
   717  		publicPw = lnwallet.DefaultPublicPassphrase
   718  		privatePw = lnwallet.DefaultPrivatePassphrase
   719  	}
   720  
   721  	// Make sure the new password meets our constraints.
   722  	if err := ValidatePassword(in.NewPassword); err != nil {
   723  		return nil, err
   724  	}
   725  
   726  	// Load the existing wallet in order to proceed with the password change.
   727  	w, err := loader.OpenExistingWallet(ctx, publicPw)
   728  	if err != nil {
   729  		return nil, err
   730  	}
   731  
   732  	// Now that we've opened the wallet, we need to close it in case of an
   733  	// error. But not if we succeed, then the caller must close it.
   734  	orderlyReturn := false
   735  	defer func() {
   736  		if !orderlyReturn {
   737  			_ = loader.UnloadWallet()
   738  		}
   739  	}()
   740  
   741  	// Before we actually change the password, we need to check if all flags
   742  	// were set correctly. The content of the previously generated macaroon
   743  	// files will become invalid after we generate a new root key. So we try
   744  	// to delete them here and they will be recreated during normal startup
   745  	// later. If they are missing, this is only an error if the
   746  	// stateless_init flag was not set.
   747  	if in.NewMacaroonRootKey || in.StatelessInit {
   748  		for _, file := range u.macaroonFiles {
   749  			err := os.Remove(file)
   750  			if err != nil && !in.StatelessInit {
   751  				return nil, fmt.Errorf("could not remove "+
   752  					"macaroon file: %v. if the wallet "+
   753  					"was initialized stateless please "+
   754  					"add the --stateless_init "+
   755  					"flag", err)
   756  			}
   757  		}
   758  	}
   759  
   760  	// Attempt to change both the public and private passphrases for the
   761  	// wallet. This will be done atomically in order to prevent one
   762  	// passphrase change from being successful and not the other.
   763  	//
   764  	// TODO(decred) This is not an atomic operation. Discuss whether we want to
   765  	// actually use the public pssword.
   766  	err = w.ChangePrivatePassphrase(ctx, privatePw, in.NewPassword)
   767  	if err != nil {
   768  		return nil, fmt.Errorf("unable to change wallet private passphrase: "+
   769  			"%v", err)
   770  	}
   771  	err = w.ChangePublicPassphrase(ctx, publicPw, in.NewPassword)
   772  	if err != nil {
   773  		return nil, fmt.Errorf("unable to change wallet public passphrase: "+
   774  			"%v", err)
   775  	}
   776  
   777  	// The next step is to load the macaroon database, change the password
   778  	// then close it again.
   779  	// Attempt to open the macaroon DB, unlock it and then change
   780  	// the passphrase.
   781  	macaroonService, err := macaroons.NewService(
   782  		u.macaroonDB, "lnd", in.StatelessInit,
   783  	)
   784  	if err != nil {
   785  		return nil, err
   786  	}
   787  
   788  	err = macaroonService.CreateUnlock(&privatePw)
   789  	if err != nil {
   790  		closeErr := macaroonService.Close()
   791  		if closeErr != nil {
   792  			return nil, fmt.Errorf("could not create unlock: %v "+
   793  				"--> follow-up error when closing: %v", err,
   794  				closeErr)
   795  		}
   796  		return nil, err
   797  	}
   798  	err = macaroonService.ChangePassword(privatePw, in.NewPassword)
   799  	if err != nil {
   800  		closeErr := macaroonService.Close()
   801  		if closeErr != nil {
   802  			return nil, fmt.Errorf("could not change password: %v "+
   803  				"--> follow-up error when closing: %v", err,
   804  				closeErr)
   805  		}
   806  		return nil, err
   807  	}
   808  
   809  	// If requested by the user, attempt to replace the existing
   810  	// macaroon root key with a new one.
   811  	if in.NewMacaroonRootKey {
   812  		err = macaroonService.GenerateNewRootKey()
   813  		if err != nil {
   814  			closeErr := macaroonService.Close()
   815  			if closeErr != nil {
   816  				return nil, fmt.Errorf("could not generate "+
   817  					"new root key: %v --> follow-up error "+
   818  					"when closing: %v", err, closeErr)
   819  			}
   820  			return nil, err
   821  		}
   822  	}
   823  
   824  	err = macaroonService.Close()
   825  	if err != nil {
   826  		return nil, fmt.Errorf("could not close macaroon service: %v",
   827  			err)
   828  	}
   829  
   830  	// Finally, send the new password across the UnlockPasswords channel to
   831  	// automatically unlock the wallet.
   832  	walletUnlockMsg := &WalletUnlockMsg{
   833  		Passphrase:    in.NewPassword,
   834  		Wallet:        w,
   835  		StatelessInit: in.StatelessInit,
   836  		UnloadWallet:  loader.UnloadWallet,
   837  	}
   838  	select {
   839  	case u.UnlockMsgs <- walletUnlockMsg:
   840  		// We need to read from the channel to let the daemon continue
   841  		// its work and to get the admin macaroon. Once the response
   842  		// arrives, we directly forward it to the client.
   843  		orderlyReturn = true
   844  		select {
   845  		case adminMac := <-u.MacResponseChan:
   846  			return &lnrpc.ChangePasswordResponse{
   847  				AdminMacaroon: adminMac,
   848  			}, nil
   849  
   850  		case <-ctx.Done():
   851  			return nil, ErrUnlockTimeout
   852  		}
   853  
   854  	case <-ctx.Done():
   855  		return nil, ErrUnlockTimeout
   856  	}
   857  }
   858  
   859  // ValidatePassword assures the password meets all of our constraints.
   860  func ValidatePassword(password []byte) error {
   861  	// Passwords should have a length of at least 8 characters.
   862  	if len(password) < 8 {
   863  		return errors.New("password must have at least 8 characters")
   864  	}
   865  
   866  	return nil
   867  }