v29.4-roots.4 · Commit 24: [PATCH 24/37] feat(qt): add guarded private key sweep dialog
src/qt/walletmodel.cpp
Before: src/qt/walletmodel.cpp · After: src/qt/walletmodel.cpp
File metadata
index 0a01c0a45b1923d39a1635ee94fa52234dc49f83..e051aae59971f7336bbc222bb35552ac293c10b0 100644
Download original patch · Release notes
Diff rows 1–99 of 99. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.
src/qt/walletmodel.cpp
CHANGED
|
@@ -22,6 +22,8 @@
|
|
| 22 |
#include <node/types.h>
|
| 23 |
#include <psbt.h>
|
| 24 |
#include <util/translation.h>
|
|
|
|
|
|
|
| 25 |
#include <wallet/coincontrol.h>
|
| 26 |
#include <wallet/wallet.h> // for CRecipient
|
| 27 |
|
|
@@ -50,14 +52,52 @@ WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, ClientModel
|
|
| 50 |
transactionTableModel = new TransactionTableModel(platformStyle, this);
|
| 51 |
recentRequestsTableModel = new RecentRequestsTableModel(this);
|
| 52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
subscribeToCoreSignals();
|
| 54 |
}
|
| 55 |
|
| 56 |
WalletModel::~WalletModel()
|
| 57 |
{
|
|
|
|
|
|
|
|
|
|
| 58 |
unsubscribeFromCoreSignals();
|
| 59 |
}
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
void WalletModel::startPollBalance()
|
| 62 |
{
|
| 63 |
// Update the cached balance right away, so every view can make use of it,
|
|
@@ -104,6 +144,12 @@ void WalletModel::pollBalanceChanged()
|
|
| 104 |
}
|
| 105 |
|
| 106 |
if (fForceCheckBalanceChanged || block_hash != m_cached_last_update_tip) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
fForceCheckBalanceChanged = false;
|
| 108 |
|
| 109 |
// Balance and number of transactions might have changed
|
|
@@ -132,6 +178,9 @@ void WalletModel::updateTransaction()
|
|
| 132 |
{
|
| 133 |
// Balance and number of transactions might have changed
|
| 134 |
fForceCheckBalanceChanged = true;
|
|
|
|
|
|
|
|
|
|
| 135 |
}
|
| 136 |
|
| 137 |
void WalletModel::updateAddressBook(const QString &address, const QString &label,
|
|
@@ -360,7 +409,10 @@ bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureStri
|
|
| 360 |
static void NotifyUnload(WalletModel* walletModel)
|
| 361 |
{
|
| 362 |
qDebug() << "NotifyUnload";
|
| 363 |
-
bool invoked = QMetaObject::invokeMethod(walletModel, "unload");
|
|
|
|
|
|
|
|
|
|
| 364 |
assert(invoked);
|
| 365 |
}
|
| 366 |
|
|
@@ -432,6 +484,7 @@ void WalletModel::subscribeToCoreSignals()
|
|
| 432 |
|
| 433 |
void WalletModel::unsubscribeFromCoreSignals()
|
| 434 |
{
|
|
|
|
| 435 |
// Disconnect signals from wallet
|
| 436 |
m_handler_unload->disconnect();
|
| 437 |
m_handler_status_changed->disconnect();
|
|
|
|
| 22 |
#include <node/types.h>
|
| 23 |
#include <psbt.h>
|
| 24 |
#include <util/translation.h>
|
| 25 |
+
#include <util/threadnames.h>
|
| 26 |
+
#include <util/rbf.h>
|
| 27 |
#include <wallet/coincontrol.h>
|
| 28 |
#include <wallet/wallet.h> // for CRecipient
|
| 29 |
|
|
|
|
| 52 |
transactionTableModel = new TransactionTableModel(platformStyle, this);
|
| 53 |
recentRequestsTableModel = new RecentRequestsTableModel(this);
|
| 54 |
|
| 55 |
+
m_sweep_context.moveToThread(&m_sweep_thread);
|
| 56 |
+
m_sweep_thread.start();
|
| 57 |
+
QTimer::singleShot(0, &m_sweep_context, [] { util::ThreadRename("qt-sweep"); });
|
| 58 |
+
|
| 59 |
subscribeToCoreSignals();
|
| 60 |
}
|
| 61 |
|
| 62 |
WalletModel::~WalletModel()
|
| 63 |
{
|
| 64 |
+
invalidateSweepRequests();
|
| 65 |
+
m_sweep_thread.quit();
|
| 66 |
+
m_sweep_thread.wait();
|
| 67 |
unsubscribeFromCoreSignals();
|
| 68 |
}
|
| 69 |
|
| 70 |
+
uint64_t WalletModel::invalidateSweepRequests()
|
| 71 |
+
{
|
| 72 |
+
++m_sweep_request_generation;
|
| 73 |
+
Q_EMIT sweepRequestInvalidated(m_sweep_request_generation);
|
| 74 |
+
return m_sweep_request_generation;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
uint64_t WalletModel::requestSweep(SecureString private_key, CTxDestination destination, bool broadcast,
|
| 78 |
+
std::optional<uint256> expected_txid)
|
| 79 |
+
{
|
| 80 |
+
const uint64_t generation{++m_sweep_request_generation};
|
| 81 |
+
QMetaObject::invokeMethod(&m_sweep_context, [this, generation, private_key = std::move(private_key), destination = std::move(destination), broadcast, expected_txid]() mutable {
|
| 82 |
+
auto result{m_wallet->sweepPrivateKey(private_key, destination, broadcast, expected_txid)};
|
| 83 |
+
private_key.clear();
|
| 84 |
+
|
| 85 |
+
const bool success{result.has_value()};
|
| 86 |
+
const QString error{success ? QString{} : QString::fromStdString(util::ErrorString(result).original)};
|
| 87 |
+
const qint64 amount{success ? result->amount : 0};
|
| 88 |
+
const qint64 fee{success ? result->fee : 0};
|
| 89 |
+
const quint64 inputs{success ? result->inputs : 0};
|
| 90 |
+
const qint64 vsize{success ? result->vsize : 0};
|
| 91 |
+
const bool rbf{success && result->tx && SignalsOptInRBF(*result->tx)};
|
| 92 |
+
const QString txid{success && result->tx ? QString::fromStdString(result->tx->GetHash().ToString()) : QString{}};
|
| 93 |
+
QMetaObject::invokeMethod(this, [this, generation, broadcast, success, error, amount, fee, inputs, vsize, rbf, txid] {
|
| 94 |
+
if (generation != m_sweep_request_generation) return;
|
| 95 |
+
Q_EMIT sweepFinished(generation, broadcast, success, error, amount, fee, inputs, vsize, rbf, txid);
|
| 96 |
+
}, Qt::QueuedConnection);
|
| 97 |
+
}, Qt::QueuedConnection);
|
| 98 |
+
return generation;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
void WalletModel::startPollBalance()
|
| 102 |
{
|
| 103 |
// Update the cached balance right away, so every view can make use of it,
|
|
|
|
| 144 |
}
|
| 145 |
|
| 146 |
if (fForceCheckBalanceChanged || block_hash != m_cached_last_update_tip) {
|
| 147 |
+
if (!m_cached_last_update_tip.IsNull() && block_hash != m_cached_last_update_tip) {
|
| 148 |
+
// A sweep preview commits to the currently observed chain state.
|
| 149 |
+
// Do not leave a key or a broadcastable preview around after a tip
|
| 150 |
+
// change; the dialog must collect fresh input and preview again.
|
| 151 |
+
invalidateSweepRequests();
|
| 152 |
+
}
|
| 153 |
fForceCheckBalanceChanged = false;
|
| 154 |
|
| 155 |
// Balance and number of transactions might have changed
|
|
|
|
| 178 |
{
|
| 179 |
// Balance and number of transactions might have changed
|
| 180 |
fForceCheckBalanceChanged = true;
|
| 181 |
+
// A competing spend or wallet update can invalidate a sweep before the
|
| 182 |
+
// next periodic balance refresh observes it.
|
| 183 |
+
invalidateSweepRequests();
|
| 184 |
}
|
| 185 |
|
| 186 |
void WalletModel::updateAddressBook(const QString &address, const QString &label,
|
|
|
|
| 409 |
static void NotifyUnload(WalletModel* walletModel)
|
| 410 |
{
|
| 411 |
qDebug() << "NotifyUnload";
|
| 412 |
+
bool invoked = QMetaObject::invokeMethod(walletModel, [walletModel] {
|
| 413 |
+
walletModel->invalidateSweepRequests();
|
| 414 |
+
Q_EMIT walletModel->unload();
|
| 415 |
+
});
|
| 416 |
assert(invoked);
|
| 417 |
}
|
| 418 |
|
|
|
|
| 484 |
|
| 485 |
void WalletModel::unsubscribeFromCoreSignals()
|
| 486 |
{
|
| 487 |
+
invalidateSweepRequests();
|
| 488 |
// Disconnect signals from wallet
|
| 489 |
m_handler_unload->disconnect();
|
| 490 |
m_handler_status_changed->disconnect();
|