Skip to content

Commit 41bf8f2

Browse files
committed
Merge bitcoin-core/gui#877: Add a menu action to restore then migrate a legacy wallet
14371fd gui: Add a menu item to restore then migrate a wallet file (Ava Chow) f11a7d2 gui: Add restore_and_migrate function to restore then migrate a wallet (Ava Chow) 16ab6df gui: Move actual migration part of migrate() to its own function (Ava Chow) 4ec2d18 wallet, interfaces, gui: Expose load_after_restore parameter (Ava Chow) Pull request description: Some users will have a backup of their legacy wallet. These cannot be restored since the "Restore Wallet" action expects to be able to load the wallet after restoring, and this fails for legacy wallets now that they are deleted. Furthermore, the "Migrate Wallet" action only allows users to migrate wallets that are in the wallets directory, so such backups cannot be migrated from the GUI. This PR resolves this issue by adding a menu item in the "Migrate Wallet" menu which allows users to select their backup file so that it will first be restored but not loaded, and then migrated. Depends on bitcoin#32620 ACKs for top commit: hebasto: ACK 14371fd. Tree-SHA512: 2b09c012f4c70d0cb283305bf3d1a18ae5a2bfb80977c91544ac1fbc29d6360df49438cfdc8f66661ddb42ddab728c8ef1f9e0d7031877fbd846f9cea957398e
2 parents 2210feb + 14371fd commit 41bf8f2

File tree

5 files changed

+93
-22
lines changed

5 files changed

+93
-22
lines changed

src/interfaces/wallet.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ class WalletLoader : public ChainClient
325325
virtual std::string getWalletDir() = 0;
326326

327327
//! Restore backup wallet
328-
virtual util::Result<std::unique_ptr<Wallet>> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, std::vector<bilingual_str>& warnings) = 0;
328+
virtual util::Result<std::unique_ptr<Wallet>> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, std::vector<bilingual_str>& warnings, bool load_after_restore) = 0;
329329

330330
//! Migrate a wallet
331331
virtual util::Result<WalletMigrationResult> migrateWallet(const std::string& name, const SecureString& passphrase) = 0;

src/qt/bitcoingui.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,32 @@ void BitcoinGUI::createActions()
485485
QAction* action = m_migrate_wallet_menu->addAction(tr("No wallets available"));
486486
action->setEnabled(false);
487487
}
488+
m_migrate_wallet_menu->addSeparator();
489+
QAction* restore_migrate_file_action = m_migrate_wallet_menu->addAction(tr("Restore and Migrate Wallet File..."));
490+
restore_migrate_file_action->setEnabled(true);
491+
492+
connect(restore_migrate_file_action, &QAction::triggered, [this] {
493+
QString name_data_file = tr("Wallet Data");
494+
QString title_windows = tr("Restore and Migrate Wallet Backup");
495+
496+
QString backup_file = GUIUtil::getOpenFileName(this, title_windows, QString(), name_data_file + QLatin1String(" (*.dat)"), nullptr);
497+
if (backup_file.isEmpty()) return;
498+
499+
bool wallet_name_ok;
500+
/*: Title of pop-up window shown when the user is attempting to
501+
restore a wallet. */
502+
QString title = tr("Restore and Migrate Wallet");
503+
//: Label of the input field where the name of the wallet is entered.
504+
QString label = tr("Wallet Name");
505+
QString wallet_name = QInputDialog::getText(this, title, label, QLineEdit::Normal, "", &wallet_name_ok);
506+
if (!wallet_name_ok || wallet_name.isEmpty()) return;
507+
508+
auto activity = new MigrateWalletActivity(m_wallet_controller, this);
509+
connect(activity, &MigrateWalletActivity::migrated, this, &BitcoinGUI::setCurrentWallet);
510+
connect(activity, &MigrateWalletActivity::migrated, rpcConsole, &RPCConsole::setCurrentWallet);
511+
auto backup_file_path = fs::PathFromString(backup_file.toStdString());
512+
activity->restore_and_migrate(backup_file_path, wallet_name.toStdString());
513+
});
488514
});
489515
connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::setPrivacy);
490516
connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::enableHistoryAction);

