Skip to content
Bitcoin Roots

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();