27 #include <validation.h>
33 #include <QAbstractButton>
35 #include <QApplication>
37 #include <QPushButton>
39 #include <QVBoxLayout>
42 #include <QDialogButtonBox>
56 void ConfirmSend(QString* text =
nullptr,
bool cancel =
false)
58 QTimer::singleShot(0, [text, cancel]() {
59 for (QWidget* widget : QApplication::topLevelWidgets()) {
60 if (widget->inherits(
"SendConfirmationDialog")) {
61 SendConfirmationDialog* dialog = qobject_cast<SendConfirmationDialog*>(widget);
62 if (text) *text = dialog->text();
63 QAbstractButton* button = dialog->button(cancel ? QMessageBox::Cancel : QMessageBox::Yes);
64 button->setEnabled(true);
74 QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>(
"entries");
75 SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget());
78 sendCoinsDialog.findChild<QFrame*>(
"frameFee")
79 ->findChild<QFrame*>(
"frameFeeSelection")
80 ->findChild<QCheckBox*>(
"optInRBF")
81 ->setCheckState(rbf ? Qt::Checked : Qt::Unchecked);
83 boost::signals2::scoped_connection c(
wallet.NotifyTransactionChanged.connect([&txid](
const uint256& hash,
ChangeType status) {
84 if (status == CT_NEW) txid = hash;
87 bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog,
"sendButtonClicked", Q_ARG(
bool,
false));
93 QModelIndex FindTx(
const QAbstractItemModel& model,
const uint256& txid)
95 QString hash = QString::fromStdString(txid.
ToString());
96 int rows = model.rowCount({});
97 for (
int row = 0; row < rows; ++row) {
98 QModelIndex index = model.index(row, 0, {});
107 void BumpFee(
TransactionView& view,
const uint256& txid,
bool expectDisabled, std::string expectError,
bool cancel)
109 QTableView* table = view.findChild<QTableView*>(
"transactionView");
110 QModelIndex index = FindTx(*table->selectionModel()->model(), txid);
111 QVERIFY2(index.isValid(),
"Could not find BumpFee txid");
115 QAction* action = view.findChild<QAction*>(
"bumpFeeAction");
116 table->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
117 action->setEnabled(expectDisabled);
118 table->customContextMenuRequested({});
119 QCOMPARE(action->isEnabled(), !expectDisabled);
121 action->setEnabled(
true);
123 if (expectError.empty()) {
124 ConfirmSend(&text, cancel);
129 QVERIFY(text.indexOf(QString::fromStdString(expectError)) != -1);
132 void CompareBalance(
WalletModel& walletModel,
CAmount expected_balance, QLabel* balance_label_to_check)
136 QCOMPARE(balance_label_to_check->text().trimmed(), balanceComparison);
156 for (
int i = 0; i < 5; ++i) {
167 wallet->SetupDescriptorScriptPubKeyMans();
175 if (!
wallet->AddWalletDescriptor(w_desc, provider,
"",
false))
assert(
false);
177 wallet->SetAddressBook(dest,
"",
"receive");
178 wallet->SetLastBlockProcessed(105,
WITH_LOCK(
node.context()->chainman->GetMutex(),
return node.context()->chainman->ActiveChain().Tip()->GetBlockHash()));
183 CWallet::ScanResult result =
wallet->ScanForWalletTransactions(
Params().GetConsensus().hashGenesisBlock, 0, {}, reserver,
true,
false);
184 QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
185 QCOMPARE(result.last_scanned_block,
WITH_LOCK(
node.context()->chainman->GetMutex(),
return node.context()->chainman->ActiveChain().Tip()->GetBlockHash()));
186 QVERIFY(result.last_failed_block.IsNull());
188 wallet->SetBroadcastTransactions(
true);
196 QVERIFY(optionsModel.Init(
error));
202 sendCoinsDialog.
setModel(&walletModel);
203 transactionView.setModel(&walletModel);
208 CompareBalance(walletModel, walletModel.
wallet().
getBalance(), sendCoinsDialog.findChild<QLabel*>(
"labelBalance"));
212 QCOMPARE(transactionTableModel->
rowCount({}), 105);
216 qApp->processEvents();
217 QCOMPARE(transactionTableModel->
rowCount({}), 107);
218 QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
219 QVERIFY(FindTx(*transactionTableModel, txid2).isValid());
222 BumpFee(transactionView, txid1,
true,
"not BIP 125 replaceable",
false);
223 BumpFee(transactionView, txid2,
false, {},
true);
224 BumpFee(transactionView, txid2,
false, {},
false);
225 BumpFee(transactionView, txid2,
true,
"already bumped",
false);
229 overviewPage.setWalletModel(&walletModel);
231 CompareBalance(walletModel, walletModel.
wallet().
getBalance(), overviewPage.findChild<QLabel*>(
"labelBalance"));
235 receiveCoinsDialog.setModel(&walletModel);
239 QLineEdit* labelInput = receiveCoinsDialog.findChild<QLineEdit*>(
"reqLabel");
240 labelInput->setText(
"TEST_LABEL_1");
247 QLineEdit* messageInput = receiveCoinsDialog.findChild<QLineEdit*>(
"reqMessage");
248 messageInput->setText(
"TEST_MESSAGE_1");
249 int initialRowCount = requestTableModel->
rowCount({});
250 QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>(
"receiveButton");
251 requestPaymentButton->click();
253 for (QWidget* widget : QApplication::topLevelWidgets()) {
254 if (widget->inherits(
"ReceiveRequestDialog")) {
256 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"payment_header")->text(), QString(
"Payment information"));
257 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"uri_tag")->text(), QString(
"URI:"));
258 QString uri = receiveRequestDialog->QObject::findChild<QLabel*>(
"uri_content")->text();
259 QCOMPARE(uri.count(
"bitcoin:"), 2);
260 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"address_tag")->text(), QString(
"Address:"));
261 QVERIFY(address.isEmpty());
262 address = receiveRequestDialog->QObject::findChild<QLabel*>(
"address_content")->text();
263 QVERIFY(!address.isEmpty());
265 QCOMPARE(uri.count(
"amount=0.00000001"), 2);
266 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"amount_tag")->text(), QString(
"Amount:"));
267 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"amount_content")->text(), QString::fromStdString(
"0.00000001 " +
CURRENCY_UNIT));
269 QCOMPARE(uri.count(
"label=TEST_LABEL_1"), 2);
270 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"label_tag")->text(), QString(
"Label:"));
271 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"label_content")->text(), QString(
"TEST_LABEL_1"));
273 QCOMPARE(uri.count(
"message=TEST_MESSAGE_1"), 2);
274 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"message_tag")->text(), QString(
"Message:"));
275 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"message_content")->text(), QString(
"TEST_MESSAGE_1"));
280 QPushButton* clearButton = receiveCoinsDialog.findChild<QPushButton*>(
"clearButton");
281 clearButton->click();
282 QCOMPARE(labelInput->text(), QString(
""));
284 QCOMPARE(messageInput->text(), QString(
""));
287 int currentRowCount = requestTableModel->
rowCount({});
288 QCOMPARE(currentRowCount, initialRowCount+1);
292 QCOMPARE(requests.size(),
size_t{1});
296 QCOMPARE(entry.
id, int64_t{1});
297 QVERIFY(entry.
date.isValid());
306 QTableView* table = receiveCoinsDialog.findChild<QTableView*>(
"recentRequestsView");
307 table->selectRow(currentRowCount-1);
308 QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>(
"removeRequestButton");
309 removeRequestButton->click();
310 QCOMPARE(requestTableModel->
rowCount({}), currentRowCount-1);
321 if (QApplication::platformName() ==
"minimal") {
326 QWARN(
"Skipping WalletTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke "
327 "with 'QT_QPA_PLATFORM=cocoa test_bitcoin-qt' on mac, or else use a linux or windows build.");
int64_t CAmount
Amount in satoshis (Can be negative)
static constexpr CAmount COIN
The amount of satoshis in one BTC.
const CChainParams & Params()
Return the currently selected parameters.
#define Assert(val)
Identity function.
Widget for entering bitcoin amounts.
void setValue(const CAmount &value)
static QString formatWithUnit(Unit unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
CPubKey GetPubKey() const
Compute the public key from a private key.
Model for Bitcoin network client.
Double ended buffer combining vector and stream-like interfaces.
Interface from Qt to configuration data structure for Bitcoin client.
BitcoinUnit getDisplayUnit() const
Overview ("home") page widget.
Line edit that can be marked as "invalid" to show input validation feedback.
Dialog for requesting payment of bitcoins.
SendCoinsRecipient recipient
Model for list of recently generated payment requests / bitcoin: URIs.
int rowCount(const QModelIndex &parent) const override
Dialog for sending bitcoins.
void setModel(WalletModel *model)
A single entry in the dialog for sending bitcoins.
std::string sPaymentRequest
QString authenticatedMerchant
UI model for the transaction table of a wallet.
@ TxHashRole
Transaction hash.
int rowCount(const QModelIndex &parent) const override
Widget showing the transaction list for a wallet, including a filter row.
Interface to Bitcoin wallet from Qt view code.
RecentRequestsTableModel * getRecentRequestsTableModel() const
void pollBalanceChanged()
interfaces::Wallet & wallet() const
TransactionTableModel * getTransactionTableModel() const
OptionsModel * getOptionsModel() const
interfaces::Node & m_node
std::string ToString() const
Top-level interface for a bitcoin node (bitcoind process).
virtual std::vector< std::string > getAddressReceiveRequests()=0
Get receive requests.
virtual CAmount getBalance()=0
Get balance.
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Descriptor with some wallet metadata.
RAII object to check and reserve a wallet rescan.
std::unique_ptr< Descriptor > Parse(const std::string &descriptor, FlatSigningProvider &out, std::string &error, bool require_checksum)
Parse a descriptor string.
const std::string CURRENCY_UNIT
std::string EncodeSecret(const CKey &key)
std::string EncodeDestination(const CTxDestination &dest)
bool error(const char *fmt, const Args &... args)
std::unique_ptr< WalletLoader > MakeWalletLoader(Chain &chain, ArgsManager &args)
Return implementation of ChainClient interface for a wallet loader.
std::unique_ptr< Wallet > MakeWallet(wallet::WalletContext &context, const std::shared_ptr< wallet::CWallet > &wallet)
Return implementation of Wallet interface.
std::unique_ptr< WalletDatabase > CreateMockWalletDatabase(DatabaseOptions &options)
Return object for accessing temporary in-memory database.
bool AddWallet(WalletContext &context, const std::shared_ptr< CWallet > &wallet)
@ WALLET_FLAG_DESCRIPTORS
Indicate that this wallet supports DescriptorScriptPubKeyMan.
bool RemoveWallet(WalletContext &context, const std::shared_ptr< CWallet > &wallet, std::optional< bool > load_on_start, std::vector< bilingual_str > &warnings)
CTxDestination GetDestinationForKey(const CPubKey &key, OutputType type)
Get a destination of the requested type (if possible) to the specified key.
void ConfirmMessage(QString *text, std::chrono::milliseconds msec)
Press "Ok" button in message box dialog.
constexpr auto MakeUCharSpan(V &&v) -> decltype(UCharSpanCast(Span{std::forward< V >(v)}))
Like the Span constructor, but for (const) unsigned char member types only.
CScript GetScriptForRawPubKey(const CPubKey &pubKey)
Generate a P2PK script for the given pubkey.
std::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
CBlock CreateAndProcessBlock(const std::vector< CMutableTransaction > &txns, const CScript &scriptPubKey, Chainstate *chainstate=nullptr)
Create a new block with just given transactions, coinbase paying to scriptPubKey, and try to add it t...
interfaces::WalletLoader * wallet_loader
Reference to chain client that should used to load or create wallets opened by the gui.
std::unique_ptr< interfaces::Chain > chain
WalletContext struct containing references to state shared between CWallet instances,...
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
ChangeType
General change type (added, updated, removed).