Bitcoin ABC 0.33.6
P2P Digital Currency
walletcontroller.cpp
Go to the documentation of this file.
1// Copyright (c) 2019 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
8#include <qt/clientmodel.h>
10#include <qt/guiconstants.h>
11#include <qt/guiutil.h>
12
13#include <interfaces/handler.h>
14#include <interfaces/node.h>
15#include <util/string.h>
16#include <util/threadnames.h>
17#include <util/translation.h>
18#include <wallet/wallet.h>
19
20#include <QApplication>
21#include <QMutexLocker>
22#include <QTimer>
23#include <QWindow>
24
25#include <algorithm>
26
27using util::Join;
28
30 const PlatformStyle *platform_style,
31 QObject *parent)
32 : QObject(parent), m_activity_thread(new QThread(this)),
33 m_activity_worker(new QObject), m_client_model(client_model),
34 m_node(client_model.node()), m_platform_style(platform_style),
35 m_options_model(client_model.getOptionsModel()) {
37 [this](std::unique_ptr<interfaces::Wallet> wallet) {
38 getOrCreateWallet(std::move(wallet));
39 });
40
41 for (std::unique_ptr<interfaces::Wallet> &wallet :
43 getOrCreateWallet(std::move(wallet));
44 }
45
47 m_activity_thread->start();
48 QTimer::singleShot(0, m_activity_worker,
49 []() { util::ThreadRename("qt-walletctrl"); });
50}
51
52// Not using the default destructor because not all member types definitions are
53// available in the header, just forward declared.
55 m_activity_thread->quit();
56 m_activity_thread->wait();
57 delete m_activity_worker;
58}
59
60std::vector<WalletModel *> WalletController::getOpenWallets() const {
61 QMutexLocker locker(&m_mutex);
62 return m_wallets;
63}
64
65std::map<std::string, bool> WalletController::listWalletDir() const {
66 QMutexLocker locker(&m_mutex);
67 std::map<std::string, bool> wallets;
68 for (const std::string &name : m_node.walletClient().listWalletDir()) {
69 wallets[name] = false;
70 }
71 for (WalletModel *wallet_model : m_wallets) {
72 auto it = wallets.find(wallet_model->wallet().getWalletName());
73 if (it != wallets.end()) {
74 it->second = true;
75 }
76 }
77 return wallets;
78}
79
81 // Once the wallet is successfully removed from the node, the model will
82 // emit the 'WalletModel::unload' signal. This signal is already connected
83 // and will complete the removal of the view from the GUI. Look at
84 // 'WalletController::getOrCreateWallet' for the signal connection.
85 wallet_model->wallet().remove();
86}
87
88void WalletController::closeWallet(WalletModel *wallet_model, QWidget *parent) {
89 QMessageBox box(parent);
90 box.setWindowTitle(tr("Close wallet"));
91 box.setText(tr("Are you sure you wish to close the wallet <i>%1</i>?")
92 .arg(GUIUtil::HtmlEscape(wallet_model->getDisplayName())));
93 box.setInformativeText(
94 tr("Closing the wallet for too long can result in having to resync the "
95 "entire chain if pruning is enabled."));
96 box.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
97 box.setDefaultButton(QMessageBox::Yes);
98 if (box.exec() != QMessageBox::Yes) {
99 return;
100 }
101
102 removeWallet(wallet_model);
103}
104
106 QMessageBox::StandardButton button = QMessageBox::question(
107 parent, tr("Close all wallets"),
108 tr("Are you sure you wish to close all wallets?"),
109 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
110 if (button != QMessageBox::Yes) {
111 return;
112 }
113
114 QMutexLocker locker(&m_mutex);
115 for (WalletModel *wallet_model : m_wallets) {
116 removeWallet(wallet_model);
117 }
118}
119
121 std::unique_ptr<interfaces::Wallet> wallet) {
122 QMutexLocker locker(&m_mutex);
123
124 // Return model instance if exists.
125 if (!m_wallets.empty()) {
126 std::string name = wallet->getWalletName();
127 for (WalletModel *wallet_model : m_wallets) {
128 if (wallet_model->wallet().getWalletName() == name) {
129 return wallet_model;
130 }
131 }
132 }
133
134 // Instantiate model and register it.
135 WalletModel *wallet_model = new WalletModel(
137 nullptr /* required for the following moveToThread() call */);
138
139 // Move WalletModel object to the thread that created the WalletController
140 // object (GUI main thread), instead of the current thread, which could be
141 // an outside wallet thread or RPC thread sending a LoadWallet notification.
142 // This ensures queued signals sent to the WalletModel object will be
143 // handled on the GUI event loop.
144 wallet_model->moveToThread(thread());
145 QMetaObject::invokeMethod(
146 this, [wallet_model, this] { wallet_model->setParent(this); },
148 m_wallets.push_back(wallet_model);
149
150 // WalletModel::startPollBalance needs to be called in a thread managed by
151 // Qt because of startTimer. Considering the current thread can be a RPC
152 // thread, better delegate the calling to Qt with Qt::AutoConnection.
153 const bool called =
154 QMetaObject::invokeMethod(wallet_model, "startPollBalance");
155 assert(called);
156
157 connect(
158 wallet_model, &WalletModel::unload, this,
159 [this, wallet_model] {
160 // Defer removeAndDeleteWallet when no modal widget is active.
161 // TODO: remove this workaround by removing usage of QDiallog::exec.
162 if (QApplication::activeModalWidget()) {
163 connect(
164 qApp, &QApplication::focusWindowChanged, wallet_model,
165 [this, wallet_model]() {
166 if (!QApplication::activeModalWidget()) {
167 removeAndDeleteWallet(wallet_model);
168 }
169 },
170 Qt::QueuedConnection);
171 } else {
172 removeAndDeleteWallet(wallet_model);
173 }
174 },
175 Qt::QueuedConnection);
176
177 // Re-emit coinsSent signal from wallet model.
178 connect(wallet_model, &WalletModel::coinsSent, this,
180
181 Q_EMIT walletAdded(wallet_model);
182
183 return wallet_model;
184}
185
187 // Unregister wallet model.
188 {
189 QMutexLocker locker(&m_mutex);
190 m_wallets.erase(
191 std::remove(m_wallets.begin(), m_wallets.end(), wallet_model));
192 }
193 Q_EMIT walletRemoved(wallet_model);
194 // Currently this can trigger the unload since the model can hold the last
195 // CWallet shared pointer.
196 delete wallet_model;
197}
198
200 WalletController *wallet_controller, QWidget *parent_widget)
201 : QObject(wallet_controller), m_wallet_controller(wallet_controller),
202 m_parent_widget(parent_widget) {}
203
205 delete m_progress_dialog;
206}
207
208void WalletControllerActivity::showProgressDialog(const QString &label_text) {
210 m_progress_dialog = new QProgressDialog(m_parent_widget);
211
212 m_progress_dialog->setLabelText(label_text);
213 m_progress_dialog->setRange(0, 0);
214 m_progress_dialog->setCancelButton(nullptr);
215 m_progress_dialog->setWindowModality(Qt::ApplicationModal);
217}
218
221 delete m_progress_dialog;
222 m_progress_dialog = nullptr;
223}
224
226 QWidget *parent_widget)
227 : WalletControllerActivity(wallet_controller, parent_widget) {
229}
230
233 delete m_passphrase_dialog;
234}
235
239 m_passphrase_dialog->setWindowModality(Qt::ApplicationModal);
240 m_passphrase_dialog->show();
241
242 connect(m_passphrase_dialog, &QObject::destroyed,
243 [this] { m_passphrase_dialog = nullptr; });
244 connect(m_passphrase_dialog, &QDialog::accepted,
245 [this] { createWallet(); });
246 connect(m_passphrase_dialog, &QDialog::rejected,
247 [this] { Q_EMIT finished(); });
248}
249
252 tr("Creating Wallet <b>%1</b>...")
253 .arg(m_create_wallet_dialog->walletName().toHtmlEscaped()));
254
255 std::string name = m_create_wallet_dialog->walletName().toStdString();
256 uint64_t flags = 0;
259 }
262 }
265 }
266
267 QTimer::singleShot(500ms, worker(), [this, name, flags] {
270
271 if (wallet) {
274 } else {
276 }
277
278 QTimer::singleShot(500, this, &CreateWalletActivity::finish);
279 });
280}
281
284
285 if (!m_error_message.empty()) {
286 QMessageBox::critical(
287 m_parent_widget, tr("Create wallet failed"),
288 QString::fromStdString(m_error_message.translated));
289 } else if (!m_warning_message.empty()) {
290 QMessageBox::warning(
291 m_parent_widget, tr("Create wallet warning"),
292 QString::fromStdString(
293 Join(m_warning_message, Untranslated("\n")).translated));
294 }
295
296 if (m_wallet_model) {
297 Q_EMIT created(m_wallet_model);
298 }
299
300 Q_EMIT finished();
301}
302
305 m_create_wallet_dialog->setWindowModality(Qt::ApplicationModal);
307
308 connect(m_create_wallet_dialog, &QObject::destroyed,
309 [this] { m_create_wallet_dialog = nullptr; });
310 connect(m_create_wallet_dialog, &QDialog::rejected,
311 [this] { Q_EMIT finished(); });
312 connect(m_create_wallet_dialog, &QDialog::accepted, [this] {
314 askPassphrase();
315 } else {
316 createWallet();
317 }
318 });
319}
320
322 QWidget *parent_widget)
323 : WalletControllerActivity(wallet_controller, parent_widget) {}
324
327
328 if (!m_error_message.empty()) {
329 QMessageBox::critical(
330 m_parent_widget, tr("Open wallet failed"),
331 QString::fromStdString(m_error_message.translated));
332 } else if (!m_warning_message.empty()) {
333 QMessageBox::warning(
334 m_parent_widget, tr("Open wallet warning"),
335 QString::fromStdString(
336 Join(m_warning_message, Untranslated("\n")).translated));
337 }
338
339 if (m_wallet_model) {
340 Q_EMIT opened(m_wallet_model);
341 }
342
343 Q_EMIT finished();
344}
345
346void OpenWalletActivity::open(const std::string &path) {
347 QString name = path.empty() ? QString("[" + tr("default wallet") + "]")
348 : QString::fromStdString(path);
349
351 tr("Opening Wallet <b>%1</b>...").arg(name.toHtmlEscaped()));
352
353 QTimer::singleShot(0, worker(), [this, path] {
355
356 if (wallet) {
359 } else {
361 }
362
363 QTimer::singleShot(0, this, &OpenWalletActivity::finish);
364 });
365}
int flags
Definition: bitcoin-tx.cpp:546
Multifunctional dialog to ask for passphrases.
@ Encrypt
Ask passphrase twice and encrypt.
Model for Bitcoin network client.
Definition: clientmodel.h:43
AskPassphraseDialog * m_passphrase_dialog
CreateWalletDialog * m_create_wallet_dialog
CreateWalletActivity(WalletController *wallet_controller, QWidget *parent_widget)
void created(WalletModel *wallet_model)
Dialog for creating wallets.
bool isMakeBlankWalletChecked() const
QString walletName() const
bool isDisablePrivateKeysChecked() const
bool isEncryptWalletChecked() const
bool isDescriptorWalletChecked() const
void opened(WalletModel *wallet_model)
OpenWalletActivity(WalletController *wallet_controller, QWidget *parent_widget)
void open(const std::string &path)
std::vector< bilingual_str > m_warning_message
WalletController *const m_wallet_controller
QProgressDialog * m_progress_dialog
interfaces::Node & node() const
QObject * worker() const
void showProgressDialog(const QString &label_text)
WalletControllerActivity(WalletController *wallet_controller, QWidget *parent_widget)
QWidget *const m_parent_widget
Controller between interfaces::Node, WalletModel instances and the GUI.
WalletController(ClientModel &client_model, const PlatformStyle *platform_style, QObject *parent)
WalletModel * getOrCreateWallet(std::unique_ptr< interfaces::Wallet > wallet)
ClientModel & m_client_model
void removeAndDeleteWallet(WalletModel *wallet_model)
void walletAdded(WalletModel *wallet_model)
void closeAllWallets(QWidget *parent=nullptr)
std::unique_ptr< interfaces::Handler > m_handler_load_wallet
QThread *const m_activity_thread
std::map< std::string, bool > listWalletDir() const
Returns all wallet names in the wallet dir mapped to whether the wallet is loaded.
std::vector< WalletModel * > getOpenWallets() const
Returns wallet models currently open.
QObject *const m_activity_worker
void walletRemoved(WalletModel *wallet_model)
const PlatformStyle *const m_platform_style
void coinsSent(interfaces::Wallet &wallet, SendCoinsRecipient recipient, QByteArray transaction)
interfaces::Node & m_node
std::vector< WalletModel * > m_wallets
void closeWallet(WalletModel *wallet_model, QWidget *parent=nullptr)
void removeWallet(WalletModel *wallet_model)
Starts the wallet closure procedure.
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:47
void coinsSent(interfaces::Wallet &wallet, SendCoinsRecipient recipient, QByteArray transaction)
interfaces::Wallet & wallet() const
Definition: walletmodel.h:150
QString getDisplayName() const
void unload()
virtual WalletClient & walletClient()=0
Get wallet client.
virtual std::vector< std::string > listWalletDir()=0
Return available wallets in wallet directory.
virtual std::unique_ptr< Handler > handleLoadWallet(LoadWalletFn fn)=0
virtual std::vector< std::unique_ptr< Wallet > > getWallets()=0
Return interfaces for accessing wallets (if any).
virtual util::Result< std::unique_ptr< Wallet > > loadWallet(const std::string &name, std::vector< bilingual_str > &warnings)=0
Load existing wallet.
virtual util::Result< std::unique_ptr< Wallet > > createWallet(const std::string &name, const SecureString &passphrase, uint64_t wallet_creation_flags, std::vector< bilingual_str > &warnings)=0
Create new wallet.
virtual void remove()=0
static const int MAX_PASSPHRASE_SIZE
Definition: guiconstants.h:17
Qt::ConnectionType blockingGUIThreadConnection()
Get connection type to call object slot in GUI thread with invokeMethod.
Definition: guiutil.cpp:374
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:261
void PolishProgressDialog(QProgressDialog *dialog)
Definition: guiutil.cpp:964
Definition: messages.h:12
bilingual_str ErrorString(const Result< T > &result)
Definition: result.h:90
auto Join(const std::vector< T > &list, const BaseType &separator, UnaryOp unary_op) -> decltype(unary_op(list.at(0)))
Join a list of items.
Definition: string.h:105
void ThreadRename(std::string &&)
Rename a thread both in terms of an internal (in-memory) name as well as its system thread name.
Definition: threadnames.cpp:48
NodeContext & m_node
Definition: interfaces.cpp:820
const char * name
Definition: rest.cpp:47
bool empty() const
Definition: translation.h:27
std::string translated
Definition: translation.h:19
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:36
assert(!tx.IsCoinBase())
@ WALLET_FLAG_DISABLE_PRIVATE_KEYS
Definition: walletutil.h:55
@ WALLET_FLAG_DESCRIPTORS
Indicate that this wallet supports DescriptorScriptPubKeyMan.
Definition: walletutil.h:70
@ WALLET_FLAG_BLANK_WALLET
Flag set when a wallet contains no HD seed and no private keys, scripts, addresses,...
Definition: walletutil.h:67