gui: Add restore_and_migrate function to restore then migrate a wallet
What changed, and why it matters
This commit adds a new GUI feature that lets a user restore a wallet backup file and then automatically convert it from the older 'legacy' wallet format to the newer 'descriptor' wallet format. It shows confirmation and progress dialogs, copies the backup into the normal wallets folder without changing the original file, and then runs the existing migration logic. There is no obvious security bug in the change itself; it is a user-facing convenience feature built on top of existing wallet restore and migration code.
No immediate security action required. As with any wallet-restore feature, users should verify they are restoring from a trusted backup and understand that migration creates new descriptor wallets and a legacy backup. Reviewers may want to confirm that the supplied path and wallet_name are validated by the underlying restoreWallet implementation, since this GUI code does not perform additional sanitization.
Security signals we found
New GUI wallet restore+migration flow added
Uses existing restoreWallet and do_migrate backend functions
User confirmation dialog present before action
Original backup file is explicitly not modified
No input validation or path sanitization visible in this diff
No evidence of vulnerability, exploit, or security fix in the diff
Evidence from the diff
The patch introduces MigrateWalletActivity::restore_and_migrate() in the Bitcoin Core Qt GUI. It prompts the user with a QMessageBox, displays a progress dialog, calls node().walletLoader().restoreWallet(path, wallet_name, m_warning_message, /load_after_restore=/false), and on success schedules do_migrate(wallet_name). The header adds the corresponding public method declaration. The change is purely additive (+40 lines) and relies on pre-existing backend APIs for restore and migration.
Changed components
src/qt/walletcontroller.cppsrc/qt/walletcontroller.hBitcoin Core Qt GUI wallet migration featureInspect captured patch +40 / −0
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 51e3b378..d1b1d908 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -489,6 +489,45 @@ void MigrateWalletActivity::migrate(const std::string& name)
do_migrate(name);
}
+void MigrateWalletActivity::restore_and_migrate(const fs::path& path, const std::string& wallet_name)
+{
+ // Warn the user about migration
+ QMessageBox box(m_parent_widget);
+ box.setWindowTitle(tr("Restore and Migrate wallet"));
+ box.setText(tr("Are you sure you wish to restore the wallet file <i>%1</i> to <i>%2</i> and migrate it?").arg(GUIUtil::HtmlEscape(fs::PathToString(path)), GUIUtil::HtmlEscape(GUIUtil::WalletDisplayName(wallet_name))));
+ box.setInformativeText(tr("Restoring the wallet will copy the backup file to the wallets directory and place it in the standard "
+ "wallet directory layout. The original file will not be modified.\n\n"
+ "Migrating the wallet will convert the restored wallet to one or more descriptor wallets. A new wallet backup will need to be made.\n"
+ "If this wallet contains any watchonly scripts, a new wallet will be created which contains those watchonly scripts.\n"
+ "If this wallet contains any solvable but not watched scripts, a different and new wallet will be created which contains those scripts.\n\n"
+ "The migration process will create a backup of the wallet before migrating. This backup file will be named "
+ "<wallet name>-<timestamp>.legacy.bak and can be found in the directory for this wallet. In the event of "
+ "an incorrect migration, the backup can be restored with the \"Restore Wallet\" functionality."));
+ box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
+ box.setDefaultButton(QMessageBox::Yes);
+ if (box.exec() != QMessageBox::Yes) return;
+
+ showProgressDialog(
+ //: Title of progress window which is displayed when wallets are being restored.
+ tr("Restore Wallet"),
+ /*: Descriptive text of the restore wallets progress window which indicates to
+ the user that wallets are currently being restored.*/
+ tr("Restoring Wallet <b>%1</b>…").arg(GUIUtil::HtmlEscape(GUIUtil::WalletDisplayName(wallet_name))));
+
+ QTimer::singleShot(0, worker(), [this, path, wallet_name] {
+ auto res{node().walletLoader().restoreWallet(path, wallet_name, m_warning_message, /*load_after_restore=*/false)};
+
+ if (!res) {
+ m_error_message = util::ErrorString(res);
+ QTimer::singleShot(0, this, &MigrateWalletActivity::finish);
+ return;
+ }
+ QTimer::singleShot(0, this, [this, wallet_name] {
+ do_migrate(wallet_name);
+ });
+ });
+}
+
void MigrateWalletActivity::finish()
{
if (!m_error_message.empty()) {
diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h
index a122c2f7..e24970db 100644
--- a/src/qt/walletcontroller.h
+++ b/src/qt/walletcontroller.h
@@ -187,6 +187,7 @@ class MigrateWalletActivity : public WalletControllerActivity
public:
MigrateWalletActivity(WalletController* wallet_controller, QWidget* parent) : WalletControllerActivity(wallet_controller, parent) {}
+ void restore_and_migrate(const fs::path& path, const std::string& wallet_name);
void migrate(const std::string& path);
Q_SIGNALS:
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.