v31.1-roots.1 · Commit 6: [PATCH 06/17] feat(wallet): preserve Roots signing and spend controls on v31
src/qt/walletmodel.cpp
Before: src/qt/walletmodel.cpp · After: src/qt/walletmodel.cpp
File metadata
index 7a35f8e4d356fb603538a596ad237a8009f89c08..a08ed9d53d0ef3068f3f4d19ea237b842611ded1 100644
Download original patch · Release notes
Diff rows 1–117 of 117. 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
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
// Distributed under the MIT software license, see the accompanying
|
|
3
3
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
4
|
|
|
5
|
+
#include <support/cleanse.h>
|
|
6
|
+
#include <util/rbf.h>
|
|
7
|
+
#include <util/threadnames.h>
|
|
5
8
|
#include <qt/walletmodel.h>
|
|
6
9
|
|
|
7
10
|
#include <qt/addresstablemodel.h>
|
|
@@ -52,14 +55,67 @@ WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, ClientModel
|
|
|
52
55
|
transactionTableModel = new TransactionTableModel(platformStyle, this);
|
|
53
56
|
recentRequestsTableModel = new RecentRequestsTableModel(this);
|
|
54
57
|
|
|
58
|
+
m_sweep_context.moveToThread(&m_sweep_thread);
|
|
59
|
+
m_sweep_thread.start();
|
|
60
|
+
QTimer::singleShot(0, &m_sweep_context, [] { util::ThreadRename("qt-sweep"); });
|
|
61
|
+
|
|
55
62
|
subscribeToCoreSignals();
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
WalletModel::~WalletModel()
|
|
59
66
|
{
|
|
67
|
+
invalidateSweepRequests();
|
|
68
|
+
m_sweep_thread.quit();
|
|
69
|
+
m_sweep_thread.wait();
|
|
60
70
|
unsubscribeFromCoreSignals();
|
|
61
71
|
}
|
|
62
72
|
|
|
73
|
+
bool WalletModel::invalidateSweepRequests()
|
|
74
|
+
{
|
|
75
|
+
if (m_sweep_authorization && !m_sweep_authorization->Cancel()) return false;
|
|
76
|
+
++m_sweep_request_generation;
|
|
77
|
+
m_sweep_authorization.reset();
|
|
78
|
+
Q_EMIT sweepRequestInvalidated(m_sweep_request_generation);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
uint64_t WalletModel::requestSweep(SecureString private_key, CTxDestination destination, bool broadcast,
|
|
83
|
+
std::optional<Txid> expected_txid)
|
|
84
|
+
{
|
|
85
|
+
if (m_sweep_authorization && !m_sweep_authorization->Cancel()) return 0;
|
|
86
|
+
const uint64_t generation{++m_sweep_request_generation};
|
|
87
|
+
auto authorization{std::make_shared<interfaces::SweepAuthorization>()};
|
|
88
|
+
m_sweep_authorization = authorization;
|
|
89
|
+
QMetaObject::invokeMethod(&m_sweep_context, [this, generation, authorization, private_key = std::move(private_key), destination = std::move(destination), broadcast, expected_txid]() mutable {
|
|
90
|
+
auto result{[&]() -> util::Result<interfaces::WalletSweepResult> {
|
|
91
|
+
try {
|
|
92
|
+
return m_wallet->sweepPrivateKey(private_key, destination, broadcast, expected_txid, authorization);
|
|
93
|
+
} catch (const std::exception&) {
|
|
94
|
+
// RPC interruption can throw during shutdown. Keep errors non-secret
|
|
95
|
+
// and finish disposal instead of unwinding through Qt's event loop.
|
|
96
|
+
return util::Error{Untranslated("Sweep interrupted or unavailable")};
|
|
97
|
+
}
|
|
98
|
+
}()};
|
|
99
|
+
if (!private_key.empty()) memory_cleanse(private_key.data(), private_key.size());
|
|
100
|
+
SecureString{}.swap(private_key);
|
|
101
|
+
|
|
102
|
+
const bool success{result.has_value()};
|
|
103
|
+
const QString error{success ? QString{} : QString::fromStdString(util::ErrorString(result).original)};
|
|
104
|
+
const qint64 amount{success ? result->amount : 0};
|
|
105
|
+
const qint64 fee{success ? result->fee : 0};
|
|
106
|
+
const quint64 inputs{success ? result->inputs : 0};
|
|
107
|
+
const qint64 vsize{success ? result->vsize : 0};
|
|
108
|
+
const bool rbf{success && result->tx && SignalsOptInRBF(*result->tx)};
|
|
109
|
+
const QString txid{success && result->tx ? QString::fromStdString(result->tx->GetHash().ToString()) : QString{}};
|
|
110
|
+
QMetaObject::invokeMethod(this, [this, generation, broadcast, success, error, amount, fee, inputs, vsize, rbf, txid] {
|
|
111
|
+
if (generation != m_sweep_request_generation) return;
|
|
112
|
+
m_sweep_authorization.reset();
|
|
113
|
+
Q_EMIT sweepFinished(generation, broadcast, success, error, amount, fee, inputs, vsize, rbf, txid);
|
|
114
|
+
}, Qt::QueuedConnection);
|
|
115
|
+
}, Qt::QueuedConnection);
|
|
116
|
+
return generation;
|
|
117
|
+
}
|
|
118
|
+
|
|
63
119
|
void WalletModel::startPollBalance()
|
|
64
120
|
{
|
|
65
121
|
// Update the cached balance right away, so every view can make use of it,
|
|
@@ -106,6 +162,13 @@ void WalletModel::pollBalanceChanged()
|
|
|
106
162
|
}
|
|
107
163
|
|
|
108
164
|
if (fForceCheckBalanceChanged || block_hash != m_cached_last_update_tip) {
|
|
165
|
+
if (!m_cached_last_update_tip.IsNull() && block_hash != m_cached_last_update_tip) {
|
|
166
|
+
// A sweep preview commits to the currently observed chain state.
|
|
167
|
+
// Do not leave a key or a broadcastable preview around after a tip
|
|
168
|
+
// change; the dialog must collect fresh input and preview again.
|
|
169
|
+
invalidateSweepRequests();
|
|
170
|
+
Q_EMIT walletStateChanged();
|
|
171
|
+
}
|
|
109
172
|
fForceCheckBalanceChanged = false;
|
|
110
173
|
|
|
111
174
|
// Balance and number of transactions might have changed
|
|
@@ -134,6 +197,10 @@ void WalletModel::updateTransaction()
|
|
|
134
197
|
{
|
|
135
198
|
// Balance and number of transactions might have changed
|
|
136
199
|
fForceCheckBalanceChanged = true;
|
|
200
|
+
Q_EMIT walletStateChanged();
|
|
201
|
+
// A competing spend or wallet update can invalidate a sweep before the
|
|
202
|
+
// next periodic balance refresh observes it.
|
|
203
|
+
invalidateSweepRequests();
|
|
137
204
|
}
|
|
138
205
|
|
|
139
206
|
void WalletModel::updateAddressBook(const QString &address, const QString &label,
|
|
@@ -351,7 +418,10 @@ bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureStri
|
|
|
351
418
|
static void NotifyUnload(WalletModel* walletModel)
|
|
352
419
|
{
|
|
353
420
|
qDebug() << "NotifyUnload";
|
|
354
|
-
bool invoked = QMetaObject::invokeMethod(walletModel, "unload");
|
|
421
|
+
bool invoked = QMetaObject::invokeMethod(walletModel, [walletModel] {
|
|
422
|
+
walletModel->invalidateSweepRequests();
|
|
423
|
+
Q_EMIT walletModel->unload();
|
|
424
|
+
});
|
|
355
425
|
assert(invoked);
|
|
356
426
|
}
|
|
357
427
|
|
|
@@ -415,6 +485,7 @@ void WalletModel::subscribeToCoreSignals()
|
|
|
415
485
|
|
|
416
486
|
void WalletModel::unsubscribeFromCoreSignals()
|
|
417
487
|
{
|
|
488
|
+
invalidateSweepRequests();
|
|
418
489
|
// Disconnect signals from wallet
|
|
419
490
|
m_handler_unload->disconnect();
|
|
420
491
|
m_handler_status_changed->disconnect();
|