Skip to content

Commit 642f272

Browse files
Sjorshebasto
authored andcommitted
gui: restore Send for external signer
Before this change the send confirmation dialog would keep the Send option disabled. The Create Unsigned choice would actually send. This is potentially confusing. With this change the Create Unsigned button will not attempt to sign and always produce a PSBT. The Send button will attempt to sign, and only return a PSBT if more signatures are needed. When using an external signer, the Create Unsigned option only appears when PSBT controls are enabled in the wallet settings. This commit maintains the pre-existing behavior of filling the PSBT (without signing) even when not using an external signer. Closes #551 Co-authored-by: Jon Atack <[email protected]> Github-Pull: bitcoin-core/gui#555 Rebased-From: 2efdfb8
1 parent 9406946 commit 642f272

File tree

2 files changed

+40
-26
lines changed

2 files changed

+40
-26
lines changed

src/qt/sendcoinsdialog.cpp

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,9 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
485485
assert(m_current_transaction);
486486

487487
const QString confirmation = tr("Confirm send coins");
488-
auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, !model->wallet().privateKeysDisabled(), model->getOptionsModel()->getEnablePSBTControls(), this);
488+
const bool enable_send{!model->wallet().privateKeysDisabled() || model->wallet().hasExternalSigner()};
489+
const bool always_show_unsigned{model->getOptionsModel()->getEnablePSBTControls()};
490+
auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, enable_send, always_show_unsigned, this);
489491
confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
490492
// TODO: Replace QDialog::exec() with safer QDialog::show().
491493
const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
@@ -498,23 +500,50 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
498500

499501
bool send_failure = false;
500502
if (retval == QMessageBox::Save) {
503+
// "Create Unsigned" clicked
501504
CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
502505
PartiallySignedTransaction psbtx(mtx);
503506
bool complete = false;
504-
// Always fill without signing first. This prevents an external signer
505-
// from being called prematurely and is not expensive.
506-
TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
507+
// Fill without signing
508+
TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete);
507509
assert(!complete);
508510
assert(err == TransactionError::OK);
511+
512+
// Copy PSBT to clipboard and offer to save
513+
presentPSBT(psbtx);
514+
} else {
515+
// "Send" clicked
516+
assert(!model->wallet().privateKeysDisabled() || model->wallet().hasExternalSigner());
517+
bool broadcast = true;
509518
if (model->wallet().hasExternalSigner()) {
519+
CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
520+
PartiallySignedTransaction psbtx(mtx);
521+
bool complete = false;
522+
// Always fill without signing first. This prevents an external signer
523+
// from being called prematurely and is not expensive.
524+
TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete);
525+
assert(!complete);
526+
assert(err == TransactionError::OK);
510527
send_failure = !signWithExternalSigner(psbtx, mtx, complete);
528+
// Don't broadcast when user rejects it on the device or there's a failure:
529+
broadcast = complete && !send_failure;
530+
if (!send_failure) {
531+
// A transaction signed with an external signer is not always complete,
532+
// e.g. in a multisig wallet.
533+
if (complete) {
534+
// Prepare transaction for broadcast transaction if complete
535+
const CTransactionRef tx = MakeTransactionRef(mtx);
536+
m_current_transaction->setWtx(tx);
537+
} else {
538+
presentPSBT(psbtx);
539+
}
540+
}
511541
}
512542

513-
// Broadcast transaction if complete (even with an external signer this
514-
// is not always the case, e.g. in a multisig wallet).
515-
if (complete) {
516-
const CTransactionRef tx = MakeTransactionRef(mtx);
517-
m_current_transaction->setWtx(tx);
543+
// Broadcast the transaction, unless an external signer was used and it
544+
// failed, or more signatures are needed.
545+
if (broadcast) {
546+
// now send the prepared transaction
518547
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
519548
// process sendStatus and on error generate message shown to user
520549
processSendCoinsReturn(sendStatus);
@@ -524,23 +553,6 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
524553
} else {
525554
send_failure = true;
526555
}
527-
return;
528-
}
529-
530-
// Copy PSBT to clipboard and offer to save
531-
assert(!complete);
532-
presentPSBT(psbtx);
533-
} else {
534-
assert(!model->wallet().privateKeysDisabled());
535-
// now send the prepared transaction
536-
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
537-
// process sendStatus and on error generate message shown to user
538-
processSendCoinsReturn(sendStatus);
539-
540-
if (sendStatus.status == WalletModel::OK) {
541-
Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
542-
} else {
543-
send_failure = true;
544556
}
545557
}
546558
if (!send_failure) {

src/qt/sendcoinsdialog.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ class SendConfirmationDialog : public QMessageBox
128128

129129
public:
130130
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);
131+
/* Returns QMessageBox::Cancel, QMessageBox::Yes when "Send" is
132+
clicked and QMessageBox::Save when "Create Unsigned" is clicked. */
131133
int exec() override;
132134

133135
private Q_SLOTS:

0 commit comments

Comments
 (0)