Skip to content

Commit 2e01b69

Browse files
committed
Merge #441: Add Create Unsigned button to SendConfirmationDialog
742918c qt: hide Create Unsigned button behind an expert mode option (Andrew Chow) 5c3b800 qt: Add Create Unsigned button to SendConfirmationDialog (Andrew Chow) Pull request description: Instead of having different buttons or changing button behavior for making a PSBT, just have SendConfirmationDialog return whether the user wants a PSBT or a broadcasted transaction. Since this dialog is used by both the bumpFeeAction and the SendCoinsDialog, changes to both to support the different behavior is needed. They will check the return value of the SendConfirmationDialog for whether a PSBT needs to be created instead of checking whether private keys are disabled. Strings used in this dialog are being slightly modified to work with both private keys enabled and disabled wallets. Moved from bitcoin/bitcoin#18789 ACKs for top commit: jarolrod: ACK 742918c ryanofsky: Code review ACK 742918c. Just suggested changes since last review. Looks great! hebasto: ACK 742918c, tested on Linux Mint 20.2 (Qt 5.12.8). Tree-SHA512: dd29f4364c7b4f15befe8fe63257b26187918786b005e0f8336183270b1a162680b93f6ced60f0285c6e607c084cc0d24950fc68a8f9c056521ede614041be66
2 parents 6182e50 + 742918c commit 2e01b69

File tree

7 files changed

+73
-28
lines changed

7 files changed

+73
-28
lines changed

src/qt/forms/optionsdialog.ui

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,16 @@
252252
</property>
253253
</widget>
254254
</item>
255+
<item>
256+
<widget class="QCheckBox" name="m_enable_psbt_controls">
257+
<property name="text">
258+
<string extracomment="An options window setting to enable PSBT controls.">Enable &amp;PSBT controls</string>
259+
</property>
260+
<property name="toolTip">
261+
<string extracomment="Tooltip text for options window setting that enables PSBT controls.">Whether to show PSBT controls.</string>
262+
</property>
263+
</widget>
264+
</item>
255265
</layout>
256266
</widget>
257267
</item>

src/qt/optionsdialog.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ void OptionsDialog::setMapper()
242242
mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures);
243243
mapper->addMapping(ui->subFeeFromAmount, OptionsModel::SubFeeFromAmount);
244244
mapper->addMapping(ui->externalSignerPath, OptionsModel::ExternalSignerPath);
245+
mapper->addMapping(ui->m_enable_psbt_controls, OptionsModel::EnablePSBTControls);
245246

246247
/* Network */
247248
mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);

src/qt/optionsmodel.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ void OptionsModel::Init(bool resetSettings)
8383
settings.setValue("fCoinControlFeatures", false);
8484
fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool();
8585

86+
if (!settings.contains("enable_psbt_controls")) {
87+
settings.setValue("enable_psbt_controls", false);
88+
}
89+
m_enable_psbt_controls = settings.value("enable_psbt_controls", false).toBool();
90+
8691
// These are shared with the core or have a command-line parameter
8792
// and we want command-line parameters to overwrite the GUI settings.
8893
//
@@ -360,6 +365,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
360365
return m_use_embedded_monospaced_font;
361366
case CoinControlFeatures:
362367
return fCoinControlFeatures;
368+
case EnablePSBTControls:
369+
return settings.value("enable_psbt_controls");
363370
case Prune:
364371
return settings.value("bPrune");
365372
case PruneSize:
@@ -507,6 +514,10 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
507514
settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
508515
Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures);
509516
break;
517+
case EnablePSBTControls:
518+
m_enable_psbt_controls = value.toBool();
519+
settings.setValue("enable_psbt_controls", m_enable_psbt_controls);
520+
break;
510521
case Prune:
511522
if (settings.value("bPrune") != value) {
512523
settings.setValue("bPrune", value);

src/qt/optionsmodel.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class OptionsModel : public QAbstractListModel
6969
SpendZeroConfChange, // bool
7070
Listen, // bool
7171
Server, // bool
72+
EnablePSBTControls, // bool
7273
OptionIDRowCount,
7374
};
7475

@@ -90,6 +91,7 @@ class OptionsModel : public QAbstractListModel
9091
bool getUseEmbeddedMonospacedFont() const { return m_use_embedded_monospaced_font; }
9192
bool getCoinControlFeatures() const { return fCoinControlFeatures; }
9293
bool getSubFeeFromAmount() const { return m_sub_fee_from_amount; }
94+
bool getEnablePSBTControls() const { return m_enable_psbt_controls; }
9395
const QString& getOverriddenByCommandLine() { return strOverriddenByCommandLine; }
9496

