Merge pull request #5256 from guggero/wallet-unlock-file

Auto-unlock wallet from password file
This commit is contained in:
Olaoluwa Osuntokun 2021-05-12 13:41:30 -07:00 committed by GitHub
commit 93730088c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 344 additions and 48 deletions

@ -293,6 +293,7 @@ type Config struct {
NoNetBootstrap bool `long:"nobootstrap" description:"If true, then automatic network bootstrapping will not be attempted."` NoNetBootstrap bool `long:"nobootstrap" description:"If true, then automatic network bootstrapping will not be attempted."`
NoSeedBackup bool `long:"noseedbackup" description:"If true, NO SEED WILL BE EXPOSED -- EVER, AND THE WALLET WILL BE ENCRYPTED USING THE DEFAULT PASSPHRASE. THIS FLAG IS ONLY FOR TESTING AND SHOULD NEVER BE USED ON MAINNET."` NoSeedBackup bool `long:"noseedbackup" description:"If true, NO SEED WILL BE EXPOSED -- EVER, AND THE WALLET WILL BE ENCRYPTED USING THE DEFAULT PASSPHRASE. THIS FLAG IS ONLY FOR TESTING AND SHOULD NEVER BE USED ON MAINNET."`
WalletUnlockPasswordFile string `long:"wallet-unlock-password-file" description:"The full path to a file (or pipe/device) that contains the password for unlocking the wallet; if set, no unlocking through RPC is possible and lnd will exit if no wallet exists or the password is incorrect"`
ResetWalletTransactions bool `long:"reset-wallet-transactions" description:"Removes all transaction history from the on-chain wallet on startup, forcing a full chain rescan starting at the wallet's birthday. Implements the same functionality as btcwallet's dropwtxmgr command. Should be set to false after successful execution to avoid rescanning on every restart of lnd."` ResetWalletTransactions bool `long:"reset-wallet-transactions" description:"Removes all transaction history from the on-chain wallet on startup, forcing a full chain rescan starting at the wallet's birthday. Implements the same functionality as btcwallet's dropwtxmgr command. Should be set to false after successful execution to avoid rescanning on every restart of lnd."`
@ -689,6 +690,9 @@ func ValidateConfig(cfg Config, usageMessage string,
cfg.Tor.WatchtowerKeyPath = CleanAndExpandPath(cfg.Tor.WatchtowerKeyPath) cfg.Tor.WatchtowerKeyPath = CleanAndExpandPath(cfg.Tor.WatchtowerKeyPath)
cfg.Watchtower.TowerDir = CleanAndExpandPath(cfg.Watchtower.TowerDir) cfg.Watchtower.TowerDir = CleanAndExpandPath(cfg.Watchtower.TowerDir)
cfg.BackupFilePath = CleanAndExpandPath(cfg.BackupFilePath) cfg.BackupFilePath = CleanAndExpandPath(cfg.BackupFilePath)
cfg.WalletUnlockPasswordFile = CleanAndExpandPath(
cfg.WalletUnlockPasswordFile,
)
// Create the lnd directory and all other sub directories if they don't // Create the lnd directory and all other sub directories if they don't
// already exist. This makes sure that directory trees are also created // already exist. This makes sure that directory trees are also created
@ -1280,6 +1284,20 @@ func ValidateConfig(cfg Config, usageMessage string,
return nil, err return nil, err
} }
switch {
// The no seed backup and auto unlock are mutually exclusive.
case cfg.NoSeedBackup && cfg.WalletUnlockPasswordFile != "":
return nil, fmt.Errorf("cannot set noseedbackup and " +
"wallet-unlock-password-file at the same time")
// If a password file was specified, we need it to exist.
case cfg.WalletUnlockPasswordFile != "" &&
!lnrpc.FileExists(cfg.WalletUnlockPasswordFile):
return nil, fmt.Errorf("wallet unlock password file %s does "+
"not exist", cfg.WalletUnlockPasswordFile)
}
// For each of the RPC listeners (REST+gRPC), we'll ensure that users // For each of the RPC listeners (REST+gRPC), we'll ensure that users
// have specified a safe combo for authentication. If not, we'll bail // have specified a safe combo for authentication. If not, we'll bail
// out with an error. Since we don't allow disabling TLS for gRPC // out with an error. Since we don't allow disabling TLS for gRPC

@ -226,16 +226,16 @@ On FreeBSD, use gmake instead of make.
Alternatively, if one doesn't wish to use `make`, then the `go` commands can be Alternatively, if one doesn't wish to use `make`, then the `go` commands can be
used directly: used directly:
```shell ```shell
cd $GOPATH/src/github.com/lightningnetwork/lnd cd $GOPATH/src/github.com/lightningnetwork/lnd
git pull git pull
GO111MODULE=on go install -v ./... GO111MODULE=on go install -v ./...
``` ```
**Tests** **Tests**
To check that `lnd` was installed properly run the following command: To check that `lnd` was installed properly run the following command:
``` ```shell
make check make check
``` ```
This command requires `bitcoind` (almost any version should do) to be available This command requires `bitcoind` (almost any version should do) to be available
@ -259,7 +259,7 @@ wallet, and the age of the earliest channels (which were created around March
The set of arguments for each of the backend modes is as follows: The set of arguments for each of the backend modes is as follows:
## btcd Options ## btcd Options
``` ```text
btcd: btcd:
--btcd.dir= The base directory that contains the node's data, logs, configuration file, etc. (default: /Users/roasbeef/Library/Application Support/Btcd) --btcd.dir= The base directory that contains the node's data, logs, configuration file, etc. (default: /Users/roasbeef/Library/Application Support/Btcd)
--btcd.rpchost= The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used. (default: localhost) --btcd.rpchost= The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used. (default: localhost)
@ -270,7 +270,7 @@ btcd:
``` ```
## Neutrino Options ## Neutrino Options
``` ```text
neutrino: neutrino:
-a, --neutrino.addpeer= Add a peer to connect with at startup -a, --neutrino.addpeer= Add a peer to connect with at startup
--neutrino.connect= Connect only to the specified peers at startup --neutrino.connect= Connect only to the specified peers at startup
@ -282,7 +282,7 @@ neutrino:
``` ```
## Bitcoind Options ## Bitcoind Options
``` ```text
bitcoind: bitcoind:
--bitcoind.dir= The base directory that contains the node's data, logs, configuration file, etc. (default: /Users/roasbeef/Library/Application Support/Bitcoin) --bitcoind.dir= The base directory that contains the node's data, logs, configuration file, etc. (default: /Users/roasbeef/Library/Application Support/Bitcoin)
--bitcoind.rpchost= The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used. (default: localhost) --bitcoind.rpchost= The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used. (default: localhost)
@ -302,8 +302,8 @@ On FreeBSD, use gmake instead of make.
To install btcd, run the following commands: To install btcd, run the following commands:
Install **btcd**: Install **btcd**:
``` ```shell
make btcd make btcd
``` ```
Alternatively, you can install [`btcd` directly from its Alternatively, you can install [`btcd` directly from its
@ -313,8 +313,8 @@ repo](https://github.com/btcsuite/btcd).
Running the following command will create `rpc.cert` and default `btcd.conf`. Running the following command will create `rpc.cert` and default `btcd.conf`.
``` ```shell
btcd --testnet --rpcuser=REPLACEME --rpcpass=REPLACEME btcd --testnet --rpcuser=REPLACEME --rpcpass=REPLACEME
``` ```
If you want to use `lnd` on testnet, `btcd` needs to first fully sync the If you want to use `lnd` on testnet, `btcd` needs to first fully sync the
testnet blockchain. Depending on your hardware, this may take up to a few testnet blockchain. Depending on your hardware, this may take up to a few
@ -326,8 +326,8 @@ directly, rather than scanning blocks or BIP 158 filters for relevant items.
While `btcd` is syncing you can check on its progress using btcd's `getinfo` While `btcd` is syncing you can check on its progress using btcd's `getinfo`
RPC command: RPC command:
``` ```shell
btcctl --testnet --rpcuser=REPLACEME --rpcpass=REPLACEME getinfo btcctl --testnet --rpcuser=REPLACEME --rpcpass=REPLACEME getinfo
{ {
"version": 120000, "version": 120000,
"protocolversion": 70002, "protocolversion": 70002,
@ -346,8 +346,8 @@ Additionally, you can monitor btcd's logs to track its syncing progress in real
time. time.
You can test your `btcd` node's connectivity using the `getpeerinfo` command: You can test your `btcd` node's connectivity using the `getpeerinfo` command:
``` ```shell
btcctl --testnet --rpcuser=REPLACEME --rpcpass=REPLACEME getpeerinfo | more btcctl --testnet --rpcuser=REPLACEME --rpcpass=REPLACEME getpeerinfo | more
``` ```
### Running lnd using the btcd backend ### Running lnd using the btcd backend
@ -356,8 +356,9 @@ If you are on testnet, run this command after `btcd` has finished syncing.
Otherwise, replace `--bitcoin.testnet` with `--bitcoin.simnet`. If you are Otherwise, replace `--bitcoin.testnet` with `--bitcoin.simnet`. If you are
installing `lnd` in preparation for the installing `lnd` in preparation for the
[tutorial](https://dev.lightning.community/tutorial), you may skip this step. [tutorial](https://dev.lightning.community/tutorial), you may skip this step.
``` ```shell
lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug --btcd.rpcuser=kek --btcd.rpcpass=kek --externalip=X.X.X.X ⛰ lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug \
--btcd.rpcuser=kek --btcd.rpcpass=kek --externalip=X.X.X.X
``` ```
## Using Neutrino ## Using Neutrino
@ -371,8 +372,9 @@ mode. A public instance of such a node can be found at
To run lnd in neutrino mode, run `lnd` with the following arguments, (swapping To run lnd in neutrino mode, run `lnd` with the following arguments, (swapping
in `--bitcoin.simnet` if needed), and also your own `btcd` node if available: in `--bitcoin.simnet` if needed), and also your own `btcd` node if available:
``` ```shell
lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug --bitcoin.node=neutrino --neutrino.connect=faucet.lightning.community ⛰ lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug \
--bitcoin.node=neutrino --neutrino.connect=faucet.lightning.community
``` ```
@ -407,7 +409,7 @@ the following:
the testnet chain (alternatively, use `--bitcoind.regtest` instead). the testnet chain (alternatively, use `--bitcoind.regtest` instead).
Here's a sample `bitcoin.conf` for use with lnd: Here's a sample `bitcoin.conf` for use with lnd:
``` ```text
testnet=1 testnet=1
server=1 server=1
daemon=1 daemon=1
@ -421,8 +423,13 @@ updated with the latest blocks on testnet, run the command below to launch
`lnd.conf` to save these options, more info on that is described further `lnd.conf` to save these options, more info on that is described further
below): below):
``` ```shell
lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug --bitcoin.node=bitcoind --bitcoind.rpcuser=REPLACEME --bitcoind.rpcpass=REPLACEME --bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332 --bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333 --externalip=X.X.X.X ⛰ lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug \
--bitcoin.node=bitcoind --bitcoind.rpcuser=REPLACEME \
--bitcoind.rpcpass=REPLACEME \
--bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332 \
--bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333 \
--externalip=X.X.X.X
``` ```
*NOTE:* *NOTE:*
@ -457,8 +464,8 @@ lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug --bitcoin.node=bitcoin
# Creating a wallet # Creating a wallet
If `lnd` is being run for the first time, create a new wallet with: If `lnd` is being run for the first time, create a new wallet with:
``` ```shell
lncli create lncli create
``` ```
This will prompt for a wallet password, and optionally a cipher seed This will prompt for a wallet password, and optionally a cipher seed
passphrase. passphrase.
@ -467,6 +474,8 @@ passphrase.
recover the wallet in case of data loss. The user should write this down and recover the wallet in case of data loss. The user should write this down and
keep in a safe place. keep in a safe place.
More [information about managing wallets can be found in the wallet management
document](wallet.md).
# Macaroons # Macaroons
@ -528,7 +537,7 @@ at the command line, you can create an `lnd.conf`.
`~/.lnd/lnd.conf` `~/.lnd/lnd.conf`
Here's a sample `lnd.conf` for `btcd` to get you started: Here's a sample `lnd.conf` for `btcd` to get you started:
``` ```text
[Application Options] [Application Options]
debuglevel=trace debuglevel=trace
maxpendingchannels=10 maxpendingchannels=10

191
docs/wallet.md Normal file

@ -0,0 +1,191 @@
# Wallet management
The wallet in the context of `lnd` is a database file (located in the data
directory, for example `~/.lnd/data/chain/bitcoin/mainnet/wallet.db` on Linux)
that contains all addresses and private keys for the on-chain **and** off-chain
(LN) funds.
The wallet is independent of the chain backend that is used (`bitcoind`, `btcd`
or `neutrino`) and must therefore be created as the first step after starting
up a fresh `lnd` node.
To protect the sensitive content of the wallet, the database is encrypted with
a password chosen by the user when creating the wallet (simply called "wallet
password"). `lnd` will not store that password anywhere by itself (as that would
defeat the purpose of the password) so every time `lnd` is restarted, its wallet
needs to be unlocked with that password. This can either be done [manually
through the command line](#unlocking-a-wallet) or (starting with `lnd` version
`v0.13.0-beta`) [automatically from a file](#auto-unlocking-a-wallet).
## Creating a wallet
If `lnd` is being run for the first time, create a new wallet with:
```shell
⛰ lncli create
```
This will prompt for a wallet password, and optionally a cipher seed
passphrase.
`lnd` will then print a 24 word cipher seed mnemonic, which can be used to
recover the wallet in case of data loss. The user should write this down and
keep in a safe place.
In case a node needs to be recovered from an existing seed, this can also be
done through the `create` command. Please refer to the
[recovery guide](recovery.md) for more information about recovering a node.
## Unlocking a wallet
Every time `lnd` starts up fresh (e.g. after a system restart or a version
upgrade) the user-chosen wallet password needs to be entered to unlock (decrypt)
the wallet database.
This will be indicated in `lnd`'s log with a message like this:
```text
2021-05-06 11:36:11.445 [INF] LTND: Waiting for wallet encryption password. Use `lncli create` to create a wallet, `lncli unlock` to unlock an existing wallet, or `lncli changepassword` to change the password of an existing wallet and unlock it.
```
Unlocking the password manually is as simple as running the command
```shell
⛰ lncli unlock
```
and then typing the wallet password.
## Auto-unlocking a wallet
In some situations (for example automated, cluster based setups) it can be
impractical to manually unlock the wallet every time `lnd` is restarted.
In `lnd` version `v0.13.0-beta` and later there is a configuration option to
tell the wallet to auto-unlock itself by reading the password from a file. This
can only be activated _after_ the wallet was created manually.
### Very basic example (not very secure)
This example only tries to give a basic, minimal example on how to use the
auto-unlock feature. Storing a password in a file on the same disk as the wallet
database is not in itself more secure than leaving the database unencrypted in
the first place. This example might be useful in a containerized environment
though where the secrets are mounted to a file anyway.
- Start `lnd` without the flag:
```shell
⛰ lnd --bitcoin.active --bitcoin.xxxx .....
```
- Create the wallet and write down the seed in a safe place:
```shell
⛰ lncli create
```
- Stop `lnd` again:
```shell
⛰ lncli stop
```
- Write the password to a file:
```shell
⛰ echo 'my-$up3r-Secret-Passw0rd' > /some/safe/location/password.txt
```
- Make sure the password file can only be read by our user:
```shell
⛰ chmod 0400 /some/safe/location/password.txt
```
- Start `lnd` with the auto-unlock flag:
```shell
⛰ lnd --bitcoin.active --bitcoin.xxxx ..... \
--wallet-unlock-password-file=/some/safe/location/password.txt
```
As with every command line flag, the `wallet-unlock-password-file` option can
also be added to `lnd`'s configuration file, for example:
```text
[Application Options]
debuglevel=debug
wallet-unlock-password-file=/some/safe/location/password.txt
[Bitcoin]
bitcoin.active=1
...
```
### More secure example with password manager and using a named pipe
This example is a bit more involved and requires the use of a password manager
of some sort. It will also only work on Unix like file systems that support
named pipes.
We will use the password manager [`pass`](https://www.passwordstore.org/) as an
example here but it should work similarly with other password managers.
- Start `lnd` without the flag:
```shell
⛰ lnd --bitcoin.active --bitcoin.xxxx .....
```
- Create the wallet and write down the seed in a safe place:
```shell
⛰ lncli create
```
- Stop `lnd` again:
```shell
⛰ lncli stop
```
- Store the password in `pass`:
```shell
⛰ pass insert lnd/my-wallet-password
```
- Create a startup script for starting `lnd`, for example `run-lnd.sh`:
```shell
#!/bin/bash
# Create a named pipe. As the name suggests, this is a FIFO (first in first
# out) pipe. Everything sent in can be read out again without the content
# actually being written to a disk.
mkfifo /tmp/wallet-password-pipe
# Read the password from the manager and attempt to write it to the pipe. Any
# write to a pipe will only be accepted once there is a process that reads
# from the pipe at the same time. That's why we need to run this process in
# the background (the ampersand & at the end) because it would block our
# script from continuing otherwise.
pass lnd/my-wallet-password > /tmp/wallet-password-pipe &
# Now we can start lnd.
lnd --bitcoin.active --bitcoin.xxxx ..... \
--wallet-unlock-password-file=/tmp/wallet-password-pipe
```
- Run the startup script instead of running `lnd` directly.
```shell
⛰ ./run-lnd.sh
```
## Changing the password
Changing the wallet password is possible but only while the wallet is locked.
So after restarting `lnd`, instead of using the `unlock` command, the
`changepassword` command can be used:
```shell
⛰ lncli changepassword
```
This will ask for the old/existing password and a new one. If successful, the
database is re-encrypted with the new password and then the wallet is also
unlocked in the process.
## DO NOT USE --noseedbackup on mainnet
There is a way to get rid of the need to unlock the wallet password: The
`--noseedbackup` flag.
Using that flag with **real funds (mainnet) is extremely risky for two reasons**:
1. On first startup a wallet is created automatically. The seed phrase (the 24
words needed to restore a wallet) is never shown to the user. Therefore if
the worst thing happens and the hard disk crashes or the wallet file is
deleted by accident, **THERE IS NO WAY OF GETTING THE FUNDS BACK**.
2. In addition to the seed not being known to the user, the wallet database is
also not protected. A well-known default password is chosen for the
encryption. Any user (or malware) with access to the wallet database can
steal the funds if they copy the file.
The `--noseedbackup` flag should only ever be used in a test setup, for example
on Bitcoin testnet, regtest or simnet.

66
lnd.go

@ -5,6 +5,7 @@
package lnd package lnd
import ( import (
"bytes"
"context" "context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
@ -278,7 +279,9 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error
var ( var (
walletInitParams = WalletUnlockParams{ walletInitParams = WalletUnlockParams{
MacResponseChan: make(chan []byte), // In case we do auto-unlock, we need to be able to send
// into the channel without blocking so we buffer it.
MacResponseChan: make(chan []byte, 1),
} }
privateWalletPw = lnwallet.DefaultPrivatePassphrase privateWalletPw = lnwallet.DefaultPrivatePassphrase
publicWalletPw = lnwallet.DefaultPublicPassphrase publicWalletPw = lnwallet.DefaultPublicPassphrase
@ -475,10 +478,63 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error
interceptorChain.SetWalletLocked() interceptorChain.SetWalletLocked()
} }
// We wait until the user provides a password over RPC. In case lnd is // If we've started in auto unlock mode, then a wallet _must_ already
// started with the --noseedbackup flag, we use the default password // exist because we never want to enable the RPC unlocker in that case.
// for wallet encryption. if cfg.WalletUnlockPasswordFile != "" && !walletExists {
if !cfg.NoSeedBackup { return fmt.Errorf("wallet unlock password file was specified " +
"but wallet does not exist; initialize the wallet " +
"before using auto unlocking")
}
// What wallet mode are we running in? We've already made sure the no
// seed backup and auto unlock aren't both set during config parsing.
switch {
// No seed backup means we're also using the default password.
case cfg.NoSeedBackup:
// We continue normally, the default password has already been
// set above.
// A password for unlocking is provided in a file.
case cfg.WalletUnlockPasswordFile != "":
ltndLog.Infof("Attempting automatic wallet unlock with " +
"password provided in file")
pwBytes, err := ioutil.ReadFile(cfg.WalletUnlockPasswordFile)
if err != nil {
return fmt.Errorf("error reading password from file "+
"%s: %v", cfg.WalletUnlockPasswordFile, err)
}
// Remove any newlines at the end of the file. The lndinit tool
// won't ever write a newline but maybe the file was provisioned
// by another process or user.
pwBytes = bytes.TrimRight(pwBytes, "\r\n")
// We have the password now, we can ask the unlocker service to
// do the unlock for us.
unlockedWallet, unloadWalletFn, err := pwService.LoadAndUnlock(
pwBytes, 0,
)
if err != nil {
return fmt.Errorf("error unlocking wallet with "+
"password from file: %v", err)
}
defer func() {
if err := unloadWalletFn(); err != nil {
ltndLog.Errorf("Could not unload wallet: %v",
err)
}
}()
privateWalletPw = pwBytes
publicWalletPw = pwBytes
walletInitParams.Wallet = unlockedWallet
walletInitParams.UnloadWallet = unloadWalletFn
// If none of the automatic startup options are selected, we fall back
// to the default behavior of waiting for the wallet creation/unlocking
// over RPC.
default:
params, err := waitForWalletPassword( params, err := waitForWalletPassword(
cfg, pwService, []btcwallet.LoaderOption{loaderOpt}, cfg, pwService, []btcwallet.LoaderOption{loaderOpt},
interceptor.ShutdownChannel(), interceptor.ShutdownChannel(),

@ -254,6 +254,11 @@
; BE USED ON MAINNET. ; BE USED ON MAINNET.
; noseedbackup=true ; noseedbackup=true
; The full path to a file (or pipe/device) that contains the password for
; unlocking the wallet; if set, no unlocking through RPC is possible and lnd
; will exit if no wallet exists or the password is incorrect
; wallet-unlock-password-file=/tmp/example.password
; Removes all transaction history from the on-chain wallet on startup, forcing a ; Removes all transaction history from the on-chain wallet on startup, forcing a
; full chain rescan starting at the wallet's birthday. Implements the same ; full chain rescan starting at the wallet's birthday. Implements the same
; functionality as btcwallet's dropwtxmgr command. Should be set to false after ; functionality as btcwallet's dropwtxmgr command. Should be set to false after

@ -401,29 +401,27 @@ func (u *UnlockerService) InitWallet(ctx context.Context,
} }
} }
// UnlockWallet sends the password provided by the incoming UnlockWalletRequest // LoadAndUnlock creates a loader for the wallet and tries to unlock the wallet
// over the UnlockMsgs channel in case it successfully decrypts an existing // with the given password and recovery window. If the drop wallet transactions
// wallet found in the chain's wallet database directory. // flag is set, the history state drop is performed before unlocking the wallet
func (u *UnlockerService) UnlockWallet(ctx context.Context, // yet again.
in *lnrpc.UnlockWalletRequest) (*lnrpc.UnlockWalletResponse, error) { func (u *UnlockerService) LoadAndUnlock(password []byte,
recoveryWindow uint32) (*wallet.Wallet, func() error, error) {
password := in.WalletPassword
recoveryWindow := uint32(in.RecoveryWindow)
loader, err := u.newLoader(recoveryWindow) loader, err := u.newLoader(recoveryWindow)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
// Check if wallet already exists. // Check if wallet already exists.
walletExists, err := loader.WalletExists() walletExists, err := loader.WalletExists()
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
if !walletExists { if !walletExists {
// Cannot unlock a wallet that does not exist! // Cannot unlock a wallet that does not exist!
return nil, fmt.Errorf("wallet not found") return nil, nil, fmt.Errorf("wallet not found")
} }
// Try opening the existing wallet with the provided password. // Try opening the existing wallet with the provided password.
@ -431,7 +429,7 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context,
if err != nil { if err != nil {
// Could not open wallet, most likely this means that provided // Could not open wallet, most likely this means that provided
// password was incorrect. // password was incorrect.
return nil, err return nil, nil, err
} }
// The user requested to drop their whole wallet transaction state to // The user requested to drop their whole wallet transaction state to
@ -447,7 +445,7 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context,
// wallet. If unloading fails, that error is probably more // wallet. If unloading fails, that error is probably more
// important to be returned to the user anyway. // important to be returned to the user anyway.
if err := loader.UnloadWallet(); err != nil { if err := loader.UnloadWallet(); err != nil {
return nil, fmt.Errorf("could not unload "+ return nil, nil, fmt.Errorf("could not unload "+
"wallet (tx history drop err: %v): %v", dropErr, "wallet (tx history drop err: %v): %v", dropErr,
err) err)
} }
@ -455,23 +453,42 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context,
// If dropping failed but unloading didn't, we'll still abort // If dropping failed but unloading didn't, we'll still abort
// and inform the user. // and inform the user.
if dropErr != nil { if dropErr != nil {
return nil, dropErr return nil, nil, dropErr
} }
// All looks good, let's now open the wallet again. // All looks good, let's now open the wallet again.
unlockedWallet, err = loader.OpenExistingWallet(password, false) unlockedWallet, err = loader.OpenExistingWallet(password, false)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
} }
return unlockedWallet, loader.UnloadWallet, nil
}
// UnlockWallet sends the password provided by the incoming UnlockWalletRequest
// over the UnlockMsgs channel in case it successfully decrypts an existing
// wallet found in the chain's wallet database directory.
func (u *UnlockerService) UnlockWallet(ctx context.Context,
in *lnrpc.UnlockWalletRequest) (*lnrpc.UnlockWalletResponse, error) {
password := in.WalletPassword
recoveryWindow := uint32(in.RecoveryWindow)
unlockedWallet, unloadFn, err := u.LoadAndUnlock(
password, recoveryWindow,
)
if err != nil {
return nil, err
}
// We successfully opened the wallet and pass the instance back to // We successfully opened the wallet and pass the instance back to
// avoid it needing to be unlocked again. // avoid it needing to be unlocked again.
walletUnlockMsg := &WalletUnlockMsg{ walletUnlockMsg := &WalletUnlockMsg{
Passphrase: password, Passphrase: password,
RecoveryWindow: recoveryWindow, RecoveryWindow: recoveryWindow,
Wallet: unlockedWallet, Wallet: unlockedWallet,
UnloadWallet: loader.UnloadWallet, UnloadWallet: unloadFn,
StatelessInit: in.StatelessInit, StatelessInit: in.StatelessInit,
} }