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