mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 15:04:44 +01:00
gui: Use wallet name for wallet migration rather than WalletModel
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 <matiasfurszyfer@protonmail.com>
This commit is contained in:
parent
c3918583dd
commit
d56a450bf5
9 changed files with 51 additions and 18 deletions
|
@ -342,6 +342,9 @@ public:
|
||||||
//! Migrate a wallet
|
//! Migrate a wallet
|
||||||
virtual util::Result<WalletMigrationResult> migrateWallet(const std::string& name, const SecureString& passphrase) = 0;
|
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.
|
//! Return available wallets in wallet directory.
|
||||||
virtual std::vector<std::pair<std::string, std::string>> listWalletDir() = 0;
|
virtual std::vector<std::pair<std::string, std::string>> listWalletDir() = 0;
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, SecureStri
|
||||||
ui->passEdit1->hide();
|
ui->passEdit1->hide();
|
||||||
setWindowTitle(tr("Encrypt wallet"));
|
setWindowTitle(tr("Encrypt wallet"));
|
||||||
break;
|
break;
|
||||||
|
case UnlockMigration:
|
||||||
case Unlock: // Ask passphrase
|
case Unlock: // Ask passphrase
|
||||||
ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
|
ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
|
||||||
ui->passLabel2->hide();
|
ui->passLabel2->hide();
|
||||||
|
@ -80,7 +81,7 @@ void AskPassphraseDialog::setModel(WalletModel *_model)
|
||||||
void AskPassphraseDialog::accept()
|
void AskPassphraseDialog::accept()
|
||||||
{
|
{
|
||||||
SecureString oldpass, newpass1, newpass2;
|
SecureString oldpass, newpass1, newpass2;
|
||||||
if (!model && mode != Encrypt)
|
if (!model && mode != Encrypt && mode != UnlockMigration)
|
||||||
return;
|
return;
|
||||||
oldpass.reserve(MAX_PASSPHRASE_SIZE);
|
oldpass.reserve(MAX_PASSPHRASE_SIZE);
|
||||||
newpass1.reserve(MAX_PASSPHRASE_SIZE);
|
newpass1.reserve(MAX_PASSPHRASE_SIZE);
|
||||||
|
@ -181,6 +182,10 @@ void AskPassphraseDialog::accept()
|
||||||
QMessageBox::critical(this, tr("Wallet unlock failed"), e.what());
|
QMessageBox::critical(this, tr("Wallet unlock failed"), e.what());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case UnlockMigration:
|
||||||
|
Assume(m_passphrase_out)->assign(oldpass);
|
||||||
|
QDialog::accept();
|
||||||
|
break;
|
||||||
case ChangePass:
|
case ChangePass:
|
||||||
if(newpass1 == newpass2)
|
if(newpass1 == newpass2)
|
||||||
{
|
{
|
||||||
|
@ -224,6 +229,7 @@ void AskPassphraseDialog::textChanged()
|
||||||
case Encrypt: // New passphrase x2
|
case Encrypt: // New passphrase x2
|
||||||
acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
|
acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
|
||||||
break;
|
break;
|
||||||
|
case UnlockMigration:
|
||||||
case Unlock: // Old passphrase x1
|
case Unlock: // Old passphrase x1
|
||||||
acceptable = !ui->passEdit1->text().isEmpty();
|
acceptable = !ui->passEdit1->text().isEmpty();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -26,6 +26,7 @@ public:
|
||||||
Encrypt, /**< Ask passphrase twice and encrypt */
|
Encrypt, /**< Ask passphrase twice and encrypt */
|
||||||
Unlock, /**< Ask passphrase and unlock */
|
Unlock, /**< Ask passphrase and unlock */
|
||||||
ChangePass, /**< Ask old passphrase + new passphrase twice */
|
ChangePass, /**< Ask old passphrase + new passphrase twice */
|
||||||
|
UnlockMigration, /**< Ask passphrase for unlocking during migration */
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit AskPassphraseDialog(Mode mode, QWidget *parent, SecureString* passphrase_out = nullptr);
|
explicit AskPassphraseDialog(Mode mode, QWidget *parent, SecureString* passphrase_out = nullptr);
|
||||||
|
|
|
@ -458,7 +458,7 @@ void BitcoinGUI::createActions()
|
||||||
connect(m_migrate_wallet_action, &QAction::triggered, [this] {
|
connect(m_migrate_wallet_action, &QAction::triggered, [this] {
|
||||||
auto activity = new MigrateWalletActivity(m_wallet_controller, this);
|
auto activity = new MigrateWalletActivity(m_wallet_controller, this);
|
||||||
connect(activity, &MigrateWalletActivity::migrated, this, &BitcoinGUI::setCurrentWallet);
|
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::setPrivacy);
|
||||||
connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::enableHistoryAction);
|
connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::enableHistoryAction);
|
||||||
|
|
|
@ -437,12 +437,12 @@ void RestoreWalletActivity::finish()
|
||||||
Q_EMIT finished();
|
Q_EMIT finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MigrateWalletActivity::migrate(WalletModel* wallet_model)
|
void MigrateWalletActivity::migrate(const std::string& name)
|
||||||
{
|
{
|
||||||
// Warn the user about migration
|
// Warn the user about migration
|
||||||
QMessageBox box(m_parent_widget);
|
QMessageBox box(m_parent_widget);
|
||||||
box.setWindowTitle(tr("Migrate wallet"));
|
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"
|
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 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"
|
"If this wallet contains any solvable but not watched scripts, a different and new wallet will be created which contains those scripts.\n\n"
|
||||||
|
@ -453,29 +453,25 @@ void MigrateWalletActivity::migrate(WalletModel* wallet_model)
|
||||||
box.setDefaultButton(QMessageBox::Yes);
|
box.setDefaultButton(QMessageBox::Yes);
|
||||||
if (box.exec() != QMessageBox::Yes) return;
|
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;
|
SecureString passphrase;
|
||||||
WalletModel::EncryptionStatus enc_status = wallet_model->getEncryptionStatus();
|
if (node().walletLoader().isEncrypted(name)) {
|
||||||
if (enc_status == WalletModel::EncryptionStatus::Locked || enc_status == WalletModel::EncryptionStatus::Unlocked) {
|
// Get the passphrase for the wallet
|
||||||
AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, m_parent_widget, &passphrase);
|
AskPassphraseDialog dlg(AskPassphraseDialog::UnlockMigration, m_parent_widget, &passphrase);
|
||||||
dlg.setModel(wallet_model);
|
if (dlg.exec() == QDialog::Rejected) return;
|
||||||
dlg.exec();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)));
|
showProgressDialog(tr("Migrate Wallet"), tr("Migrating Wallet <b>%1</b>…").arg(GUIUtil::HtmlEscape(name)));
|
||||||
|
|
||||||
QTimer::singleShot(0, worker(), [this, name, passphrase] {
|
QTimer::singleShot(0, worker(), [this, name, passphrase] {
|
||||||
auto res{node().walletLoader().migrateWallet(name, passphrase)};
|
auto res{node().walletLoader().migrateWallet(name, passphrase)};
|
||||||
|
|
||||||
if (res) {
|
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) {
|
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) {
|
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));
|
m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(res->wallet));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -66,8 +66,6 @@ public:
|
||||||
void closeWallet(WalletModel* wallet_model, QWidget* parent = nullptr);
|
void closeWallet(WalletModel* wallet_model, QWidget* parent = nullptr);
|
||||||
void closeAllWallets(QWidget* parent = nullptr);
|
void closeAllWallets(QWidget* parent = nullptr);
|
||||||
|
|
||||||
void migrateWallet(WalletModel* wallet_model, QWidget* parent = nullptr);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void walletAdded(WalletModel* wallet_model);
|
void walletAdded(WalletModel* wallet_model);
|
||||||
void walletRemoved(WalletModel* wallet_model);
|
void walletRemoved(WalletModel* wallet_model);
|
||||||
|
@ -186,7 +184,7 @@ class MigrateWalletActivity : public WalletControllerActivity
|
||||||
public:
|
public:
|
||||||
MigrateWalletActivity(WalletController* wallet_controller, QWidget* parent) : WalletControllerActivity(wallet_controller, parent) {}
|
MigrateWalletActivity(WalletController* wallet_controller, QWidget* parent) : WalletControllerActivity(wallet_controller, parent) {}
|
||||||
|
|
||||||
void migrate(WalletModel* wallet_model);
|
void migrate(const std::string& path);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void migrated(WalletModel* wallet_model);
|
void migrated(WalletModel* wallet_model);
|
||||||
|
|
|
@ -652,6 +652,21 @@ public:
|
||||||
};
|
};
|
||||||
return out;
|
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
|
std::string getWalletDir() override
|
||||||
{
|
{
|
||||||
return fs::PathToString(GetWalletDir());
|
return fs::PathToString(GetWalletDir());
|
||||||
|
|
|
@ -187,6 +187,17 @@ bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
|
||||||
return m_batch->Read(DBKeys::BESTBLOCK_NOMERKLE, 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)
|
bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
|
||||||
{
|
{
|
||||||
return WriteIC(DBKeys::ORDERPOSNEXT, nOrderPosNext);
|
return WriteIC(DBKeys::ORDERPOSNEXT, nOrderPosNext);
|
||||||
|
|
|
@ -247,6 +247,9 @@ public:
|
||||||
bool WriteBestBlock(const CBlockLocator& locator);
|
bool WriteBestBlock(const CBlockLocator& locator);
|
||||||
bool ReadBestBlock(CBlockLocator& locator);
|
bool ReadBestBlock(CBlockLocator& locator);
|
||||||
|
|
||||||
|
// Returns true if wallet stores encryption keys
|
||||||
|
bool IsEncrypted();
|
||||||
|
|
||||||
bool WriteOrderPosNext(int64_t nOrderPosNext);
|
bool WriteOrderPosNext(int64_t nOrderPosNext);
|
||||||
|
|
||||||
bool ReadPool(int64_t nPool, CKeyPool& keypool);
|
bool ReadPool(int64_t nPool, CKeyPool& keypool);
|
||||||
|
|
Loading…
Add table
Reference in a new issue