v30.3-roots.1 · Commit 26: [PATCH 26/68] feat(qt): enhance privacy-aware coin control
src/qt/sendcoinsdialog.cpp
Before: src/qt/sendcoinsdialog.cpp · After: src/qt/sendcoinsdialog.cpp
File metadata
index 8fea0af8e27d7852f6e5df6d02532a928bf9339f..e8308101af7d49e1a4a75c516376af080260545c 100644
Download original patch · Release notes
Diff rows 1–93 of 93. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. The two line-number columns refer to before and after.
src/qt/sendcoinsdialog.cpp
CHANGED
|
@@ -164,6 +164,8 @@ void SendCoinsDialog::setModel(WalletModel *_model)
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
connect(_model, &WalletModel::balanceChanged, this, &SendCoinsDialog::setBalance);
|
|
167
|
+
connect(_model, &WalletModel::balanceChanged, this, &SendCoinsDialog::walletChanged);
|
|
168
|
+
connect(_model, &WalletModel::unload, this, &SendCoinsDialog::reject);
|
|
167
169
|
connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &SendCoinsDialog::refreshBalance);
|
|
168
170
|
refreshBalance();
|
|
169
171
|
|
|
@@ -370,6 +372,8 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
|
|
|
370
372
|
|
|
371
373
|
}
|
|
372
374
|
|
|
375
|
+
appendCoinControlSummary(question_string);
|
|
376
|
+
|
|
373
377
|
// Show the signaling in the prepared transaction, rather than the
|
|
374
378
|
// requested value, so the confirmation states the effective result even
|
|
375
379
|
// when no fee is required.
|
|
@@ -484,6 +488,45 @@ bool SendCoinsDialog::signWithExternalSigner(PartiallySignedTransaction& psbtx,
|
|
|
484
488
|
return true;
|
|
485
489
|
}
|
|
486
490
|
|
|
491
|
+
void SendCoinsDialog::appendCoinControlSummary(QString& text) const
|
|
492
|
+
{
|
|
493
|
+
if (!m_current_transaction || !m_current_transaction->getWtx()) return;
|
|
494
|
+
|
|
495
|
+
const CTransaction& tx{*m_current_transaction->getWtx()};
|
|
496
|
+
std::vector<COutPoint> outpoints;
|
|
497
|
+
outpoints.reserve(tx.vin.size());
|
|
498
|
+
for (const CTxIn& input : tx.vin) outpoints.push_back(input.prevout);
|
|
499
|
+
|
|
500
|
+
CAmount input_amount{0};
|
|
501
|
+
for (const auto& coin : model->wallet().getCoins(outpoints)) input_amount += coin.txout.nValue;
|
|
502
|
+
const CAmount fee{m_current_transaction->getTransactionFee()};
|
|
503
|
+
const CAmount change{input_amount - m_current_transaction->getTotalTransactionAmount() - fee};
|
|
504
|
+
const BitcoinUnit unit{model->getOptionsModel()->getDisplayUnit()};
|
|
505
|
+
|
|
506
|
+
text.append("<hr /><b>" + tr("Effective coin control") + "</b><br />");
|
|
507
|
+
text.append(tr("Inputs: %1 (%2)")
|
|
508
|
+
.arg(tx.vin.size())
|
|
509
|
+
.arg(BitcoinUnits::formatHtmlWithUnit(unit, input_amount)) + "<br />");
|
|
510
|
+
text.append(tr("Estimated virtual size: %1 vB").arg(m_current_transaction->getTransactionSize()) + "<br />");
|
|
511
|
+
const unsigned int vsize{m_current_transaction->getTransactionSize()};
|
|
512
|
+
text.append(tr("Fee rate: %1 sat/vB")
|
|
513
|
+
.arg(QString::number(vsize > 0 ? static_cast<double>(fee) / vsize : 0, 'f', 1)) + "<br />");
|
|
514
|
+
text.append(tr("Change: %1").arg(BitcoinUnits::formatHtmlWithUnit(unit, std::max<CAmount>(change, 0))) + "<br />");
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
void SendCoinsDialog::invalidateSendPreview()
|
|
518
|
+
{
|
|
519
|
+
if (m_sending) return;
|
|
520
|
+
m_current_transaction.reset();
|
|
521
|
+
m_coin_control->UnSelectAll();
|
|
522
|
+
coinControlUpdateLabels();
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
void SendCoinsDialog::walletChanged([[maybe_unused]] const interfaces::WalletBalances& balances)
|
|
526
|
+
{
|
|
527
|
+
invalidateSendPreview();
|
|
528
|
+
}
|
|
529
|
+
|
|
487
530
|
void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
|
|
488
531
|
{
|
|
489
532
|
if(!model || !model->getOptionsModel())
|
|
@@ -506,6 +549,11 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
|
|
|
506
549
|
fNewRecipientAllowed = true;
|
|
507
550
|
return;
|
|
508
551
|
}
|
|
552
|
+
if (!m_current_transaction) {
|
|
553
|
+
Q_EMIT message(tr("Send Coins"), tr("Wallet state changed. Review the current inputs and create a new preview."), CClientUIInterface::MSG_WARNING);
|
|
554
|
+
fNewRecipientAllowed = true;
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
509
557
|
|
|
510
558
|
bool send_failure = false;
|
|
511
559
|
if (retval == QMessageBox::Save) {
|
|
@@ -552,9 +600,18 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
|
|
|
552
600
|
// Broadcast the transaction, unless an external signer was used and it
|
|
553
601
|
// failed, or more signatures are needed.
|
|
554
602
|
if (broadcast) {
|
|
555
|
-
// now send the prepared transaction
|
|
556
|
-
model->sendCoins(*m_current_transaction);
|
|
557
|
-
Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
|
|
603
|
+
// Keep the prepared choice valid through the synchronous commit.
|
|
604
|
+
// Wallet balance notifications from the successful commit must not
|
|
605
|
+
// invalidate its own transaction before it is announced.
|
|
606
|
+
m_sending = true;
|
|
607
|
+
try {
|
|
608
|
+
model->sendCoins(*m_current_transaction);
|
|
609
|
+
Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
|
|
610
|
+
} catch (const std::runtime_error& e) {
|
|
611
|
+
Q_EMIT message(tr("Send Coins"), QString::fromStdString(e.what()), CClientUIInterface::MSG_ERROR);
|
|
612
|
+
send_failure = true;
|
|
613
|
+
}
|
|
614
|
+
m_sending = false;
|
|
558
615
|
}
|
|
559
616
|
}
|
|
560
617
|
if (!send_failure) {
|