LCOV - code coverage report
Current view: top level - src/qt - walletcontroller.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 0 214 0.0 %
Date: 2020-09-26 01:30:44 Functions: 0 50 0.0 %

          Line data    Source code
       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             : 
       7             : #include <qt/askpassphrasedialog.h>
       8             : #include <qt/clientmodel.h>
       9             : #include <qt/createwalletdialog.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/translation.h>
      18             : #include <wallet/wallet.h>
      19             : 
      20             : #include <algorithm>
      21             : 
      22             : #include <QApplication>
      23             : #include <QMessageBox>
      24             : #include <QMutexLocker>
      25             : #include <QThread>
      26             : #include <QTimer>
      27             : #include <QWindow>
      28             : 
      29           0 : WalletController::WalletController(ClientModel& client_model, const PlatformStyle* platform_style, QObject* parent)
      30           0 :     : QObject(parent)
      31           0 :     , m_activity_thread(new QThread(this))
      32           0 :     , m_activity_worker(new QObject)
      33           0 :     , m_client_model(client_model)
      34           0 :     , m_node(client_model.node())
      35           0 :     , m_platform_style(platform_style)
      36           0 :     , m_options_model(client_model.getOptionsModel())
      37           0 : {
      38           0 :     m_handler_load_wallet = m_node.walletClient().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
      39           0 :         getOrCreateWallet(std::move(wallet));
      40           0 :     });
      41             : 
      42           0 :     for (std::unique_ptr<interfaces::Wallet>& wallet : m_node.walletClient().getWallets()) {
      43           0 :         getOrCreateWallet(std::move(wallet));
      44             :     }
      45             : 
      46           0 :     m_activity_worker->moveToThread(m_activity_thread);
      47           0 :     m_activity_thread->start();
      48           0 : }
      49             : 
      50             : // Not using the default destructor because not all member types definitions are
      51             : // available in the header, just forward declared.
      52           0 : WalletController::~WalletController()
      53           0 : {
      54           0 :     m_activity_thread->quit();
      55           0 :     m_activity_thread->wait();
      56           0 :     delete m_activity_worker;
      57           0 : }
      58             : 
      59           0 : std::vector<WalletModel*> WalletController::getOpenWallets() const
      60             : {
      61           0 :     QMutexLocker locker(&m_mutex);
      62           0 :     return m_wallets;
      63           0 : }
      64             : 
      65           0 : std::map<std::string, bool> WalletController::listWalletDir() const
      66             : {
      67           0 :     QMutexLocker locker(&m_mutex);
      68           0 :     std::map<std::string, bool> wallets;
      69           0 :     for (const std::string& name : m_node.walletClient().listWalletDir()) {
      70           0 :         wallets[name] = false;
      71             :     }
      72           0 :     for (WalletModel* wallet_model : m_wallets) {
      73           0 :         auto it = wallets.find(wallet_model->wallet().getWalletName());
      74           0 :         if (it != wallets.end()) it->second = true;
      75           0 :     }
      76             :     return wallets;
      77           0 : }
      78             : 
      79           0 : void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent)
      80             : {
      81           0 :     QMessageBox box(parent);
      82           0 :     box.setWindowTitle(tr("Close wallet"));
      83           0 :     box.setText(tr("Are you sure you wish to close the wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(wallet_model->getDisplayName())));
      84           0 :     box.setInformativeText(tr("Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled."));
      85           0 :     box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
      86           0 :     box.setDefaultButton(QMessageBox::Yes);
      87           0 :     if (box.exec() != QMessageBox::Yes) return;
      88             : 
      89             :     // First remove wallet from node.
      90           0 :     wallet_model->wallet().remove();
      91             :     // Now release the model.
      92           0 :     removeAndDeleteWallet(wallet_model);
      93           0 : }
      94             : 
      95           0 : void WalletController::closeAllWallets(QWidget* parent)
      96             : {
      97           0 :     QMessageBox::StandardButton button = QMessageBox::question(parent, tr("Close all wallets"),
      98           0 :         tr("Are you sure you wish to close all wallets?"),
      99           0 :         QMessageBox::Yes|QMessageBox::Cancel,
     100             :         QMessageBox::Yes);
     101           0 :     if (button != QMessageBox::Yes) return;
     102             : 
     103           0 :     QMutexLocker locker(&m_mutex);
     104           0 :     for (WalletModel* wallet_model : m_wallets) {
     105           0 :         wallet_model->wallet().remove();
     106           0 :         Q_EMIT walletRemoved(wallet_model);
     107           0 :         delete wallet_model;
     108           0 :     }
     109           0 :     m_wallets.clear();
     110           0 : }
     111             : 
     112           0 : WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet)
     113             : {
     114           0 :     QMutexLocker locker(&m_mutex);
     115             : 
     116             :     // Return model instance if exists.
     117           0 :     if (!m_wallets.empty()) {
     118           0 :         std::string name = wallet->getWalletName();
     119           0 :         for (WalletModel* wallet_model : m_wallets) {
     120           0 :             if (wallet_model->wallet().getWalletName() == name) {
     121           0 :                 return wallet_model;
     122             :             }
     123           0 :         }
     124           0 :     }
     125             : 
     126             :     // Instantiate model and register it.
     127           0 :     WalletModel* wallet_model = new WalletModel(std::move(wallet), m_client_model, m_platform_style, nullptr);
     128             :     // Handler callback runs in a different thread so fix wallet model thread affinity.
     129           0 :     wallet_model->moveToThread(thread());
     130           0 :     wallet_model->setParent(this);
     131           0 :     m_wallets.push_back(wallet_model);
     132             : 
     133           0 :     // WalletModel::startPollBalance needs to be called in a thread managed by
     134           0 :     // Qt because of startTimer. Considering the current thread can be a RPC
     135             :     // thread, better delegate the calling to Qt with Qt::AutoConnection.
     136           0 :     const bool called = QMetaObject::invokeMethod(wallet_model, "startPollBalance");
     137           0 :     assert(called);
     138             : 
     139           0 :     connect(wallet_model, &WalletModel::unload, this, [this, wallet_model] {
     140             :         // Defer removeAndDeleteWallet when no modal widget is active.
     141             :         // TODO: remove this workaround by removing usage of QDiallog::exec.
     142           0 :         if (QApplication::activeModalWidget()) {
     143           0 :             connect(qApp, &QApplication::focusWindowChanged, wallet_model, [this, wallet_model]() {
     144           0 :                 if (!QApplication::activeModalWidget()) {
     145           0 :                     removeAndDeleteWallet(wallet_model);
     146           0 :                 }
     147           0 :             }, Qt::QueuedConnection);
     148           0 :         } else {
     149           0 :             removeAndDeleteWallet(wallet_model);
     150             :         }
     151           0 :     }, Qt::QueuedConnection);
     152             : 
     153             :     // Re-emit coinsSent signal from wallet model.
     154           0 :     connect(wallet_model, &WalletModel::coinsSent, this, &WalletController::coinsSent);
     155             : 
     156             :     // Notify walletAdded signal on the GUI thread.
     157           0 :     Q_EMIT walletAdded(wallet_model);
     158             : 
     159           0 :     return wallet_model;
     160           0 : }
     161             : 
     162           0 : void WalletController::removeAndDeleteWallet(WalletModel* wallet_model)
     163             : {
     164             :     // Unregister wallet model.
     165             :     {
     166           0 :         QMutexLocker locker(&m_mutex);
     167           0 :         m_wallets.erase(std::remove(m_wallets.begin(), m_wallets.end(), wallet_model));
     168           0 :     }
     169           0 :     Q_EMIT walletRemoved(wallet_model);
     170             :     // Currently this can trigger the unload since the model can hold the last
     171             :     // CWallet shared pointer.
     172           0 :     delete wallet_model;
     173           0 : }
     174             : 
     175           0 : WalletControllerActivity::WalletControllerActivity(WalletController* wallet_controller, QWidget* parent_widget)
     176           0 :     : QObject(wallet_controller)
     177           0 :     , m_wallet_controller(wallet_controller)
     178           0 :     , m_parent_widget(parent_widget)
     179           0 : {
     180           0 : }
     181             : 
     182           0 : WalletControllerActivity::~WalletControllerActivity()
     183           0 : {
     184           0 :     delete m_progress_dialog;
     185           0 : }
     186             : 
     187           0 : void WalletControllerActivity::showProgressDialog(const QString& label_text)
     188             : {
     189           0 :     assert(!m_progress_dialog);
     190           0 :     m_progress_dialog = new QProgressDialog(m_parent_widget);
     191             : 
     192           0 :     m_progress_dialog->setLabelText(label_text);
     193           0 :     m_progress_dialog->setRange(0, 0);
     194           0 :     m_progress_dialog->setCancelButton(nullptr);
     195           0 :     m_progress_dialog->setWindowModality(Qt::ApplicationModal);
     196           0 :     GUIUtil::PolishProgressDialog(m_progress_dialog);
     197           0 : }
     198             : 
     199           0 : void WalletControllerActivity::destroyProgressDialog()
     200             : {
     201           0 :     assert(m_progress_dialog);
     202           0 :     delete m_progress_dialog;
     203           0 :     m_progress_dialog = nullptr;
     204           0 : }
     205             : 
     206           0 : CreateWalletActivity::CreateWalletActivity(WalletController* wallet_controller, QWidget* parent_widget)
     207           0 :     : WalletControllerActivity(wallet_controller, parent_widget)
     208           0 : {
     209           0 :     m_passphrase.reserve(MAX_PASSPHRASE_SIZE);
     210           0 : }
     211             : 
     212           0 : CreateWalletActivity::~CreateWalletActivity()
     213           0 : {
     214           0 :     delete m_create_wallet_dialog;
     215           0 :     delete m_passphrase_dialog;
     216           0 : }
     217             : 
     218           0 : void CreateWalletActivity::askPassphrase()
     219             : {
     220           0 :     m_passphrase_dialog = new AskPassphraseDialog(AskPassphraseDialog::Encrypt, m_parent_widget, &m_passphrase);
     221           0 :     m_passphrase_dialog->setWindowModality(Qt::ApplicationModal);
     222           0 :     m_passphrase_dialog->show();
     223             : 
     224           0 :     connect(m_passphrase_dialog, &QObject::destroyed, [this] {
     225           0 :         m_passphrase_dialog = nullptr;
     226           0 :     });
     227           0 :     connect(m_passphrase_dialog, &QDialog::accepted, [this] {
     228           0 :         createWallet();
     229           0 :     });
     230           0 :     connect(m_passphrase_dialog, &QDialog::rejected, [this] {
     231           0 :         Q_EMIT finished();
     232           0 :     });
     233           0 : }
     234             : 
     235           0 : void CreateWalletActivity::createWallet()
     236             : {
     237           0 :     showProgressDialog(tr("Creating Wallet <b>%1</b>...").arg(m_create_wallet_dialog->walletName().toHtmlEscaped()));
     238             : 
     239           0 :     std::string name = m_create_wallet_dialog->walletName().toStdString();
     240             :     uint64_t flags = 0;
     241           0 :     if (m_create_wallet_dialog->isDisablePrivateKeysChecked()) {
     242             :         flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
     243           0 :     }
     244           0 :     if (m_create_wallet_dialog->isMakeBlankWalletChecked()) {
     245           0 :         flags |= WALLET_FLAG_BLANK_WALLET;
     246           0 :     }
     247           0 :     if (m_create_wallet_dialog->isDescriptorWalletChecked()) {
     248           0 :         flags |= WALLET_FLAG_DESCRIPTORS;
     249           0 :     }
     250             : 
     251           0 :     QTimer::singleShot(500, worker(), [this, name, flags] {
     252           0 :         std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().createWallet(name, m_passphrase, flags, m_error_message, m_warning_message);
     253             : 
     254           0 :         if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
     255             : 
     256           0 :         QTimer::singleShot(500, this, &CreateWalletActivity::finish);
     257           0 :     });
     258           0 : }
     259             : 
     260           0 : void CreateWalletActivity::finish()
     261             : {
     262           0 :     destroyProgressDialog();
     263             : 
     264           0 :     if (!m_error_message.empty()) {
     265           0 :         QMessageBox::critical(m_parent_widget, tr("Create wallet failed"), QString::fromStdString(m_error_message.translated));
     266           0 :     } else if (!m_warning_message.empty()) {
     267           0 :         QMessageBox::warning(m_parent_widget, tr("Create wallet warning"), QString::fromStdString(Join(m_warning_message, Untranslated("\n")).translated));
     268           0 :     }
     269             : 
     270           0 :     if (m_wallet_model) Q_EMIT created(m_wallet_model);
     271             : 
     272           0 :     Q_EMIT finished();
     273           0 : }
     274             : 
     275           0 : void CreateWalletActivity::create()
     276             : {
     277           0 :     m_create_wallet_dialog = new CreateWalletDialog(m_parent_widget);
     278           0 :     m_create_wallet_dialog->setWindowModality(Qt::ApplicationModal);
     279           0 :     m_create_wallet_dialog->show();
     280             : 
     281           0 :     connect(m_create_wallet_dialog, &QObject::destroyed, [this] {
     282           0 :         m_create_wallet_dialog = nullptr;
     283           0 :     });
     284           0 :     connect(m_create_wallet_dialog, &QDialog::rejected, [this] {
     285           0 :         Q_EMIT finished();
     286           0 :     });
     287           0 :     connect(m_create_wallet_dialog, &QDialog::accepted, [this] {
     288           0 :         if (m_create_wallet_dialog->isEncryptWalletChecked()) {
     289           0 :             askPassphrase();
     290           0 :         } else {
     291           0 :             createWallet();
     292             :         }
     293           0 :     });
     294           0 : }
     295             : 
     296           0 : OpenWalletActivity::OpenWalletActivity(WalletController* wallet_controller, QWidget* parent_widget)
     297           0 :     : WalletControllerActivity(wallet_controller, parent_widget)
     298           0 : {
     299           0 : }
     300             : 
     301           0 : void OpenWalletActivity::finish()
     302             : {
     303           0 :     destroyProgressDialog();
     304             : 
     305           0 :     if (!m_error_message.empty()) {
     306           0 :         QMessageBox::critical(m_parent_widget, tr("Open wallet failed"), QString::fromStdString(m_error_message.translated));
     307           0 :     } else if (!m_warning_message.empty()) {
     308           0 :         QMessageBox::warning(m_parent_widget, tr("Open wallet warning"), QString::fromStdString(Join(m_warning_message, Untranslated("\n")).translated));
     309           0 :     }
     310             : 
     311           0 :     if (m_wallet_model) Q_EMIT opened(m_wallet_model);
     312             : 
     313           0 :     Q_EMIT finished();
     314           0 : }
     315             : 
     316           0 : void OpenWalletActivity::open(const std::string& path)
     317             : {
     318           0 :     QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
     319             : 
     320           0 :     showProgressDialog(tr("Opening Wallet <b>%1</b>...").arg(name.toHtmlEscaped()));
     321             : 
     322           0 :     QTimer::singleShot(0, worker(), [this, path] {
     323           0 :         std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().loadWallet(path, m_error_message, m_warning_message);
     324             : 
     325           0 :         if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
     326             : 
     327           0 :         QTimer::singleShot(0, this, &OpenWalletActivity::finish);
     328           0 :     });
     329           0 : }

Generated by: LCOV version 1.15