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