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::getOpenWallets() const
44 {
45  QMutexLocker locker(&m_mutex);
46  return m_wallets;
47 }
48 
49 std::map<std::string, bool> WalletController::listWalletDir() const
50 {
51  QMutexLocker locker(&m_mutex);
52  std::map<std::string, bool> wallets;
53  for (const std::string& name : m_node.listWalletDir()) {
54  wallets[name] = false;
55  }
56  for (WalletModel* wallet_model : m_wallets) {
57  auto it = wallets.find(wallet_model->wallet().getWalletName());
58  if (it != wallets.end()) it->second = true;
59  }
60  return wallets;
61 }
62 
63 OpenWalletActivity* WalletController::openWallet(const std::string& name, QWidget* parent)
64 {
65  OpenWalletActivity* activity = new OpenWalletActivity(this, name);
66  activity->moveToThread(&m_activity_thread);
67  return activity;
68 }
69 
70 void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent)
71 {
72  QMessageBox box(parent);
73  box.setWindowTitle(tr("Close wallet"));
74  box.setText(tr("Are you sure you wish to close wallet <i>%1</i>?").arg(wallet_model->getDisplayName()));
75  box.setInformativeText(tr("Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled."));
76  box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
77  box.setDefaultButton(QMessageBox::Yes);
78  if (box.exec() != QMessageBox::Yes) return;
79 
80  // First remove wallet from node.
81  wallet_model->wallet().remove();
82  // Now release the model.
83  removeAndDeleteWallet(wallet_model);
84 }
85 
86 WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet)
87 {
88  QMutexLocker locker(&m_mutex);
89 
90  // Return model instance if exists.
91  if (!m_wallets.empty()) {
92  std::string name = wallet->getWalletName();
93  for (WalletModel* wallet_model : m_wallets) {
94  if (wallet_model->wallet().getWalletName() == name) {
95  return wallet_model;
96  }
97  }
98  }
99 
100  // Instantiate model and register it.
101  WalletModel* wallet_model = new WalletModel(std::move(wallet), m_node, m_platform_style, m_options_model, nullptr);
102  // Handler callback runs in a different thread so fix wallet model thread affinity.
103  wallet_model->moveToThread(thread());
104  wallet_model->setParent(this);
105  m_wallets.push_back(wallet_model);
106 
107  connect(wallet_model, &WalletModel::unload, [this, wallet_model] {
108  // Defer removeAndDeleteWallet when no modal widget is active.
109  // TODO: remove this workaround by removing usage of QDiallog::exec.
110  if (QApplication::activeModalWidget()) {
111  connect(qApp, &QApplication::focusWindowChanged, wallet_model, [this, wallet_model]() {
112  if (!QApplication::activeModalWidget()) {
113  removeAndDeleteWallet(wallet_model);
114  }
115  }, Qt::QueuedConnection);
116  } else {
117  removeAndDeleteWallet(wallet_model);
118  }
119  });
120 
121  // Re-emit coinsSent signal from wallet model.
122  connect(wallet_model, &WalletModel::coinsSent, this, &WalletController::coinsSent);
123 
124  // Notify walletAdded signal on the GUI thread.
125  Q_EMIT walletAdded(wallet_model);
126 
127  return wallet_model;
128 }
129 
131 {
132  // Unregister wallet model.
133  {
134  QMutexLocker locker(&m_mutex);
135  m_wallets.erase(std::remove(m_wallets.begin(), m_wallets.end(), wallet_model));
136  }
137  Q_EMIT walletRemoved(wallet_model);
138  // Currently this can trigger the unload since the model can hold the last
139  // CWallet shared pointer.
140  delete wallet_model;
141 }
142 
143 
144 OpenWalletActivity::OpenWalletActivity(WalletController* wallet_controller, const std::string& name)
145  : m_wallet_controller(wallet_controller)
146  , m_name(name)
147 {}
148 
150 {
151  std::string error, warning;
152  std::unique_ptr<interfaces::Wallet> wallet = m_wallet_controller->m_node.loadWallet(m_name, error, warning);
153  if (!warning.empty()) {
154  Q_EMIT message(QMessageBox::Warning, QString::fromStdString(warning));
155  }
156  if (wallet) {
157  Q_EMIT opened(m_wallet_controller->getOrCreateWallet(std::move(wallet)));
158  } else {
159  Q_EMIT message(QMessageBox::Critical, QString::fromStdString(error));
160  }
161  Q_EMIT finished();
162 }
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
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 coinsSent(WalletModel *wallet_model, SendCoinsRecipient recipient, QByteArray transaction)
std::map< std::string, bool > listWalletDir() const
Returns all wallet names in the wallet dir mapped to whether the wallet is loaded.
OpenWalletActivity * openWallet(const std::string &name, QWidget *parent=nullptr)
void closeWallet(WalletModel *wallet_model, QWidget *parent=nullptr)
const char * name
Definition: rest.cpp:39
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
std::vector< WalletModel * > getOpenWallets() const
Returns wallet models currently open.
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
auto it
Definition: validation.cpp:366
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:47
void removeAndDeleteWallet(WalletModel *wallet_model)