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. The two line-number columns refer to before and after.
src/qt/walletmodel.cpp
CHANGED
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
#include <node/types.h>
|
|
23
23
|
#include <psbt.h>
|
|
24
24
|
#include <util/translation.h>
|
|
25
|
+
#include <util/threadnames.h>
|
|
26
|
+
#include <util/rbf.h>
|
|
25
27
|
#include <wallet/coincontrol.h>
|
|
26
28
|
#include <wallet/wallet.h> // for CRecipient
|
|
27
29
|
|
|
@@ -50,14 +52,52 @@ WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, ClientModel
|
|
|
50
52
|
transactionTableModel = new TransactionTableModel(platformStyle, this);
|
|
51
53
|
recentRequestsTableModel = new RecentRequestsTableModel(this);
|
|
52
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
|
+
|
|
53
59
|
subscribeToCoreSignals();
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
WalletModel::~WalletModel()
|
|
57
63
|
{
|
|
64
|
+
invalidateSweepRequests();
|
|
65
|
+
m_sweep_thread.quit();
|
|
66
|
+
m_sweep_thread.wait();
|
|
58
67
|
unsubscribeFromCoreSignals();
|
|
59
68
|
}
|
|
60
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
|
+
|
|
61
101
|
void WalletModel::startPollBalance()
|
|
62
102
|
{
|
|
63
103
|
// Update the cached balance right away, so every view can make use of it,
|
|
@@ -104,6 +144,12 @@ void WalletModel::pollBalanceChanged()
|
|
|
104
144
|
}
|
|
105
145
|
|
|
106
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
|
+
}
|
|
107
153
|
fForceCheckBalanceChanged = false;
|
|
108
154
|
|
|
109
155
|
// Balance and number of transactions might have changed
|
|
@@ -132,6 +178,9 @@ void WalletModel::updateTransaction()
|
|
|
132
178
|
{
|
|
133
179
|
// Balance and number of transactions might have changed
|
|
134
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();
|
|
135
184
|
}
|
|
136
185
|
|
|
137
186
|
void WalletModel::updateAddressBook(const QString &address, const QString &label,
|
|
@@ -360,7 +409,10 @@ bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureStri
|
|
|
360
409
|
static void NotifyUnload(WalletModel* walletModel)
|
|
361
410
|
{
|
|
362
411
|
qDebug() << "NotifyUnload";
|
|
363
|
-
bool invoked = QMetaObject::invokeMethod(walletModel, "unload");
|
|
412
|
+
bool invoked = QMetaObject::invokeMethod(walletModel, [walletModel] {
|
|
413
|
+
walletModel->invalidateSweepRequests();
|
|
414
|
+
Q_EMIT walletModel->unload();
|
|
415
|
+
});
|
|
364
416
|
assert(invoked);
|
|
365
417
|
}
|
|
366
418
|
|
|
@@ -432,6 +484,7 @@ void WalletModel::subscribeToCoreSignals()
|
|
|
432
484
|
|
|
433
485
|
void WalletModel::unsubscribeFromCoreSignals()
|
|
434
486
|
{
|
|
487
|
+
invalidateSweepRequests();
|
|
435
488
|
// Disconnect signals from wallet
|
|
436
489
|
m_handler_unload->disconnect();
|
|
437
490
|
m_handler_status_changed->disconnect();
|