v30.3-roots.1 · Commit 26: [PATCH 26/68] feat(qt): enhance privacy-aware coin control
src/qt/test/wallettests.cpp
Before: src/qt/test/wallettests.cpp · After: src/qt/test/wallettests.cpp
File metadata
index f879ef70db688a472bd9bf6020e2b7281f9216f8..13952ea2dca9828d151144b01a72686e9d18322e 100644
Download original patch · Release notes
Diff rows 1–125 of 125. 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/test/wallettests.cpp
CHANGED
|
@@ -10,9 +10,11 @@
|
|
| 10 |
#include <interfaces/node.h>
|
| 11 |
#include <key_io.h>
|
| 12 |
#include <logging.h>
|
|
|
|
| 13 |
#include <qt/bitcoinamountfield.h>
|
| 14 |
#include <qt/bitcoinunits.h>
|
| 15 |
#include <qt/clientmodel.h>
|
|
|
|
| 16 |
#include <qt/optionsmodel.h>
|
| 17 |
#include <qt/overviewpage.h>
|
| 18 |
#include <qt/platformstyle.h>
|
|
@@ -49,9 +51,11 @@
|
|
| 49 |
#include <QLineEdit>
|
| 50 |
#include <QObject>
|
| 51 |
#include <QPushButton>
|
|
|
|
| 52 |
#include <QSettings>
|
| 53 |
#include <QTest>
|
| 54 |
#include <QTimer>
|
|
|
|
| 55 |
#include <QVBoxLayout>
|
| 56 |
#include <QTextEdit>
|
| 57 |
#include <QListView>
|
|
@@ -427,6 +431,82 @@ void TestGUI(interfaces::Node& node, const std::shared_ptr<CWallet>& wallet)
|
|
| 427 |
// Check 'UseAvailableBalance' functionality
|
| 428 |
VerifyUseAvailableBalance(sendCoinsDialog, walletModel);
|
| 429 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 430 |
// Send two transactions, and verify they are added to transaction list.
|
| 431 |
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
|
| 432 |
QCOMPARE(transactionTableModel->rowCount({}), 105);
|
|
@@ -447,6 +527,10 @@ void TestGUI(interfaces::Node& node, const std::shared_ptr<CWallet>& wallet)
|
|
| 447 |
}
|
| 448 |
QVERIFY(no_rbf_confirmation.contains("Not signalling Replace-By-Fee, BIP-125."));
|
| 449 |
QVERIFY(rbf_confirmation.contains("signals Replace-By-Fee (BIP-125)"));
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
|
| 451 |
wallet->m_signal_rbf = true;
|
| 452 |
SendCoinsDialog default_rbf_dialog(platformStyle.get());
|
|
@@ -600,7 +684,10 @@ void TestGUIWatchOnly(interfaces::Node& node, TestChain100Setup& test)
|
|
| 600 |
timer.start(500);
|
| 601 |
|
| 602 |
// Send tx and verify PSBT copied to the clipboard.
|
| 603 |
-
SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 5 * COIN, /*rbf=*/false, QMessageBox::Save);
|
|
|
|
|
|
|
|
|
|
| 604 |
const std::string& psbt_string = QApplication::clipboard()->text().toStdString();
|
| 605 |
QVERIFY(!psbt_string.empty());
|
| 606 |
|
|
|
|
| 10 |
#include <interfaces/node.h>
|
| 11 |
#include <key_io.h>
|
| 12 |
#include <logging.h>
|
| 13 |
+
#include <policy/feerate.h>
|
| 14 |
#include <qt/bitcoinamountfield.h>
|
| 15 |
#include <qt/bitcoinunits.h>
|
| 16 |
#include <qt/clientmodel.h>
|
| 17 |
+
#include <qt/coincontroldialog.h>
|
| 18 |
#include <qt/optionsmodel.h>
|
| 19 |
#include <qt/overviewpage.h>
|
| 20 |
#include <qt/platformstyle.h>
|
|
|
|
| 51 |
#include <QLineEdit>
|
| 52 |
#include <QObject>
|
| 53 |
#include <QPushButton>
|
| 54 |
+
#include <QRadioButton>
|
| 55 |
#include <QSettings>
|
| 56 |
#include <QTest>
|
| 57 |
#include <QTimer>
|
| 58 |
+
#include <QTreeWidget>
|
| 59 |
#include <QVBoxLayout>
|
| 60 |
#include <QTextEdit>
|
| 61 |
#include <QListView>
|
|
|
|
| 431 |
// Check 'UseAvailableBalance' functionality
|
| 432 |
VerifyUseAvailableBalance(sendCoinsDialog, walletModel);
|
| 433 |
|
| 434 |
+
// The coin control view presents wallet-owned facts and keeps filtering a
|
| 435 |
+
// display-only operation: it must not alter the selected outpoints.
|
| 436 |
+
CoinControlDialog coin_control_dialog(*sendCoinsDialog.getCoinControl(), &walletModel, platformStyle.get());
|
| 437 |
+
QTreeWidget* coin_tree = coin_control_dialog.findChild<QTreeWidget*>("treeWidget");
|
| 438 |
+
QLineEdit* coin_filter = coin_control_dialog.findChild<QLineEdit*>("lineEditFilter");
|
| 439 |
+
QLabel* available = coin_control_dialog.findChild<QLabel*>("labelAvailable");
|
| 440 |
+
QLabel* fee_rate = coin_control_dialog.findChild<QLabel*>("labelCoinControlFeeRate");
|
| 441 |
+
QLabel* rbf_summary = coin_control_dialog.findChild<QLabel*>("labelCoinControlRbf");
|
| 442 |
+
QLabel* selection_notice = coin_control_dialog.findChild<QLabel*>("labelSelectionNotice");
|
| 443 |
+
QVERIFY(coin_tree && coin_filter && available && fee_rate && rbf_summary && selection_notice);
|
| 444 |
+
QCOMPARE(coin_tree->headerItem()->text(6), "Status");
|
| 445 |
+
QCOMPARE(coin_tree->headerItem()->text(7), "Effective value");
|
| 446 |
+
QCOMPARE(coin_tree->headerItem()->text(8), "Input bytes");
|
| 447 |
+
QVERIFY(available->text().contains("eligible"));
|
| 448 |
+
QVERIFY(rbf_summary->text().contains("Replace-By-Fee"));
|
| 449 |
+
QCOMPARE(coin_filter->accessibleName(), "Filter coin selection");
|
| 450 |
+
QVERIFY(coin_tree->accessibleDescription().contains("Status column"));
|
| 451 |
+
const auto selected_before_filter = sendCoinsDialog.getCoinControl()->ListSelected();
|
| 452 |
+
coin_filter->setText("not-a-wallet-coin");
|
| 453 |
+
qApp->processEvents();
|
| 454 |
+
for (int row = 0; row < coin_tree->topLevelItemCount(); ++row) QVERIFY(coin_tree->topLevelItem(row)->isHidden());
|
| 455 |
+
QVERIFY(sendCoinsDialog.getCoinControl()->ListSelected() == selected_before_filter);
|
| 456 |
+
coin_filter->clear();
|
| 457 |
+
QVERIFY(coin_tree->focusPolicy() != Qt::NoFocus);
|
| 458 |
+
QVERIFY(QMetaObject::invokeMethod(&coin_control_dialog, "walletChanged"));
|
| 459 |
+
QVERIFY(!sendCoinsDialog.getCoinControl()->HasSelected());
|
| 460 |
+
QVERIFY(!selection_notice->isHidden());
|
| 461 |
+
QVERIFY(selection_notice->text().contains("cleared"));
|
| 462 |
+
coin_control_dialog.reject();
|
| 463 |
+
|
| 464 |
+
// Effective value must use the active coin-control fee rate, not fall back
|
| 465 |
+
// to face value. Check the formatted value, its numeric sorting key, and
|
| 466 |
+
// the item sorting implementation independently of localized labels.
|
| 467 |
+
const CFeeRate effective_fee_rate{1000};
|
| 468 |
+
const auto coin_groups = walletModel.wallet().listCoins();
|
| 469 |
+
QVERIFY(!coin_groups.empty());
|
| 470 |
+
const COutPoint effective_outpoint{std::get<0>(coin_groups.begin()->second.front())};
|
| 471 |
+
const auto effective_coins = walletModel.wallet().getCoins({effective_outpoint}, effective_fee_rate);
|
| 472 |
+
QCOMPARE(effective_coins.size(), 1U);
|
| 473 |
+
const auto& effective_coin = effective_coins.front();
|
| 474 |
+
QVERIFY(effective_coin.effective_value);
|
| 475 |
+
QVERIFY(*effective_coin.effective_value < effective_coin.txout.nValue);
|
| 476 |
+
|
| 477 |
+
wallet::CCoinControl effective_control;
|
| 478 |
+
effective_control.m_feerate = effective_fee_rate;
|
| 479 |
+
CoinControlDialog effective_dialog(effective_control, &walletModel, platformStyle.get());
|
| 480 |
+
QRadioButton* list_mode = effective_dialog.findChild<QRadioButton*>("radioListMode");
|
| 481 |
+
QTreeWidget* effective_tree = effective_dialog.findChild<QTreeWidget*>("treeWidget");
|
| 482 |
+
QVERIFY(list_mode && effective_tree);
|
| 483 |
+
list_mode->setChecked(true);
|
| 484 |
+
qApp->processEvents();
|
| 485 |
+
QTreeWidgetItem* effective_item{nullptr};
|
| 486 |
+
for (int row = 0; row < effective_tree->topLevelItemCount(); ++row) {
|
| 487 |
+
QTreeWidgetItem* item = effective_tree->topLevelItem(row);
|
| 488 |
+
if (item->data(3, Qt::UserRole).toString() == QString::fromStdString(effective_outpoint.hash.GetHex()) &&
|
| 489 |
+
item->data(3, Qt::UserRole + 1).toUInt() == effective_outpoint.n) {
|
| 490 |
+
effective_item = item;
|
| 491 |
+
break;
|
| 492 |
+
}
|
| 493 |
+
}
|
| 494 |
+
QVERIFY(effective_item);
|
| 495 |
+
QCOMPARE(effective_item->text(7), BitcoinUnits::format(walletModel.getOptionsModel()->getDisplayUnit(), *effective_coin.effective_value));
|
| 496 |
+
QCOMPARE(effective_item->data(7, Qt::UserRole).toLongLong(), qlonglong{*effective_coin.effective_value});
|
| 497 |
+
|
| 498 |
+
QTreeWidget effective_sort_tree;
|
| 499 |
+
effective_sort_tree.setColumnCount(9);
|
| 500 |
+
auto* larger_effective = new CCoinControlWidgetItem(&effective_sort_tree);
|
| 501 |
+
larger_effective->setText(7, "0.00000001");
|
| 502 |
+
larger_effective->setData(7, Qt::UserRole, qlonglong{*effective_coin.effective_value + 1});
|
| 503 |
+
auto* smaller_effective = new CCoinControlWidgetItem(&effective_sort_tree);
|
| 504 |
+
smaller_effective->setText(7, "9.99999999");
|
| 505 |
+
smaller_effective->setData(7, Qt::UserRole, qlonglong{*effective_coin.effective_value});
|
| 506 |
+
effective_sort_tree.sortItems(7, Qt::AscendingOrder);
|
| 507 |
+
QCOMPARE(effective_sort_tree.topLevelItem(0)->data(7, Qt::UserRole).toLongLong(), qlonglong{*effective_coin.effective_value});
|
| 508 |
+
effective_dialog.reject();
|
| 509 |
+
|
| 510 |
// Send two transactions, and verify they are added to transaction list.
|
| 511 |
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
|
| 512 |
QCOMPARE(transactionTableModel->rowCount({}), 105);
|
|
|
|
| 527 |
}
|
| 528 |
QVERIFY(no_rbf_confirmation.contains("Not signalling Replace-By-Fee, BIP-125."));
|
| 529 |
QVERIFY(rbf_confirmation.contains("signals Replace-By-Fee (BIP-125)"));
|
| 530 |
+
QVERIFY(rbf_confirmation.contains("Effective coin control"));
|
| 531 |
+
QVERIFY(rbf_confirmation.contains("Inputs:"));
|
| 532 |
+
QVERIFY(rbf_confirmation.contains("Fee rate:"));
|
| 533 |
+
QVERIFY(rbf_confirmation.contains("Change:"));
|
| 534 |
|
| 535 |
wallet->m_signal_rbf = true;
|
| 536 |
SendCoinsDialog default_rbf_dialog(platformStyle.get());
|
|
|
|
| 684 |
timer.start(500);
|
| 685 |
|
| 686 |
// Send tx and verify PSBT copied to the clipboard.
|
| 687 |
+
QString psbt_confirmation;
|
| 688 |
+
SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 5 * COIN, /*rbf=*/false, QMessageBox::Save, &psbt_confirmation);
|
| 689 |
+
QVERIFY(psbt_confirmation.contains("Effective coin control"));
|
| 690 |
+
QVERIFY(psbt_confirmation.contains("Inputs:"));
|
| 691 |
const std::string& psbt_string = QApplication::clipboard()->text().toStdString();
|
| 692 |
QVERIFY(!psbt_string.empty());
|
| 693 |
|