Skip to content

Commit 48aae2c

Browse files
committed
gui: Add File > Migrate Wallet
1 parent 577be88 commit 48aae2c

File tree

4 files changed

+100
-0
lines changed

4 files changed

+100
-0
lines changed

src/qt/bitcoingui.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,10 @@ void BitcoinGUI::createActions()
359359
m_close_all_wallets_action = new QAction(tr("Close All Wallets…"), this);
360360
m_close_all_wallets_action->setStatusTip(tr("Close all wallets"));
361361

362+
m_migrate_wallet_action = new QAction(tr("Migrate Wallet"), this);
363+
m_migrate_wallet_action->setEnabled(false);
364+
m_migrate_wallet_action->setStatusTip(tr("Migrate a wallet"));
365+
362366
showHelpMessageAction = new QAction(tr("&Command-line options"), this);
363367
showHelpMessageAction->setMenuRole(QAction::NoRole);
364368
showHelpMessageAction->setStatusTip(tr("Show the %1 help message to get a list with possible Bitcoin command-line options").arg(PACKAGE_NAME));
@@ -456,6 +460,11 @@ void BitcoinGUI::createActions()
456460
connect(m_close_all_wallets_action, &QAction::triggered, [this] {
457461
m_wallet_controller->closeAllWallets(this);
458462
});
463+
connect(m_migrate_wallet_action, &QAction::triggered, [this] {
464+
auto activity = new MigrateWalletActivity(m_wallet_controller, this);
465+
connect(activity, &MigrateWalletActivity::migrated, this, &BitcoinGUI::setCurrentWallet);
466+
activity->migrate(walletFrame->currentWalletModel());
467+
});
459468
connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::setPrivacy);
460469
connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::enableHistoryAction);
461470
}
@@ -483,6 +492,7 @@ void BitcoinGUI::createMenuBar()
483492
file->addAction(m_open_wallet_action);
484493
file->addAction(m_close_wallet_action);
485494
file->addAction(m_close_all_wallets_action);
495+
file->addAction(m_migrate_wallet_action);
486496
file->addSeparator();
487497
file->addAction(backupWalletAction);
488498
file->addAction(m_restore_wallet_action);
@@ -767,6 +777,7 @@ void BitcoinGUI::setCurrentWallet(WalletModel* wallet_model)
767777
}
768778
}
769779
updateWindowTitle();
780+
m_migrate_wallet_action->setEnabled(wallet_model->wallet().isLegacy());
770781
}
771782

772783
void BitcoinGUI::setCurrentWalletBySelectorIndex(int index)
@@ -800,6 +811,7 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled)
800811
openAction->setEnabled(enabled);
801812
m_close_wallet_action->setEnabled(enabled);
802813
m_close_all_wallets_action->setEnabled(enabled);
814+
m_migrate_wallet_action->setEnabled(enabled);
803815
}
804816

805817
void BitcoinGUI::createTrayIcon()

src/qt/bitcoingui.h

+2
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ class BitcoinGUI : public QMainWindow
163163
QAction* m_wallet_selector_label_action = nullptr;
164164
QAction* m_wallet_selector_action = nullptr;
165165
QAction* m_mask_values_action{nullptr};
166+
QAction* m_migrate_wallet_action{nullptr};
167+
QMenu* m_migrate_wallet_menu{nullptr};
166168

167169
QLabel *m_wallet_selector_label = nullptr;
168170
QComboBox* m_wallet_selector = nullptr;

src/qt/walletcontroller.cpp

+64
Original file line numberDiff line numberDiff line change
@@ -432,3 +432,67 @@ void RestoreWalletActivity::finish()
432432

