Skip to content

Commit b11d4e1

Browse files
hebastoknst
authored andcommitted
Merge bitcoin-core/gui#441: Add Create Unsigned button to SendConfirmationDialog
BACKPORT NOTE: Formatting in src/qt/optionsmodel.h has been changed in bitcoin#3267. These changes has not been backported on time; done with this backport 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#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
1 parent 320c5d4 commit b11d4e1

File tree

6 files changed

+117
-70
lines changed

6 files changed

+117
-70
lines changed

src/qt/forms/optionsdialog.ui

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,16 @@
347347
</property>
348348
</widget>
349349
</item>
350+
<item>
351+
<widget class="QCheckBox" name="m_enable_psbt_controls">
352+
<property name="text">
353+
<string extracomment="An options window setting to enable PSBT controls.">Enable &amp;PSBT controls</string>
354+
</property>
355+
<property name="toolTip">
356+
<string extracomment="Tooltip text for options window setting that enables PSBT controls.">Whether to show PSBT controls.</string>
357+
</property>
358+
</widget>
359+
</item>
350360
<item>
351361
<widget class="QCheckBox" name="keepChangeAddress">
352362
<property name="toolTip">

src/qt/optionsdialog.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ void OptionsDialog::setMapper()
329329
/* Wallet */
330330
mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures);
331331
mapper->addMapping(ui->subFeeFromAmount, OptionsModel::SubFeeFromAmount);
332+
mapper->addMapping(ui->m_enable_psbt_controls, OptionsModel::EnablePSBTControls);
332333
mapper->addMapping(ui->keepChangeAddress, OptionsModel::KeepChangeAddress);
333334
mapper->addMapping(ui->showMasternodesTab, OptionsModel::ShowMasternodesTab);
334335
mapper->addMapping(ui->showGovernanceTab, OptionsModel::ShowGovernanceTab);

src/qt/optionsmodel.cpp

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

160+
if (!settings.contains("enable_psbt_controls")) {
161+
settings.setValue("enable_psbt_controls", false);
162+
}
163+
m_enable_psbt_controls = settings.value("enable_psbt_controls", false).toBool();
164+
160165
if (!settings.contains("fKeepChangeAddress"))
161166
settings.setValue("fKeepChangeAddress", false);
162167
fKeepChangeAddress = settings.value("fKeepChangeAddress", false).toBool();
@@ -551,6 +556,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
551556
#ifdef ENABLE_WALLET
552557
case CoinControlFeatures:
553558
return fCoinControlFeatures;
559+
case EnablePSBTControls:
560+
return settings.value("enable_psbt_controls");
554561
case KeepChangeAddress:
555562
return fKeepChangeAddress;
556563
#endif // ENABLE_WALLET
@@ -800,6 +807,10 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
800807
settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
801808
Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures);
802809
break;
810+
case EnablePSBTControls:
811+
m_enable_psbt_controls = value.toBool();
812+
settings.setValue("enable_psbt_controls", m_enable_psbt_controls);
813+
break;
803814
case KeepChangeAddress:
804815
fKeepChangeAddress = value.toBool();
805816
settings.setValue("fKeepChangeAddress", fKeepChangeAddress);

src/qt/optionsmodel.h

Lines changed: 46 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -45,49 +45,50 @@ class OptionsModel : public QAbstractListModel
4545
explicit OptionsModel(QObject *parent = nullptr, bool resetSettings = false);
4646

