Bitcoin Core 29.99.0
P2P Digital Currency
addressbooktests.cpp
Go to the documentation of this file.
1// Copyright (c) 2017-present The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
6#include <qt/test/util.h>
8
9#include <interfaces/chain.h>
10#include <interfaces/node.h>
11#include <qt/addressbookpage.h>
12#include <qt/clientmodel.h>
14#include <qt/optionsmodel.h>
15#include <qt/platformstyle.h>
17#include <qt/walletmodel.h>
18
19#include <key.h>
20#include <key_io.h>
21#include <wallet/wallet.h>
22#include <wallet/test/util.h>
23#include <walletinitinterface.h>
24
25#include <chrono>
26
27#include <QApplication>
28#include <QLineEdit>
29#include <QMessageBox>
30#include <QTableView>
31#include <QTimer>
32
34using wallet::CWallet;
39
40namespace
41{
42
47void EditAddressAndSubmit(
48 EditAddressDialog* dialog,
49 const QString& label, const QString& address, QString expected_msg)
50{
51 QString warning_text;
52
53 dialog->findChild<QLineEdit*>("labelEdit")->setText(label);
54 dialog->findChild<QValidatedLineEdit*>("addressEdit")->setText(address);
55
56 ConfirmMessage(&warning_text, 5ms);
57 dialog->accept();
58 QCOMPARE(warning_text, expected_msg);
59}
60
73void TestAddAddressesToSendBook(interfaces::Node& node)
74{
76 auto wallet_loader = interfaces::MakeWalletLoader(*test.m_node.chain, *Assert(test.m_node.args));
77 test.m_node.wallet_loader = wallet_loader.get();
78 node.setContext(&test.m_node);
79 const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockableWalletDatabase());
80 wallet->LoadWallet();
81 wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
82 {
83 LOCK(wallet->cs_wallet);
84 wallet->SetupDescriptorScriptPubKeyMans();
85 }
86
87 auto build_address{[]() {
89 return std::make_pair(dest, QString::fromStdString(EncodeDestination(dest)));
90 }};
91
92 CTxDestination r_key_dest, s_key_dest;
93
94 // Add a preexisting "receive" entry in the address book.
95 QString preexisting_r_address;
96 QString r_label("already here (r)");
97
98 // Add a preexisting "send" entry in the address book.
99 QString preexisting_s_address;
100 QString s_label("already here (s)");
101
102 // Define a new address (which should add to the address book successfully).
103 QString new_address_a;
104 QString new_address_b;
105
106 std::tie(r_key_dest, preexisting_r_address) = build_address();
107 std::tie(s_key_dest, preexisting_s_address) = build_address();
108 std::tie(std::ignore, new_address_a) = build_address();
109 std::tie(std::ignore, new_address_b) = build_address();
110
111 {
112 LOCK(wallet->cs_wallet);
113 wallet->SetAddressBook(r_key_dest, r_label.toStdString(), wallet::AddressPurpose::RECEIVE);
114 wallet->SetAddressBook(s_key_dest, s_label.toStdString(), wallet::AddressPurpose::SEND);
115 }
116
117 auto check_addbook_size = [&wallet](int expected_size) {
118 LOCK(wallet->cs_wallet);
119 QCOMPARE(static_cast<int>(wallet->m_address_book.size()), expected_size);
120 };
121
122 // We should start with the two addresses we added earlier and nothing else.
123 check_addbook_size(2);
124
125 // Initialize relevant QT models.
126 std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
127 OptionsModel optionsModel(node);
128 bilingual_str error;
129 QVERIFY(optionsModel.Init(error));
130 ClientModel clientModel(node, &optionsModel);
131 WalletContext& context = *node.walletLoader().context();
132 AddWallet(context, wallet);
133 WalletModel walletModel(interfaces::MakeWallet(context, wallet), clientModel, platformStyle.get());
134 RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
136 editAddressDialog.setModel(walletModel.getAddressTableModel());
137
139 address_book.setModel(walletModel.getAddressTableModel());
140 auto table_view = address_book.findChild<QTableView*>("tableView");
141 QCOMPARE(table_view->model()->rowCount(), 1);
142
143 EditAddressAndSubmit(
144 &editAddressDialog, QString("uhoh"), preexisting_r_address,
145 QString(
146 "Address \"%1\" already exists as a receiving address with label "
147 "\"%2\" and so cannot be added as a sending address."
148 ).arg(preexisting_r_address).arg(r_label));
149 check_addbook_size(2);
150 QCOMPARE(table_view->model()->rowCount(), 1);
151
152 EditAddressAndSubmit(
153 &editAddressDialog, QString("uhoh, different"), preexisting_s_address,
154 QString(
155 "The entered address \"%1\" is already in the address book with "
156 "label \"%2\"."
157 ).arg(preexisting_s_address).arg(s_label));
158 check_addbook_size(2);
159 QCOMPARE(table_view->model()->rowCount(), 1);
160
161 // Submit a new address which should add successfully - we expect the
162 // warning message to be blank.
163 EditAddressAndSubmit(
164 &editAddressDialog, QString("io - new A"), new_address_a, QString(""));
165 check_addbook_size(3);
166 QCOMPARE(table_view->model()->rowCount(), 2);
167
168 EditAddressAndSubmit(
169 &editAddressDialog, QString("io - new B"), new_address_b, QString(""));
170 check_addbook_size(4);
171 QCOMPARE(table_view->model()->rowCount(), 3);
172
173 auto search_line = address_book.findChild<QLineEdit*>("searchLineEdit");
174
175 search_line->setText(r_label);
176 QCOMPARE(table_view->model()->rowCount(), 0);
177
178 search_line->setText(s_label);
179 QCOMPARE(table_view->model()->rowCount(), 1);
180
181 search_line->setText("io");
182 QCOMPARE(table_view->model()->rowCount(), 2);
183
184 // Check wildcard "?".
185 search_line->setText("io?new");
186 QCOMPARE(table_view->model()->rowCount(), 0);
187 search_line->setText("io???new");
188 QCOMPARE(table_view->model()->rowCount(), 2);
189
190 // Check wildcard "*".
191 search_line->setText("io*new");
192 QCOMPARE(table_view->model()->rowCount(), 2);
193 search_line->setText("*");
194 QCOMPARE(table_view->model()->rowCount(), 3);
195
196 search_line->setText(preexisting_r_address);
197 QCOMPARE(table_view->model()->rowCount(), 0);
198
199 search_line->setText(preexisting_s_address);
200 QCOMPARE(table_view->model()->rowCount(), 1);
201
202 search_line->setText(new_address_a);
203 QCOMPARE(table_view->model()->rowCount(), 1);
204
205 search_line->setText(new_address_b);
206 QCOMPARE(table_view->model()->rowCount(), 1);
207
208 search_line->setText("");
209 QCOMPARE(table_view->model()->rowCount(), 3);
210}
211
212} // namespace
213
215{
216#ifdef Q_OS_MACOS
217 if (QApplication::platformName() == "minimal") {
218 // Disable for mac on "minimal" platform to avoid crashes inside the Qt
219 // framework when it tries to look up unimplemented cocoa functions,
220 // and fails to handle returned nulls
221 // (https://bugreports.qt.io/browse/QTBUG-49686).
222 qWarning() << "Skipping AddressBookTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke "
223 "with 'QT_QPA_PLATFORM=cocoa test_bitcoin-qt' on mac, or else use a linux or windows build.";
224 return;
225 }
226#endif
227 TestAddAddressesToSendBook(m_node);
228}
std::variant< CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, PayToAnchor, WitnessUnknown > CTxDestination
A txout script categorized into standard templates.
Definition: addresstype.h:143
#define Assert(val)
Identity function.
Definition: check.h:106
Widget that shows a list of sending or receiving addresses.
@ ForEditing
Open address book for editing.
interfaces::Node & m_node
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:182
Model for Bitcoin network client.
Definition: clientmodel.h:57
Dialog for editing an address and associated information.
void accept() override
Interface from Qt to configuration data structure for Bitcoin client.
Definition: optionsmodel.h:43
static const PlatformStyle * instantiate(const QString &platformId)
Get style associated with provided platform name, or 0 if not known.
Line edit that can be marked as "invalid" to show input validation feedback.
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:49
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:71
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:301
CKey GenerateRandomKey(bool compressed) noexcept
Definition: key.cpp:352
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:294
std::unique_ptr< WalletLoader > MakeWalletLoader(Chain &chain, ArgsManager &args)
Return implementation of ChainClient interface for a wallet loader.
Definition: dummywallet.cpp:60
std::unique_ptr< Wallet > MakeWallet(wallet::WalletContext &context, const std::shared_ptr< wallet::CWallet > &wallet)
Return implementation of Wallet interface.
Definition: interfaces.cpp:684
Definition: messages.h:20
std::unique_ptr< WalletDatabase > CreateMockableWalletDatabase(MockableData records)
Definition: util.cpp:186
bool AddWallet(WalletContext &context, const std::shared_ptr< CWallet > &wallet)
Definition: wallet.cpp:149
@ WALLET_FLAG_DESCRIPTORS
Indicate that this wallet supports DescriptorScriptPubKeyMan.
Definition: walletutil.h:74
bool RemoveWallet(WalletContext &context, const std::shared_ptr< CWallet > &wallet, std::optional< bool > load_on_start, std::vector< bilingual_str > &warnings)
Definition: wallet.cpp:161
void ConfirmMessage(QString *text, std::chrono::milliseconds msec)
Press "Ok" button in message box dialog.
Definition: util.cpp:16
node::NodeContext m_node
Definition: setup_common.h:66
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
Definition: setup_common.h:146
Bilingual messages:
Definition: translation.h:24
ArgsManager * args
Definition: context.h:74
interfaces::WalletLoader * wallet_loader
Definition: context.h:82
std::unique_ptr< interfaces::Chain > chain
Definition: context.h:76
WalletContext struct containing references to state shared between CWallet instances,...
Definition: context.h:36
#define LOCK(cs)
Definition: sync.h:257