diff options
author | Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> | 2022-01-09 17:39:31 +0200 |
---|---|---|
committer | Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> | 2022-01-09 17:46:23 +0200 |
commit | 2e01b6986099715afa40ed6464da4b321b630e9c (patch) | |
tree | 33e5b1839011cac6ece63b977a535ee647adf173 | |
parent | 6182e5086fc82b49a5f0d2abdab4bc7e2a4686ef (diff) | |
parent | 742918c8ef353993a07c060f476a160e8272a9ef (diff) |
Merge bitcoin-core/gui#441: Add Create Unsigned button to SendConfirmationDialog
742918c8ef353993a07c060f476a160e8272a9ef qt: hide Create Unsigned button behind an expert mode option (Andrew Chow)
5c3b800acd3ceb75ff6bbac8d0e2e1aaa95b0728 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 https://github.com/bitcoin/bitcoin/pull/18789
ACKs for top commit:
jarolrod:
ACK 742918c
ryanofsky:
Code review ACK 742918c8ef353993a07c060f476a160e8272a9ef. Just suggested changes since last review. Looks great!
hebasto:
ACK 742918c8ef353993a07c060f476a160e8272a9ef, tested on Linux Mint 20.2 (Qt 5.12.8).
Tree-SHA512: dd29f4364c7b4f15befe8fe63257b26187918786b005e0f8336183270b1a162680b93f6ced60f0285c6e607c084cc0d24950fc68a8f9c056521ede614041be66
-rw-r--r-- | src/qt/forms/optionsdialog.ui | 10 | ||||
-rw-r--r-- | src/qt/optionsdialog.cpp | 1 | ||||
-rw-r--r-- | src/qt/optionsmodel.cpp | 11 | ||||
-rw-r--r-- | src/qt/optionsmodel.h | 3 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.cpp | 54 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.h | 9 | ||||
-rw-r--r-- | src/qt/walletmodel.cpp | 13 |
7 files changed, 73 insertions, 28 deletions
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 6b3a4630a3..5438811aff 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -252,6 +252,16 @@ </property> </widget> </item> + <item> + <widget class="QCheckBox" name="m_enable_psbt_controls"> + <property name="text"> + <string extracomment="An options window setting to enable PSBT controls.">Enable &PSBT controls</string> + </property> + <property name="toolTip"> + <string extracomment="Tooltip text for options window setting that enables PSBT controls.">Whether to show PSBT controls.</string> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index a851f99a3a..177f45cb7b 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -242,6 +242,7 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures); mapper->addMapping(ui->subFeeFromAmount, OptionsModel::SubFeeFromAmount); mapper->addMapping(ui->externalSignerPath, OptionsModel::ExternalSignerPath); + mapper->addMapping(ui->m_enable_psbt_controls, OptionsModel::EnablePSBTControls); /* Network */ mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index fe40c9ef3c..e0219084b7 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -83,6 +83,11 @@ void OptionsModel::Init(bool resetSettings) settings.setValue("fCoinControlFeatures", false); fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool(); + if (!settings.contains("enable_psbt_controls")) { + settings.setValue("enable_psbt_controls", false); + } + m_enable_psbt_controls = settings.value("enable_psbt_controls", false).toBool(); + // These are shared with the core or have a command-line parameter // and we want command-line parameters to overwrite the GUI settings. // @@ -360,6 +365,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return m_use_embedded_monospaced_font; case CoinControlFeatures: return fCoinControlFeatures; + case EnablePSBTControls: + return settings.value("enable_psbt_controls"); case Prune: return settings.value("bPrune"); case PruneSize: @@ -507,6 +514,10 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in settings.setValue("fCoinControlFeatures", fCoinControlFeatures); Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures); break; + case EnablePSBTControls: + m_enable_psbt_controls = value.toBool(); + settings.setValue("enable_psbt_controls", m_enable_psbt_controls); + break; case Prune: if (settings.value("bPrune") != value) { settings.setValue("bPrune", value); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 059fe2381f..bb9a8c1f8c 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -69,6 +69,7 @@ public: SpendZeroConfChange, // bool Listen, // bool Server, // bool + EnablePSBTControls, // bool OptionIDRowCount, }; @@ -90,6 +91,7 @@ public: bool getUseEmbeddedMonospacedFont() const { return m_use_embedded_monospaced_font; } bool getCoinControlFeatures() const { return fCoinControlFeatures; } bool getSubFeeFromAmount() const { return m_sub_fee_from_amount; } + bool getEnablePSBTControls() const { return m_enable_psbt_controls; } const QString& getOverriddenByCommandLine() { return strOverriddenByCommandLine; } /* Explicit setters */ @@ -115,6 +117,7 @@ private: bool m_use_embedded_monospaced_font; bool fCoinControlFeatures; bool m_sub_fee_from_amount; + bool m_enable_psbt_controls; /* settings that were overridden by command-line */ QString strOverriddenByCommandLine; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index d76f9fade0..cbaf7563cd 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -324,16 +324,22 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa formatted.append(recipientElement); } - if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) { - question_string.append(tr("Do you want to draft this transaction?")); - } else { - question_string.append(tr("Are you sure you want to send?")); - } - + /*: Message displayed when attempting to create a transaction. Cautionary text to prompt the user to verify + that the displayed transaction details represent the transaction the user intends to create. */ + question_string.append(tr("Do you want to create this transaction?")); question_string.append("<br /><span style='font-size:10pt;'>"); if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) { + /*: Text to inform a user attempting to create a transaction of their current options. At this stage, + a user can only create a PSBT. This string is displayed when private keys are disabled and an external + signer is not available. */ 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)); + } else if (model->getOptionsModel()->getEnablePSBTControls()) { + /*: Text to inform a user attempting to create a transaction of their current options. At this stage, + a user can send their transaction or create a PSBT. This string is displayed when both private keys + and PSBT controls are enabled. */ + 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)); } else { + /*: Text to prompt a user to review the details of the transaction they are attempting to send. */ question_string.append(tr("Please, review your transaction.")); } question_string.append("</span>%1"); @@ -397,21 +403,20 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked) if (!PrepareSendText(question_string, informative_text, detailed_text)) return; assert(m_current_transaction); - const QString confirmation = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Confirm transaction proposal") : tr("Confirm send coins"); - const QString confirmButtonText = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Create Unsigned") : tr("Sign and send"); - auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this); + const QString confirmation = tr("Confirm send coins"); + auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, !model->wallet().privateKeysDisabled(), model->getOptionsModel()->getEnablePSBTControls(), this); confirmationDialog->setAttribute(Qt::WA_DeleteOnClose); // TODO: Replace QDialog::exec() with safer QDialog::show(). const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec()); - if(retval != QMessageBox::Yes) + if(retval != QMessageBox::Yes && retval != QMessageBox::Save) { fNewRecipientAllowed = true; return; } bool send_failure = false; - if (model->wallet().privateKeysDisabled()) { + if (retval == QMessageBox::Save) { CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())}; PartiallySignedTransaction psbtx(mtx); bool complete = false; @@ -512,6 +517,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked) assert(false); } // msgBox.exec() } else { + assert(!model->wallet().privateKeysDisabled()); // now send the prepared transaction WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction); // process sendStatus and on error generate message shown to user @@ -1031,8 +1037,8 @@ void SendCoinsDialog::coinControlUpdateLabels() } } -SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, const QString& _confirmButtonText, QWidget* parent) - : QMessageBox(parent), secDelay(_secDelay), confirmButtonText(_confirmButtonText) +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) + : QMessageBox(parent), secDelay(_secDelay), m_enable_send(enable_send) { setIcon(QMessageBox::Question); setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines). @@ -1040,18 +1046,20 @@ SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QStri setInformativeText(informative_text); setDetailedText(detailed_text); setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); + if (always_show_unsigned || !enable_send) addButton(QMessageBox::Save); setDefaultButton(QMessageBox::Cancel); yesButton = button(QMessageBox::Yes); if (confirmButtonText.isEmpty()) { confirmButtonText = yesButton->text(); } - updateYesButton(); + m_psbt_button = button(QMessageBox::Save); + updateButtons(); connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown); } int SendConfirmationDialog::exec() { - updateYesButton(); + updateButtons(); countDownTimer.start(1000); return QMessageBox::exec(); } @@ -1059,7 +1067,7 @@ int SendConfirmationDialog::exec() void SendConfirmationDialog::countDown() { secDelay--; - updateYesButton(); + updateButtons(); if(secDelay <= 0) { @@ -1067,16 +1075,24 @@ void SendConfirmationDialog::countDown() } } -void SendConfirmationDialog::updateYesButton() +void SendConfirmationDialog::updateButtons() { if(secDelay > 0) { yesButton->setEnabled(false); - yesButton->setText(confirmButtonText + " (" + QString::number(secDelay) + ")"); + yesButton->setText(confirmButtonText + (m_enable_send ? (" (" + QString::number(secDelay) + ")") : QString(""))); + if (m_psbt_button) { + m_psbt_button->setEnabled(false); + m_psbt_button->setText(m_psbt_button_text + " (" + QString::number(secDelay) + ")"); + } } else { - yesButton->setEnabled(true); + yesButton->setEnabled(m_enable_send); yesButton->setText(confirmButtonText); + if (m_psbt_button) { + m_psbt_button->setEnabled(true); + m_psbt_button->setText(m_psbt_button_text); + } } } diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 4e43697f78..ad31790db6 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -114,18 +114,21 @@ class SendConfirmationDialog : public QMessageBox Q_OBJECT public: - 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); + 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); int exec() override; private Q_SLOTS: void countDown(); - void updateYesButton(); + void updateButtons(); private: QAbstractButton *yesButton; + QAbstractButton *m_psbt_button; QTimer countDownTimer; int secDelay; - QString confirmButtonText; + QString confirmButtonText{tr("Send")}; + bool m_enable_send; + QString m_psbt_button_text{tr("Create Unsigned")}; }; #endif // BITCOIN_QT_SENDCOINSDIALOG_H diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index cfc255c843..af9b0f14e5 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -480,10 +480,9 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash) return false; } - const bool create_psbt = m_wallet->privateKeysDisabled(); - // allow a user based fee verification - QString questionString = create_psbt ? tr("Do you want to draft a transaction with fee increase?") : tr("Do you want to increase the fee?"); + /*: Asks a user if they would like to manually increase the fee of a transaction that has already been created. */ + QString questionString = tr("Do you want to increase the fee?"); questionString.append("<br />"); questionString.append("<table style=\"text-align: left;\">"); questionString.append("<tr><td>"); @@ -506,13 +505,13 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash) 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.")); } - auto confirmationDialog = new SendConfirmationDialog(tr("Confirm fee bump"), questionString); + auto confirmationDialog = new SendConfirmationDialog(tr("Confirm fee bump"), questionString, "", "", SEND_CONFIRM_DELAY, !m_wallet->privateKeysDisabled(), getOptionsModel()->getEnablePSBTControls(), nullptr); confirmationDialog->setAttribute(Qt::WA_DeleteOnClose); // TODO: Replace QDialog::exec() with safer QDialog::show(). const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec()); // cancel sign&broadcast if user doesn't want to bump the fee - if (retval != QMessageBox::Yes) { + if (retval != QMessageBox::Yes && retval != QMessageBox::Save) { return false; } @@ -523,7 +522,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash) } // Short-circuit if we are returning a bumped transaction PSBT to clipboard - if (create_psbt) { + if (retval == QMessageBox::Save) { PartiallySignedTransaction psbtx(mtx); bool complete = false; 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) return true; } + assert(!m_wallet->privateKeysDisabled()); + // sign bumped transaction if (!m_wallet->signBumpTransaction(mtx)) { QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't sign transaction.")); |