9597
/* Explicit setters */
@@ -115,6 +117,7 @@ class OptionsModel : public QAbstractListModel
115117
bool m_use_embedded_monospaced_font;
116118
bool fCoinControlFeatures;
117119
bool m_sub_fee_from_amount;
120+
bool m_enable_psbt_controls;
118121
/* settings that were overridden by command-line */
119122
QString strOverriddenByCommandLine;
120123

src/qt/sendcoinsdialog.cpp

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -324,16 +324,22 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
324324
formatted.append(recipientElement);
325325
}
326326

327-
if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) {
328-
question_string.append(tr("Do you want to draft this transaction?"));
329-
} else {
330-
question_string.append(tr("Are you sure you want to send?"));
331-
}
332-
327+
/*: Message displayed when attempting to create a transaction. Cautionary text to prompt the user to verify
328+
that the displayed transaction details represent the transaction the user intends to create. */
329+
question_string.append(tr("Do you want to create this transaction?"));
333330
question_string.append("<br /><span style='font-size:10pt;'>");
334331
if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) {
332+
/*: Text to inform a user attempting to create a transaction of their current options. At this stage,
333+
a user can only create a PSBT. This string is displayed when private keys are disabled and an external
334+
signer is not available. */
335335
question_string.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
336+
} else if (model->getOptionsModel()->getEnablePSBTControls()) {
337+
/*: Text to inform a user attempting to create a transaction of their current options. At this stage,
338+
a user can send their transaction or create a PSBT. This string is displayed when both private keys
339+
and PSBT controls are enabled. */
340+
question_string.append(tr("Please, review your transaction. You can create and send this transaction or create a Partially Signed Bitcoin Transaction (PSBT), which you can save or copy and then sign with, e.g., an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
336341
} else {
342+
/*: Text to prompt a user to review the details of the transaction they are attempting to send. */
337343
question_string.append(tr("Please, review your transaction."));
338344
}
339345
question_string.append("</span>%1");
@@ -397,21 +403,20 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
397403
if (!PrepareSendText(question_string, informative_text, detailed_text)) return;
398404
assert(m_current_transaction);
399405

400-
const QString confirmation = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
401-
const QString confirmButtonText = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Create Unsigned") : tr("Sign and send");
402-
auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
406+
const QString confirmation = tr("Confirm send coins");
407+
auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, !model->wallet().privateKeysDisabled(), model->getOptionsModel()->getEnablePSBTControls(), this);
403408
confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
404409
// TODO: Replace QDialog::exec() with safer QDialog::show().
405410
const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
406411

407-
if(retval != QMessageBox::Yes)
412+
if(retval != QMessageBox::Yes && retval != QMessageBox::Save)
408413
{
409414
fNewRecipientAllowed = true;
410415
return;
411416
}
412417

413418
bool send_failure = false;
414-
if (model->wallet().privateKeysDisabled()) {
419+
if (retval == QMessageBox::Save) {
415420
CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
416421
PartiallySignedTransaction psbtx(mtx);
417422
bool complete = false;
@@ -512,6 +517,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
512517
assert(false);
513518
} // msgBox.exec()
514519
} else {
520+
assert(!model->wallet().privateKeysDisabled());
515521
// now send the prepared transaction
516522
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
517523
// process sendStatus and on error generate message shown to user
@@ -1031,52 +1037,62 @@ void SendCoinsDialog::coinControlUpdateLabels()
10311037
}
10321038
}
10331039

1034-
SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, const QString& _confirmButtonText, QWidget* parent)
1035-
: QMessageBox(parent), secDelay(_secDelay), confirmButtonText(_confirmButtonText)
1040+
SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, bool enable_send, bool always_show_unsigned, QWidget* parent)
1041+
: QMessageBox(parent), secDelay(_secDelay), m_enable_send(enable_send)
10361042
{
10371043
setIcon(QMessageBox::Question);
10381044
setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
10391045
setText(text);
10401046
setInformativeText(informative_text);
10411047
setDetailedText(detailed_text);
10421048
setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
1049+
if (always_show_unsigned || !enable_send) addButton(QMessageBox::Save);
10431050
setDefaultButton(QMessageBox::Cancel);
10441051
yesButton = button(QMessageBox::Yes);
10451052
if (confirmButtonText.isEmpty()) {
10461053
confirmButtonText = yesButton->text();
10471054
}
1048-
updateYesButton();
1055+
m_psbt_button = button(QMessageBox::Save);
1056+
updateButtons();
10491057
connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown);
10501058
}
10511059

10521060
int SendConfirmationDialog::exec()
10531061
{
1054-
updateYesButton();
1062+
updateButtons();
10551063
countDownTimer.start(1000);
10561064
return QMessageBox::exec();
10571065
}
10581066

