22#include <validation.h>
31#include <QAbstractButton>
33#include <QApplication>
40#include <QDialogButtonBox>
45void ConfirmSend(QString* text =
nullptr,
bool cancel =
false)
47 QTimer::singleShot(0, [text, cancel]() {
48 for (QWidget* widget : QApplication::topLevelWidgets()) {
49 if (widget->inherits(
"SendConfirmationDialog")) {
50 SendConfirmationDialog* dialog = qobject_cast<SendConfirmationDialog*>(widget);
51 if (text) *text = dialog->text();
52 QAbstractButton* button = dialog->button(cancel ? QMessageBox::Cancel : QMessageBox::Yes);
53 button->setEnabled(true);
63 QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>(
"entries");
64 SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget());
67 sendCoinsDialog.findChild<QFrame*>(
"frameFee")
68 ->findChild<QFrame*>(
"frameFeeSelection")
69 ->findChild<QCheckBox*>(
"optInRBF")
70 ->setCheckState(rbf ? Qt::Checked : Qt::Unchecked);
72 boost::signals2::scoped_connection c(
wallet.NotifyTransactionChanged.connect([&txid](
const uint256& hash,
ChangeType status) {
73 if (status == CT_NEW) txid = hash;
76 bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog,
"sendButtonClicked", Q_ARG(
bool,
false));
82QModelIndex FindTx(
const QAbstractItemModel& model,
const uint256& txid)
84 QString hash = QString::fromStdString(txid.
ToString());
85 int rows = model.rowCount({});
86 for (
int row = 0; row < rows; ++row) {
87 QModelIndex index = model.index(row, 0, {});
96void BumpFee(
TransactionView& view,
const uint256& txid,
bool expectDisabled, std::string expectError,
bool cancel)
98 QTableView* table = view.findChild<QTableView*>(
"transactionView");
99 QModelIndex index = FindTx(*table->selectionModel()->model(), txid);
100 QVERIFY2(index.isValid(),
"Could not find BumpFee txid");
104 QAction* action = view.findChild<QAction*>(
"bumpFeeAction");
105 table->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
106 action->setEnabled(expectDisabled);
107 table->customContextMenuRequested({});
108 QCOMPARE(action->isEnabled(), !expectDisabled);
110 action->setEnabled(
true);
112 if (expectError.empty()) {
113 ConfirmSend(&text, cancel);
118 QVERIFY(text.indexOf(QString::fromStdString(expectError)) != -1);
138 for (
int i = 0; i < 5; ++i) {
149 wallet->SetupDescriptorScriptPubKeyMans();
157 if (!
wallet->AddWalletDescriptor(w_desc, provider,
"",
false))
assert(
false);
159 wallet->SetAddressBook(dest,
"",
"receive");
160 wallet->SetLastBlockProcessed(105,
node.context()->chainman->ActiveChain().Tip()->GetBlockHash());
170 wallet->SetBroadcastTransactions(
true);
182 sendCoinsDialog.
setModel(&walletModel);
183 transactionView.setModel(&walletModel);
187 QLabel* balanceLabel = sendCoinsDialog.findChild<QLabel*>(
"labelBalance");
188 QString balanceText = balanceLabel->text();
189 int unit = walletModel.getOptionsModel()->getDisplayUnit();
190 CAmount balance = walletModel.wallet().getBalance();
192 QCOMPARE(balanceText, balanceComparison);
197 QCOMPARE(transactionTableModel->
rowCount({}), 105);
200 QCOMPARE(transactionTableModel->
rowCount({}), 107);
201 QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
202 QVERIFY(FindTx(*transactionTableModel, txid2).isValid());
205 BumpFee(transactionView, txid1,
true ,
"not BIP 125 replaceable" ,
false );
206 BumpFee(transactionView, txid2,
false , {} ,
true );
207 BumpFee(transactionView, txid2,
false , {} ,
false );
208 BumpFee(transactionView, txid2,
true ,
"already bumped" ,
false );
212 overviewPage.setWalletModel(&walletModel);
213 QLabel* balanceLabel = overviewPage.findChild<QLabel*>(
"labelBalance");
214 QString balanceText = balanceLabel->text().trimmed();
215 int unit = walletModel.getOptionsModel()->getDisplayUnit();
216 CAmount balance = walletModel.wallet().getBalance();
218 QCOMPARE(balanceText, balanceComparison);
222 receiveCoinsDialog.setModel(&walletModel);
226 QLineEdit* labelInput = receiveCoinsDialog.findChild<QLineEdit*>(
"reqLabel");
227 labelInput->setText(
"TEST_LABEL_1");
234 QLineEdit* messageInput = receiveCoinsDialog.findChild<QLineEdit*>(
"reqMessage");
235 messageInput->setText(
"TEST_MESSAGE_1");
236 int initialRowCount = requestTableModel->
rowCount({});
237 QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>(
"receiveButton");
238 requestPaymentButton->click();
240 for (QWidget* widget : QApplication::topLevelWidgets()) {
241 if (widget->inherits(
"ReceiveRequestDialog")) {
243 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"payment_header")->text(), QString(
"Payment information"));
244 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"uri_tag")->text(), QString(
"URI:"));
245 QString uri = receiveRequestDialog->QObject::findChild<QLabel*>(
"uri_content")->text();
246 QCOMPARE(uri.count(
"bitcoin:"), 2);
247 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"address_tag")->text(), QString(
"Address:"));
248 QVERIFY(address.isEmpty());
249 address = receiveRequestDialog->QObject::findChild<QLabel*>(
"address_content")->text();
250 QVERIFY(!address.isEmpty());
252 QCOMPARE(uri.count(
"amount=0.00000001"), 2);
253 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"amount_tag")->text(), QString(
"Amount:"));
254 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"amount_content")->text(), QString::fromStdString(
"0.00000001 " +
CURRENCY_UNIT));
256 QCOMPARE(uri.count(
"label=TEST_LABEL_1"), 2);
257 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"label_tag")->text(), QString(
"Label:"));
258 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"label_content")->text(), QString(
"TEST_LABEL_1"));
260 QCOMPARE(uri.count(
"message=TEST_MESSAGE_1"), 2);
261 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"message_tag")->text(), QString(
"Message:"));
262 QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>(
"message_content")->text(), QString(
"TEST_MESSAGE_1"));
267 QPushButton* clearButton = receiveCoinsDialog.findChild<QPushButton*>(
"clearButton");
268 clearButton->click();
269 QCOMPARE(labelInput->text(), QString(
""));
271 QCOMPARE(messageInput->text(), QString(
""));
274 int currentRowCount = requestTableModel->
rowCount({});
275 QCOMPARE(currentRowCount, initialRowCount+1);
278 std::vector<std::string> requests = walletModel.wallet().getAddressReceiveRequests();
279 QCOMPARE(requests.size(),
size_t{1});
283 QCOMPARE(entry.
id, int64_t{1});
284 QVERIFY(entry.
date.isValid());
293 QTableView* table = receiveCoinsDialog.findChild<QTableView*>(
"recentRequestsView");
294 table->selectRow(currentRowCount-1);
295 QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>(
"removeRequestButton");
296 removeRequestButton->click();
297 QCOMPARE(requestTableModel->
rowCount({}), currentRowCount-1);
300 QCOMPARE(walletModel.wallet().getAddressReceiveRequests().size(),
size_t{0});
308 if (QApplication::platformName() ==
"minimal") {
313 QWARN(
"Skipping WalletTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke "
314 "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(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
Double ended buffer combining vector and stream-like interfaces.
CPubKey GetPubKey() const
Compute the public key from a private key.
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Model for Bitcoin network client.
Interface from Qt to configuration data structure for Bitcoin client.
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.
Descriptor with some wallet metadata.
Interface to Bitcoin wallet from Qt view code.
RAII object to check and reserve a wallet rescan.
interfaces::Node & m_node
std::string ToString() const
Top-level interface for a bitcoin node (bitcoind process).
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
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)
std::unique_ptr< Wallet > MakeWallet(const std::shared_ptr< CWallet > &wallet)
std::unique_ptr< WalletClient > MakeWalletClient(Chain &chain, ArgsManager &args)
Return implementation of ChainClient interface for a wallet client.
CTxDestination GetDestinationForKey(const CPubKey &key, OutputType type)
Get a destination of the requested type (if possible) to the specified key.
void ConfirmMessage(QString *text, int msec)
Press "Ok" button in message box dialog.
constexpr auto MakeUCharSpan(V &&v) -> decltype(UCharSpanCast(MakeSpan(std::forward< V >(v))))
Like MakeSpan, 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.
uint256 last_failed_block
Height of the most recent block that could not be scanned due to read errors or pruning.
enum CWallet::ScanResult::@17 status
uint256 last_scanned_block
Hash and height of most recent block that was successfully scanned.
std::unique_ptr< interfaces::Chain > chain
interfaces::WalletClient * wallet_client
Reference to chain client that should used to load or create wallets opened by the gui.
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
CBlock CreateAndProcessBlock(const std::vector< CMutableTransaction > &txns, const CScript &scriptPubKey, CChainState *chainstate=nullptr)
Create a new block with just given transactions, coinbase paying to scriptPubKey, and try to add it t...
WalletContext struct containing references to state shared between CWallet instances,...
bool error(const char *fmt, const Args &... args)
ChangeType
General change type (added, updated, removed).
bool RemoveWallet(WalletContext &context, const std::shared_ptr< CWallet > &wallet, std::optional< bool > load_on_start, std::vector< bilingual_str > &warnings)
bool AddWallet(WalletContext &context, const std::shared_ptr< CWallet > &wallet)
std::unique_ptr< WalletDatabase > CreateMockWalletDatabase()
Return object for accessing temporary in-memory database.
@ WALLET_FLAG_DESCRIPTORS
Indicate that this wallet supports DescriptorScriptPubKeyMan.