433433
Q_EMIT finished();
434434
}
435+
436+
void MigrateWalletActivity::migrate(WalletModel* wallet_model)
437+
{
438+
// Warn the user about migration
439+
QMessageBox box(m_parent_widget);
440+
box.setWindowTitle(tr("Migrate wallet"));
441+
box.setText(tr("Are you sure you wish to migrate the wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(wallet_model->getDisplayName())));
442+
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"
443+
"If this wallet contains any watchonly scripts, a new wallet will be created which contains those watchonly scripts.\n"
444+
"If this wallet contains any solvable but not watched scripts, a different and new wallet will be created which contains those scripts.\n\n"
445+
"The migration process will create a backup of the wallet before migrating. This backup file will be named "
446+
"<wallet name>-<timestamp>.legacy.bak and can be found in the directory for this wallet. In the event of "
447+
"an incorrect migration, the backup can be restored with the \"Restore Wallet\" functionality."));
448+
box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
449+
box.setDefaultButton(QMessageBox::Yes);
450+
if (box.exec() != QMessageBox::Yes) return;
451+
452+
// Get the passphrase if it is encrypted regardless of it is locked or unlocked. We need the passphrase itself.
453+
SecureString passphrase;
454+
WalletModel::EncryptionStatus enc_status = wallet_model->getEncryptionStatus();
455+
if (enc_status == WalletModel::EncryptionStatus::Locked || enc_status == WalletModel::EncryptionStatus::Unlocked) {
456+
AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, m_parent_widget, &passphrase);
457+
dlg.setModel(wallet_model);
458+
dlg.exec();
459+
}
460+
461+
// GUI needs to remove the wallet so that it can actually be unloaded by migration
462+
const std::string name = wallet_model->wallet().getWalletName();
463+
m_wallet_controller->removeAndDeleteWallet(wallet_model);
464+
465+
showProgressDialog(tr("Migrate Wallet"), tr("Migrating Wallet <b>%1</b>…").arg(GUIUtil::HtmlEscape(name)));
466+
467+
QTimer::singleShot(0, worker(), [this, name, passphrase] {
468+
auto res{node().walletLoader().migrateWallet(name, passphrase)};
469+
470+
if (res) {
471+
m_success_message = tr("The wallet '%1' was migrated successfully.").arg(GUIUtil::HtmlEscape(res->wallet->getWalletName()));
472+
if (res->watchonly_wallet_name) {
473+
m_success_message += tr(" Watchonly scripts have been migrated to a new wallet named '%1'.").arg(GUIUtil::HtmlEscape(res->watchonly_wallet_name.value()));
474+
}
475+
if (res->solvables_wallet_name) {
476+
m_success_message += tr(" Solvable but not watched scripts have been migrated to a new wallet named '%1'.").arg(GUIUtil::HtmlEscape(res->solvables_wallet_name.value()));
477+
}
478+
m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(res->wallet));
479+
} else {
480+
m_error_message = util::ErrorString(res);
481+
}
482+
483+
QTimer::singleShot(0, this, &MigrateWalletActivity::finish);
484+
});
485+
}
486+
487+
void MigrateWalletActivity::finish()
488+
{
489+
if (!m_error_message.empty()) {
490+
QMessageBox::critical(m_parent_widget, tr("Migration failed"), QString::fromStdString(m_error_message.translated));
491+
} else {
492+
QMessageBox::information(m_parent_widget, tr("Migration Successful"), m_success_message);
493+
}
494+
495+
if (m_wallet_model) Q_EMIT migrated(m_wallet_model);
496+
497+
Q_EMIT finished();
498+
}

src/qt/walletcontroller.h

+22
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class path;
4040
class AskPassphraseDialog;
4141
class CreateWalletActivity;
4242
class CreateWalletDialog;
43+
class MigrateWalletActivity;
4344
class OpenWalletActivity;
4445
class WalletControllerActivity;
4546

@@ -65,6 +66,8 @@ class WalletController : public QObject
6566
void closeWallet(WalletModel* wallet_model, QWidget* parent = nullptr);
6667
void closeAllWallets(QWidget* parent = nullptr);
6768

69+
void migrateWallet(WalletModel* wallet_model, QWidget* parent = nullptr);
70+
6871
Q_SIGNALS:
6972
void walletAdded(WalletModel* wallet_model);
7073
void walletRemoved(WalletModel* wallet_model);
@@ -83,6 +86,7 @@ class WalletController : public QObject
8386
std::unique_ptr<interfaces::Handler> m_handler_load_wallet;
8487

8588
friend class WalletControllerActivity;
89+
friend class MigrateWalletActivity;
8690
};
8791

8892
class WalletControllerActivity : public QObject
@@ -175,4 +179,22 @@ class RestoreWalletActivity : public WalletControllerActivity
175179
void finish();
176180
};
177181

182+
class MigrateWalletActivity : public WalletControllerActivity
183+
{
184+
Q_OBJECT
185+
186+
public:
187+
MigrateWalletActivity(WalletController* wallet_controller, QWidget* parent) : WalletControllerActivity(wallet_controller, parent) {}
188+
189+
void migrate(WalletModel* wallet_model);
190+
191+
Q_SIGNALS:
192+
void migrated(WalletModel* wallet_model);
193+
194+
private:
195+
QString m_success_message;
196+
197+
void finish();
198+
};
199+
178200
#endif // BITCOIN_QT_WALLETCONTROLLER_H

0 commit comments

Comments
 (0)