Skip to content
Bitcoin Roots

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