v29.4-roots.3 · Commit 28: [PATCH 28/30] fix(wallet): secure asynchronous sweep cancellation
src/qt/sweepdialog.cpp
Before: src/qt/sweepdialog.cpp · After: src/qt/sweepdialog.cpp
File metadata
index b3b34e5dc8f50739b02c7d5490481fa6c73ad4da..630c562dd7a50d3686f512a576d154662d68bafb 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 <uint256.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 <uint256.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();
|