qt: Add Create Unsigned button to SendConfirmationDialog

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.
This commit is contained in:
Andrew Chow committed 2021-10-07 15:23:26 -04:00
1 parent 571bb94dfb
commit 5c3b800acd
3 files changed
+43 -28

No files matched your search

+30 -19
View File
@@ -324,16 +324,17 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
formatted.append(recipientElement); formatted.append(recipientElement);
} }
if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) { /*: Message displayed when attempting to create a transaction. Cautionary text to prompt the user to verify
question_string.append(tr("Do you want to draft this transaction?")); that the displayed transaction details represent the transaction the user intends to create. */
} else { question_string.append(tr("Do you want to create this transaction?"));
question_string.append(tr("Are you sure you want to send?"));
}
question_string.append("<br /><span style='font-size:10pt;'>"); question_string.append("<br /><span style='font-size:10pt;'>");
if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) { 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)); 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 { } 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(tr("Please, review your transaction."));
} }
question_string.append("</span>%1"); question_string.append("</span>%1");
@@ -397,21 +398,20 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
if (!PrepareSendText(question_string, informative_text, detailed_text)) return; if (!PrepareSendText(question_string, informative_text, detailed_text)) return;
assert(m_current_transaction); assert(m_current_transaction);
const QString confirmation = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Confirm transaction proposal") : tr("Confirm send coins"); const QString confirmation = 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, !model->wallet().privateKeysDisabled(), true, this);
auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
confirmationDialog->setAttribute(Qt::WA_DeleteOnClose); confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
// TODO: Replace QDialog::exec() with safer QDialog::show(). // TODO: Replace QDialog::exec() with safer QDialog::show().
const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec()); const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
if(retval != QMessageBox::Yes) if(retval != QMessageBox::Yes && retval != QMessageBox::Save)
{ {
fNewRecipientAllowed = true; fNewRecipientAllowed = true;
return; return;
} }
bool send_failure = false; bool send_failure = false;
if (model->wallet().privateKeysDisabled()) { if (retval == QMessageBox::Save) {
CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())}; CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
PartiallySignedTransaction psbtx(mtx); PartiallySignedTransaction psbtx(mtx);
bool complete = false; bool complete = false;
@@ -512,6 +512,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
assert(false); assert(false);
} // msgBox.exec() } // msgBox.exec()
} else { } else {
assert(!model->wallet().privateKeysDisabled());
// now send the prepared transaction // now send the prepared transaction
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction); WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
// process sendStatus and on error generate message shown to user // process sendStatus and on error generate message shown to user
@@ -1031,8 +1032,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) 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), confirmButtonText(_confirmButtonText) : QMessageBox(parent), secDelay(_secDelay), m_enable_send(enable_send)
{ {
setIcon(QMessageBox::Question); setIcon(QMessageBox::Question);
setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines). setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
@@ -1040,18 +1041,20 @@ SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QStri
setInformativeText(informative_text); setInformativeText(informative_text);
setDetailedText(detailed_text); setDetailedText(detailed_text);
setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
if (always_show_unsigned || !enable_send) addButton(QMessageBox::Save);
setDefaultButton(QMessageBox::Cancel); setDefaultButton(QMessageBox::Cancel);
yesButton = button(QMessageBox::Yes); yesButton = button(QMessageBox::Yes);
if (confirmButtonText.isEmpty()) { if (confirmButtonText.isEmpty()) {
confirmButtonText = yesButton->text(); confirmButtonText = yesButton->text();
} }
updateYesButton(); m_psbt_button = button(QMessageBox::Save);
updateButtons();
connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown); connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown);
} }
int SendConfirmationDialog::exec() int SendConfirmationDialog::exec()
{ {
updateYesButton(); updateButtons();
countDownTimer.start(1000); countDownTimer.start(1000);
return QMessageBox::exec(); return QMessageBox::exec();
} }
@@ -1059,7 +1062,7 @@ int SendConfirmationDialog::exec()
void SendConfirmationDialog::countDown() void SendConfirmationDialog::countDown()
{ {
secDelay--; secDelay--;
updateYesButton(); updateButtons();
if(secDelay <= 0) if(secDelay <= 0)
{ {
@@ -1067,16 +1070,24 @@ void SendConfirmationDialog::countDown()
} }
} }
void SendConfirmationDialog::updateYesButton() void SendConfirmationDialog::updateButtons()
{ {
if(secDelay > 0) if(secDelay > 0)
{ {
yesButton->setEnabled(false); 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 else
{ {
yesButton->setEnabled(true); yesButton->setEnabled(m_enable_send);
yesButton->setText(confirmButtonText); yesButton->setText(confirmButtonText);
if (m_psbt_button) {
m_psbt_button->setEnabled(true);
m_psbt_button->setText(m_psbt_button_text);
}
} }
} }
+6 -3
View File
@@ -114,18 +114,21 @@ class SendConfirmationDialog : public QMessageBox
Q_OBJECT Q_OBJECT
public: 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; int exec() override;
private Q_SLOTS: private Q_SLOTS:
void countDown(); void countDown();
void updateYesButton(); void updateButtons();
private: private:
QAbstractButton *yesButton; QAbstractButton *yesButton;
QAbstractButton *m_psbt_button;
QTimer countDownTimer; QTimer countDownTimer;
int secDelay; 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 #endif // BITCOIN_QT_SENDCOINSDIALOG_H
+7 -6
View File
@@ -480,10 +480,9 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
return false; return false;
} }
const bool create_psbt = m_wallet->privateKeysDisabled();
// allow a user based fee verification // 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("<br />");
questionString.append("<table style=\"text-align: left;\">"); questionString.append("<table style=\"text-align: left;\">");
questionString.append("<tr><td>"); 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.")); 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(), true, nullptr);
confirmationDialog->setAttribute(Qt::WA_DeleteOnClose); confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
// TODO: Replace QDialog::exec() with safer QDialog::show(). // TODO: Replace QDialog::exec() with safer QDialog::show().
const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec()); const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
// cancel sign&broadcast if user doesn't want to bump the fee // 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; 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 // Short-circuit if we are returning a bumped transaction PSBT to clipboard
if (create_psbt) { if (retval == QMessageBox::Save) {
PartiallySignedTransaction psbtx(mtx); PartiallySignedTransaction psbtx(mtx);
bool complete = false; bool complete = false;
const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, nullptr, psbtx, complete); 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; return true;
} }
assert(!m_wallet->privateKeysDisabled());
// sign bumped transaction // sign bumped transaction
if (!m_wallet->signBumpTransaction(mtx)) { if (!m_wallet->signBumpTransaction(mtx)) {
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't sign transaction.")); QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't sign transaction."));