From 1aa84f8a3bd82974e07ba32e6241e0a20660ae55 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Tue, 12 Feb 2019 08:19:05 -1000 Subject: [PATCH] Merge #15153: gui: Add Open Wallet menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1951ea434 gui: Show indeterminate progress dialog while opening walllet (João Barbosa) 8847cdaaa gui: Add OpenWalletActivity (João Barbosa) 4c8982a88 interfaces: Avoid interface instance if wallet is null (João Barbosa) be82dea23 gui: Add thread to run background activity in WalletController (João Barbosa) 6c49a55b4 gui: Add Open Wallet menu (João Barbosa) 32a8c6abf gui: Add openWallet and getWalletsAvailableToOpen to WalletController (João Barbosa) ab288b4e5 interfaces: Add loadWallet to Node (João Barbosa) 17abc0fd5 wallet: Factor out LoadWallet (João Barbosa) Pull request description: The *Open Wallet* menu has all the available wallets currently not loaded. The list of the available wallets comes from `listWalletDir`. In the future the menu can be replaced by a custom dialog. screenshot 2019-01-12 at 12 17 02 Tree-SHA512: ebfd75eee0c8264863748899843afab67dadb7dff21313c11e3cb5b6108d954978dd1f1ae786bc07580c5a771ea4ab38d18c1643c9b9b3683ed53f0f6c582e38 --- src/dummywallet.cpp | 9 +++++++ src/interfaces/node.cpp | 6 +++++ src/interfaces/node.h | 5 ++++ src/interfaces/wallet.cpp | 2 +- src/qt/bitcoingui.cpp | 37 ++++++++++++++++++++++++++++ src/qt/bitcoingui.h | 1 + src/qt/dash.cpp | 2 +- src/qt/walletcontroller.cpp | 49 ++++++++++++++++++++++++++++++++++++- src/qt/walletcontroller.h | 30 +++++++++++++++++++++++ src/wallet/rpcwallet.cpp | 15 +++--------- src/wallet/wallet.cpp | 22 +++++++++++++++++ src/wallet/wallet.h | 1 + 12 files changed, 164 insertions(+), 15 deletions(-) diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp index d207e49d07..3e52120005 100644 --- a/src/dummywallet.cpp +++ b/src/dummywallet.cpp @@ -10,6 +10,10 @@ class CWallet; +namespace interfaces { +class Chain; +} + class DummyWalletInit : public WalletInitInterface { public: @@ -89,6 +93,11 @@ std::vector> GetWallets() throw std::logic_error("Wallet function called in non-wallet build."); } +std::shared_ptr LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning) +{ + throw std::logic_error("Wallet function called in non-wallet build."); +} + namespace interfaces { class Wallet; diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index d5fb759424..0064731861 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -49,6 +49,7 @@ class CWallet; fs::path GetWalletDir(); std::vector ListWalletDir(); std::vector> GetWallets(); +std::shared_ptr LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning); namespace interfaces { @@ -378,6 +379,11 @@ public: } return wallets; } + std::unique_ptr loadWallet(const std::string& name, std::string& error, std::string& warning) override + { + return MakeWallet(LoadWallet(*m_interfaces.chain, name, error, warning)); + } + EVO& evo() override { return m_evo; } LLMQ& llmq() override { return m_llmq; } Masternode::Sync& masternodeSync() override { return m_masternodeSync; } diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 40f68e3052..c21c2d821f 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -253,6 +253,11 @@ public: //! Return interfaces for accessing wallets (if any). virtual std::vector> getWallets() = 0; + //! Attempts to load a wallet from file or directory. + //! The loaded wallet is also notified to handlers previously registered + //! with handleLoadWallet. + virtual std::unique_ptr loadWallet(const std::string& name, std::string& error, std::string& warning) = 0; + //! Return interface for accessing evo related handler. virtual EVO& evo() = 0; diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index 7841757740..2556f8b6d8 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -610,7 +610,7 @@ public: } // namespace -std::unique_ptr MakeWallet(const std::shared_ptr& wallet) { return MakeUnique(wallet); } +std::unique_ptr MakeWallet(const std::shared_ptr& wallet) { return wallet ? MakeUnique(wallet) : nullptr; } std::unique_ptr MakeWalletClient(Chain& chain, std::vector wallet_filenames) { diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bb982547a3..8de6c6672d 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -427,6 +427,10 @@ void BitcoinGUI::createActions() openAction = new QAction(tr("Open &URI..."), this); openAction->setStatusTip(tr("Open a dash: URI or payment request")); + m_open_wallet_action = new QAction(tr("Open Wallet"), this); + m_open_wallet_action->setMenu(new QMenu(this)); + m_open_wallet_action->setStatusTip(tr("Open a wallet")); + showHelpMessageAction = new QAction(tr("&Command-line options"), this); showHelpMessageAction->setMenuRole(QAction::NoRole); showHelpMessageAction->setStatusTip(tr("Show the %1 help message to get a list with possible Dash command-line options").arg(tr(PACKAGE_NAME))); @@ -475,6 +479,37 @@ void BitcoinGUI::createActions() connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses); connect(usedReceivingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedReceivingAddresses); connect(openAction, &QAction::triggered, this, &BitcoinGUI::openClicked); + connect(m_open_wallet_action->menu(), &QMenu::aboutToShow, [this] { + m_open_wallet_action->menu()->clear(); + for (std::string path : m_wallet_controller->getWalletsAvailableToOpen()) { + QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path); + QAction* action = m_open_wallet_action->menu()->addAction(name); + connect(action, &QAction::triggered, [this, name, path] { + OpenWalletActivity* activity = m_wallet_controller->openWallet(path); + + QProgressDialog* dialog = new QProgressDialog(this); + dialog->setLabelText(tr("Opening Wallet %1...").arg(name.toHtmlEscaped())); + dialog->setRange(0, 0); + dialog->setCancelButton(nullptr); + dialog->setWindowModality(Qt::ApplicationModal); + dialog->show(); + + connect(activity, &OpenWalletActivity::message, this, [this] (QMessageBox::Icon icon, QString text) { + QMessageBox box; + box.setIcon(icon); + box.setText(tr("Open Wallet Failed")); + box.setInformativeText(text); + box.setStandardButtons(QMessageBox::Ok); + box.setDefaultButton(QMessageBox::Ok); + connect(this, &QObject::destroyed, &box, &QDialog::accept); + box.exec(); + }); + connect(activity, &OpenWalletActivity::opened, this, &BitcoinGUI::setCurrentWallet); + connect(activity, &OpenWalletActivity::finished, activity, &QObject::deleteLater); + connect(activity, &OpenWalletActivity::finished, dialog, &QObject::deleteLater); + }); + } + }); } #endif // ENABLE_WALLET @@ -499,6 +534,8 @@ void BitcoinGUI::createMenuBar() QMenu *file = appMenuBar->addMenu(tr("&File")); if(walletFrame) { + file->addAction(m_open_wallet_action); + file->addSeparator(); file->addAction(openAction); file->addAction(backupWalletAction); file->addAction(signMessageAction); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index e946f58b44..d9e089cd3b 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -159,6 +159,7 @@ private: QAction* showBackupsAction = nullptr; QAction* openAction = nullptr; QAction* showHelpMessageAction = nullptr; + QAction* m_open_wallet_action{nullptr}; QAction* showCoinJoinHelpAction = nullptr; QAction* m_wallet_selector_action = nullptr; diff --git a/src/qt/dash.cpp b/src/qt/dash.cpp index 01471ea04e..24603e1c46 100644 --- a/src/qt/dash.cpp +++ b/src/qt/dash.cpp @@ -479,7 +479,7 @@ int GuiMain(int argc, char* argv[]) // IMPORTANT if it is no longer a typedef use the normal variant above qRegisterMetaType< CAmount >("CAmount"); qRegisterMetaType< std::function >("std::function"); - + qRegisterMetaType("QMessageBox::Icon"); /// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these // Command-line options take precedence: node->setupServerArgs(); diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index bcbb967df5..f22cd87ff2 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -26,11 +27,17 @@ WalletController::WalletController(interfaces::Node& node, OptionsModel* options for (std::unique_ptr& wallet : m_node.getWallets()) { getOrCreateWallet(std::move(wallet)); } + + m_activity_thread.start(); } // Not using the default destructor because not all member types definitions are // available in the header, just forward declared. -WalletController::~WalletController() {} +WalletController::~WalletController() +{ + m_activity_thread.quit(); + m_activity_thread.wait(); +} std::vector WalletController::getWallets() const { @@ -38,6 +45,25 @@ std::vector WalletController::getWallets() const return m_wallets; } +std::vector WalletController::getWalletsAvailableToOpen() const +{ + QMutexLocker locker(&m_mutex); + std::vector wallets = m_node.listWalletDir(); + for (WalletModel* wallet_model : m_wallets) { + auto it = std::remove(wallets.begin(), wallets.end(), wallet_model->wallet().getWalletName()); + if (it != wallets.end()) wallets.erase(it); + } + return wallets; +} + +OpenWalletActivity* WalletController::openWallet(const std::string& name, QWidget* parent) +{ + OpenWalletActivity* activity = new OpenWalletActivity(this, name); + activity->moveToThread(&m_activity_thread); + QMetaObject::invokeMethod(activity, "open", Qt::QueuedConnection); + return activity; +} + WalletModel* WalletController::getOrCreateWallet(std::unique_ptr wallet) { QMutexLocker locker(&m_mutex); @@ -111,3 +137,24 @@ void WalletController::removeAndDeleteWallet(WalletModel* wallet_model) // CWallet shared pointer. delete wallet_model; } + + +OpenWalletActivity::OpenWalletActivity(WalletController* wallet_controller, const std::string& name) + : m_wallet_controller(wallet_controller) + , m_name(name) +{} + +void OpenWalletActivity::open() +{ + std::string error, warning; + std::unique_ptr wallet = m_wallet_controller->m_node.loadWallet(m_name, error, warning); + if (!warning.empty()) { + Q_EMIT message(QMessageBox::Warning, QString::fromStdString(warning)); + } + if (wallet) { + Q_EMIT opened(m_wallet_controller->getOrCreateWallet(std::move(wallet))); + } else { + Q_EMIT message(QMessageBox::Critical, QString::fromStdString(error)); + } + Q_EMIT finished(); +} diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h index 9e19c4264f..58a7c3050f 100644 --- a/src/qt/walletcontroller.h +++ b/src/qt/walletcontroller.h @@ -12,7 +12,9 @@ #include #include +#include #include +#include class OptionsModel; class PlatformStyle; @@ -22,6 +24,8 @@ class Handler; class Node; } // namespace interfaces +class OpenWalletActivity; + /** * Controller between interfaces::Node, WalletModel instances and the GUI. */ @@ -37,6 +41,9 @@ public: ~WalletController(); std::vector getWallets() const; + std::vector getWalletsAvailableToOpen() const; + + OpenWalletActivity* openWallet(const std::string& name, QWidget* parent = nullptr); private Q_SLOTS: void addWallet(WalletModel* wallet_model); @@ -48,11 +55,34 @@ Q_SIGNALS: void coinsSent(WalletModel* wallet_model, SendCoinsRecipient recipient, QByteArray transaction); private: + QThread m_activity_thread; interfaces::Node& m_node; OptionsModel* const m_options_model; mutable QMutex m_mutex; std::vector m_wallets; std::unique_ptr m_handler_load_wallet; + + friend class OpenWalletActivity; +}; + +class OpenWalletActivity : public QObject +{ + Q_OBJECT + +public: + OpenWalletActivity(WalletController* wallet_controller, const std::string& name); + +public Q_SLOTS: + void open(); + +Q_SIGNALS: + void message(QMessageBox::Icon icon, const QString text); + void finished(); + void opened(WalletModel* wallet_model); + +private: + WalletController* const m_wallet_controller; + std::string const m_name; }; #endif // BITCOIN_QT_WALLETCONTROLLER_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 90ff086926..f8a88ca435 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2981,7 +2981,6 @@ static UniValue loadwallet(const JSONRPCRequest& request) ); WalletLocation location(request.params[0].get_str()); - std::string error; if (!location.Exists()) { throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + location.GetName() + " not found."); @@ -2993,17 +2992,9 @@ static UniValue loadwallet(const JSONRPCRequest& request) } } - std::string warning; - if (!CWallet::Verify(*g_rpc_interfaces->chain, location, false, error, warning)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error); - } - - std::shared_ptr const wallet = CWallet::CreateWalletFromFile(*g_rpc_interfaces->chain, location); - if (!wallet) { - throw JSONRPCError(RPC_WALLET_ERROR, "Wallet loading failed."); - } - - wallet->postInitProcess(); + std::string error, warning; + std::shared_ptr const wallet = LoadWallet(*g_rpc_interfaces->chain, location, error, warning); + if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error); UniValue obj(UniValue::VOBJ); obj.pushKV("name", wallet->GetName()); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 276f0236aa..7d3e7e08da 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -148,6 +148,28 @@ void UnloadWallet(std::shared_ptr&& wallet) } } +std::shared_ptr LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::string& warning) +{ + if (!CWallet::Verify(chain, location, false, error, warning)) { + error = "Wallet file verification failed: " + error; + return nullptr; + } + + std::shared_ptr wallet = CWallet::CreateWalletFromFile(chain, location); + if (!wallet) { + error = "Wallet loading failed."; + return nullptr; + } + AddWallet(wallet); + wallet->postInitProcess(); + return wallet; +} + +std::shared_ptr LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning) +{ + return LoadWallet(chain, WalletLocation(name), error, warning); +} + const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); /** @defgroup mapWallet diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 86d0590a53..d01d5c4512 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -70,6 +70,7 @@ bool RemoveWallet(const std::shared_ptr& wallet); bool HasWallets(); std::vector> GetWallets(); std::shared_ptr GetWallet(const std::string& name); +std::shared_ptr LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::string& warning); static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000; //! -paytxfee default