From 4acd063ba6fbe32bf28babac052cbbfd1d965c56 Mon Sep 17 00:00:00 2001 From: Pol Espinasa Date: Wed, 20 May 2026 21:31:56 +0200 Subject: [PATCH] wallet: make loading the wallet after migrating optional Loading the wallet after migrating is not a necessary step. By making it optional pruned nodes can also migrate legacy wallets. Also remove the migrated wallet from the list of wallets to load on startup. --- src/wallet/wallet.cpp | 54 +++++++++++++++++++++++++++---------------- src/wallet/wallet.h | 4 ++-- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c84effe7237..e64b27a633e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -109,7 +109,14 @@ bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name) bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name) { const auto update_function = [&wallet_name](common::SettingsValue& setting_value) { - if (!setting_value.isArray()) return interfaces::SettingsAction::SKIP_WRITE; + if (!setting_value.isArray()) { + if (wallet_name.empty() && setting_value.isNull()) { + // Empty setting suppresses backwards-compatible default wallet autoload. + setting_value.setArray(); + return interfaces::SettingsAction::WRITE; + } + return interfaces::SettingsAction::SKIP_WRITE; + } common::SettingsValue new_value(common::SettingsValue::VARR); for (const auto& value : setting_value.getValues()) { if (!value.isStr() || value.get_str() != wallet_name) new_value.push_back(value); @@ -4177,7 +4184,7 @@ static std::string MigrationPrefixName(CWallet& wallet) return name.empty() ? "default_wallet" : name; } -bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, MigrationResult& res) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) +bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, MigrationResult& res, const bool load_on_startup = true) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) { AssertLockHeld(wallet.cs_wallet); @@ -4240,7 +4247,7 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, } // Add the wallet to settings - UpdateWalletSetting(*context.chain, wallet_name, /*load_on_startup=*/true, warnings); + UpdateWalletSetting(*context.chain, wallet_name, load_on_startup, warnings); } if (data->solvable_descs.size() > 0) { wallet.WalletLogPrintf("Making a new watchonly wallet containing the unwatched solvable scripts\n"); @@ -4279,7 +4286,7 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, } // Add the wallet to settings - UpdateWalletSetting(*context.chain, wallet_name, /*load_on_startup=*/true, warnings); + UpdateWalletSetting(*context.chain, wallet_name, load_on_startup, warnings); } } @@ -4294,7 +4301,7 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, }); } -util::Result MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context) +util::Result MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context, bool load_wallet) { std::vector warnings; bilingual_str error; @@ -4336,10 +4343,10 @@ util::Result MigrateLegacyToDescriptor(const std::string& walle return util::Error{Untranslated("Wallet loading failed.") + Untranslated(" ") + error}; } - return MigrateLegacyToDescriptor(std::move(local_wallet), passphrase, context); + return MigrateLegacyToDescriptor(std::move(local_wallet), passphrase, context, load_wallet); } -util::Result MigrateLegacyToDescriptor(std::shared_ptr local_wallet, const SecureString& passphrase, WalletContext& context) +util::Result MigrateLegacyToDescriptor(std::shared_ptr local_wallet, const SecureString& passphrase, WalletContext& context, bool load_wallet) { MigrationResult res; bilingual_str error; @@ -4405,7 +4412,7 @@ util::Result MigrateLegacyToDescriptor(std::shared_ptr // Do the migration of keys and scripts for non-empty wallets, and cleanup if it fails if (HasLegacyRecords(*local_wallet)) { - success = DoMigration(*local_wallet, context, error, res); + success = DoMigration(*local_wallet, context, error, res, load_wallet); // No scripts mean empty wallet after migration empty_local_wallet = local_wallet->GetAllScriptPubKeyMans().empty(); } else { @@ -4447,30 +4454,37 @@ util::Result MigrateLegacyToDescriptor(std::shared_ptr for (const auto& path_to_remove : paths_to_remove) fs::remove(path_to_remove); } - LogInfo("Loading new wallets after migration...\n"); - // Migration successful, load all the migrated wallets. + if (load_wallet) { + LogInfo("Loading new wallets after migration...\n"); + /** We only override the load_on_startup setting in case the user explicitly said + * that he does not want to load the wallet, otherwise keep the old wallet configuration */ + } else { + UpdateWalletSetting(*context.chain, wallet_name, /*load_on_startup=*/false, warnings); + } + // Migration successful, if load_wallet is set load all the migrated wallets. bool main_wallet_set{false}; for (std::shared_ptr* wallet_ptr : {&local_wallet, &res.watchonly_wallet, &res.solvables_wallet}) { if (success && *wallet_ptr) { std::shared_ptr& wallet = *wallet_ptr; - // Track db path and load wallet + // Track db path track_for_cleanup(*wallet); assert(wallet.use_count() == 1); std::string wallet_name = wallet->GetName(); wallet.reset(); - wallet = LoadWallet(context, wallet_name, /*load_on_start=*/std::nullopt, options, status, error, warnings); - if (!wallet) { - LogError("Failed to load wallet '%s' after migration. Rolling back migration to preserve consistency. " - "Error cause: %s\n", wallet_name, error.original); - success = false; - break; + if (load_wallet) { + wallet = LoadWallet(context, wallet_name, /*load_on_start=*/std::nullopt, options, status, error, warnings); + if (!wallet) { + LogError("Failed to load wallet '%s' after migration. Rolling back migration to preserve consistency. " + "Error cause: %s\n", wallet_name, error.original); + success = false; + break; + } } - - // Set the first successfully loaded wallet as the main one. + // Set the first wallet as the main one. // The loop order is intentional and must always start with the local wallet. if (!main_wallet_set) { res.wallet_name = wallet_name; - res.wallet = std::move(wallet); + if (load_wallet) res.wallet = std::move(wallet); main_wallet_set = true; } if (wallet_ptr == &res.watchonly_wallet) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index b964846f351..dfd52c1dbfe 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1143,9 +1143,9 @@ struct MigrationResult { }; //! Do all steps to migrate a legacy wallet to a descriptor wallet -[[nodiscard]] util::Result MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context); +[[nodiscard]] util::Result MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context, bool load_wallet = true); //! Requirement: The wallet provided to this function must be isolated, with no attachment to the node's context. -[[nodiscard]] util::Result MigrateLegacyToDescriptor(std::shared_ptr local_wallet, const SecureString& passphrase, WalletContext& context); +[[nodiscard]] util::Result MigrateLegacyToDescriptor(std::shared_ptr local_wallet, const SecureString& passphrase, WalletContext& context, bool load_wallet = true); //! Determine the path that the wallet is stored in util::Result GetWalletPath(const std::string& name);