Bitcoin Core  22.99.0
P2P Digital Currency
walletcontroller.cpp
Go to the documentation of this file.
1 // Copyright (c) 2019-2021 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 <external_signer.h>
15 #include <interfaces/handler.h>
16 #include <interfaces/node.h>
17 #include <util/string.h>
18 #include <util/threadnames.h>
19 #include <util/translation.h>
20 #include <wallet/wallet.h>
21 
22 #include <algorithm>
23 #include <chrono>
24 
25 #include <QApplication>
26 #include <QMessageBox>
27 #include <QMutexLocker>
28 #include <QThread>
29 #include <QTimer>
30 #include <QWindow>
31 
36 
37 WalletController::WalletController(ClientModel& client_model, const PlatformStyle* platform_style, QObject* parent)
38  : QObject(parent)
39  , m_activity_thread(new QThread(this))
40  , m_activity_worker(new QObject)
41  , m_client_model(client_model)
42  , m_node(client_model.node())
43  , m_platform_style(platform_style)
44  , m_options_model(client_model.getOptionsModel())
45 {
46  m_handler_load_wallet = m_node.walletLoader().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
47  getOrCreateWallet(std::move(wallet));
48  });
49 
50  m_activity_worker->moveToThread(m_activity_thread);
51  m_activity_thread->start();
52  QTimer::singleShot(0, m_activity_worker, []() {
53  util::ThreadRename("qt-walletctrl");
54  });
55 }
56 
57 // Not using the default destructor because not all member types definitions are
58 // available in the header, just forward declared.
60 {
61  m_activity_thread->quit();
62  m_activity_thread->wait();
63  delete m_activity_worker;
64 }
65 
66 std::map<std::string, bool> WalletController::listWalletDir() const
67 {
68  QMutexLocker locker(&m_mutex);
69  std::map<std::string, bool> wallets;
70  for (const std::string& name : m_node.walletLoader().listWalletDir()) {
71  wallets[name] = false;
72  }
73  for (WalletModel* wallet_model : m_wallets) {
74  auto it = wallets.find(wallet_model->wallet().getWalletName());
75  if (it != wallets.end()) it->second = true;
76  }
77  return wallets;
78 }
79 
80 void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent)
81 {
82  QMessageBox box(parent);
83  box.setWindowTitle(tr("Close wallet"));
84  box.setText(tr("Are you sure you wish to close the wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(wallet_model->getDisplayName())));
85  box.setInformativeText(tr("Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled."));
86  box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
87  box.setDefaultButton(QMessageBox::Yes);
88  if (box.exec() != QMessageBox::Yes) return;
89 
90  // First remove wallet from node.
91  wallet_model->wallet().remove();
92  // Now release the model.
93  removeAndDeleteWallet(wallet_model);
94 }
95 
96 void WalletController::closeAllWallets(QWidget* parent)
97 {
98  QMessageBox::StandardButton button = QMessageBox::question(parent, tr("Close all wallets"),
99  tr("Are you sure you wish to close all wallets?"),
100  QMessageBox::Yes|QMessageBox::Cancel,
101  QMessageBox::Yes);
102  if (button != QMessageBox::Yes) return;
103 
104  QMutexLocker locker(&m_mutex);
105  for (WalletModel* wallet_model : m_wallets) {
106  wallet_model->wallet().remove();
107  Q_EMIT walletRemoved(wallet_model);
108  delete wallet_model;
109  }
110  m_wallets.clear();
111 }
112 
113 WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet)
114 {
115  QMutexLocker locker(&m_mutex);
116 
117  // Return model instance if exists.
118  if (!m_wallets.empty()) {
119  std::string name = wallet->getWalletName();
120  for (WalletModel* wallet_model : m_wallets) {
121  if (wallet_model->wallet().getWalletName() == name) {
122  return wallet_model;
123  }
124  }
125  }
126 
127  // Instantiate model and register it.
128  WalletModel* wallet_model = new WalletModel(std::move(wallet), m_client_model, m_platform_style,
129  nullptr /* required for the following moveToThread() call */);
130 
131  // Move WalletModel object to the thread that created the WalletController
132  // object (GUI main thread), instead of the current thread, which could be
133  // an outside wallet thread or RPC thread sending a LoadWallet notification.
134  // This ensures queued signals sent to the WalletModel object will be
135  // handled on the GUI event loop.
136  wallet_model->moveToThread(thread());
137  // setParent(parent) must be called in the thread which created the parent object. More details in #18948.
138  GUIUtil::ObjectInvoke(this, [wallet_model, this] {
139  wallet_model->setParent(this);
141 
142  m_wallets.push_back(wallet_model);
143 
144  // WalletModel::startPollBalance needs to be called in a thread managed by
145  // Qt because of startTimer. Considering the current thread can be a RPC
146  // thread, better delegate the calling to Qt with Qt::AutoConnection.
147  const bool called = QMetaObject::invokeMethod(wallet_model, "startPollBalance");
148  assert(called);
149 
150  connect(wallet_model, &WalletModel::unload, this, [this, wallet_model] {
151  // Defer removeAndDeleteWallet when no modal widget is active.
152  // TODO: remove this workaround by removing usage of QDialog::exec.
153  if (QApplication::activeModalWidget()) {
154  connect(qApp, &QApplication::focusWindowChanged, wallet_model, [this, wallet_model]() {
155  if (!QApplication::activeModalWidget()) {
156  removeAndDeleteWallet(wallet_model);
157  }
158  }, Qt::QueuedConnection);
159  } else {
160  removeAndDeleteWallet(wallet_model);
161  }
162  }, Qt::QueuedConnection);
163 
164  // Re-emit coinsSent signal from wallet model.
165  connect(wallet_model, &WalletModel::coinsSent, this, &WalletController::coinsSent);
166 
167  Q_EMIT walletAdded(wallet_model);
168 
169  return wallet_model;
170 }
171 
173 {
174  // Unregister wallet model.
175  {
176  QMutexLocker locker(&m_mutex);
177  m_wallets.erase(std::remove(m_wallets.begin(), m_wallets.end(), wallet_model));
178  }
179  Q_EMIT walletRemoved(wallet_model);
180  // Currently this can trigger the unload since the model can hold the last
181  // CWallet shared pointer.
182  delete wallet_model;
183 }
184 
185 WalletControllerActivity::WalletControllerActivity(WalletController* wallet_controller, QWidget* parent_widget)
186  : QObject(wallet_controller)
187  , m_wallet_controller(wallet_controller)
188  , m_parent_widget(parent_widget)
189 {
190  connect(this, &WalletControllerActivity::finished, this, &QObject::deleteLater);
191 }
192 
193 void WalletControllerActivity::showProgressDialog(const QString& title_text, const QString& label_text)
194 {
195  auto progress_dialog = new QProgressDialog(m_parent_widget);
196  progress_dialog->setAttribute(Qt::WA_DeleteOnClose);
197  connect(this, &WalletControllerActivity::finished, progress_dialog, &QWidget::close);
198 
199  progress_dialog->setWindowTitle(title_text);
200  progress_dialog->setLabelText(label_text);
201  progress_dialog->setRange(0, 0);
202  progress_dialog->setCancelButton(nullptr);
203  progress_dialog->setWindowModality(Qt::ApplicationModal);
204  GUIUtil::PolishProgressDialog(progress_dialog);
205  // The setValue call forces QProgressDialog to start the internal duration estimation.
206  // See details in https://bugreports.qt.io/browse/QTBUG-47042.
207  progress_dialog->setValue(0);
208 }
209 
210 CreateWalletActivity::CreateWalletActivity(WalletController* wallet_controller, QWidget* parent_widget)
211  : WalletControllerActivity(wallet_controller, parent_widget)
212 {
214 }
215 
217 {
218  delete m_create_wallet_dialog;
219  delete m_passphrase_dialog;
220 }
221 
223 {
225  m_passphrase_dialog->setWindowModality(Qt::ApplicationModal);
226  m_passphrase_dialog->show();
227 
228  connect(m_passphrase_dialog, &QObject::destroyed, [this] {
229  m_passphrase_dialog = nullptr;
230  });
231  connect(m_passphrase_dialog, &QDialog::accepted, [this] {
232  createWallet();
233  });
234  connect(m_passphrase_dialog, &QDialog::rejected, [this] {
235  Q_EMIT finished();
236  });
237 }
238 
240 {
242  //: Title of window indicating the progress of creation of a new wallet.
243  tr("Create Wallet"),
244  /*: Descriptive text of the create wallet progress window which indicates
245  to the user which wallet is currently being created. */
246  tr("Creating Wallet <b>%1</b>…").arg(m_create_wallet_dialog->walletName().toHtmlEscaped()));
247 
248  std::string name = m_create_wallet_dialog->walletName().toStdString();
249  uint64_t flags = 0;
252  }
255  }
258  }
261  }
262 
263  QTimer::singleShot(500ms, worker(), [this, name, flags] {
264  std::unique_ptr<interfaces::Wallet> wallet = node().walletLoader().createWallet(name, m_passphrase, flags, m_error_message, m_warning_message);
265 
267 
268  QTimer::singleShot(500ms, this, &CreateWalletActivity::finish);
269  });
270 }
271 
273 {
274  if (!m_error_message.empty()) {
275  QMessageBox::critical(m_parent_widget, tr("Create wallet failed"), QString::fromStdString(m_error_message.translated));
276  } else if (!m_warning_message.empty()) {
277  QMessageBox::warning(m_parent_widget, tr("Create wallet warning"), QString::fromStdString(Join(m_warning_message, Untranslated("\n")).translated));
278  }
279 
281 
282  Q_EMIT finished();
283 }
284 
286 {
288 
289  std::vector<std::unique_ptr<interfaces::ExternalSigner>> signers;
290  try {
291  signers = node().listExternalSigners();
292  } catch (const std::runtime_error& e) {
293  QMessageBox::critical(nullptr, tr("Can't list signers"), e.what());
294  }
296 
297  m_create_wallet_dialog->setWindowModality(Qt::ApplicationModal);
298  m_create_wallet_dialog->show();
299 
300  connect(m_create_wallet_dialog, &QObject::destroyed, [this] {
301  m_create_wallet_dialog = nullptr;
302  });
303  connect(m_create_wallet_dialog, &QDialog::rejected, [this] {
304  Q_EMIT finished();
305  });
306  connect(m_create_wallet_dialog, &QDialog::accepted, [this] {
308  askPassphrase();
309  } else {
310  createWallet();
311  }
312  });
313 }
314 
315 OpenWalletActivity::OpenWalletActivity(WalletController* wallet_controller, QWidget* parent_widget)
316  : WalletControllerActivity(wallet_controller, parent_widget)
317 {
318 }
319 
321 {
322  if (!m_error_message.empty()) {
323  QMessageBox::critical(m_parent_widget, tr("Open wallet failed"), QString::fromStdString(m_error_message.translated));
324  } else if (!m_warning_message.empty()) {
325  QMessageBox::warning(m_parent_widget, tr("Open wallet warning"), QString::fromStdString(Join(m_warning_message, Untranslated("\n")).translated));
326  }
327 
328  if (m_wallet_model) Q_EMIT opened(m_wallet_model);
329 
330  Q_EMIT finished();
331 }
332 
333 void OpenWalletActivity::open(const std::string& path)
334 {
335  QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
336 
338  //: Title of window indicating the progress of opening of a wallet.
339  tr("Open Wallet"),
340  /*: Descriptive text of the open wallet progress window which indicates
341  to the user which wallet is currently being opened. */
342  tr("Opening Wallet <b>%1</b>…").arg(name.toHtmlEscaped()));
343 
344  QTimer::singleShot(0, worker(), [this, path] {
345  std::unique_ptr<interfaces::Wallet> wallet = node().walletLoader().loadWallet(path, m_error_message, m_warning_message);
346 
348 
349  QTimer::singleShot(0, this, &OpenWalletActivity::finish);
350  });
351 }
352 
353 LoadWalletsActivity::LoadWalletsActivity(WalletController* wallet_controller, QWidget* parent_widget)
354  : WalletControllerActivity(wallet_controller, parent_widget)
355 {
356 }
357 
359 {
361  //: Title of progress window which is displayed when wallets are being loaded.
362  tr("Load Wallets"),
363  /*: Descriptive text of the load wallets progress window which indicates to
364  the user that wallets are currently being loaded.*/
365  tr("Loading wallets…"));
366 
367  QTimer::singleShot(0, worker(), [this] {
368  for (auto& wallet : node().walletLoader().getWallets()) {
370  }
371 
372  QTimer::singleShot(0, this, [this] { Q_EMIT finished(); });
373  });
374 }
WalletController::walletRemoved
void walletRemoved(WalletModel *wallet_model)
CreateWalletActivity::~CreateWalletActivity
virtual ~CreateWalletActivity()
Definition: walletcontroller.cpp:216
WalletController::m_handler_load_wallet
std::unique_ptr< interfaces::Handler > m_handler_load_wallet
Definition: walletcontroller.h:79
WalletModel::unload
void unload()
assert
assert(!tx.IsCoinBase())
WalletControllerActivity::m_warning_message
std::vector< bilingual_str > m_warning_message
Definition: walletcontroller.h:105
wallet.h
WalletModel
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:52
WalletControllerActivity::finished
void finished()
WalletController::removeAndDeleteWallet
void removeAndDeleteWallet(WalletModel *wallet_model)
Definition: walletcontroller.cpp:172
CreateWalletDialog::isMakeBlankWalletChecked
bool isMakeBlankWalletChecked() const
Definition: createwalletdialog.cpp:152
flags
int flags
Definition: bitcoin-tx.cpp:529
CreateWalletActivity::CreateWalletActivity
CreateWalletActivity(WalletController *wallet_controller, QWidget *parent_widget)
Definition: walletcontroller.cpp:210
walletcontroller.h
CreateWalletDialog::isDisablePrivateKeysChecked
bool isDisablePrivateKeysChecked() const
Definition: createwalletdialog.cpp:147
walletmodel.h
WalletControllerActivity::worker
QObject * worker() const
Definition: walletcontroller.h:97
string.h
WalletControllerActivity::m_error_message
bilingual_str m_error_message
Definition: walletcontroller.h:104
m_node
node::NodeContext m_node
Definition: bitcoin-gui.cpp:36
CreateWalletActivity::m_create_wallet_dialog
CreateWalletDialog * m_create_wallet_dialog
Definition: walletcontroller.h:128
handler.h
wallet::WALLET_FLAG_DESCRIPTORS
@ WALLET_FLAG_DESCRIPTORS
Indicate that this wallet supports DescriptorScriptPubKeyMan.
Definition: walletutil.h:66
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:66
WalletController::walletAdded
void walletAdded(WalletModel *wallet_model)
WalletController::m_wallets
std::vector< WalletModel * > m_wallets
Definition: walletcontroller.h:78
wallet
Definition: node.h:38
WalletController::m_client_model
ClientModel & m_client_model
Definition: walletcontroller.h:73
OpenWalletActivity::OpenWalletActivity
OpenWalletActivity(WalletController *wallet_controller, QWidget *parent_widget)
Definition: walletcontroller.cpp:315
MAX_PASSPHRASE_SIZE
static const int MAX_PASSPHRASE_SIZE
Definition: guiconstants.h:20
CreateWalletActivity::createWallet
void createWallet()
Definition: walletcontroller.cpp:239
OpenWalletActivity::finish
void finish()
Definition: walletcontroller.cpp:320
bilingual_str::empty
bool empty() const
Definition: translation.h:27
CreateWalletActivity::askPassphrase
void askPassphrase()
Definition: walletcontroller.cpp:222
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:368
wallet::WALLET_FLAG_EXTERNAL_SIGNER
@ WALLET_FLAG_EXTERNAL_SIGNER
Indicates that the wallet needs an external signer.
Definition: walletutil.h:69
interfaces::Wallet::remove
virtual void remove()=0
WalletModel::wallet
interfaces::Wallet & wallet() const
Definition: walletmodel.h:145
external_signer.h
Untranslated
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:46
CreateWalletDialog::isEncryptWalletChecked
bool isEncryptWalletChecked() const
Definition: createwalletdialog.cpp:142
OpenWalletActivity::opened
void opened(WalletModel *wallet_model)
interfaces::WalletLoader::handleLoadWallet
virtual std::unique_ptr< Handler > handleLoadWallet(LoadWalletFn fn)=0
WalletControllerActivity::m_parent_widget
QWidget *const m_parent_widget
Definition: walletcontroller.h:102
CreateWalletActivity::m_passphrase
SecureString m_passphrase
Definition: walletcontroller.h:127
node.h
wallet::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:63
interfaces::WalletLoader::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.
wallet::WALLET_FLAG_DISABLE_PRIVATE_KEYS
@ WALLET_FLAG_DISABLE_PRIVATE_KEYS
Definition: walletutil.h:51
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:103
WalletController::m_platform_style
const PlatformStyle *const m_platform_style
Definition: walletcontroller.h:75
WalletController
Controller between interfaces::Node, WalletModel instances and the GUI.
Definition: walletcontroller.h:45
createwalletdialog.h
CreateWalletActivity::created
void created(WalletModel *wallet_model)
GUIUtil::PolishProgressDialog
void PolishProgressDialog(QProgressDialog *dialog)
Definition: guiutil.cpp:875
guiutil.h
WalletModel::coinsSent
void coinsSent(WalletModel *wallet, SendCoinsRecipient recipient, QByteArray transaction)
WalletControllerActivity::showProgressDialog
void showProgressDialog(const QString &title_text, const QString &label_text)
Definition: walletcontroller.cpp:193
bilingual_str::translated
std::string translated
Definition: translation.h:18
CreateWalletDialog::setSigners
void setSigners(const std::vector< std::unique_ptr< interfaces::ExternalSigner >> &signers)
Definition: createwalletdialog.cpp:116
interfaces::WalletLoader::listWalletDir
virtual std::vector< std::string > listWalletDir()=0
Return available wallets in wallet directory.
WalletController::coinsSent
void coinsSent(WalletModel *wallet_model, SendCoinsRecipient recipient, QByteArray transaction)
interfaces::Node::walletLoader
virtual WalletLoader & walletLoader()=0
Get wallet loader.
WalletControllerActivity::node
interfaces::Node & node() const
Definition: walletcontroller.h:96
WalletController::getOrCreateWallet
WalletModel * getOrCreateWallet(std::unique_ptr< interfaces::Wallet > wallet)
Definition: walletcontroller.cpp:113
WalletController::~WalletController
~WalletController()
Definition: walletcontroller.cpp:59
name
const char * name
Definition: rest.cpp:52
LoadWalletsActivity::load
void load()
Definition: walletcontroller.cpp:358
WalletControllerActivity::m_wallet_controller
WalletController *const m_wallet_controller
Definition: walletcontroller.h:101
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:80
guiconstants.h
GUIUtil::HtmlEscape
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:239
CreateWalletActivity::m_passphrase_dialog
AskPassphraseDialog * m_passphrase_dialog
Definition: walletcontroller.h:129
CreateWalletActivity::finish
void finish()
Definition: walletcontroller.cpp:272
translation.h
CreateWalletDialog
Dialog for creating wallets.
Definition: createwalletdialog.h:24
WalletController::m_activity_worker
QObject *const m_activity_worker
Definition: walletcontroller.h:72
WalletController::WalletController
WalletController(ClientModel &client_model, const PlatformStyle *platform_style, QObject *parent)
Definition: walletcontroller.cpp:37
CreateWalletDialog::isDescriptorWalletChecked
bool isDescriptorWalletChecked() const
Definition: createwalletdialog.cpp:157
WalletController::m_node
interfaces::Node & m_node
Definition: walletcontroller.h:74
WalletControllerActivity::WalletControllerActivity
WalletControllerActivity(WalletController *wallet_controller, QWidget *parent_widget)
Definition: walletcontroller.cpp:185
GUIUtil::blockingGUIThreadConnection
Qt::ConnectionType blockingGUIThreadConnection()
Get connection type to call object slot in GUI thread with invokeMethod.
Definition: guiutil.cpp:369
WalletController::m_activity_thread
QThread *const m_activity_thread
Definition: walletcontroller.h:71
WalletController::m_mutex
QMutex m_mutex
Definition: walletcontroller.h:77
node
Definition: init.h:22
LoadWalletsActivity::LoadWalletsActivity
LoadWalletsActivity(WalletController *wallet_controller, QWidget *parent_widget)
Definition: walletcontroller.cpp:353
interfaces::WalletLoader::loadWallet
virtual std::unique_ptr< Wallet > loadWallet(const std::string &name, bilingual_str &error, std::vector< bilingual_str > &warnings)=0
Load existing wallet.
OpenWalletActivity::open
void open(const std::string &path)
Definition: walletcontroller.cpp:333
interfaces::Node::listExternalSigners
virtual std::vector< std::unique_ptr< ExternalSigner > > listExternalSigners()=0
Return list of external signers (attached devices which can sign transactions).
WalletModel::getDisplayName
QString getDisplayName() const
Definition: walletmodel.cpp:582
CreateWalletDialog::isExternalSignerChecked
bool isExternalSignerChecked() const
Definition: createwalletdialog.cpp:162
PlatformStyle
Definition: platformstyle.h:13
CreateWalletDialog::walletName
QString walletName() const
Definition: createwalletdialog.cpp:137
threadnames.h
AskPassphraseDialog::Encrypt
@ Encrypt
Ask passphrase twice and encrypt.
Definition: askpassphrasedialog.h:26
askpassphrasedialog.h
WalletController::closeAllWallets
void closeAllWallets(QWidget *parent=nullptr)
Definition: walletcontroller.cpp:96
CreateWalletActivity::create
void create()
Definition: walletcontroller.cpp:285
clientmodel.h
WalletControllerActivity
Definition: walletcontroller.h:84