Skip to content
Bitcoin Roots

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. Before is on the left; after is on the right. Unified view fits narrow screens more easily.

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