v31.1-roots.1 · Commit 6: [PATCH 06/17] feat(wallet): preserve Roots signing and spend controls on v31
src/qt/sendcoinsdialog.cpp
Before: src/qt/sendcoinsdialog.cpp · After: src/qt/sendcoinsdialog.cpp
File metadata
index 50b0c4cf74f2ea8e3b533d8561a4fb9ce69e75bb..3da3b377ed3244bfe83367f474e2d510742f40e9 100644
Download original patch · Release notes
Diff rows 1–128 of 128. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.
src/qt/sendcoinsdialog.cpp
CHANGED
|
@@ -23,6 +23,7 @@
|
|
| 23 |
#include <node/types.h>
|
| 24 |
#include <policy/fees/block_policy_estimator.h>
|
| 25 |
#include <txmempool.h>
|
|
|
|
| 26 |
#include <validation.h>
|
| 27 |
#include <wallet/coincontrol.h>
|
| 28 |
#include <wallet/fees.h>
|
|
@@ -160,6 +161,8 @@ void SendCoinsDialog::setModel(WalletModel *_model)
|
|
| 160 |
}
|
| 161 |
|
| 162 |
connect(_model, &WalletModel::balanceChanged, this, &SendCoinsDialog::setBalance);
|
|
|
|
|
|
|
| 163 |
connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &SendCoinsDialog::refreshBalance);
|
| 164 |
refreshBalance();
|
| 165 |
|
|
@@ -197,7 +200,7 @@ void SendCoinsDialog::setModel(WalletModel *_model)
|
|
| 197 |
updateSmartFeeLabel();
|
| 198 |
|
| 199 |
// set default rbf checkbox state
|
| 200 |
-
ui->optInRBF->setCheckState(Qt::Checked);
|
| 201 |
|
| 202 |
if (model->wallet().hasExternalSigner()) {
|
| 203 |
//: "device" usually means a hardware wallet.
|
|
@@ -361,16 +364,20 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
|
|
| 361 |
question_string.append("<span style='color:#aa0000; font-weight:bold;'>");
|
| 362 |
question_string.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
|
| 363 |
question_string.append("</span><br />");
|
|
|
|
| 364 |
|
| 365 |
-
// append RBF message according to transaction's signalling
|
| 366 |
-
question_string.append("<span style='font-size:10pt; font-weight:normal;'>");
|
| 367 |
-
if (ui->optInRBF->isChecked()) {
|
| 368 |
-
question_string.append(tr("You can increase the fee later (signals Replace-By-Fee, BIP-125)."));
|
| 369 |
-
} else {
|
| 370 |
-
question_string.append(tr("Not signalling Replace-By-Fee, BIP-125."));
|
| 371 |
-
}
|
| 372 |
-
question_string.append("</span>");
|
|
|
|
|
|
|
| 373 |
}
|
|
|
|
| 374 |
|
| 375 |
// add total amount in all subdivision units
|
| 376 |
question_string.append("<hr />");
|
|
@@ -475,6 +482,45 @@ bool SendCoinsDialog::signWithExternalSigner(PartiallySignedTransaction& psbtx,
|
|
| 475 |
return true;
|
| 476 |
}
|
| 477 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 478 |
void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
|
| 479 |
{
|
| 480 |
if(!model || !model->getOptionsModel())
|
|
@@ -497,6 +543,11 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
|
|
| 497 |
fNewRecipientAllowed = true;
|
| 498 |
return;
|
| 499 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 500 |
|
| 501 |
bool send_failure = false;
|
| 502 |
if (retval == QMessageBox::Save) {
|
|
@@ -543,9 +594,18 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
|
|
| 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
|
| 547 |
-
model->sendCoins(*m_current_transaction);
|
| 548 |
-
Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 549 |
}
|
| 550 |
}
|
| 551 |
if (!send_failure) {
|
|
|
|
| 23 |
#include <node/types.h>
|
| 24 |
#include <policy/fees/block_policy_estimator.h>
|
| 25 |
#include <txmempool.h>
|
| 26 |
+
#include <util/rbf.h>
|
| 27 |
#include <validation.h>
|
| 28 |
#include <wallet/coincontrol.h>
|
| 29 |
#include <wallet/fees.h>
|
|
|
|
| 161 |
}
|
| 162 |
|
| 163 |
connect(_model, &WalletModel::balanceChanged, this, &SendCoinsDialog::setBalance);
|
| 164 |
+
connect(_model, &WalletModel::walletStateChanged, this, &SendCoinsDialog::walletChanged);
|
| 165 |
+
connect(_model, &WalletModel::unload, this, &SendCoinsDialog::reject);
|
| 166 |
connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &SendCoinsDialog::refreshBalance);
|
| 167 |
refreshBalance();
|
| 168 |
|
|
|
|
| 200 |
updateSmartFeeLabel();
|
| 201 |
|
| 202 |
// set default rbf checkbox state
|
| 203 |
+
ui->optInRBF->setChecked(model->wallet().getDefaultRbf());
|
| 204 |
|
| 205 |
if (model->wallet().hasExternalSigner()) {
|
| 206 |
//: "device" usually means a hardware wallet.
|
|
|
|
| 364 |
question_string.append("<span style='color:#aa0000; font-weight:bold;'>");
|
| 365 |
question_string.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
|
| 366 |
question_string.append("</span><br />");
|
| 367 |
+
}
|
| 368 |
|
| 369 |
+
appendCoinControlSummary(question_string);
|
| 370 |
+
|
| 371 |
+
// Show the signaling in the prepared transaction, rather than the
|
| 372 |
+
// requested value, so the confirmation states the effective result even
|
| 373 |
+
// when no fee is required.
|
| 374 |
+
question_string.append("<span style='font-size:10pt; font-weight:normal;'>");
|
| 375 |
+
if (SignalsOptInRBF(*m_current_transaction->getWtx())) {
|
| 376 |
+
question_string.append(tr("This transaction signals Replace-By-Fee (BIP-125). Replacement acceptance depends on network policy."));
|
| 377 |
+
} else {
|
| 378 |
+
question_string.append(tr("Not signalling Replace-By-Fee, BIP-125."));
|
| 379 |
}
|
| 380 |
+
question_string.append("</span>");
|
| 381 |
|
| 382 |
// add total amount in all subdivision units
|
| 383 |
question_string.append("<hr />");
|
|
|
|
| 482 |
return true;
|
| 483 |
}
|
| 484 |
|
| 485 |
+
void SendCoinsDialog::appendCoinControlSummary(QString& text) const
|
| 486 |
+
{
|
| 487 |
+
if (!m_current_transaction || !m_current_transaction->getWtx()) return;
|
| 488 |
+
|
| 489 |
+
const CTransaction& tx{*m_current_transaction->getWtx()};
|
| 490 |
+
std::vector<COutPoint> outpoints;
|
| 491 |
+
outpoints.reserve(tx.vin.size());
|
| 492 |
+
for (const CTxIn& input : tx.vin) outpoints.push_back(input.prevout);
|
| 493 |
+
|
| 494 |
+
CAmount input_amount{0};
|
| 495 |
+
for (const auto& coin : model->wallet().getCoins(outpoints)) input_amount += coin.txout.nValue;
|
| 496 |
+
const CAmount fee{m_current_transaction->getTransactionFee()};
|
| 497 |
+
const CAmount change{input_amount - m_current_transaction->getTotalTransactionAmount() - fee};
|
| 498 |
+
const BitcoinUnit unit{model->getOptionsModel()->getDisplayUnit()};
|
| 499 |
+
|
| 500 |
+
text.append("<hr /><b>" + tr("Effective coin control") + "</b><br />");
|
| 501 |
+
text.append(tr("Inputs: %1 (%2)")
|
| 502 |
+
.arg(tx.vin.size())
|
| 503 |
+
.arg(BitcoinUnits::formatHtmlWithUnit(unit, input_amount)) + "<br />");
|
| 504 |
+
text.append(tr("Estimated virtual size: %1 vB").arg(m_current_transaction->getTransactionSize()) + "<br />");
|
| 505 |
+
const unsigned int vsize{m_current_transaction->getTransactionSize()};
|
| 506 |
+
text.append(tr("Fee rate: %1 sat/vB")
|
| 507 |
+
.arg(QString::number(vsize > 0 ? static_cast<double>(fee) / vsize : 0, 'f', 1)) + "<br />");
|
| 508 |
+
text.append(tr("Change: %1").arg(BitcoinUnits::formatHtmlWithUnit(unit, std::max<CAmount>(change, 0))) + "<br />");
|
| 509 |
+
}
|
| 510 |
+
|
| 511 |
+
void SendCoinsDialog::invalidateSendPreview()
|
| 512 |
+
{
|
| 513 |
+
if (m_sending) return;
|
| 514 |
+
m_current_transaction.reset();
|
| 515 |
+
m_coin_control->UnSelectAll();
|
| 516 |
+
coinControlUpdateLabels();
|
| 517 |
+
}
|
| 518 |
+
|
| 519 |
+
void SendCoinsDialog::walletChanged()
|
| 520 |
+
{
|
| 521 |
+
invalidateSendPreview();
|
| 522 |
+
}
|
| 523 |
+
|
| 524 |
void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
|
| 525 |
{
|
| 526 |
if(!model || !model->getOptionsModel())
|
|
|
|
| 543 |
fNewRecipientAllowed = true;
|
| 544 |
return;
|
| 545 |
}
|
| 546 |
+
if (!m_current_transaction) {
|
| 547 |
+
Q_EMIT message(tr("Send Coins"), tr("Wallet state changed. Review the current inputs and create a new preview."), CClientUIInterface::MSG_WARNING);
|
| 548 |
+
fNewRecipientAllowed = true;
|
| 549 |
+
return;
|
| 550 |
+
}
|
| 551 |
|
| 552 |
bool send_failure = false;
|
| 553 |
if (retval == QMessageBox::Save) {
|
|
|
|
| 594 |
// Broadcast the transaction, unless an external signer was used and it
|
| 595 |
// failed, or more signatures are needed.
|
| 596 |
if (broadcast) {
|
| 597 |
+
// Keep the prepared choice valid through the synchronous commit.
|
| 598 |
+
// Wallet balance notifications from the successful commit must not
|
| 599 |
+
// invalidate its own transaction before it is announced.
|
| 600 |
+
m_sending = true;
|
| 601 |
+
try {
|
| 602 |
+
model->sendCoins(*m_current_transaction);
|
| 603 |
+
Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
|
| 604 |
+
} catch (const std::runtime_error& e) {
|
| 605 |
+
Q_EMIT message(tr("Send Coins"), QString::fromStdString(e.what()), CClientUIInterface::MSG_ERROR);
|
| 606 |
+
send_failure = true;
|
| 607 |
+
}
|
| 608 |
+
m_sending = false;
|
| 609 |
}
|
| 610 |
}
|
| 611 |
if (!send_failure) {
|