-
Notifications
You must be signed in to change notification settings - Fork 1.2k
backport: batch 2 of migratewallet RPC #7277
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
base: develop
Are you sure you want to change the base?
Changes from all commits
0695a4f
a084e37
3c17eb4
92d651e
16071be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -558,3 +558,67 @@ void RescanWalletActivity::finish() | |||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Q_EMIT finished(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void MigrateWalletActivity::migrate(WalletModel* wallet_model) | ||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| // 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(wallet_model->getDisplayName()))); | ||||||||||||||||||||||||||||||||||||||
| 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; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Get the passphrase if it is encrypted regardless of it is locked or unlocked. We need the passphrase itself. | ||||||||||||||||||||||||||||||||||||||
| SecureString passphrase; | ||||||||||||||||||||||||||||||||||||||
| WalletModel::EncryptionStatus enc_status = wallet_model->getEncryptionStatus(); | ||||||||||||||||||||||||||||||||||||||
| if (enc_status == WalletModel::EncryptionStatus::Locked || enc_status == WalletModel::EncryptionStatus::Unlocked) { | ||||||||||||||||||||||||||||||||||||||
| AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, m_parent_widget, &passphrase); | ||||||||||||||||||||||||||||||||||||||
| dlg.setModel(wallet_model); | ||||||||||||||||||||||||||||||||||||||
| dlg.exec(); | ||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For encrypted wallets, Useful? React with 👍 / 👎.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. backported code; if there's no relevant backport from bitcoin-core, this comment would be ignored. @thepastaclaw any relevant backport for this fix? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes — the relevant upstream follow-up is bitcoin-core/gui#824, specifically commit d56a450 ( That commit changes the migration passphrase flow to use AskPassphraseDialog dlg(AskPassphraseDialog::UnlockMigration, m_parent_widget, &passphrase);
if (dlg.exec() == QDialog::Rejected) return;So this Codex finding is real upstream-covered behavior, but it is part of the broader “migrate legacy wallets that are not loaded” GUI follow-up rather than a small standalone fix on top of gui#738.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it can't be done right now, because bitcoin-core/gui#824 depends on bitcoin#27790 which seems impossible to be done right now; I am going to prioritize it for batch 3 but it seems as non-trivial yet. For Bitcoin Core the gap between bitcoin-core/gui#824 and bitcoin-core/gui#738 is 2 major versions btw. |
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // GUI needs to remove the wallet so that it can actually be unloaded by migration | ||||||||||||||||||||||||||||||||||||||
| const std::string name = wallet_model->wallet().getWalletName(); | ||||||||||||||||||||||||||||||||||||||
| m_wallet_controller->removeAndDeleteWallet(wallet_model); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+581
to
+590
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Abort migration when passphrase entry is cancelled. The dialog result is ignored, so canceling still removes the wallet and proceeds with migration using an empty passphrase. Suggested fix if (enc_status == WalletModel::EncryptionStatus::Locked || enc_status == WalletModel::EncryptionStatus::Unlocked) {
AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, m_parent_widget, &passphrase);
dlg.setModel(wallet_model);
- dlg.exec();
+ if (dlg.exec() != QDialog::Accepted) return;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| showProgressDialog(tr("Migrate Wallet"), tr("Migrating Wallet <b>%1</b>…").arg(GUIUtil::HtmlEscape(name))); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| QTimer::singleShot(0, worker(), [this, name, passphrase] { | ||||||||||||||||||||||||||||||||||||||
| auto res{node().walletLoader().migrateWallet(name, passphrase)}; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (res) { | ||||||||||||||||||||||||||||||||||||||
| m_success_message = tr("The wallet '%1' was migrated successfully.").arg(GUIUtil::HtmlEscape(res->wallet->getWalletName())); | ||||||||||||||||||||||||||||||||||||||
| if (res->watchonly_wallet_name) { | ||||||||||||||||||||||||||||||||||||||
| m_success_message += QChar(' ') + tr("Watchonly scripts have been migrated to a new wallet named '%1'.").arg(GUIUtil::HtmlEscape(res->watchonly_wallet_name.value())); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| if (res->solvables_wallet_name) { | ||||||||||||||||||||||||||||||||||||||
| m_success_message += QChar(' ') + tr("Solvable but not watched scripts have been migrated to a new wallet named '%1'.").arg(GUIUtil::HtmlEscape(res->solvables_wallet_name.value())); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(res->wallet)); | ||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||
| m_error_message = util::ErrorString(res); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| QTimer::singleShot(0, this, &MigrateWalletActivity::finish); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void MigrateWalletActivity::finish() | ||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| if (!m_error_message.empty()) { | ||||||||||||||||||||||||||||||||||||||
| QMessageBox::critical(m_parent_widget, tr("Migration failed"), QString::fromStdString(m_error_message.translated)); | ||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||
| QMessageBox::information(m_parent_widget, tr("Migration Successful"), m_success_message); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (m_wallet_model) Q_EMIT migrated(m_wallet_model); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Q_EMIT finished(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,6 +52,7 @@ using interfaces::Wallet; | |
| using interfaces::WalletAddress; | ||
| using interfaces::WalletBalances; | ||
| using interfaces::WalletLoader; | ||
| using interfaces::WalletMigrationResult; | ||
| using interfaces::WalletOrderForm; | ||
| using interfaces::WalletTx; | ||
| using interfaces::WalletTxOut; | ||
|
|
@@ -809,6 +810,18 @@ class WalletLoaderImpl : public WalletLoader | |
| return util::Error{error}; | ||
| } | ||
| } | ||
| util::Result<WalletMigrationResult> migrateWallet(const std::string& name, const SecureString& passphrase) override | ||
| { | ||
| auto res = wallet::MigrateLegacyToDescriptor(name, passphrase, m_context); | ||
| if (!res) return util::Error{util::ErrorString(res)}; | ||
| WalletMigrationResult out{ | ||
| .wallet = MakeWallet(m_context, res->wallet), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If the post-migration Useful? React with 👍 / 👎. |
||
| .watchonly_wallet_name = res->watchonly_wallet ? std::make_optional(res->watchonly_wallet->GetName()) : std::nullopt, | ||
| .solvables_wallet_name = res->solvables_wallet ? std::make_optional(res->solvables_wallet->GetName()) : std::nullopt, | ||
| .backup_path = res->backup_path, | ||
| }; | ||
| return {std::move(out)}; // std::move to work around clang bug | ||
| } | ||
| std::string getWalletDir() override | ||
| { | ||
| return fs::PathToString(GetWalletDir()); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Propagate migrated wallet selection to RPC console as well.
Migration updates the GUI current wallet, but not the RPC console context (unlike open/create/restore flows).
Suggested fix
connect(m_migrate_wallet_action, &QAction::triggered, [this] { auto activity = new MigrateWalletActivity(m_wallet_controller, this); connect(activity, &MigrateWalletActivity::migrated, this, &BitcoinGUI::setCurrentWallet); + connect(activity, &MigrateWalletActivity::migrated, rpcConsole, &RPCConsole::setCurrentWallet); activity->migrate(walletFrame->currentWalletModel()); });📝 Committable suggestion
🤖 Prompt for AI Agents