diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -16,15 +16,18 @@ Configuration ------------- +Wallets created or loaded in the GUI will now be automatically loaded on +startup, so they don't need to be manually reloaded next time Bitcoin is +started. The list of wallets to load on startup is stored in +`\/settings.json` and augments any command line or `bitcoin.conf` +`-wallet=` settings that specify more wallets to load. Wallets that are +unloaded in the GUI get removed from the settings list so they won't load again +automatically next startup. + The `createwallet`, `loadwallet`, and `unloadwallet` RPCs now accept -`load_on_startup` options that modify bitcoin's dynamic configuration in -`\/settings.json`, and can add or remove a wallet from the list of -wallets automatically loaded at startup. Unless these options are explicitly -set to true or false, the load on startup wallet list is not modified, so this -change is backwards compatible. - -In the future, the GUI will start updating the same startup wallet list as the -RPCs to automatically reopen wallets previously opened in the GUI. +`load_on_startup` options to modify the settings list. Unless these options are +explicitly set to true or false, the list is not modified, so the RPC methods +remain backwards compatible. RPCs ---- diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -409,7 +409,9 @@ Amount getDefaultMaxTxFee() override { return m_wallet->m_default_max_tx_fee; } - void remove() override { RemoveWallet(m_wallet); } + void remove() override { + RemoveWallet(m_wallet, false /* load_on_start */); + } bool isLegacy() override { return m_wallet->IsLegacy(); } std::unique_ptr handleUnload(UnloadFn fn) override { return MakeHandler(m_wallet->NotifyUnload.connect(fn)); @@ -511,16 +513,17 @@ WalletCreationStatus &status, bilingual_str &error, std::vector &warnings) override { std::shared_ptr wallet; - status = CreateWallet(*m_context.chain, passphrase, - wallet_creation_flags, name, error, warnings, - wallet); + status = CreateWallet( + *m_context.chain, passphrase, wallet_creation_flags, name, + true /* load_on_start */, error, warnings, wallet); return MakeWallet(std::move(wallet)); } std::unique_ptr loadWallet(const std::string &name, bilingual_str &error, std::vector &warnings) override { return MakeWallet(LoadWallet(*m_context.chain, WalletLocation(name), - error, warnings)); + true /* load_on_start */, error, + warnings)); } std::string getWalletDir() override { return GetWalletDir().string(); } std::vector listWalletDir() override { diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp --- a/src/qt/test/addressbooktests.cpp +++ b/src/qt/test/addressbooktests.cpp @@ -122,7 +122,7 @@ AddWallet(wallet); WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get()); - RemoveWallet(wallet); + RemoveWallet(wallet, std::nullopt); EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress); editAddressDialog.setModel(walletModel.getAddressTableModel()); diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -157,7 +157,7 @@ AddWallet(wallet); WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get()); - RemoveWallet(wallet); + RemoveWallet(wallet, std::nullopt); SendCoinsDialog sendCoinsDialog(platformStyle.get(), &walletModel); { diff --git a/src/wallet/load.h b/src/wallet/load.h --- a/src/wallet/load.h +++ b/src/wallet/load.h @@ -37,12 +37,4 @@ //! Close all wallets. void UnloadWallets(); -//! Add wallet name to persistent configuration so it will be loaded on startup. -bool AddWalletSetting(interfaces::Chain &chain, const std::string &wallet_name); - -//! Remove wallet name from persistent configuration so it will not be loaded on -//! startup. -bool RemoveWalletSetting(interfaces::Chain &chain, - const std::string &wallet_name); - #endif // BITCOIN_WALLET_LOAD_H diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp --- a/src/wallet/load.cpp +++ b/src/wallet/load.cpp @@ -140,40 +140,8 @@ while (!wallets.empty()) { auto wallet = wallets.back(); wallets.pop_back(); - RemoveWallet(wallet); + std::vector warnings; + RemoveWallet(wallet, std::nullopt, warnings); UnloadWallet(std::move(wallet)); } } - -bool AddWalletSetting(interfaces::Chain &chain, - const std::string &wallet_name) { - util::SettingsValue setting_value = chain.getRwSetting("wallet"); - if (!setting_value.isArray()) { - setting_value.setArray(); - } - for (const util::SettingsValue &value : setting_value.getValues()) { - if (value.isStr() && value.get_str() == wallet_name) { - return true; - } - } - setting_value.push_back(wallet_name); - return chain.updateRwSetting("wallet", setting_value); -} - -bool RemoveWalletSetting(interfaces::Chain &chain, - const std::string &wallet_name) { - util::SettingsValue setting_value = chain.getRwSetting("wallet"); - if (!setting_value.isArray()) { - return true; - } - util::SettingsValue new_value(util::SettingsValue::VARR); - for (const util::SettingsValue &value : setting_value.getValues()) { - if (!value.isStr() || value.get_str() != wallet_name) { - new_value.push_back(value); - } - } - if (new_value.size() == setting_value.size()) { - return true; - } - return chain.updateRwSetting("wallet", new_value); -} diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -203,22 +203,6 @@ return label; } -static void UpdateWalletSetting(interfaces::Chain &chain, - const std::string &wallet_name, - const UniValue &load_on_startup, - std::vector &warnings) { - if (load_on_startup.isTrue() && !AddWalletSetting(chain, wallet_name)) { - warnings.emplace_back( - Untranslated("Wallet load on startup setting could not be updated, " - "so wallet may not be loaded next node startup.")); - } else if (load_on_startup.isFalse() && - !RemoveWalletSetting(chain, wallet_name)) { - warnings.emplace_back( - Untranslated("Wallet load on startup setting could not be updated, " - "so wallet may still be loaded next node startup.")); - } -} - static UniValue getnewaddress(const Config &config, const JSONRPCRequest &request) { RPCHelpMan{ @@ -3161,15 +3145,16 @@ bilingual_str error; std::vector warnings; + std::optional load_on_start = + request.params[1].isNull() + ? std::nullopt + : std::make_optional(request.params[1].get_bool()); std::shared_ptr const wallet = - LoadWallet(*context.chain, location, error, warnings); + LoadWallet(*context.chain, location, load_on_start, error, warnings); if (!wallet) { throw JSONRPCError(RPC_WALLET_ERROR, error.original); } - UpdateWalletSetting(*context.chain, location.GetName(), request.params[1], - warnings); - UniValue obj(UniValue::VOBJ); obj.pushKV("name", wallet->GetName()); obj.pushKV("warning", Join(warnings, Untranslated("\n")).original); @@ -3334,9 +3319,13 @@ bilingual_str error; std::shared_ptr wallet; - WalletCreationStatus status = - CreateWallet(*context.chain, passphrase, flags, - request.params[0].get_str(), error, warnings, wallet); + std::optional load_on_start = + request.params[6].isNull() + ? std::nullopt + : std::make_optional(request.params[6].get_bool()); + WalletCreationStatus status = CreateWallet( + *context.chain, passphrase, flags, request.params[0].get_str(), + load_on_start, error, warnings, wallet); switch (status) { case WalletCreationStatus::CREATION_FAILED: throw JSONRPCError(RPC_WALLET_ERROR, error.original); @@ -3347,9 +3336,6 @@ // no default case, so the compiler can warn about missing cases } - UpdateWalletSetting(*context.chain, request.params[0].get_str(), - request.params[6], warnings); - UniValue obj(UniValue::VOBJ); obj.pushKV("name", wallet->GetName()); obj.pushKV("warning", Join(warnings, Untranslated("\n")).original); @@ -3404,15 +3390,16 @@ // Release the "main" shared pointer and prevent further notifications. // Note that any attempt to load the same wallet would fail until the wallet // is destroyed (see CheckUniqueFileid). - if (!RemoveWallet(wallet)) { + std::vector warnings; + std::optional load_on_start = + request.params[1].isNull() + ? std::nullopt + : std::make_optional(request.params[1].get_bool()); + if (!RemoveWallet(wallet, load_on_start, warnings)) { throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded"); } - interfaces::Chain &chain = wallet->chain(); - std::vector warnings; - UnloadWallet(std::move(wallet)); - UpdateWalletSetting(chain, wallet_name, request.params[1], warnings); UniValue result(UniValue::VOBJ); result.pushKV("warning", Join(warnings, Untranslated("\n")).original); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -254,7 +254,7 @@ "rescanning the relevant blocks (see -reindex and " "-rescan options).\"}},{\"success\":true}]", 0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW)); - RemoveWallet(wallet); + RemoveWallet(wallet, std::nullopt); } } @@ -311,7 +311,7 @@ request.params.setArray(); request.params.push_back(backup_file); ::dumpwallet().HandleRequest(GetConfig(), request); - RemoveWallet(wallet); + RemoveWallet(wallet, std::nullopt); } // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME @@ -330,7 +330,7 @@ wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); ::importwallet().HandleRequest(GetConfig(), request); - RemoveWallet(wallet); + RemoveWallet(wallet, std::nullopt); BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U); BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -55,23 +55,28 @@ void UnloadWallet(std::shared_ptr &&wallet); bool AddWallet(const std::shared_ptr &wallet); -bool RemoveWallet(const std::shared_ptr &wallet); +bool RemoveWallet(const std::shared_ptr &wallet, + std::optional load_on_start, + std::vector &warnings); +bool RemoveWallet(const std::shared_ptr &wallet, + std::optional load_on_start); std::vector> GetWallets(); std::shared_ptr GetWallet(const std::string &name); std::shared_ptr LoadWallet(interfaces::Chain &chain, const WalletLocation &location, + std::optional load_on_start, bilingual_str &error, std::vector &warnings); std::unique_ptr HandleLoadWallet(LoadWalletFn load_wallet); enum class WalletCreationStatus { SUCCESS, CREATION_FAILED, ENCRYPTION_FAILED }; -WalletCreationStatus CreateWallet(interfaces::Chain &chain, - const SecureString &passphrase, - uint64_t wallet_creation_flags, - const std::string &name, bilingual_str &error, - std::vector &warnings, - std::shared_ptr &result); +WalletCreationStatus +CreateWallet(interfaces::Chain &chain, const SecureString &passphrase, + uint64_t wallet_creation_flags, const std::string &name, + std::optional load_on_start, bilingual_str &error, + std::vector &warnings, + std::shared_ptr &result); //! -paytxfee default constexpr Amount DEFAULT_PAY_TX_FEE = Amount::zero(); //! -fallbackfee default @@ -1631,4 +1636,12 @@ const std::vector &txouts, bool use_max_sig = false); +//! Add wallet name to persistent configuration so it will be loaded on startup. +bool AddWalletSetting(interfaces::Chain &chain, const std::string &wallet_name); + +//! Remove wallet name from persistent configuration so it will not be loaded on +//! startup. +bool RemoveWalletSetting(interfaces::Chain &chain, + const std::string &wallet_name); + #endif // BITCOIN_WALLET_WALLET_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -24,6 +24,7 @@ #include