Bitcoin ABC  0.22.13
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 
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/translation.h>
17 #include <wallet/wallet.h>
18 
19 #include <QApplication>
20 #include <QMutexLocker>
21 #include <QTimer>
22 #include <QWindow>
23 
24 #include <algorithm>
25 
27  const PlatformStyle *platform_style,
28  QObject *parent)
29  : QObject(parent), m_activity_thread(new QThread(this)),
30  m_activity_worker(new QObject), m_client_model(client_model),
31  m_node(client_model.node()), m_platform_style(platform_style),
32  m_options_model(client_model.getOptionsModel()) {
34  [this](std::unique_ptr<interfaces::Wallet> wallet) {
35  getOrCreateWallet(std::move(wallet));
36  });
37 
38  for (std::unique_ptr<interfaces::Wallet> &wallet : m_node.getWallets()) {
39  getOrCreateWallet(std::move(wallet));
40  }
41 
42  m_activity_worker->moveToThread(m_activity_thread);
43  m_activity_thread->start();
44 }
45 
46 // Not using the default destructor because not all member types definitions are
47 // available in the header, just forward declared.
49  m_activity_thread->quit();
50  m_activity_thread->wait();
51  delete m_activity_worker;
52 }
53 
54 std::vector<WalletModel *> WalletController::getOpenWallets() const {
55  QMutexLocker locker(&m_mutex);
56  return m_wallets;
57 }
58 
59 std::map<std::string, bool> WalletController::listWalletDir() const {
60  QMutexLocker locker(&m_mutex);
61  std::map<std::string, bool> wallets;
62  for (const std::string &name : m_node.listWalletDir()) {
63  wallets[name] = false;
64  }
65  for (WalletModel *wallet_model : m_wallets) {
66  auto it = wallets.find(wallet_model->wallet().getWalletName());
67  if (it != wallets.end()) {
68  it->second = true;
69  }
70  }
71  return wallets;
72 }
73 
74 void WalletController::closeWallet(WalletModel *wallet_model, QWidget *parent) {
75  QMessageBox box(parent);
76  box.setWindowTitle(tr("Close wallet"));
77  box.setText(tr("Are you sure you wish to close the wallet <i>%1</i>?")
78  .arg(GUIUtil::HtmlEscape(wallet_model->getDisplayName())));
79  box.setInformativeText(
80  tr("Closing the wallet for too long can result in having to resync the "
81  "entire chain if pruning is enabled."));
82  box.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
83  box.setDefaultButton(QMessageBox::Yes);
84  if (box.exec() != QMessageBox::Yes) {
85  return;
86  }
87 
88  // First remove wallet from node.
89  wallet_model->wallet().remove();
90  // Now release the model.
91  removeAndDeleteWallet(wallet_model);
92 }
93 
95  std::unique_ptr<interfaces::Wallet> wallet) {
96  QMutexLocker locker(&m_mutex);
97 
98  // Return model instance if exists.
99  if (!m_wallets.empty()) {
100  std::string name = wallet->getWalletName();
101  for (WalletModel *wallet_model : m_wallets) {
102  if (wallet_model->wallet().getWalletName() == name) {
103  return wallet_model;
104  }
105  }
106  }
107 
108  // Instantiate model and register it.
109  WalletModel *wallet_model = new WalletModel(
110  std::move(wallet), m_client_model, m_platform_style, nullptr);
111  // Handler callback runs in a different thread so fix wallet model thread
112  // affinity.
113  wallet_model->moveToThread(thread());
114  wallet_model->setParent(this);
115  m_wallets.push_back(wallet_model);
116 
117  // WalletModel::startPollBalance needs to be called in a thread managed by
118  // Qt because of startTimer. Considering the current thread can be a RPC
119  // thread, better delegate the calling to Qt with Qt::AutoConnection.
120  const bool called =
121  QMetaObject::invokeMethod(wallet_model, "startPollBalance");
122  assert(called);
123 
124  connect(
125  wallet_model, &WalletModel::unload, this,
126  [this, wallet_model] {
127  // Defer removeAndDeleteWallet when no modal widget is active.
128  // TODO: remove this workaround by removing usage of QDiallog::exec.
129  if (QApplication::activeModalWidget()) {
130  connect(
131  qApp, &QApplication::focusWindowChanged, wallet_model,
132  [this, wallet_model]() {
133  if (!QApplication::activeModalWidget()) {
134  removeAndDeleteWallet(wallet_model);
135  }
136  },
137  Qt::QueuedConnection);
138  } else {
139  removeAndDeleteWallet(wallet_model);
140  }
141  },
142  Qt::QueuedConnection);
143 
144  // Re-emit coinsSent signal from wallet model.
145  connect(wallet_model, &WalletModel::coinsSent, this,
147 
148  // Notify walletAdded signal on the GUI thread.
149  Q_EMIT walletAdded(wallet_model);
150 
151  return wallet_model;
152 }
153 
155  // Unregister wallet model.
156  {
157  QMutexLocker locker(&m_mutex);
158  m_wallets.erase(
159  std::remove(m_wallets.begin(), m_wallets.end(), wallet_model));
160  }
161  Q_EMIT walletRemoved(wallet_model);
162  // Currently this can trigger the unload since the model can hold the last
163  // CWallet shared pointer.
164  delete wallet_model;
165 }
166 
168  WalletController *wallet_controller, QWidget *parent_widget,
169  const CChainParams &chainparams)
170  : QObject(wallet_controller), m_wallet_controller(wallet_controller),
171  m_parent_widget(parent_widget), m_chainparams(chainparams) {}
172 
174  delete m_progress_dialog;
175 }
176 
177 void WalletControllerActivity::showProgressDialog(const QString &label_text) {
178  assert(!m_progress_dialog);
179  m_progress_dialog = new QProgressDialog(m_parent_widget);
180 
181  m_progress_dialog->setLabelText(label_text);
182  m_progress_dialog->setRange(0, 0);
183  m_progress_dialog->setCancelButton(nullptr);
184  m_progress_dialog->setWindowModality(Qt::ApplicationModal);
186 }
187 
189  assert(m_progress_dialog);
190  delete m_progress_dialog;
191  m_progress_dialog = nullptr;
192 }
193 
195  QWidget *parent_widget,
196  const CChainParams &chainparams)
197  : WalletControllerActivity(wallet_controller, parent_widget, chainparams) {
199 }
200 
202  delete m_create_wallet_dialog;
203  delete m_passphrase_dialog;
204 }
205 
209  m_passphrase_dialog->setWindowModality(Qt::ApplicationModal);
210  m_passphrase_dialog->show();
211 
212  connect(m_passphrase_dialog, &QObject::destroyed,
213  [this] { m_passphrase_dialog = nullptr; });
214  connect(m_passphrase_dialog, &QDialog::accepted,
215  [this] { createWallet(); });
216  connect(m_passphrase_dialog, &QDialog::rejected,
217  [this] { Q_EMIT finished(); });
218 }
219 
222  tr("Creating Wallet <b>%1</b>...")
223  .arg(m_create_wallet_dialog->walletName().toHtmlEscaped()));
224 
225  std::string name = m_create_wallet_dialog->walletName().toStdString();
226  uint64_t flags = 0;
229  }
231  flags |= WALLET_FLAG_BLANK_WALLET;
232  }
234  flags |= WALLET_FLAG_DESCRIPTORS;
235  }
236 
237  QTimer::singleShot(500, worker(), [this, name, flags] {
238  WalletCreationStatus status;
239  std::unique_ptr<interfaces::Wallet> wallet =
242 
243  if (status == WalletCreationStatus::SUCCESS) {
245  m_wallet_controller->getOrCreateWallet(std::move(wallet));
246  }
247 
248  QTimer::singleShot(500, this, &CreateWalletActivity::finish);
249  });
250 }
251 
254 
255  if (!m_error_message.empty()) {
256  QMessageBox::critical(
257  m_parent_widget, tr("Create wallet failed"),
258  QString::fromStdString(m_error_message.translated));
259  } else if (!m_warning_message.empty()) {
260  QMessageBox::warning(
261  m_parent_widget, tr("Create wallet warning"),
262  QString::fromStdString(
263  Join(m_warning_message, Untranslated("\n")).translated));
264  }
265 
266  if (m_wallet_model) {
267  Q_EMIT created(m_wallet_model);
268  }
269 
270  Q_EMIT finished();
271 }
272 
275  m_create_wallet_dialog->setWindowModality(Qt::ApplicationModal);
276  m_create_wallet_dialog->show();
277 
278  connect(m_create_wallet_dialog, &QObject::destroyed,
279  [this] { m_create_wallet_dialog = nullptr; });
280  connect(m_create_wallet_dialog, &QDialog::rejected,
281  [this] { Q_EMIT finished(); });
282  connect(m_create_wallet_dialog, &QDialog::accepted, [this] {
284  askPassphrase();
285  } else {
286  createWallet();
287  }
288  });
289 }
290 
292  QWidget *parent_widget,
293  const CChainParams &chainparams)
294  : WalletControllerActivity(wallet_controller, parent_widget, chainparams) {}
295 
298 
299  if (!m_error_message.empty()) {
300  QMessageBox::critical(
301  m_parent_widget, tr("Open wallet failed"),
302  QString::fromStdString(m_error_message.translated));
303  } else if (!m_warning_message.empty()) {
304  QMessageBox::warning(
305  m_parent_widget, tr("Open wallet warning"),
306  QString::fromStdString(
307  Join(m_warning_message, Untranslated("\n")).translated));
308  }
309 
310  if (m_wallet_model) {
311  Q_EMIT opened(m_wallet_model);
312  }
313 
314  Q_EMIT finished();
315 }
316 
317 void OpenWalletActivity::open(const std::string &path) {
318  QString name = path.empty() ? QString("[" + tr("default wallet") + "]")
319  : QString::fromStdString(path);
320 
322  tr("Opening Wallet <b>%1</b>...").arg(name.toHtmlEscaped()));
323 
324  QTimer::singleShot(0, worker(), [this, path] {
325  std::unique_ptr<interfaces::Wallet> wallet = node().loadWallet(
327 
328  if (wallet) {
330  m_wallet_controller->getOrCreateWallet(std::move(wallet));
331  }
332 
333  QTimer::singleShot(0, this, &OpenWalletActivity::finish);
334  });
335 }
static const int MAX_PASSPHRASE_SIZE
Definition: guiconstants.h:14
CreateWalletActivity(WalletController *wallet_controller, QWidget *parent_widget, const CChainParams &chainparams)
interfaces::Wallet & wallet() const
Definition: walletmodel.h:150
bool isDescriptorWalletChecked() const
interfaces::Node & node() const
WalletControllerActivity(WalletController *wallet_controller, QWidget *parent_widget, const CChainParams &chainparams)
bool isEncryptWalletChecked() const
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:34
bool empty() const
Definition: translation.h:27
CreateWalletDialog * m_create_wallet_dialog
void coinsSent(interfaces::Wallet &wallet, SendCoinsRecipient recipient, QByteArray transaction)
NodeContext & m_node
Definition: chain.cpp:442
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:36
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()
void showProgressDialog(const QString &label_text)
Ask passphrase twice and encrypt.
QString walletName() const
Controller between interfaces::Node, WalletModel instances and the GUI.
QProgressDialog * m_progress_dialog
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system...
Definition: chainparams.h:47
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:239
std::string translated
Definition: translation.h:19
bool isDisablePrivateKeysChecked() const
virtual std::vector< std::unique_ptr< Wallet > > getWallets()=0
Return interfaces for accessing wallets (if any).
WalletCreationStatus
Definition: wallet.h:68
QObject *const m_activity_worker
std::map< std::string, bool > listWalletDir() const
Returns all wallet names in the wallet dir mapped to whether the wallet is loaded.
const CChainParams & m_chainparams
virtual std::unique_ptr< Wallet > createWallet(const CChainParams &params, const SecureString &passphrase, uint64_t wallet_creation_flags, const std::string &name, bilingual_str &error, std::vector< bilingual_str > &warnings, WalletCreationStatus &status)=0
Create a wallet from file.
bool isMakeBlankWalletChecked() const
void coinsSent(interfaces::Wallet &wallet, SendCoinsRecipient recipient, QByteArray transaction)
void created(WalletModel *wallet_model)
void closeWallet(WalletModel *wallet_model, QWidget *parent=nullptr)
const char * name
Definition: rest.cpp:43
virtual void remove()=0
QWidget *const m_parent_widget
OpenWalletActivity(WalletController *wallet_controller, QWidget *parent_widget, const CChainParams &chainparams)
Flag set when a wallet contains no HD seed and no private keys, scripts, addresses, and other watch only things, and is therefore "blank.".
Definition: walletutil.h:67
void open(const std::string &path)
std::vector< bilingual_str > m_warning_message
ClientModel & m_client_model
interfaces::Node & m_node
void walletAdded(WalletModel *wallet_model)
void PolishProgressDialog(QProgressDialog *dialog)
Definition: guiutil.cpp:891
Model for Bitcoin network client.
Definition: clientmodel.h:34
std::vector< WalletModel * > m_wallets
AskPassphraseDialog * m_passphrase_dialog
int flags
Definition: bitcoin-tx.cpp:529
virtual std::unique_ptr< Handler > handleLoadWallet(LoadWalletFn fn)=0
std::vector< WalletModel * > getOpenWallets() const
Returns wallet models currently open.
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:47
Multifunctional dialog to ask for passphrases.
Dialog for creating wallets.
WalletModel * getOrCreateWallet(std::unique_ptr< interfaces::Wallet > wallet)
void walletRemoved(WalletModel *wallet_model)
QObject * worker() const
QString getDisplayName() const
QThread *const m_activity_thread
WalletController(ClientModel &client_model, const PlatformStyle *platform_style, QObject *parent)
std::unique_ptr< interfaces::Handler > m_handler_load_wallet
Indicate that this wallet supports DescriptorScriptPubKeyMan.
Definition: walletutil.h:70
void removeAndDeleteWallet(WalletModel *wallet_model)
virtual std::unique_ptr< Wallet > loadWallet(const CChainParams &params, const std::string &name, bilingual_str &error, std::vector< bilingual_str > &warnings) const =0
Attempts to load a wallet from file or directory.
WalletController *const m_wallet_controller