Bitcoin Core  0.18.99
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 
5 #include <qt/walletcontroller.h>
6 
7 #include <interfaces/handler.h>
8 #include <interfaces/node.h>
9 
10 #include <algorithm>
11 
12 #include <QApplication>
13 #include <QMessageBox>
14 #include <QMutexLocker>
15 #include <QThread>
16 #include <QWindow>
17 
18 WalletController::WalletController(interfaces::Node& node, const PlatformStyle* platform_style, OptionsModel* options_model, QObject* parent)
19  : QObject(parent)
20  , m_node(node)
21  , m_platform_style(platform_style)
22  , m_options_model(options_model)
23 {
24  m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
25  getOrCreateWallet(std::move(wallet));
26  });
27 
28  for (std::unique_ptr<interfaces::Wallet>& wallet : m_node.getWallets()) {
29  getOrCreateWallet(std::move(wallet));
30  }
31 
32  m_activity_thread.start();
33 }
34 
35 // Not using the default destructor because not all member types definitions are
36 // available in the header, just forward declared.
38 {
39  m_activity_thread.quit();
40  m_activity_thread.wait();
41 }
42 
43 std::vector<WalletModel*> WalletController::getWallets() const
44 {
45  QMutexLocker locker(&m_mutex);
46  return m_wallets;
47 }
48 
49 std::vector<std::string> WalletController::getWalletsAvailableToOpen() const
50 {
51  QMutexLocker locker(&m_mutex);
52  std::vector<std::string> wallets = m_node.listWalletDir();
53  for (WalletModel* wallet_model : m_wallets) {
54  auto it = std::remove(wallets.begin(), wallets.end(), wallet_model->wallet().getWalletName());
55  if (it != wallets.end()) wallets.erase(it);
56  }
57  return wallets;
58 }
59 
60 OpenWalletActivity* WalletController::openWallet(const std::string& name, QWidget* parent)
61 {
62  OpenWalletActivity* activity = new OpenWalletActivity(this, name);
63  activity->moveToThread(&m_activity_thread);
64  return activity;
65 }
66 
67 void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent)
68 {
69  QMessageBox box(parent);
70  box.setWindowTitle(tr("Close wallet"));
71  box.setText(tr("Are you sure you wish to close wallet <i>%1</i>?").arg(wallet_model->getDisplayName()));
72  box.setInformativeText(tr("Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled."));
73  box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
74  box.setDefaultButton(QMessageBox::Yes);
75  if (box.exec() != QMessageBox::Yes) return;
76 
77  // First remove wallet from node.
78  wallet_model->wallet().remove();
79  // Now release the model.
80  removeAndDeleteWallet(wallet_model);
81 }
82 
83 WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet)
84 {
85  QMutexLocker locker(&m_mutex);
86 
87  // Return model instance if exists.
88  if (!m_wallets.empty()) {
89  std::string name = wallet->getWalletName();
90  for (WalletModel* wallet_model : m_wallets) {
91  if (wallet_model->wallet().getWalletName() == name) {
92  return wallet_model;
93  }
94  }
95  }
96 
97  // Instantiate model and register it.
98  WalletModel* wallet_model = new WalletModel(std::move(wallet), m_node, m_platform_style, m_options_model, nullptr);
99  m_wallets.push_back(wallet_model);
100 
101  connect(wallet_model, &WalletModel::unload, [this, wallet_model] {
102  // Defer removeAndDeleteWallet when no modal widget is active.
103  // TODO: remove this workaround by removing usage of QDiallog::exec.
104  if (QApplication::activeModalWidget()) {
105  connect(qApp, &QApplication::focusWindowChanged, wallet_model, [this, wallet_model]() {
106  if (!QApplication::activeModalWidget()) {
107  removeAndDeleteWallet(wallet_model);
108  }
109  }, Qt::QueuedConnection);
110  } else {
111  removeAndDeleteWallet(wallet_model);
112  }
113  });
114 
115  // Re-emit coinsSent signal from wallet model.
116  connect(wallet_model, &WalletModel::coinsSent, this, &WalletController::coinsSent);
117 
118  // Notify walletAdded signal on the GUI thread.
119  if (QThread::currentThread() == thread()) {
120  addWallet(wallet_model);
121  } else {
122  // Handler callback runs in a different thread so fix wallet model thread affinity.
123  wallet_model->moveToThread(thread());
124  QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection, Q_ARG(WalletModel*, wallet_model));
125  }
126 
127  return wallet_model;
128 }
129 
131 {
132  // Take ownership of the wallet model and register it.
133  wallet_model->setParent(this);
134  Q_EMIT walletAdded(wallet_model);
135 }
136 
138 {
139  // Unregister wallet model.
140  {
141  QMutexLocker locker(&m_mutex);
142  m_wallets.erase(std::remove(m_wallets.begin(), m_wallets.end(), wallet_model));
143  }
144  Q_EMIT walletRemoved(wallet_model);
145  // Currently this can trigger the unload since the model can hold the last
146  // CWallet shared pointer.
147  delete wallet_model;
148 }
149 
150 
151 OpenWalletActivity::OpenWalletActivity(WalletController* wallet_controller, const std::string& name)
152  : m_wallet_controller(wallet_controller)
153  , m_name(name)
154 {}
155 
157 {
158  std::string error, warning;
159  std::unique_ptr<interfaces::Wallet> wallet = m_wallet_controller->m_node.loadWallet(m_name, error, warning);
160  if (!warning.empty()) {
161  Q_EMIT message(QMessageBox::Warning, QString::fromStdString(warning));
162  }
163  if (wallet) {
164  Q_EMIT opened(m_wallet_controller->getOrCreateWallet(std::move(wallet)));
165  } else {
166  Q_EMIT message(QMessageBox::Critical, QString::fromStdString(error));
167  }
168  Q_EMIT finished();
169 }
std::vector< WalletModel * > m_wallets
interfaces::Wallet & wallet() const
Definition: walletmodel.h:223
void coinsSent(WalletModel *wallet, SendCoinsRecipient recipient, QByteArray transaction)
std::string const m_name
std::vector< WalletModel * > getWallets() const
void opened(WalletModel *wallet_model)
const PlatformStyle *const m_platform_style
virtual std::vector< std::string > listWalletDir()=0
Return available wallets in wallet directory.
void unload()
Controller between interfaces::Node, WalletModel instances and the GUI.
WalletController(interfaces::Node &node, const PlatformStyle *platform_style, OptionsModel *options_model, QObject *parent)
virtual std::vector< std::unique_ptr< Wallet > > getWallets()=0
Return interfaces for accessing wallets (if any).
void message(QMessageBox::Icon icon, const QString text)
void addWallet(WalletModel *wallet_model)
void coinsSent(WalletModel *wallet_model, SendCoinsRecipient recipient, QByteArray transaction)
std::vector< std::string > getWalletsAvailableToOpen() const
OpenWalletActivity * openWallet(const std::string &name, QWidget *parent=nullptr)
void closeWallet(WalletModel *wallet_model, QWidget *parent=nullptr)
const char * name
Definition: rest.cpp:38
virtual void remove()=0
interfaces::Node & m_node
void walletAdded(WalletModel *wallet_model)
OpenWalletActivity(WalletController *wallet_controller, const std::string &name)
virtual std::unique_ptr< Handler > handleLoadWallet(LoadWalletFn fn)=0
friend class OpenWalletActivity
Interface from Qt to configuration data structure for Bitcoin client.
Definition: optionsmodel.h:29
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:125
WalletModel * getOrCreateWallet(std::unique_ptr< interfaces::Wallet > wallet)
void walletRemoved(WalletModel *wallet_model)
QString getDisplayName() const
std::unique_ptr< interfaces::Handler > m_handler_load_wallet
virtual std::unique_ptr< Wallet > loadWallet(const std::string &name, std::string &error, std::string &warning)=0
Attempts to load a wallet from file or directory.
WalletController *const m_wallet_controller
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:36
OptionsModel *const m_options_model
bool error(const char *fmt, const Args &... args)
Definition: system.h:61
void removeAndDeleteWallet(WalletModel *wallet_model)