src/qt/walletcontroller.cpp

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ void RestoreWalletActivity::restore(const fs::path& backup_file, const std::stri
409409
tr("Restoring Wallet <b>%1</b>…").arg(name.toHtmlEscaped()));
410410

411411
QTimer::singleShot(0, worker(), [this, backup_file, wallet_name] {
412-
auto wallet{node().walletLoader().restoreWallet(backup_file, wallet_name, m_warning_message)};
412+
auto wallet{node().walletLoader().restoreWallet(backup_file, wallet_name, m_warning_message, /*load_after_restore=*/true)};
413413

414414
if (wallet) {
415415
m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(*wallet));
@@ -439,22 +439,8 @@ void RestoreWalletActivity::finish()
439439
Q_EMIT finished();
440440
}
441441

442-
void MigrateWalletActivity::migrate(const std::string& name)
442+
void MigrateWalletActivity::do_migrate(const std::string& name)
443443
{
444-
// Warn the user about migration
445-
QMessageBox box(m_parent_widget);
446-
box.setWindowTitle(tr("Migrate wallet"));
447-
box.setText(tr("Are you sure you wish to migrate the wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(GUIUtil::WalletDisplayName(name))));
448-
box.setInformativeText(tr("Migrating the wallet will convert this wallet to one or more descriptor wallets. A new wallet backup will need to be made.\n"
449-
"If this wallet contains any watchonly scripts, a new wallet will be created which contains those watchonly scripts.\n"
450-
"If this wallet contains any solvable but not watched scripts, a different and new wallet will be created which contains those scripts.\n\n"
451-
"The migration process will create a backup of the wallet before migrating. This backup file will be named "
452-
"<wallet name>-<timestamp>.legacy.bak and can be found in the directory for this wallet. In the event of "
453-
"an incorrect migration, the backup can be restored with the \"Restore Wallet\" functionality."));
454-
box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
455-
box.setDefaultButton(QMessageBox::Yes);
456-
if (box.exec() != QMessageBox::Yes) return;
457-
458444
SecureString passphrase;
459445
if (node().walletLoader().isEncrypted(name)) {
460446
// Get the passphrase for the wallet
@@ -484,6 +470,64 @@ void MigrateWalletActivity::migrate(const std::string& name)
484470
});
485471
}
486472

473+
void MigrateWalletActivity::migrate(const std::string& name)
474+
{
475+
// Warn the user about migration
476+
QMessageBox box(m_parent_widget);
477+
box.setWindowTitle(tr("Migrate wallet"));
478+
box.setText(tr("Are you sure you wish to migrate the wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(GUIUtil::WalletDisplayName(name))));
479+
box.setInformativeText(tr("Migrating the wallet will convert this wallet to one or more descriptor wallets. A new wallet backup will need to be made.\n"
480+
"If this wallet contains any watchonly scripts, a new wallet will be created which contains those watchonly scripts.\n"
481+
"If this wallet contains any solvable but not watched scripts, a different and new wallet will be created which contains those scripts.\n\n"
482+
"The migration process will create a backup of the wallet before migrating. This backup file will be named "
483+
"<wallet name>-<timestamp>.legacy.bak and can be found in the directory for this wallet. In the event of "
484+
"an incorrect migration, the backup can be restored with the \"Restore Wallet\" functionality."));
485+
box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
486+
box.setDefaultButton(QMessageBox::Yes);
487+
if (box.exec() != QMessageBox::Yes) return;
488+
489+
do_migrate(name);
490+
}
491+
492+
void MigrateWalletActivity::restore_and_migrate(const fs::path& path, const std::string& wallet_name)
493+
{
494+
// Warn the user about migration
495+
QMessageBox box(m_parent_widget);
496+
box.setWindowTitle(tr("Restore and Migrate wallet"));
497+
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))));
498+
box.setInformativeText(tr("Restoring the wallet will copy the backup file to the wallets directory and place it in the standard "
499+
"wallet directory layout. The original file will not be modified.\n\n"
500+
"Migrating the wallet will convert the restored wallet to one or more descriptor wallets. A new wallet backup will need to be made.\n"
501+
"If this wallet contains any watchonly scripts, a new wallet will be created which contains those watchonly scripts.\n"
502+
"If this wallet contains any solvable but not watched scripts, a different and new wallet will be created which contains those scripts.\n\n"
503+
"The migration process will create a backup of the wallet before migrating. This backup file will be named "
504+
"<wallet name>-<timestamp>.legacy.bak and can be found in the directory for this wallet. In the event of "
505+
"an incorrect migration, the backup can be restored with the \"Restore Wallet\" functionality."));
506+
box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
507+
box.setDefaultButton(QMessageBox::Yes);
508+
if (box.exec() != QMessageBox::Yes) return;
509+
510+
showProgressDialog(
511+
//: Title of progress window which is displayed when wallets are being restored.
512+
tr("Restore Wallet"),
513+
/*: Descriptive text of the restore wallets progress window which indicates to
514+
the user that wallets are currently being restored.*/
515+
tr("Restoring Wallet <b>%1</b>…").arg(GUIUtil::HtmlEscape(GUIUtil::WalletDisplayName(wallet_name))));
516+
517+
QTimer::singleShot(0, worker(), [this, path, wallet_name] {
518+
auto res{node().walletLoader().restoreWallet(path, wallet_name, m_warning_message, /*load_after_restore=*/false)};
519+
520+
if (!res) {
521+
m_error_message = util::ErrorString(res);
522+
QTimer::singleShot(0, this, &MigrateWalletActivity::finish);
523+
return;
524+
}
525+
QTimer::singleShot(0, this, [this, wallet_name] {
526+
do_migrate(wallet_name);
527+
});
528+
});
529+
}
530+
487531
void MigrateWalletActivity::finish()
488532
{
489533
if (!m_error_message.empty()) {

src/qt/walletcontroller.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ class MigrateWalletActivity : public WalletControllerActivity
187187
public:
188188
MigrateWalletActivity(WalletController* wallet_controller, QWidget* parent) : WalletControllerActivity(wallet_controller, parent) {}
189189

190+
void restore_and_migrate(const fs::path& path, const std::string& wallet_name);
190191
void migrate(const std::string& path);
191192

192193
Q_SIGNALS:
@@ -195,6 +196,7 @@ class MigrateWalletActivity : public WalletControllerActivity
195196
private:
196197
QString m_success_message;
197198

199+
void do_migrate(const std::string& name);
198200
void finish();
199201
};
200202

src/wallet/interfaces.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -597,16 +597,15 @@ class WalletLoaderImpl : public WalletLoader
597597
return util::Error{error};
598598
}
599599
}
600-
util::Result<std::unique_ptr<Wallet>> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, std::vector<bilingual_str>& warnings) override
600+
util::Result<std::unique_ptr<Wallet>> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, std::vector<bilingual_str>& warnings, bool load_after_restore) override
601601
{
602602
DatabaseStatus status;
603603
bilingual_str error;
604-
std::unique_ptr<Wallet> wallet{MakeWallet(m_context, RestoreWallet(m_context, backup_file, wallet_name, /*load_on_start=*/true, status, error, warnings))};
605-
if (wallet) {
606-
return wallet;
607-
} else {
604+
std::unique_ptr<Wallet> wallet{MakeWallet(m_context, RestoreWallet(m_context, backup_file, wallet_name, /*load_on_start=*/true, status, error, warnings, load_after_restore))};
605+
if (!error.empty()) {
608606
return util::Error{error};
609607
}
608+
return wallet;
610609
}
611610
util::Result<WalletMigrationResult> migrateWallet(const std::string& name, const SecureString& passphrase) override
612611
{

0 commit comments

Comments
 (0)