gui: Store PSBT in std::optional in PSBTOperationsDialog
What changed, and why it matters
This is a small code-quality change in Bitcoin Core's graphical wallet interface. It switches the internal storage of a partially-signed Bitcoin transaction (PSBT) from a plain object to an optional wrapper, so the code no longer keeps a default/empty PSBT sitting around before one is actually loaded. The change itself does not fix a known crash or vulnerability, but it removes a class of potential bugs where an uninitialized PSBT could accidentally be used.
No urgent action required. Reviewers may want to confirm that all slots accessing m_transaction_data are only reachable after openWithPSBT has populated the optional, or add explicit checks/early returns to prevent dereferencing an empty optional.
Security signals we found
Use of std::optional to represent an object that may not yet be initialized
Removal of default-constructed state for a transaction object
No explicit null/empty checks introduced alongside the optional change
Evidence from the diff
The commit refactors PSBTOperationsDialog to store m_transaction_data as std::optional
Changed components
src/qt/psbtoperationsdialog.cppsrc/qt/psbtoperationsdialog.hInspect captured patch +10 / −10
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
index 64ce6d28..c77c827d 100644
--- a/src/qt/psbtoperationsdialog.cpp
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -59,7 +59,7 @@ void PSBTOperationsDialog::openWithPSBT(PartiallySignedTransaction psbtx)
bool complete = FinalizePSBT(psbtx); // Make sure all existing signatures are fully combined before checking for completeness.
if (m_wallet_model) {
size_t n_could_sign;
- const auto err{m_wallet_model->wallet().fillPSBT({.sign = false, .bip32_derivs= true}, &n_could_sign, m_transaction_data, complete)};
+ const auto err{m_wallet_model->wallet().fillPSBT({.sign = false, .bip32_derivs= true}, &n_could_sign, *m_transaction_data, complete)};
if (err) {
showStatus(tr("Failed to load transaction: %1")
.arg(QString::fromStdString(PSBTErrorString(*err).translated)),
@@ -83,7 +83,7 @@ void PSBTOperationsDialog::signTransaction()
WalletModel::UnlockContext ctx(m_wallet_model->requestUnlock());
- const auto err{m_wallet_model->wallet().fillPSBT({.sign = true, .bip32_derivs = true}, &n_signed, m_transaction_data, complete)};
+ const auto err{m_wallet_model->wallet().fillPSBT({.sign = true, .bip32_derivs = true}, &n_signed, *m_transaction_data, complete)};
if (err) {
showStatus(tr("Failed to sign transaction: %1")
@@ -110,7 +110,7 @@ void PSBTOperationsDialog::signTransaction()
void PSBTOperationsDialog::broadcastTransaction()
{
CMutableTransaction mtx;
- if (!FinalizeAndExtractPSBT(m_transaction_data, mtx)) {
+ if (!FinalizeAndExtractPSBT(*m_transaction_data, mtx)) {
// This is never expected to fail unless we were given a malformed PSBT
// (e.g. with an invalid signature.)
showStatus(tr("Unknown error processing transaction."), StatusLevel::ERR);
@@ -133,19 +133,19 @@ void PSBTOperationsDialog::broadcastTransaction()
void PSBTOperationsDialog::copyToClipboard() {
DataStream ssTx{};
- ssTx << m_transaction_data;
+ ssTx << *m_transaction_data;
GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
showStatus(tr("PSBT copied to clipboard."), StatusLevel::INFO);
}
void PSBTOperationsDialog::saveTransaction() {
DataStream ssTx{};
- ssTx << m_transaction_data;
+ ssTx << *m_transaction_data;
QString selected_filter;
QString filename_suggestion = "";
bool first = true;
- for (const CTxOut& out : m_transaction_data.tx->vout) {
+ for (const CTxOut& out : m_transaction_data->tx->vout) {
if (!first) {
filename_suggestion.append("-");
}
@@ -171,8 +171,8 @@ void PSBTOperationsDialog::saveTransaction() {
}
void PSBTOperationsDialog::updateTransactionDisplay() {
- m_ui->transactionDescription->setText(renderTransaction(m_transaction_data));
- showTransactionStatus(m_transaction_data);
+ m_ui->transactionDescription->setText(renderTransaction(*m_transaction_data));
+ showTransactionStatus(*m_transaction_data);
}
QString PSBTOperationsDialog::renderTransaction(const PartiallySignedTransaction &psbtx)
@@ -251,7 +251,7 @@ size_t PSBTOperationsDialog::couldSignInputs(const PartiallySignedTransaction &p
size_t n_signed;
bool complete;
- const auto err{m_wallet_model->wallet().fillPSBT({.sign = false, .bip32_derivs = false}, &n_signed, m_transaction_data, complete)};
+ const auto err{m_wallet_model->wallet().fillPSBT({.sign = false, .bip32_derivs = false}, &n_signed, *m_transaction_data, complete)};
if (err) {
return 0;
diff --git a/src/qt/psbtoperationsdialog.h b/src/qt/psbtoperationsdialog.h
index 55c6ec50..be2b7425 100644
--- a/src/qt/psbtoperationsdialog.h
+++ b/src/qt/psbtoperationsdialog.h
@@ -35,7 +35,7 @@ public Q_SLOTS:
private:
Ui::PSBTOperationsDialog* m_ui;
- PartiallySignedTransaction m_transaction_data;
+ std::optional<PartiallySignedTransaction> m_transaction_data;
WalletModel* m_wallet_model;
ClientModel* m_client_model;
Why this scored 16/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.