Skip to content
Bitcoin Roots

v30.3-roots.1 · Commit 28: [PATCH 28/68] fix(wallet): secure asynchronous sweep cancellation

src/qt/sweepdialog.cpp

Before: src/qt/sweepdialog.cpp · After: src/qt/sweepdialog.cpp

File metadata
index c77b34aa8d609797ac24e4042c79037c993cfc42..c410a97daa2643469ab2a149605b2ae48d09a3fd 100644

Download original patch · Release notes

Diff rows 1–85 of 85. 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/sweepdialog.cpp CHANGED
@@ -7,11 +7,13 @@
7
  #include <qt/bitcoinunits.h>
8
  #include <qt/optionsmodel.h>
9
  #include <qt/walletmodel.h>
 
10
  #include <util/translation.h>
11
  #include <primitives/transaction_identifier.h>
12
 
13
- #include <QDialogButtonBox>
14
  #include <QCheckBox>
 
15
  #include <QFormLayout>
16
  #include <QKeyEvent>
17
  #include <QKeySequence>
@@ -28,6 +30,18 @@ class SecureLineEdit final : public QLineEdit
28
  public:
29
  using QLineEdit::QLineEdit;
30
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  protected:
32
  void keyPressEvent(QKeyEvent* event) override
33
  {
@@ -118,7 +132,10 @@ SweepDialog::~SweepDialog() { clearSensitive(); }
118
 
119
  void SweepDialog::invalidate()
120
  {
121
- if (m_request_generation != 0) m_wallet_model->invalidateSweepRequests();
 
 
 
122
  m_request_generation = 0;
123
  m_private_key_cache.clear();
124
  m_preview_txid.clear();
@@ -147,13 +164,13 @@ void SweepDialog::preview()
147
  {
148
  const CTxDestination destination{DecodeDestination(m_destination->text().toStdString())};
149
  if (!IsValidDestination(destination)) { setResult(tr("Enter a valid destination address."), false); return; }
150
- m_private_key_cache = SecureString{m_private_key->text().toStdString()};
151
- if (m_private_key_cache.empty()) { setResult(tr("Enter a private key."), false); return; }
152
  const QSignalBlocker block{m_private_key};
153
- m_private_key->clear();
 
154
  setBusy(true);
155
  m_result->setText(tr("Scanning for eligible coins and preparing a preview…"));
156
  m_request_generation = m_wallet_model->requestSweep(m_private_key_cache, destination, false);
 
157
  }
158
 
159
  void SweepDialog::broadcast()
@@ -170,6 +187,18 @@ void SweepDialog::broadcast()
170
  m_preview_current = false;
171
  m_result->setText(tr("Rechecking the preview and broadcasting the sweep…"));
172
  m_request_generation = m_wallet_model->requestSweep(m_private_key_cache, destination, true, expected_txid);
 
 
 
 
 
 
 
 
 
 
 
 
173
  }
174
 
175
  void SweepDialog::handleSweepFinished(uint64_t generation, bool broadcast, bool success, const QString& error,
@@ -204,7 +233,7 @@ void SweepDialog::handleSweepFinished(uint64_t generation, bool broadcast, bool
204
 
205
  void SweepDialog::clearSensitive()
206
  {
207
- if (m_request_generation != 0) m_wallet_model->invalidateSweepRequests();
208
  m_request_generation = 0;
209
  const QSignalBlocker block{m_private_key};
210
  m_private_key->clear();
 
7
  #include <qt/bitcoinunits.h>
8
  #include <qt/optionsmodel.h>
9
  #include <qt/walletmodel.h>
10
+ #include <support/cleanse.h>
11
  #include <util/translation.h>
12
  #include <primitives/transaction_identifier.h>
13
 
14
+ #include <QByteArray>
15
  #include <QCheckBox>
16
+ #include <QDialogButtonBox>
17
  #include <QFormLayout>
18
  #include <QKeyEvent>
19
  #include <QKeySequence>
 
30
  public:
31
  using QLineEdit::QLineEdit;
32
 
33
+ SecureString TakeText()
34
+ {
35
+ QByteArray encoded{text().toLatin1()};
36
+ SecureString result{encoded.constData(), static_cast<size_t>(encoded.size())};
37
+ if (!encoded.isEmpty()) memory_cleanse(encoded.data(), encoded.size());
38
+ // QLineEdit does not expose its internal buffer for cleansing. Replace
39
+ // its contents before clearing so the widget no longer retains the WIF.
40
+ setText(QString(text().size(), QChar{' '}));
41
+ clear();
42
+ return result;
43
+ }
44
+
45
  protected:
46
  void keyPressEvent(QKeyEvent* event) override
47
  {
 
132
 
133
  void SweepDialog::invalidate()
134
  {
135
+ if (m_request_generation != 0 && !m_wallet_model->invalidateSweepRequests()) {
136
+ m_result->setText(tr("Sweep broadcast submission has started and can no longer be cancelled. Wait for its result."));
137
+ return;
138
+ }
139
  m_request_generation = 0;
140
  m_private_key_cache.clear();
141
  m_preview_txid.clear();
 
164
  {
165
  const CTxDestination destination{DecodeDestination(m_destination->text().toStdString())};
166
  if (!IsValidDestination(destination)) { setResult(tr("Enter a valid destination address."), false); return; }
 
 
167
  const QSignalBlocker block{m_private_key};
168
+ m_private_key_cache = static_cast<SecureLineEdit*>(m_private_key)->TakeText();
169
+ if (m_private_key_cache.empty()) { setResult(tr("Enter a private key."), false); return; }
170
  setBusy(true);
171
  m_result->setText(tr("Scanning for eligible coins and preparing a preview…"));
172
  m_request_generation = m_wallet_model->requestSweep(m_private_key_cache, destination, false);
173
+ if (m_request_generation == 0) setResult(tr("Another sweep broadcast is already being submitted."), false);
174
  }
175
 
176
  void SweepDialog::broadcast()
 
187
  m_preview_current = false;
188
  m_result->setText(tr("Rechecking the preview and broadcasting the sweep…"));
189
  m_request_generation = m_wallet_model->requestSweep(m_private_key_cache, destination, true, expected_txid);
190
+ if (m_request_generation == 0) setResult(tr("Sweep broadcast submission has already started. Wait for its result."), false);
191
+ }
192
+
193
+ void SweepDialog::reject()
194
+ {
195
+ if (m_request_generation != 0 && !m_wallet_model->invalidateSweepRequests()) {
196
+ m_result->setText(tr("Sweep broadcast submission has started and can no longer be cancelled. Wait for its result."));
197
+ return;
198
+ }
199
+ m_request_generation = 0;
200
+ clearSensitive();
201
+ QDialog::reject();
202
  }
203
 
204
  void SweepDialog::handleSweepFinished(uint64_t generation, bool broadcast, bool success, const QString& error,
 
233
 
234
  void SweepDialog::clearSensitive()
235
  {
236
+ if (m_request_generation != 0 && !m_wallet_model->invalidateSweepRequests()) return;
237
  m_request_generation = 0;
238
  const QSignalBlocker block{m_private_key};
239
  m_private_key->clear();