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")) {
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");
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);
215 QCOMPARE(transactionTableModel->
rowCount({}), 107);
216 QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
217 QVERIFY(FindTx(*transactionTableModel, txid2).isValid());
220 BumpFee(transactionView, txid1,
true ,
"not BIP 125 replaceable" ,
false );
221 BumpFee(transactionView, txid2,
false , {} ,
true );
222 BumpFee(transactionView, txid2,
false , {} ,
false );
223 BumpFee(transactionView, txid2,
true ,
"already bumped" ,
false );
229 CompareBalance(walletModel, walletModel.
wallet().
getBalance(), overviewPage.findChild<QLabel*>(
"labelBalance"));
233 receiveCoinsDialog.
setModel(&walletModel);
237 QLineEdit* labelInput = receiveCoinsDialog.findChild<QLineEdit*>(
"reqLabel");
238 labelInput->setText(
"TEST_LABEL_1");
245 QLineEdit* messageInput = receiveCoinsDialog.findChild<QLineEdit*>(
"reqMessage");
246 messageInput->setText(
"TEST_MESSAGE_1");
247 int initialRowCount = requestTableModel->
rowCount({});
248 QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>(
"receiveButton");
249 requestPaymentButton->click();
251 for (QWidget* widget : QApplication::topLevelWidgets()) {
252 if (widget->inherits(
"ReceiveRequestDialog")) {
254 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"payment_header")->text(), QString(
"Payment information"));
255 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"uri_tag")->text(), QString(
"URI:"));
256 QString uri = receiveRequestDialog->QObject::findChild<QLabel*>(
"uri_content")->text();
257 QCOMPARE(uri.count(
"bitcoin:"), 2);
258 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"address_tag")->text(), QString(
"Address:"));
259 QVERIFY(address.isEmpty());
260 address = receiveRequestDialog->QObject::findChild<QLabel*>(
"address_content")->text();
261 QVERIFY(!address.isEmpty());
263 QCOMPARE(uri.count(
"amount=0.00000001"), 2);
264 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"amount_tag")->text(), QString(
"Amount:"));
265 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"amount_content")->text(), QString::fromStdString(
"0.00000001 " +
CURRENCY_UNIT));
267 QCOMPARE(uri.count(
"label=TEST_LABEL_1"), 2);
268 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"label_tag")->text(), QString(
"Label:"));
269 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"label_content")->text(), QString(
"TEST_LABEL_1"));
271 QCOMPARE(uri.count(
"message=TEST_MESSAGE_1"), 2);
272 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"message_tag")->text(), QString(
"Message:"));
273 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"message_content")->text(), QString(
"TEST_MESSAGE_1"));
278 QPushButton* clearButton = receiveCoinsDialog.findChild<QPushButton*>(
"clearButton");
279 clearButton->click();
280 QCOMPARE(labelInput->text(), QString(
""));
282 QCOMPARE(messageInput->text(), QString(
""));
285 int currentRowCount = requestTableModel->
rowCount({});
286 QCOMPARE(currentRowCount, initialRowCount+1);
290 QCOMPARE(requests.size(),
size_t{1});
293 QCOMPARE(entry.nVersion,
int{1});
294 QCOMPARE(entry.id, int64_t{1});
295 QVERIFY(entry.date.isValid());
296 QCOMPARE(entry.recipient.address, address);
297 QCOMPARE(entry.recipient.label, QString{
"TEST_LABEL_1"});
298 QCOMPARE(entry.recipient.amount,
CAmount{1});
299 QCOMPARE(entry.recipient.message, QString{
"TEST_MESSAGE_1"});
300 QCOMPARE(entry.recipient.sPaymentRequest, std::string{});
301 QCOMPARE(entry.recipient.authenticatedMerchant, QString{});
304 QTableView* table = receiveCoinsDialog.findChild<QTableView*>(
"recentRequestsView");
305 table->selectRow(currentRowCount-1);
306 QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>(
"removeRequestButton");
307 removeRequestButton->click();
308 QCOMPARE(requestTableModel->
rowCount({}), currentRowCount-1);
319 if (QApplication::platformName() ==
"minimal") {
324 QWARN(
"Skipping WalletTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke " 325 "with 'QT_QPA_PLATFORM=cocoa test_bitcoin-qt' on mac, or else use a linux or windows build.");
Widget for entering bitcoin amounts.
std::unique_ptr< interfaces::Chain > chain
Model for list of recently generated payment requests / bitcoin: URIs.
Dialog for requesting payment of bitcoins.
OptionsModel * getOptionsModel() const
void setWalletModel(WalletModel *walletModel)
interfaces::Wallet & wallet() const
CPubKey GetPubKey() const
Compute the public key from a private key.
TransactionTableModel * getTransactionTableModel() const
CScript GetScriptForRawPubKey(const CPubKey &pubKey)
Generate a P2PK script for the given pubkey.
BitcoinUnit getDisplayUnit() const
std::unique_ptr< Wallet > MakeWallet(wallet::WalletContext &context, const std::shared_ptr< wallet::CWallet > &wallet)
Return implementation of Wallet interface.
RAII object to check and reserve a wallet rescan.
Double ended buffer combining vector and stream-like interfaces.
Line edit that can be marked as "invalid" to show input validation feedback.
A single entry in the dialog for sending bitcoins.
int rowCount(const QModelIndex &parent) const override
static QString formatWithUnit(Unit unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
void ConfirmMessage(QString *text, std::chrono::milliseconds msec)
Press "Ok" button in message box dialog.
int64_t CAmount
Amount in satoshis (Can be negative)
void setModel(WalletModel *model)
std::unique_ptr< Descriptor > Parse(const std::string &descriptor, FlatSigningProvider &out, std::string &error, bool require_checksum)
Parse a descriptor string.
std::unique_ptr< WalletLoader > MakeWalletLoader(Chain &chain, ArgsManager &args)
Return implementation of ChainClient interface for a wallet loader.
RecentRequestsTableModel * getRecentRequestsTableModel() const
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...
std::unique_ptr< WalletDatabase > CreateMockWalletDatabase(DatabaseOptions &options)
Return object for accessing temporary in-memory database.
Widget showing the transaction list for a wallet, including a filter row.
Indicate that this wallet supports DescriptorScriptPubKeyMan.
Dialog for sending bitcoins.
const std::string CURRENCY_UNIT
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
std::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
std::string ToString() const
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
UI model for the transaction table of a wallet.
Model for Bitcoin network client.
void setModel(WalletModel *model)
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Descriptor with some wallet metadata.
bool RemoveWallet(WalletContext &context, const std::shared_ptr< CWallet > &wallet, std::optional< bool > load_on_start, std::vector< bilingual_str > &warnings)
interfaces::Node & m_node
int rowCount(const QModelIndex &parent) const override
Interface from Qt to configuration data structure for Bitcoin client.
const CChainParams & Params()
Return the currently selected parameters.
Interface to Bitcoin wallet from Qt view code.
WalletContext struct containing references to state shared between CWallet instances, like the reference to the chain interface, and the list of opened wallets.
constexpr auto MakeUCharSpan(V &&v) -> decltype(UCharSpanCast(Span
Like the Span constructor, but for (const) unsigned char member types only.
CTxDestination GetDestinationForKey(const CPubKey &key, OutputType type)
Get a destination of the requested type (if possible) to the specified key.
std::string EncodeDestination(const CTxDestination &dest)
virtual CAmount getBalance()=0
Get balance.
virtual std::vector< std::string > getAddressReceiveRequests()=0
Get receive requests.
ChangeType
General change type (added, updated, removed).
std::string EncodeSecret(const CKey &key)
interfaces::WalletLoader * wallet_loader
Reference to chain client that should used to load or create wallets opened by the gui...
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Top-level interface for a bitcoin node (bitcoind process).
bool AddWallet(WalletContext &context, const std::shared_ptr< CWallet > &wallet)
bool error(const char *fmt, const Args &... args)
Overview ("home") page widget.
void setValue(const CAmount &value)
#define Assert(val)
Identity function.
void pollBalanceChanged()
static constexpr CAmount COIN
The amount of satoshis in one BTC.