Skip to content

Commit

Permalink
gui: Use wallet name for wallet migration rather than WalletModel
Browse files Browse the repository at this point in the history
To prepare for migrating wallets that are not loaded, when migration
occurs in the GUI, it should not rely on a WalletModel existing.

Co-authored-by: furszy <[email protected]>
  • Loading branch information
achow101 and furszy committed Aug 13, 2024
1 parent c391858 commit d56a450
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 18 deletions.
3 changes: 3 additions & 0 deletions src/interfaces/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,9 @@ class WalletLoader : public ChainClient
//! Migrate a wallet
virtual util::Result<WalletMigrationResult> migrateWallet(const std::string& name, const SecureString& passphrase) = 0;

//! Returns true if wallet stores encryption keys
virtual bool isEncrypted(const std::string& wallet_name) = 0;

//! Return available wallets in wallet directory.
virtual std::vector<std::pair<std::string, std::string>> listWalletDir() = 0;

Expand Down
8 changes: 7 additions & 1 deletion src/qt/askpassphrasedialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, SecureStri
ui->passEdit1->hide();
setWindowTitle(tr("Encrypt wallet"));
break;
case UnlockMigration:
case Unlock: // Ask passphrase
ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
ui->passLabel2->hide();
Expand Down Expand Up @@ -80,7 +81,7 @@ void AskPassphraseDialog::setModel(WalletModel *_model)
void AskPassphraseDialog::accept()
{
SecureString oldpass, newpass1, newpass2;
if (!model && mode != Encrypt)
if (!model && mode != Encrypt && mode != UnlockMigration)
return;
oldpass.reserve(MAX_PASSPHRASE_SIZE);
newpass1.reserve(MAX_PASSPHRASE_SIZE);
Expand Down Expand Up @@ -181,6 +182,10 @@ void AskPassphraseDialog::accept()
QMessageBox::critical(this, tr("Wallet unlock failed"), e.what());
}
break;
case UnlockMigration:
Assume(m_passphrase_out)->assign(oldpass);
QDialog::accept();
break;
case ChangePass:
if(newpass1 == newpass2)
{
Expand Down Expand Up @@ -224,6 +229,7 @@ void AskPassphraseDialog::textChanged()
case Encrypt: // New passphrase x2
acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
break;
case UnlockMigration:
case Unlock: // Old passphrase x1
acceptable = !ui->passEdit1->text().isEmpty();
break;
Expand Down
1 change: 1 addition & 0 deletions src/qt/askpassphrasedialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class AskPassphraseDialog : public QDialog
Encrypt, /**< Ask passphrase twice and encrypt */
Unlock, /**< Ask passphrase and unlock */
ChangePass, /**< Ask old passphrase + new passphrase twice */
UnlockMigration, /**< Ask passphrase for unlocking during migration */
};

explicit AskPassphraseDialog(Mode mode, QWidget *parent, SecureString* passphrase_out = nullptr);
Expand Down
2 changes: 1 addition & 1 deletion src/qt/bitcoingui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ void BitcoinGUI::createActions()
connect(m_migrate_wallet_action, &QAction::triggered, [this] {
auto activity = new MigrateWalletActivity(m_wallet_controller, this);
connect(activity, &MigrateWalletActivity::migrated, this, &BitcoinGUI::setCurrentWallet);
activity->migrate(walletFrame->currentWalletModel());
activity->migrate(walletFrame->currentWalletModel()->wallet().getWalletName());
});
connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::setPrivacy);
connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::enableHistoryAction);
Expand Down
22 changes: 9 additions & 13 deletions src/qt/walletcontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,12 +437,12 @@ void RestoreWalletActivity::finish()
Q_EMIT finished();
}

