v30.3-roots.1 · Commit 24: [PATCH 24/68] 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 694fb535b572ec192b2bb7c1db240052d2e9495e..9936f1c045d89e23583fc45e1f7f014319a360cf 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>
|
|
27
29
|
|
|
@@ -51,14 +53,52 @@ WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, ClientModel
|
|
|
51
53
|
transactionTableModel = new TransactionTableModel(platformStyle, this);
|
|
52
54
|
recentRequestsTableModel = new RecentRequestsTableModel(this);
|
|
53
55
|
|
|
56
|
+
m_sweep_context.moveToThread(&m_sweep_thread);
|
|
57
|
+
m_sweep_thread.start();
|
|
58
|
+
QTimer::singleShot(0, &m_sweep_context, [] { util::ThreadRename("qt-sweep"); });
|
|
59
|
+
|
|
54
60
|
subscribeToCoreSignals();
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
WalletModel::~WalletModel()
|
|
58
64
|
{
|
|
65
|
+
invalidateSweepRequests();
|
|
66
|
+
m_sweep_thread.quit();
|
|
67
|
+
m_sweep_thread.wait();
|
|
59
68
|
unsubscribeFromCoreSignals();
|
|
60
69
|
}
|
|
61
70
|
|
|
71
|
+
uint64_t WalletModel::invalidateSweepRequests()
|
|
72
|
+
{
|
|
73
|
+
++m_sweep_request_generation;
|
|
74
|
+
Q_EMIT sweepRequestInvalidated(m_sweep_request_generation);
|
|
75
|
+
return m_sweep_request_generation;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
uint64_t WalletModel::requestSweep(SecureString private_key, CTxDestination destination, bool broadcast,
|
|
79
|
+
std::optional<Txid> expected_txid)
|
|
80
|
+
{
|
|
81
|
+
const uint64_t generation{++m_sweep_request_generation};
|
|
82
|
+
QMetaObject::invokeMethod(&m_sweep_context, [this, generation, private_key = std::move(private_key), destination = std::move(destination), broadcast, expected_txid]() mutable {
|
|
83
|
+
auto result{m_wallet->sweepPrivateKey(private_key, destination, broadcast, expected_txid)};
|
|
84
|
+
private_key.clear();
|
|
85
|
+
|
|
86
|
+
const bool success{result.has_value()};
|
|
87
|
+
const QString error{success ? QString{} : QString::fromStdString(util::ErrorString(result).original)};
|
|
88
|
+
const qint64 amount{success ? result->amount : 0};
|
|
89
|
+
const qint64 fee{success ? result->fee : 0};
|
|
90
|
+
const quint64 inputs{success ? result->inputs : 0};
|
|
91
|
+
const qint64 vsize{success ? result->vsize : 0};
|
|
92
|
+
const bool rbf{success && result->tx && SignalsOptInRBF(*result->tx)};
|
|
93
|
+
const QString txid{success && result->tx ? QString::fromStdString(result->tx->GetHash().ToString()) : QString{}};
|
|
94
|
+
QMetaObject::invokeMethod(this, [this, generation, broadcast, success, error, amount, fee, inputs, vsize, rbf, txid] {
|
|
95
|
+
if (generation != m_sweep_request_generation) return;
|
|
96
|
+
Q_EMIT sweepFinished(generation, broadcast, success, error, amount, fee, inputs, vsize, rbf, txid);
|
|
97
|
+
}, Qt::QueuedConnection);
|
|
98
|
+
}, Qt::QueuedConnection);
|
|
99
|
+
return generation;
|
|
100
|
+
}
|
|
101
|
+
|
|
62
102
|
void WalletModel::startPollBalance()
|
|
63
103
|
{
|
|
64
104
|
// Update the cached balance right away, so every view can make use of it,
|
|
@@ -105,6 +145,12 @@ void WalletModel::pollBalanceChanged()
|
|
|
105
145
|
}
|
|
106
146
|
|
|
107
147
|
if (fForceCheckBalanceChanged || block_hash != m_cached_last_update_tip) {
|
|
148
|
+
if (!m_cached_last_update_tip.IsNull() && block_hash != m_cached_last_update_tip) {
|
|
149
|
+
// A sweep preview commits to the currently observed chain state.
|
|
150
|
+
// Do not leave a key or a broadcastable preview around after a tip
|
|
151
|
+
// change; the dialog must collect fresh input and preview again.
|
|
152
|
+
invalidateSweepRequests();
|
|
153
|
+
}
|
|
108
154
|
fForceCheckBalanceChanged = false;
|
|
109
155
|
|
|
110
156
|
// Balance and number of transactions might have changed
|
|
@@ -133,6 +179,9 @@ void WalletModel::updateTransaction()
|
|
|
133
179
|
{
|
|
134
180
|
// Balance and number of transactions might have changed
|
|
135
181
|
fForceCheckBalanceChanged = true;
|
|
182
|
+
// A competing spend or wallet update can invalidate a sweep before the
|
|
183
|
+
// next periodic balance refresh observes it.
|
|
184
|
+
invalidateSweepRequests();
|
|
136
185
|
}
|
|
137
186
|
|
|
138
187
|
void WalletModel::updateAddressBook(const QString &address, const QString &label,
|
|
@@ -354,7 +403,10 @@ bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureStri
|
|
|
354
403
|
static void NotifyUnload(WalletModel* walletModel)
|
|
355
404
|
{
|
|
356
405
|
qDebug() << "NotifyUnload";
|
|
357
|
-
bool invoked = QMetaObject::invokeMethod(walletModel, "unload");
|
|
406
|
+
bool invoked = QMetaObject::invokeMethod(walletModel, [walletModel] {
|
|
407
|
+
walletModel->invalidateSweepRequests();
|
|
408
|
+
Q_EMIT walletModel->unload();
|
|
409
|
+
});
|
|
358
410
|
assert(invoked);
|
|
359
411
|
}
|
|
360
412
|
|
|
@@ -418,6 +470,7 @@ void WalletModel::subscribeToCoreSignals()
|
|
|
418
470
|
|
|
419
471
|
void WalletModel::unsubscribeFromCoreSignals()
|
|
420
472
|
{
|
|
473
|
+
invalidateSweepRequests();
|
|
421
474
|
// Disconnect signals from wallet
|
|
422
475
|
m_handler_unload->disconnect();
|
|
423
476
|
m_handler_status_changed->disconnect();
|