Skip to content
Bitcoin Roots

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. 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>
27
 
@@ -51,14 +53,52 @@ WalletModel::WalletModel(std::unique_ptr<interfaces::Wallet> wallet, ClientModel
51
  transactionTableModel = new TransactionTableModel(platformStyle, this);
52
  recentRequestsTableModel = new RecentRequestsTableModel(this);
53
 
 
 
 
 
54
  subscribeToCoreSignals();
55
  }
56
 
57
  WalletModel::~WalletModel()
58
  {
 
 
 
59
  unsubscribeFromCoreSignals();
60
  }
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  void WalletModel::startPollBalance()
63
  {
64
  // Update the cached balance right away, so every view can make use of it,
@@ -105,6 +145,12 @@ void WalletModel::pollBalanceChanged()
105
  }
106
 
107
  if (fForceCheckBalanceChanged || block_hash != m_cached_last_update_tip) {
 
 
 
 
 
 
108
  fForceCheckBalanceChanged = false;
109
 
110
  // Balance and number of transactions might have changed
@@ -133,6 +179,9 @@ void WalletModel::updateTransaction()
133
  {
134
  // Balance and number of transactions might have changed
135
  fForceCheckBalanceChanged = true;
 
 
 
136
  }
137
 
138
  void WalletModel::updateAddressBook(const QString &address, const QString &label,
@@ -354,7 +403,10 @@ bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureStri
354
  static void NotifyUnload(WalletModel* walletModel)
355
  {
356
  qDebug() << "NotifyUnload";
357
- bool invoked = QMetaObject::invokeMethod(walletModel, "unload");
 
 
 
358
  assert(invoked);
359
  }
360
 
@@ -418,6 +470,7 @@ void WalletModel::subscribeToCoreSignals()
418
 
419
  void WalletModel::unsubscribeFromCoreSignals()
420
  {
 
421
  // Disconnect signals from wallet
422
  m_handler_unload->disconnect();
423
  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>
29
 
 
53
  transactionTableModel = new TransactionTableModel(platformStyle, this);
54
  recentRequestsTableModel = new RecentRequestsTableModel(this);
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
+
60
  subscribeToCoreSignals();
61
  }
62
 
63
  WalletModel::~WalletModel()
64
  {
65
+ invalidateSweepRequests();
66
+ m_sweep_thread.quit();
67
+ m_sweep_thread.wait();
68
  unsubscribeFromCoreSignals();
69
  }
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
+
102
  void WalletModel::startPollBalance()
103
  {
104
  // Update the cached balance right away, so every view can make use of it,
 
145
  }
146
 
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
+ }
154
  fForceCheckBalanceChanged = false;
155
 
156
  // Balance and number of transactions might have changed
 
179
  {
180
  // Balance and number of transactions might have changed
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();
185
  }
186
 
187
  void WalletModel::updateAddressBook(const QString &address, const QString &label,
 
403
  static void NotifyUnload(WalletModel* walletModel)
404
  {
405
  qDebug() << "NotifyUnload";
406
+ bool invoked = QMetaObject::invokeMethod(walletModel, [walletModel] {
407
+ walletModel->invalidateSweepRequests();
408
+ Q_EMIT walletModel->unload();
409
+ });
410
  assert(invoked);
411
  }
412
 
 
470
 
471
  void WalletModel::unsubscribeFromCoreSignals()
472
  {
473
+ invalidateSweepRequests();
474
  // Disconnect signals from wallet
475
  m_handler_unload->disconnect();
476
  m_handler_status_changed->disconnect();