void MigrateWalletActivity::migrate(WalletModel* wallet_model)
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(wallet_model->getDisplayName())));
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"
Expand All @@ -453,29 +453,25 @@ void MigrateWalletActivity::migrate(WalletModel* wallet_model)
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();
if (node().walletLoader().isEncrypted(name)) {
// Get the passphrase for the wallet
AskPassphraseDialog dlg(AskPassphraseDialog::UnlockMigration, m_parent_widget, &passphrase);
if (dlg.exec() == QDialog::Rejected) return;
}

// GUI needs to remove the wallet so that it can actually be unloaded by migration
const std::string name = wallet_model->wallet().getWalletName();
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()));
m_success_message = tr("The wallet '%1' was migrated successfully.").arg(GUIUtil::HtmlEscape(GUIUtil::WalletDisplayName(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()));
m_success_message += QChar(' ') + tr("Watchonly scripts have been migrated to a new wallet named '%1'.").arg(GUIUtil::HtmlEscape(GUIUtil::WalletDisplayName(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_success_message += QChar(' ') + tr("Solvable but not watched scripts have been migrated to a new wallet named '%1'.").arg(GUIUtil::HtmlEscape(GUIUtil::WalletDisplayName(res->solvables_wallet_name.value())));
}
m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(res->wallet));
} else {
Expand Down
4 changes: 1 addition & 3 deletions src/qt/walletcontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ class WalletController : public QObject
void closeWallet(WalletModel* wallet_model, QWidget* parent = nullptr);
void closeAllWallets(QWidget* parent = nullptr);

void migrateWallet(WalletModel* wallet_model, QWidget* parent = nullptr);

Q_SIGNALS:
void walletAdded(WalletModel* wallet_model);
void walletRemoved(WalletModel* wallet_model);
Expand Down Expand Up @@ -186,7 +184,7 @@ class MigrateWalletActivity : public WalletControllerActivity
public:
MigrateWalletActivity(WalletController* wallet_controller, QWidget* parent) : WalletControllerActivity(wallet_controller, parent) {}

void migrate(WalletModel* wallet_model);
void migrate(const std::string& path);

Q_SIGNALS:
void migrated(WalletModel* wallet_model);
Expand Down
15 changes: 15 additions & 0 deletions src/wallet/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,21 @@ class WalletLoaderImpl : public WalletLoader
};
return out;
}
bool isEncrypted(const std::string& wallet_name) override
{
auto wallets{GetWallets(m_context)};
auto it = std::find_if(wallets.begin(), wallets.end(), [&](std::shared_ptr<CWallet> w){ return w->GetName() == wallet_name; });
if (it != wallets.end()) return (*it)->IsCrypted();

// Unloaded wallet, read db
DatabaseOptions options;
options.require_existing = true;
DatabaseStatus status;
bilingual_str error;
auto db = MakeWalletDatabase(wallet_name, options, status, error);
if (!db) return false;
return WalletBatch(*db).IsEncrypted();
}
std::string getWalletDir() override
{
return fs::PathToString(GetWalletDir());
Expand Down
11 changes: 11 additions & 0 deletions src/wallet/walletdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,17 @@ bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
return m_batch->Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
}

bool WalletBatch::IsEncrypted()
{
DataStream prefix;
prefix << DBKeys::MASTER_KEY;
if (auto cursor = m_batch->GetNewPrefixCursor(prefix)) {
DataStream k, v;
if (cursor->Next(k, v) == DatabaseCursor::Status::MORE) return true;
}
return false;
}

bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
{
return WriteIC(DBKeys::ORDERPOSNEXT, nOrderPosNext);
Expand Down
3 changes: 3 additions & 0 deletions src/wallet/walletdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ class WalletBatch
bool WriteBestBlock(const CBlockLocator& locator);
bool ReadBestBlock(CBlockLocator& locator);

// Returns true if wallet stores encryption keys
bool IsEncrypted();

bool WriteOrderPosNext(int64_t nOrderPosNext);

bool ReadPool(int64_t nPool, CKeyPool& keypool);
Expand Down

0 comments on commit d56a450

Please sign in to comment.