From 8224de599b06c70a8198e41a9f118cd365173a1f Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 16 Apr 2021 14:28:47 +0200 Subject: [PATCH 1/3] walletunlocker: extract LoadAndUnlock As a preparation to do auto-unlock without the unlock RPC, we extract the relevant part into its own method in the unlocker service. --- walletunlocker/service.go | 49 ++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/walletunlocker/service.go b/walletunlocker/service.go index c624296f..be9ae08b 100644 --- a/walletunlocker/service.go +++ b/walletunlocker/service.go @@ -401,29 +401,27 @@ func (u *UnlockerService) InitWallet(ctx context.Context, } } -// 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) +// LoadAndUnlock creates a loader for the wallet and tries to unlock the wallet +// with the given password and recovery window. If the drop wallet transactions +// flag is set, the history state drop is performed before unlocking the wallet +// yet again. +func (u *UnlockerService) LoadAndUnlock(password []byte, + recoveryWindow uint32) (*wallet.Wallet, func() error, error) { loader, err := u.newLoader(recoveryWindow) if err != nil { - return nil, err + return nil, nil, err } // Check if wallet already exists. walletExists, err := loader.WalletExists() if err != nil { - return nil, err + return nil, nil, err } if !walletExists { // 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. @@ -431,7 +429,7 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context, if err != nil { // Could not open wallet, most likely this means that provided // password was incorrect. - return nil, err + return nil, nil, err } // 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 // important to be returned to the user anyway. 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, err) } @@ -455,23 +453,42 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context, // If dropping failed but unloading didn't, we'll still abort // and inform the user. if dropErr != nil { - return nil, dropErr + return nil, nil, dropErr } // All looks good, let's now open the wallet again. unlockedWallet, err = loader.OpenExistingWallet(password, false) 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 // avoid it needing to be unlocked again. walletUnlockMsg := &WalletUnlockMsg{ Passphrase: password, RecoveryWindow: recoveryWindow, Wallet: unlockedWallet, - UnloadWallet: loader.UnloadWallet, + UnloadWallet: unloadFn, StatelessInit: in.StatelessInit, } From 571d00b32c699371f4e630dfe1f27878daee3045 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 16 Apr 2021 14:28:49 +0200 Subject: [PATCH 2/3] config+lnd: add wallet-unlock-password-file option In automated or unattended setups such as cluster/container environments, unlocking the wallet through RPC presents a set of challenges. Usually the password is present as a file somewhere in the container already anyway so we might also just read it from there. --- config.go | 20 ++++++++++++++- lnd.go | 66 +++++++++++++++++++++++++++++++++++++++++++++---- sample-lnd.conf | 5 ++++ 3 files changed, 85 insertions(+), 6 deletions(-) diff --git a/config.go b/config.go index 0a23839c..b8fd7862 100644 --- a/config.go +++ b/config.go @@ -292,7 +292,8 @@ type Config struct { 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."` @@ -687,6 +688,9 @@ func ValidateConfig(cfg Config, usageMessage string, cfg.Tor.WatchtowerKeyPath = CleanAndExpandPath(cfg.Tor.WatchtowerKeyPath) cfg.Watchtower.TowerDir = CleanAndExpandPath(cfg.Watchtower.TowerDir) cfg.BackupFilePath = CleanAndExpandPath(cfg.BackupFilePath) + cfg.WalletUnlockPasswordFile = CleanAndExpandPath( + cfg.WalletUnlockPasswordFile, + ) // Create the lnd directory and all other sub directories if they don't // already exist. This makes sure that directory trees are also created @@ -1278,6 +1282,20 @@ func ValidateConfig(cfg Config, usageMessage string, 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 // 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 diff --git a/lnd.go b/lnd.go index 7c7c80c4..81831ffd 100644 --- a/lnd.go +++ b/lnd.go @@ -5,6 +5,7 @@ package lnd import ( + "bytes" "context" "crypto/tls" "fmt" @@ -278,7 +279,9 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error var ( 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 publicWalletPw = lnwallet.DefaultPublicPassphrase @@ -475,10 +478,63 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error interceptorChain.SetWalletLocked() } - // We wait until the user provides a password over RPC. In case lnd is - // started with the --noseedbackup flag, we use the default password - // for wallet encryption. - if !cfg.NoSeedBackup { + // If we've started in auto unlock mode, then a wallet _must_ already + // exist because we never want to enable the RPC unlocker in that case. + if cfg.WalletUnlockPasswordFile != "" && !walletExists { + 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( cfg, pwService, []btcwallet.LoaderOption{loaderOpt}, interceptor.ShutdownChannel(), diff --git a/sample-lnd.conf b/sample-lnd.conf index 2d63c619..5224675b 100644 --- a/sample-lnd.conf +++ b/sample-lnd.conf @@ -254,6 +254,11 @@ ; BE USED ON MAINNET. ; 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 ; full chain rescan starting at the wallet's birthday. Implements the same ; functionality as btcwallet's dropwtxmgr command. Should be set to false after From d13fb1660861840eb76325167122b9ca464c2055 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 6 May 2021 12:19:12 +0200 Subject: [PATCH 3/3] docs: add wallet management doc, fix markup in INSTALL doc To give users an idea how the new auto-unlock flag can be used in a more safe way than just writing the password to a file, we add a new wallet management document and describe the unlock feature in detail. --- docs/INSTALL.md | 61 +++++++++------- docs/wallet.md | 191 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+), 26 deletions(-) create mode 100644 docs/wallet.md diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 6c721ef7..ecff6b04 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -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 used directly: ```shell -cd $GOPATH/src/github.com/lightningnetwork/lnd -git pull -GO111MODULE=on go install -v ./... +⛰ cd $GOPATH/src/github.com/lightningnetwork/lnd +⛰ git pull +⛰ GO111MODULE=on go install -v ./... ``` **Tests** To check that `lnd` was installed properly run the following command: -``` -make check +```shell +⛰ make check ``` 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: ## btcd Options -``` +```text 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) @@ -270,7 +270,7 @@ btcd: ``` ## Neutrino Options -``` +```text neutrino: -a, --neutrino.addpeer= Add a peer to connect with at startup --neutrino.connect= Connect only to the specified peers at startup @@ -282,7 +282,7 @@ neutrino: ``` ## Bitcoind Options -``` +```text bitcoind: --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) @@ -302,8 +302,8 @@ On FreeBSD, use gmake instead of make. To install btcd, run the following commands: Install **btcd**: -``` -make btcd +```shell +⛰ make btcd ``` 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`. -``` -btcd --testnet --rpcuser=REPLACEME --rpcpass=REPLACEME +```shell +⛰ btcd --testnet --rpcuser=REPLACEME --rpcpass=REPLACEME ``` 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 @@ -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` RPC command: -``` -btcctl --testnet --rpcuser=REPLACEME --rpcpass=REPLACEME getinfo +```shell +⛰ btcctl --testnet --rpcuser=REPLACEME --rpcpass=REPLACEME getinfo { "version": 120000, "protocolversion": 70002, @@ -346,8 +346,8 @@ Additionally, you can monitor btcd's logs to track its syncing progress in real time. You can test your `btcd` node's connectivity using the `getpeerinfo` command: -``` -btcctl --testnet --rpcuser=REPLACEME --rpcpass=REPLACEME getpeerinfo | more +```shell +⛰ btcctl --testnet --rpcuser=REPLACEME --rpcpass=REPLACEME getpeerinfo | more ``` ### 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 installing `lnd` in preparation for the [tutorial](https://dev.lightning.community/tutorial), you may skip this step. -``` -lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug --btcd.rpcuser=kek --btcd.rpcpass=kek --externalip=X.X.X.X +```shell +⛰ lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug \ + --btcd.rpcuser=kek --btcd.rpcpass=kek --externalip=X.X.X.X ``` ## 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 in `--bitcoin.simnet` if needed), and also your own `btcd` node if available: -``` -lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug --bitcoin.node=neutrino --neutrino.connect=faucet.lightning.community +```shell +⛰ 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). Here's a sample `bitcoin.conf` for use with lnd: -``` +```text testnet=1 server=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 below): -``` -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 +```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 ``` *NOTE:* @@ -457,8 +464,8 @@ lnd --bitcoin.active --bitcoin.testnet --debuglevel=debug --bitcoin.node=bitcoin # Creating a wallet If `lnd` is being run for the first time, create a new wallet with: -``` -lncli create +```shell +⛰ lncli create ``` This will prompt for a wallet password, and optionally a cipher seed passphrase. @@ -467,6 +474,8 @@ passphrase. recover the wallet in case of data loss. The user should write this down and keep in a safe place. +More [information about managing wallets can be found in the wallet management +document](wallet.md). # Macaroons @@ -528,7 +537,7 @@ at the command line, you can create an `lnd.conf`. `~/.lnd/lnd.conf` Here's a sample `lnd.conf` for `btcd` to get you started: -``` +```text [Application Options] debuglevel=trace maxpendingchannels=10 diff --git a/docs/wallet.md b/docs/wallet.md new file mode 100644 index 00000000..5d8d57dd --- /dev/null +++ b/docs/wallet.md @@ -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.