4747
enum OptionID {
48-
StartAtStartup, // bool
49-
ShowTrayIcon, // bool
50-
MinimizeToTray, // bool
51-
MapPortUPnP, // bool
52-
MapPortNatpmp, // bool
53-
MinimizeOnClose, // bool
54-
ProxyUse, // bool
55-
ProxyIP, // QString
56-
ProxyPort, // int
57-
ProxyUseTor, // bool
58-
ProxyIPTor, // QString
59-
ProxyPortTor, // int
60-
DisplayUnit, // BitcoinUnit
61-
ThirdPartyTxUrls, // QString
62-
Digits, // QString
63-
Theme, // QString
64-
FontFamily, // int
65-
FontScale, // int
66-
FontWeightNormal, // int
67-
FontWeightBold, // int
68-
Language, // QString
69-
CoinControlFeatures, // bool
70-
SubFeeFromAmount, // bool
71-
KeepChangeAddress, // bool
72-
ThreadsScriptVerif, // int
73-
Prune, // bool
74-
PruneSize, // int
75-
DatabaseCache, // int
76-
SpendZeroConfChange, // bool
77-
ShowMasternodesTab, // bool
78-
ShowGovernanceTab, // bool
79-
CoinJoinEnabled, // bool
80-
ShowAdvancedCJUI, // bool
81-
ShowCoinJoinPopups, // bool
82-
LowKeysWarning, // bool
83-
CoinJoinSessions, // int
84-
CoinJoinRounds, // int
85-
CoinJoinAmount, // int
86-
CoinJoinDenomsGoal, // int
87-
CoinJoinDenomsHardCap,// int
88-
CoinJoinMultiSession, // bool
89-
Listen, // bool
90-
Server, // bool
48+
StartAtStartup, // bool
49+
ShowTrayIcon, // bool
50+
MinimizeToTray, // bool
51+
MapPortUPnP, // bool
52+
MapPortNatpmp, // bool
53+
MinimizeOnClose, // bool
54+
ProxyUse, // bool
55+
ProxyIP, // QString
56+
ProxyPort, // int
57+
ProxyUseTor, // bool
58+
ProxyIPTor, // QString
59+
ProxyPortTor, // int
60+
DisplayUnit, // BitcoinUnit
61+
ThirdPartyTxUrls, // QString
62+
Digits, // QString
63+
Theme, // QString
64+
FontFamily, // int
65+
FontScale, // int
66+
FontWeightNormal, // int
67+
FontWeightBold, // int
68+
Language, // QString
69+
CoinControlFeatures, // bool
70+
SubFeeFromAmount, // bool
71+
KeepChangeAddress, // bool
72+
ThreadsScriptVerif, // int
73+
Prune, // bool
74+
PruneSize, // int
75+
DatabaseCache, // int
76+
SpendZeroConfChange, // bool
77+
ShowMasternodesTab, // bool
78+
ShowGovernanceTab, // bool
79+
CoinJoinEnabled, // bool
80+
ShowAdvancedCJUI, // bool
81+
ShowCoinJoinPopups, // bool
82+
LowKeysWarning, // bool
83+
CoinJoinSessions, // int
84+
CoinJoinRounds, // int
85+
CoinJoinAmount, // int
86+
CoinJoinDenomsGoal, // int
87+
CoinJoinDenomsHardCap, // int
88+
CoinJoinMultiSession, // bool
89+
Listen, // bool
90+
Server, // bool
91+
EnablePSBTControls, // bool
9192
OptionIDRowCount,
9293
};
9394

@@ -108,6 +109,7 @@ class OptionsModel : public QAbstractListModel
108109
QString getThirdPartyTxUrls() const { return strThirdPartyTxUrls; }
109110
bool getCoinControlFeatures() const { return fCoinControlFeatures; }
110111
bool getSubFeeFromAmount() const { return m_sub_fee_from_amount; }
112+
bool getEnablePSBTControls() const { return m_enable_psbt_controls; }
111113
bool getKeepChangeAddress() const { return fKeepChangeAddress; }
112114
bool getShowAdvancedCJUI() { return fShowAdvancedCJUI; }
113115
const QString& getOverriddenByCommandLine() { return strOverriddenByCommandLine; }
@@ -136,6 +138,7 @@ class OptionsModel : public QAbstractListModel
136138
QString strThirdPartyTxUrls;
137139
bool fCoinControlFeatures;
138140
bool m_sub_fee_from_amount;
141+
bool m_enable_psbt_controls;
139142
bool fKeepChangeAddress;
140143
bool fShowAdvancedCJUI;
141144
/* settings that were overridden by command-line */

