diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index 06dfd1106be..f59b532cef4 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -724,6 +724,7 @@ static RPCHelpMan migratewallet() HELP_REQUIRING_PASSPHRASE, { {"wallet_name", RPCArg::Type::STR, RPCArg::DefaultHint{"the wallet name from the RPC endpoint"}, "The name of the wallet to migrate. If provided both here and in the RPC endpoint, the two must be identical."}, + {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The wallet passphrase"}, }, RPCResult{ RPCResult::Type::OBJ, "", "", @@ -752,15 +753,14 @@ static RPCHelpMan migratewallet() wallet_name = request.params[0].get_str(); } - WalletContext& context = EnsureWalletContext(request.context); - { - std::shared_ptr wallet = GetWallet(context, wallet_name); - if (wallet && wallet->IsCrypted()) { - throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: migratewallet on encrypted wallets is currently unsupported."); - } + SecureString wallet_pass; + wallet_pass.reserve(100); + if (!request.params[1].isNull()) { + wallet_pass = std::string_view{request.params[1].get_str()}; } - util::Result res = MigrateLegacyToDescriptor(wallet_name, context); + WalletContext& context = EnsureWalletContext(request.context); + util::Result res = MigrateLegacyToDescriptor(wallet_name, wallet_pass, context); if (!res) { throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(res).original); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 52666eac66a..c999adf8caf 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3875,7 +3875,7 @@ std::optional CWallet::GetDescriptorsForLegacy(bilingual_str& err std::optional res = legacy_spkm->MigrateToDescriptor(); if (res == std::nullopt) { - error = _("Error: Unable to produce descriptors for this legacy wallet. Make sure the wallet is unlocked first"); + error = _("Error: Unable to produce descriptors for this legacy wallet. Make sure to provide the wallet's passphrase if it is encrypted."); return std::nullopt; } return res; @@ -4165,7 +4165,7 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, return true; } -util::Result MigrateLegacyToDescriptor(const std::string& wallet_name, WalletContext& context) +util::Result MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context) { MigrationResult res; bilingual_str error; @@ -4215,6 +4215,19 @@ util::Result MigrateLegacyToDescriptor(const std::string& walle { LOCK(local_wallet->cs_wallet); + // Unlock the wallet if needed + if (local_wallet->IsLocked() && !local_wallet->Unlock(passphrase)) { + if (passphrase.find('\0') == std::string::npos) { + return util::Error{Untranslated("Error: Wallet decryption failed, the wallet passphrase was not provided or was incorrect.")}; + } else { + return util::Error{Untranslated("Error: Wallet decryption failed, the wallet passphrase entered was incorrect. " + "The passphrase contains a null character (ie - a zero byte). " + "If this passphrase was set with a version of this software prior to 25.0, " + "please try again with only the characters up to — but not including — " + "the first null character.")}; + } + } + // First change to using SQLite if (!local_wallet->MigrateToSQLite(error)) return util::Error{error}; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 40720acc8e5..cab516f382e 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1007,7 +1007,7 @@ struct MigrationResult { }; //! Do all steps to migrate a legacy wallet to a descriptor wallet -util::Result MigrateLegacyToDescriptor(const std::string& wallet_name, WalletContext& context); +util::Result MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context); } // namespace wallet #endif // BITCOIN_WALLET_WALLET_H diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py index 72c5fe7b840..24373f81131 100755 --- a/test/functional/wallet_migration.py +++ b/test/functional/wallet_migration.py @@ -403,7 +403,6 @@ class WalletMigrationTest(BitcoinTestFramework): wallet.encryptwallet("pass") - assert_raises_rpc_error(-15, "Error: migratewallet on encrypted wallets is currently unsupported.", wallet.migratewallet) # TODO: Fix migratewallet so that we can actually migrate encrypted wallets def run_test(self):