Skip to content

Commit c6dd565

Browse files
committed
[gui] watch-only wallet: copy PSBT to clipboard
1 parent 39465d5 commit c6dd565

File tree

3 files changed

+56
-20
lines changed

3 files changed

+56
-20
lines changed

src/qt/sendcoinsdialog.cpp

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@
2121
#include <chainparams.h>
2222
#include <interfaces/node.h>
2323
#include <key_io.h>
24-
#include <wallet/coincontrol.h>
25-
#include <ui_interface.h>
26-
#include <txmempool.h>
2724
#include <policy/fees.h>
25+
#include <txmempool.h>
26+
#include <ui_interface.h>
27+
#include <wallet/coincontrol.h>
2828
#include <wallet/fees.h>
29+
#include <wallet/psbtwallet.h>
2930

3031
#include <QFontMetrics>
3132
#include <QScrollBar>
@@ -186,6 +187,11 @@ void SendCoinsDialog::setModel(WalletModel *_model)
186187
// set default rbf checkbox state
187188
ui->optInRBF->setCheckState(Qt::Checked);
188189

190+
if (model->privateKeysDisabled()) {
191+
ui->sendButton->setText(tr("Cr&eate Unsigned"));
192+
ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
193+
}
194+
189195
// set the smartfee-sliders default value (wallets default conf.target or last stored value)
190196
QSettings settings;
191197
if (settings.value("nSmartFeeSliderPosition").toInt() != 0) {
@@ -305,9 +311,19 @@ void SendCoinsDialog::on_sendButton_clicked()
305311
formatted.append(recipientElement);
306312
}
307313

308-
QString questionString = tr("Are you sure you want to send?");
314+
QString questionString;
315+
if (model->privateKeysDisabled()) {
316+
questionString.append(tr("Do you want to draft this transaction?"));
317+
} else {
318+
questionString.append(tr("Are you sure you want to send?"));
319+
}
320+
309321
questionString.append("<br /><span style='font-size:10pt;'>");
310-
questionString.append(tr("Please, review your transaction."));
322+
if (model->privateKeysDisabled()) {
323+
questionString.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
324+
} else {
325+
questionString.append(tr("Please, review your transaction."));
326+
}
311327
questionString.append("</span>%1");
312328

313329
if(txFee > 0)
@@ -358,8 +374,9 @@ void SendCoinsDialog::on_sendButton_clicked()
358374
} else {
359375
questionString = questionString.arg("<br /><br />" + formatted.at(0));
360376
}
361-
362-
SendConfirmationDialog confirmationDialog(tr("Confirm send coins"), questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, this);
377+
const QString confirmation = model->privateKeysDisabled() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
378+
const QString confirmButtonText = model->privateKeysDisabled() ? tr("Copy PSBT to clipboard") : tr("Send");
379+
SendConfirmationDialog confirmationDialog(confirmation, questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
363380
confirmationDialog.exec();
364381
QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
365382

@@ -369,17 +386,35 @@ void SendCoinsDialog::on_sendButton_clicked()
369386
return;
370387
}
371388

372-
// now send the prepared transaction
373-
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
374-
// process sendStatus and on error generate message shown to user
375-
processSendCoinsReturn(sendStatus);
389+
bool send_failure = false;
390+
if (model->privateKeysDisabled()) {
391+
CMutableTransaction mtx = CMutableTransaction{*(currentTransaction.getWtx())};
392+
PartiallySignedTransaction psbtx(mtx);
393+
bool complete = false;
394+
const TransactionError err = model->wallet().fillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */);
395+
assert(!complete);
396+
assert(err == TransactionError::OK);
397+
// Serialize the PSBT
398+
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
399+
ssTx << psbtx;
400+
GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
401+
Q_EMIT message(tr("PSBT copied"), "Copied to clipboard", CClientUIInterface::MSG_INFORMATION);
402+
} else {
403+
// now send the prepared transaction
404+
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
405+
// process sendStatus and on error generate message shown to user
406+
processSendCoinsReturn(sendStatus);
376407

377-
if (sendStatus.status == WalletModel::OK)
378-
{
408+
if (sendStatus.status == WalletModel::OK) {
409+
Q_EMIT coinsSent(currentTransaction.getWtx()->GetHash());
410+
} else {
411+
send_failure = true;
412+
}
413+
}
414+
if (!send_failure) {
379415
accept();
380416
CoinControlDialog::coinControl()->UnSelectAll();
381417
coinControlUpdateLabels();
382-
Q_EMIT coinsSent(currentTransaction.getWtx()->GetHash());
383418
}
384419
fNewRecipientAllowed = true;
385420
}
@@ -875,8 +910,8 @@ void SendCoinsDialog::coinControlUpdateLabels()
875910
}
876911
}
877912

878-
SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, QWidget* parent)
879-
: QMessageBox(parent), secDelay(_secDelay)
913+
SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, const QString& _confirmButtonText, QWidget* parent)
914+
: QMessageBox(parent), secDelay(_secDelay), confirmButtonText(_confirmButtonText)
880915
{
881916
setIcon(QMessageBox::Question);
882917
setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
@@ -913,11 +948,11 @@ void SendConfirmationDialog::updateYesButton()
913948
if(secDelay > 0)
914949
{
915950
yesButton->setEnabled(false);
916-
yesButton->setText(tr("Send") + " (" + QString::number(secDelay) + ")");
951+
yesButton->setText(confirmButtonText + " (" + QString::number(secDelay) + ")");
917952
}
918953
else
919954
{
920955
yesButton->setEnabled(true);
921-
yesButton->setText(tr("Send"));
956+
yesButton->setText(confirmButtonText);
922957
}
923958
}

src/qt/sendcoinsdialog.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class SendConfirmationDialog : public QMessageBox
108108
Q_OBJECT
109109

110110
public:
111-
SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, QWidget* parent = nullptr);
111+
SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, const QString& confirmText = "Send", QWidget* parent = nullptr);
112112
int exec();
113113

114114
private Q_SLOTS:
@@ -119,6 +119,7 @@ private Q_SLOTS:
119119
QAbstractButton *yesButton;
120120
QTimer countDownTimer;
121121
int secDelay;
122+
QString confirmButtonText;
122123
};
123124

124125
#endif // BITCOIN_QT_SENDCOINSDIALOG_H

src/qt/walletmodel.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
183183
std::string strFailReason;
184184

185185
auto& newTx = transaction.getWtx();
186-
newTx = m_wallet->createTransaction(vecSend, coinControl, true /* sign */, nChangePosRet, nFeeRequired, strFailReason);
186+
newTx = m_wallet->createTransaction(vecSend, coinControl, !privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired, strFailReason);
187187
transaction.setTransactionFee(nFeeRequired);
188188
if (fSubtractFeeFromAmount && newTx)
189189
transaction.reassignAmounts(nChangePosRet);

0 commit comments

Comments
 (0)