src/qt/sendcoinsdialog.cpp

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ using wallet::DEFAULT_PAY_TX_FEE;
4242
#include <QSettings>
4343
#include <QTextDocument>
4444

45-
#define SEND_CONFIRM_DELAY 3
46-
4745
static constexpr std::array confTargets{2, 4, 6, 12, 24, 48, 144, 504, 1008};
4846
int getConfTargetForIndex(int index) {
4947
if (index+1 > static_cast<int>(confTargets.size())) {
@@ -366,15 +364,26 @@ bool SendCoinsDialog::send(const QList<SendCoinsRecipient>& recipients, QString&
366364
formatted_short.erase(formatted_short.begin() + MAX_SEND_POPUP_ENTRIES, formatted_short.end());
367365
}
368366

369-
if (model->wallet().privateKeysDisabled()) {
370-
question_string.append(tr("Do you want to draft this transaction?"));
367+
/*: Message displayed when attempting to create a transaction. Cautionary text to prompt the user to verify
368+
that the displayed transaction details represent the transaction the user intends to create. */
369+
question_string.append(tr("Do you want to create this transaction?"));
370+
question_string.append("<br /><span style='font-size:10pt;'>");
371+
// TODO: re-enable it when external signer will be backported
372+
// if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) {
373+
const bool external_signer_available{false};
374+
if (external_signer_available) {
375+
/*: Text to inform a user attempting to create a transaction of their current options. At this stage,
376+
a user can only create a PSBT. This string is displayed when private keys are disabled and an external
377+
signer is not available. */
378+
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));
379+
} else if (model->getOptionsModel()->getEnablePSBTControls()) {
380+
/*: Text to inform a user attempting to create a transaction of their current options. At this stage,
381+
a user can send their transaction or create a PSBT. This string is displayed when both private keys
382+
and PSBT controls are enabled. */
383+
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));
371384
} else {
372-
question_string.append(tr("Are you sure you want to send?"));
373-
}
374-
if (model->wallet().privateKeysDisabled()) {
375-
question_string.append("<br /><span style='font-size:10pt;'>");
376-
question_string.append(tr("This will produce a Partially Signed 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));
377-
question_string.append("</span>");
385+
/*: Text to prompt a user to review the details of the transaction they are attempting to send. */
386+
question_string.append(tr("Please, review your transaction."));
378387
}
379388
question_string.append("<br /><br />");
380389
question_string.append(formatted_short.join("<br />"));
@@ -472,21 +481,20 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
472481
if (!PrepareSendText(question_string, informative_text, detailed_text)) return;
473482
assert(m_current_transaction);
474483

475-
const QString confirmation = model->wallet().privateKeysDisabled() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
476-
const QString confirmButtonText = model->wallet().privateKeysDisabled() ? tr("Create Unsigned") : tr("Send");
477-
auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
484+
const QString confirmation = tr("Confirm send coins");
485+
auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, !model->wallet().privateKeysDisabled(), model->getOptionsModel()->getEnablePSBTControls(), this);
478486
confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
479487
// TODO: Replace QDialog::exec() with safer QDialog::show().
480488
const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
481489

482-
if(retval != QMessageBox::Yes)
490+
if(retval != QMessageBox::Yes && retval != QMessageBox::Save)
483491
{
484492
fNewRecipientAllowed = true;
485493
return;
486494
}
487495

488496
bool send_failure = false;
489-
if (model->wallet().privateKeysDisabled()) {
497+
if (retval == QMessageBox::Save) {
490498
CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
491499
PartiallySignedTransaction psbtx(mtx);
492500
bool complete = false;
@@ -536,6 +544,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
536544
assert(false);
537545
}
538546
} else {
547+
assert(!model->wallet().privateKeysDisabled());
539548
// now send the prepared transaction
540549
model->sendCoins(*m_current_transaction, m_coin_control->IsUsingCoinJoin());
541550
Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
@@ -1050,8 +1059,8 @@ void SendCoinsDialog::keepChangeAddressChanged(bool checked)
10501059
fKeepChangeAddress = checked;
10511060
}
10521061

1053-
SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, const QString& _confirmButtonText, QWidget* parent)
1054-
: QMessageBox(parent), secDelay(_secDelay), confirmButtonText(_confirmButtonText)
1062+
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)
1063+
: QMessageBox(parent), secDelay(_secDelay), m_enable_send(enable_send)
10551064
{
10561065
GUIUtil::updateFonts();
10571066
setIcon(QMessageBox::Question);
@@ -1060,43 +1069,53 @@ SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QStri
10601069
setInformativeText(informative_text);
10611070
setDetailedText(detailed_text);
10621071
setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
1072+
if (always_show_unsigned || !enable_send) addButton(QMessageBox::Save);
10631073
setDefaultButton(QMessageBox::Cancel);
10641074
yesButton = button(QMessageBox::Yes);
10651075
if (confirmButtonText.isEmpty()) {
10661076
confirmButtonText = yesButton->text();
10671077
}
1068-
updateYesButton();
1078+
m_psbt_button = button(QMessageBox::Save);
1079+
updateButtons();
10691080
connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown);
10701081
}
10711082

