Skip to content

gui: Add a menu action to restore then migrate a legacy wallet #877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/interfaces/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ class WalletLoader : public ChainClient
virtual std::string getWalletDir() = 0;

//! Restore backup wallet
virtual util::Result<std::unique_ptr<Wallet>> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, std::vector<bilingual_str>& warnings) = 0;
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;

//! Migrate a wallet
virtual util::Result<WalletMigrationResult> migrateWallet(const std::string& name, const SecureString& passphrase) = 0;
Expand Down
26 changes: 26 additions & 0 deletions src/qt/bitcoingui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,32 @@ void BitcoinGUI::createActions()
QAction* action = m_migrate_wallet_menu->addAction(tr("No wallets available"));
action->setEnabled(false);
}
m_migrate_wallet_menu->addSeparator();
QAction* restore_migrate_file_action = m_migrate_wallet_menu->addAction(tr("Restore and Migrate Wallet File..."));
restore_migrate_file_action->setEnabled(true);

connect(restore_migrate_file_action, &QAction::triggered, [this] {
QString name_data_file = tr("Wallet Data");
QString title_windows = tr("Restore and Migrate Wallet Backup");

QString backup_file = GUIUtil::getOpenFileName(this, title_windows, QString(), name_data_file + QLatin1String(" (*.dat)"), nullptr);
if (backup_file.isEmpty()) return;

bool wallet_name_ok;
/*: Title of pop-up window shown when the user is attempting to
restore a wallet. */
QString title = tr("Restore and Migrate Wallet");
//: Label of the input field where the name of the wallet is entered.
QString label = tr("Wallet Name");
QString wallet_name = QInputDialog::getText(this, title, label, QLineEdit::Normal, "", &wallet_name_ok);
if (!wallet_name_ok || wallet_name.isEmpty()) return;

auto activity = new MigrateWalletActivity(m_wallet_controller, this);
connect(activity, &MigrateWalletActivity::migrated, this, &BitcoinGUI::setCurrentWallet);
connect(activity, &MigrateWalletActivity::migrated, rpcConsole, &RPCConsole::setCurrentWallet);
auto backup_file_path = fs::PathFromString(backup_file.toStdString());
activity->restore_and_migrate(backup_file_path, wallet_name.toStdString());
});
});
connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::setPrivacy);
connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::enableHistoryAction);
Expand Down
76 changes: 60 additions & 16 deletions src/qt/walletcontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ void RestoreWalletActivity::restore(const fs::path& backup_file, const std::stri
tr("Restoring Wallet <b>%1</b>…").arg(name.toHtmlEscaped()));

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

if (wallet) {
m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(*wallet));
Expand Down Expand Up @@ -439,22 +439,8 @@ void RestoreWalletActivity::finish()
Q_EMIT finished();
}

void MigrateWalletActivity::migrate(const std::string& name)
void MigrateWalletActivity::do_migrate(const std::string& name)
{
// Warn the user about migration
QMessageBox box(m_parent_widget);
box.setWindowTitle(tr("Migrate wallet"));
box.setText(tr("Are you sure you wish to migrate the wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(GUIUtil::WalletDisplayName(name))));
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"
"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;

SecureString passphrase;
if (node().walletLoader().isEncrypted(name)) {
// Get the passphrase for the wallet
Expand Down Expand Up @@ -484,6 +470,64 @@ void MigrateWalletActivity::migrate(const std::string& name)
});
}

void MigrateWalletActivity::migrate(const std::string& name)
{
// Warn the user about migration
QMessageBox box(m_parent_widget);
box.setWindowTitle(tr("Migrate wallet"));
box.setText(tr("Are you sure you wish to migrate the wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(GUIUtil::WalletDisplayName(name))));
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"
"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;

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(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()) {
Expand Down
2 changes: 2 additions & 0 deletions src/qt/walletcontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -195,6 +196,7 @@ class MigrateWalletActivity : public WalletControllerActivity
private:
QString m_success_message;

void do_migrate(const std::string& name);
void finish();
};

Expand Down
9 changes: 4 additions & 5 deletions src/wallet/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -608,16 +608,15 @@ class WalletLoaderImpl : public WalletLoader
return util::Error{error};
}
}
util::Result<std::unique_ptr<Wallet>> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, std::vector<bilingual_str>& warnings) override
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
{
DatabaseStatus status;
bilingual_str error;
std::unique_ptr<Wallet> wallet{MakeWallet(m_context, RestoreWallet(m_context, backup_file, wallet_name, /*load_on_start=*/true, status, error, warnings))};
if (wallet) {
return wallet;
} else {
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))};
if (!error.empty()) {
return util::Error{error};
}
return wallet;
}
util::Result<WalletMigrationResult> migrateWallet(const std::string& name, const SecureString& passphrase) override
{
Expand Down
Loading