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. Before is on the left; after is on the right. Unified view fits narrow screens more easily.
src/qt/walletmodel.cpp
CHANGED
|
@@ -2,6 +2,9 @@
|
|
| 2 |
// Distributed under the MIT software license, see the accompanying
|
| 3 |
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
| 4 |
|
|
|
|
|
|
|
|
|
|
| 5 |
#include <qt/walletmodel.h>
|
| 6 |
|
| 7 |
#include <qt/addresstablemodel.h>
|
|
@@ -52,14 +55,67 @@ WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, ClientModel
|
|
| 52 |
transactionTableModel = new TransactionTableModel(platformStyle, this);
|
| 53 |
recentRequestsTableModel = new RecentRequestsTableModel(this);
|
| 54 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
subscribeToCoreSignals();
|
| 56 |
}
|
| 57 |
|
| 58 |
WalletModel::~WalletModel()
|
| 59 |
{
|
|
|
|
|
|
|
|
|
|
| 60 |
unsubscribeFromCoreSignals();
|
| 61 |
}
|
| 62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
void WalletModel::startPollBalance()
|
| 64 |
{
|
| 65 |
// Update the cached balance right away, so every view can make use of it,
|
|
@@ -106,6 +162,13 @@ void WalletModel::pollBalanceChanged()
|
|
| 106 |
}
|
| 107 |
|
| 108 |
if (fForceCheckBalanceChanged || block_hash != m_cached_last_update_tip) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
fForceCheckBalanceChanged = false;
|
| 110 |
|
| 111 |
// Balance and number of transactions might have changed
|
|
@@ -134,6 +197,10 @@ void WalletModel::updateTransaction()
|
|
| 134 |
{
|
| 135 |
// Balance and number of transactions might have changed
|
| 136 |
fForceCheckBalanceChanged = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
}
|
| 138 |
|
| 139 |
void WalletModel::updateAddressBook(const QString &address, const QString &label,
|
|
@@ -351,7 +418,10 @@ bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureStri
|
|
| 351 |
static void NotifyUnload(WalletModel* walletModel)
|
| 352 |
{
|
| 353 |
qDebug() << "NotifyUnload";
|
| 354 |
-
bool invoked = QMetaObject::invokeMethod(walletModel, "unload");
|
|
|
|
|
|
|
|
|
|
| 355 |
assert(invoked);
|
| 356 |
}
|
| 357 |
|
|
@@ -415,6 +485,7 @@ void WalletModel::subscribeToCoreSignals()
|
|
| 415 |
|
| 416 |
void WalletModel::unsubscribeFromCoreSignals()
|
| 417 |
{
|
|
|
|
| 418 |
// Disconnect signals from wallet
|
| 419 |
m_handler_unload->disconnect();
|
| 420 |
m_handler_status_changed->disconnect();
|
|
|
|
| 2 |
// Distributed under the MIT software license, see the accompanying
|
| 3 |
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
| 4 |
|
| 5 |
+
#include <support/cleanse.h>
|
| 6 |
+
#include <util/rbf.h>
|
| 7 |
+
#include <util/threadnames.h>
|
| 8 |
#include <qt/walletmodel.h>
|
| 9 |
|
| 10 |
#include <qt/addresstablemodel.h>
|
|
|
|
| 55 |
transactionTableModel = new TransactionTableModel(platformStyle, this);
|
| 56 |
recentRequestsTableModel = new RecentRequestsTableModel(this);
|
| 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 |
+
|
| 62 |
subscribeToCoreSignals();
|
| 63 |
}
|
| 64 |
|
| 65 |
WalletModel::~WalletModel()
|
| 66 |
{
|
| 67 |
+
invalidateSweepRequests();
|
| 68 |
+
m_sweep_thread.quit();
|
| 69 |
+
m_sweep_thread.wait();
|
| 70 |
unsubscribeFromCoreSignals();
|
| 71 |
}
|
| 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 |
+
|
| 119 |
void WalletModel::startPollBalance()
|
| 120 |
{
|
| 121 |
// Update the cached balance right away, so every view can make use of it,
|
|
|
|
| 162 |
}
|
| 163 |
|
| 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 |
+
}
|
| 172 |
fForceCheckBalanceChanged = false;
|
| 173 |
|
| 174 |
// Balance and number of transactions might have changed
|
|
|
|
| 197 |
{
|
| 198 |
// Balance and number of transactions might have changed
|
| 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();
|
| 204 |
}
|
| 205 |
|
| 206 |
void WalletModel::updateAddressBook(const QString &address, const QString &label,
|
|
|
|
| 418 |
static void NotifyUnload(WalletModel* walletModel)
|
| 419 |
{
|
| 420 |
qDebug() << "NotifyUnload";
|
| 421 |
+
bool invoked = QMetaObject::invokeMethod(walletModel, [walletModel] {
|
| 422 |
+
walletModel->invalidateSweepRequests();
|
| 423 |
+
Q_EMIT walletModel->unload();
|
| 424 |
+
});
|
| 425 |
assert(invoked);
|
| 426 |
}
|
| 427 |
|
|
|
|
| 485 |
|
| 486 |
void WalletModel::unsubscribeFromCoreSignals()
|
| 487 |
{
|
| 488 |
+
invalidateSweepRequests();
|
| 489 |
// Disconnect signals from wallet
|
| 490 |
m_handler_unload->disconnect();
|
| 491 |
m_handler_status_changed->disconnect();
|