10721083
int SendConfirmationDialog::exec()
10731084
{
1074-
updateYesButton();
1085+
updateButtons();
10751086
countDownTimer.start(1s);
10761087
return QMessageBox::exec();
10771088
}
10781089

10791090
void SendConfirmationDialog::countDown()
10801091
{
10811092
secDelay--;
1082-
updateYesButton();
1093+
updateButtons();
10831094

10841095
if(secDelay <= 0)
10851096
{
10861097
countDownTimer.stop();
10871098
}
10881099
}
10891100

1090-
void SendConfirmationDialog::updateYesButton()
1101+
void SendConfirmationDialog::updateButtons()
10911102
{
10921103
if(secDelay > 0)
10931104
{
10941105
yesButton->setEnabled(false);
1095-
yesButton->setText(confirmButtonText + " (" + QString::number(secDelay) + ")");
1106+
yesButton->setText(confirmButtonText + (m_enable_send ? (" (" + QString::number(secDelay) + ")") : QString("")));
1107+
if (m_psbt_button) {
1108+
m_psbt_button->setEnabled(false);
1109+
m_psbt_button->setText(m_psbt_button_text + " (" + QString::number(secDelay) + ")");
1110+
}
10961111
}
10971112
else
10981113
{
1099-
yesButton->setEnabled(true);
1114+
yesButton->setEnabled(m_enable_send);
11001115
yesButton->setText(confirmButtonText);
1116+
if (m_psbt_button) {
1117+
m_psbt_button->setEnabled(true);
1118+
m_psbt_button->setText(m_psbt_button_text);
1119+
}
11011120
}
11021121
}

src/qt/sendcoinsdialog.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,25 +112,28 @@ private Q_SLOTS:
112112
};
113113

114114

115-
115+
#define SEND_CONFIRM_DELAY 3
116116
class SendConfirmationDialog : public QMessageBox
117117
{
118118
Q_OBJECT
119119

120120
public:
121121
SendConfirmationDialog(const QString &title, const QString &text, int secDelay = 0, QWidget *parent = nullptr);
122-
SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = 0, const QString& confirmText = "", QWidget* parent = nullptr);
122+
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);
123123
int exec() override;
124124

125125
private Q_SLOTS:
126126
void countDown();
127-
void updateYesButton();
127+
void updateButtons();
128128

129129
private:
130130
QAbstractButton *yesButton;
131+
QAbstractButton *m_psbt_button;
131132
QTimer countDownTimer;
132133
int secDelay;
133-
QString confirmButtonText;
134+
QString confirmButtonText{tr("Send")};
135+
bool m_enable_send;
136+
QString m_psbt_button_text{tr("Create Unsigned")};
134137
};
135138

136139
#endif // BITCOIN_QT_SENDCOINSDIALOG_H

0 commit comments

Comments
 (0)