diff options
Diffstat (limited to 'src/qt/sendcoinsdialog.cpp')
-rw-r--r-- | src/qt/sendcoinsdialog.cpp | 136 |
1 files changed, 90 insertions, 46 deletions
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 345c258845..7b389a48d6 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -219,11 +219,8 @@ SendCoinsDialog::~SendCoinsDialog() delete ui; } -void SendCoinsDialog::on_sendButton_clicked() +bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informative_text, QString& detailed_text) { - if(!model || !model->getOptionsModel()) - return; - QList<SendCoinsRecipient> recipients; bool valid = true; @@ -246,7 +243,7 @@ void SendCoinsDialog::on_sendButton_clicked() if(!valid || recipients.isEmpty()) { - return; + return false; } fNewRecipientAllowed = false; @@ -255,11 +252,11 @@ void SendCoinsDialog::on_sendButton_clicked() { // Unlock wallet was cancelled fNewRecipientAllowed = true; - return; + return false; } // prepare transaction for getting txFee earlier - WalletModelTransaction currentTransaction(recipients); + m_current_transaction = MakeUnique<WalletModelTransaction>(recipients); WalletModel::SendCoinsReturn prepareStatus; // Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled @@ -269,22 +266,20 @@ void SendCoinsDialog::on_sendButton_clicked() updateCoinControlState(ctrl); - prepareStatus = model->prepareTransaction(currentTransaction, ctrl); + prepareStatus = model->prepareTransaction(*m_current_transaction, ctrl); // process prepareStatus and on error generate message shown to user processSendCoinsReturn(prepareStatus, - BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee())); + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), m_current_transaction->getTransactionFee())); if(prepareStatus.status != WalletModel::OK) { fNewRecipientAllowed = true; - return; + return false; } - CAmount txFee = currentTransaction.getTransactionFee(); - - // Format confirmation message + CAmount txFee = m_current_transaction->getTransactionFee(); QStringList formatted; - for (const SendCoinsRecipient &rcp : currentTransaction.getRecipients()) + for (const SendCoinsRecipient &rcp : m_current_transaction->getRecipients()) { // generate amount string with wallet name in case of multiwallet QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); @@ -311,72 +306,82 @@ void SendCoinsDialog::on_sendButton_clicked() formatted.append(recipientElement); } - QString questionString; if (model->wallet().privateKeysDisabled()) { - questionString.append(tr("Do you want to draft this transaction?")); + question_string.append(tr("Do you want to draft this transaction?")); } else { - questionString.append(tr("Are you sure you want to send?")); + question_string.append(tr("Are you sure you want to send?")); } - questionString.append("<br /><span style='font-size:10pt;'>"); + question_string.append("<br /><span style='font-size:10pt;'>"); if (model->wallet().privateKeysDisabled()) { - 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)); + 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 { - questionString.append(tr("Please, review your transaction.")); + question_string.append(tr("Please, review your transaction.")); } - questionString.append("</span>%1"); + question_string.append("</span>%1"); if(txFee > 0) { // append fee string if a fee is required - questionString.append("<hr /><b>"); - questionString.append(tr("Transaction fee")); - questionString.append("</b>"); + question_string.append("<hr /><b>"); + question_string.append(tr("Transaction fee")); + question_string.append("</b>"); // append transaction size - questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB): "); + question_string.append(" (" + QString::number((double)m_current_transaction->getTransactionSize() / 1000) + " kB): "); // append transaction fee value - questionString.append("<span style='color:#aa0000; font-weight:bold;'>"); - questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); - questionString.append("</span><br />"); + question_string.append("<span style='color:#aa0000; font-weight:bold;'>"); + question_string.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); + question_string.append("</span><br />"); // append RBF message according to transaction's signalling - questionString.append("<span style='font-size:10pt; font-weight:normal;'>"); + question_string.append("<span style='font-size:10pt; font-weight:normal;'>"); if (ui->optInRBF->isChecked()) { - questionString.append(tr("You can increase the fee later (signals Replace-By-Fee, BIP-125).")); + question_string.append(tr("You can increase the fee later (signals Replace-By-Fee, BIP-125).")); } else { - questionString.append(tr("Not signalling Replace-By-Fee, BIP-125.")); + question_string.append(tr("Not signalling Replace-By-Fee, BIP-125.")); } - questionString.append("</span>"); + question_string.append("</span>"); } // add total amount in all subdivision units - questionString.append("<hr />"); - CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee; + question_string.append("<hr />"); + CAmount totalAmount = m_current_transaction->getTotalTransactionAmount() + txFee; QStringList alternativeUnits; for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) { if(u != model->getOptionsModel()->getDisplayUnit()) alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount)); } - questionString.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount")) + question_string.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount")) .arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount))); - questionString.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>") + question_string.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>") .arg(alternativeUnits.join(" " + tr("or") + " "))); - QString informative_text; - QString detailed_text; if (formatted.size() > 1) { - questionString = questionString.arg(""); + question_string = question_string.arg(""); informative_text = tr("To review recipient list click \"Show Details...\""); detailed_text = formatted.join("\n\n"); } else { - questionString = questionString.arg("<br /><br />" + formatted.at(0)); + question_string = question_string.arg("<br /><br />" + formatted.at(0)); } + + return true; +} + +void SendCoinsDialog::on_sendButton_clicked() +{ + if(!model || !model->getOptionsModel()) + return; + + QString question_string, informative_text, detailed_text; + if (!PrepareSendText(question_string, informative_text, detailed_text)) return; + assert(m_current_transaction); + const QString confirmation = model->wallet().privateKeysDisabled() ? tr("Confirm transaction proposal") : tr("Confirm send coins"); - const QString confirmButtonText = model->wallet().privateKeysDisabled() ? tr("Copy PSBT to clipboard") : tr("Send"); - SendConfirmationDialog confirmationDialog(confirmation, questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this); + const QString confirmButtonText = model->wallet().privateKeysDisabled() ? tr("Create Unsigned") : tr("Send"); + SendConfirmationDialog confirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this); confirmationDialog.exec(); QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result()); @@ -388,7 +393,7 @@ void SendCoinsDialog::on_sendButton_clicked() bool send_failure = false; if (model->wallet().privateKeysDisabled()) { - CMutableTransaction mtx = CMutableTransaction{*(currentTransaction.getWtx())}; + CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())}; PartiallySignedTransaction psbtx(mtx); bool complete = false; const TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete); @@ -398,15 +403,51 @@ void SendCoinsDialog::on_sendButton_clicked() CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << psbtx; GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str()); - Q_EMIT message(tr("PSBT copied"), "Copied to clipboard", CClientUIInterface::MSG_INFORMATION); + QMessageBox msgBox; + msgBox.setText("Unsigned Transaction"); + msgBox.setInformativeText("The PSBT has been copied to the clipboard. You can also save it."); + msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard); + msgBox.setDefaultButton(QMessageBox::Discard); + switch (msgBox.exec()) { + case QMessageBox::Save: { + QString selectedFilter; + QString fileNameSuggestion = ""; + bool first = true; + for (const SendCoinsRecipient &rcp : m_current_transaction->getRecipients()) { + if (!first) { + fileNameSuggestion.append(" - "); + } + QString labelOrAddress = rcp.label.isEmpty() ? rcp.address : rcp.label; + QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); + fileNameSuggestion.append(labelOrAddress + "-" + amount); + first = false; + } + fileNameSuggestion.append(".psbt"); + QString filename = GUIUtil::getSaveFileName(this, + tr("Save Transaction Data"), fileNameSuggestion, + tr("Partially Signed Transaction (Binary) (*.psbt)"), &selectedFilter); + if (filename.isEmpty()) { + return; + } + std::ofstream out(filename.toLocal8Bit().data()); + out << ssTx.str(); + out.close(); + Q_EMIT message(tr("PSBT saved"), "PSBT saved to disk", CClientUIInterface::MSG_INFORMATION); + break; + } + case QMessageBox::Discard: + break; + default: + assert(false); + } } else { // now send the prepared transaction - WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction); + WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction); // process sendStatus and on error generate message shown to user processSendCoinsReturn(sendStatus); if (sendStatus.status == WalletModel::OK) { - Q_EMIT coinsSent(currentTransaction.getWtx()->GetHash()); + Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash()); } else { send_failure = true; } @@ -417,10 +458,13 @@ void SendCoinsDialog::on_sendButton_clicked() coinControlUpdateLabels(); } fNewRecipientAllowed = true; + m_current_transaction.reset(); } void SendCoinsDialog::clear() { + m_current_transaction.reset(); + // Clear coin control settings CoinControlDialog::coinControl()->UnSelectAll(); ui->checkBoxCoinControlChange->setChecked(false); |