10591067
void SendConfirmationDialog::countDown()
10601068
{
10611069
secDelay--;
1062-
updateYesButton();
1070+
updateButtons();
10631071

10641072
if(secDelay <= 0)
10651073
{
10661074
countDownTimer.stop();
10671075
}
10681076
}
10691077

1070-
void SendConfirmationDialog::updateYesButton()
1078+
void SendConfirmationDialog::updateButtons()
10711079
{
10721080
if(secDelay > 0)
10731081
{
10741082
yesButton->setEnabled(false);
1075-
yesButton->setText(confirmButtonText + " (" + QString::number(secDelay) + ")");
1083+
yesButton->setText(confirmButtonText + (m_enable_send ? (" (" + QString::number(secDelay) + ")") : QString("")));
1084+
if (m_psbt_button) {
1085+
m_psbt_button->setEnabled(false);
1086+
m_psbt_button->setText(m_psbt_button_text + " (" + QString::number(secDelay) + ")");
1087+
}
10761088
}
10771089
else
10781090
{
1079-
yesButton->setEnabled(true);
1091+
yesButton->setEnabled(m_enable_send);
10801092
yesButton->setText(confirmButtonText);
1093+
if (m_psbt_button) {
1094+
m_psbt_button->setEnabled(true);
1095+
m_psbt_button->setText(m_psbt_button_text);
1096+
}
10811097
}
10821098
}

src/qt/sendcoinsdialog.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,18 +114,21 @@ class SendConfirmationDialog : public QMessageBox
114114
Q_OBJECT
115115

116116
public:
117-
SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, const QString& confirmText = "", QWidget* parent = nullptr);
117+
SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, bool enable_send = true, bool always_show_unsigned = true, QWidget* parent = nullptr);
118118
int exec() override;
119119

120120
private Q_SLOTS:
121121
void countDown();
122-
void updateYesButton();
122+
void updateButtons();
123123

124124
private:
125125
QAbstractButton *yesButton;
126+
QAbstractButton *m_psbt_button;
126127
QTimer countDownTimer;
127128
int secDelay;
128-
QString confirmButtonText;
129+
QString confirmButtonText{tr("Send")};
130+
bool m_enable_send;
131+
QString m_psbt_button_text{tr("Create Unsigned")};
129132
};
130133

131134
#endif // BITCOIN_QT_SENDCOINSDIALOG_H

src/qt/walletmodel.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -480,10 +480,9 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
480480
return false;
481481
}
482482

483-
const bool create_psbt = m_wallet->privateKeysDisabled();
484-
485483
// allow a user based fee verification
486-
QString questionString = create_psbt ? tr("Do you want to draft a transaction with fee increase?") : tr("Do you want to increase the fee?");
484+
/*: Asks a user if they would like to manually increase the fee of a transaction that has already been created. */
485+
QString questionString = tr("Do you want to increase the fee?");
487486
questionString.append("<br />");
488487
questionString.append("<table style=\"text-align: left;\">");
489488
questionString.append("<tr><td>");
@@ -506,13 +505,13 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
506505
questionString.append(tr("Warning: This may pay the additional fee by reducing change outputs or adding inputs, when necessary. It may add a new change output if one does not already exist. These changes may potentially leak privacy."));
507506
}
508507

509-
auto confirmationDialog = new SendConfirmationDialog(tr("Confirm fee bump"), questionString);
508+
auto confirmationDialog = new SendConfirmationDialog(tr("Confirm fee bump"), questionString, "", "", SEND_CONFIRM_DELAY, !m_wallet->privateKeysDisabled(), getOptionsModel()->getEnablePSBTControls(), nullptr);
510509
confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
511510
// TODO: Replace QDialog::exec() with safer QDialog::show().
512511
const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
513512

514513
// cancel sign&broadcast if user doesn't want to bump the fee
515-
if (retval != QMessageBox::Yes) {
514+
if (retval != QMessageBox::Yes && retval != QMessageBox::Save) {
516515
return false;
517516
}
518517

@@ -523,7 +522,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
523522
}
524523

525524
// Short-circuit if we are returning a bumped transaction PSBT to clipboard
526-
if (create_psbt) {
525+
if (retval == QMessageBox::Save) {
527526
PartiallySignedTransaction psbtx(mtx);
528527
bool complete = false;
529528
const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
@@ -539,6 +538,8 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
539538
return true;
540539
}
541540

541+
assert(!m_wallet->privateKeysDisabled());
542+
542543
// sign bumped transaction
543544
if (!m_wallet->signBumpTransaction(mtx)) {
544545
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't sign transaction."));

0 commit comments

Comments
 (0)