diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -132,6 +132,8 @@ compat/glibcxx_sanity.cpp compat/strnlen.cpp fs.cpp + interface/handler.cpp + interface/node.cpp logging.cpp random.cpp rcu.cpp diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -141,6 +141,8 @@ httpserver.h \ indirectmap.h \ init.h \ + interface/handler.h \ + interface/node.h \ key.h \ keystore.h \ dbwrapper.h \ @@ -432,6 +434,8 @@ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ fs.cpp \ + interface/handler.cpp \ + interface/node.cpp \ logging.cpp \ random.cpp \ rcu.cpp \ diff --git a/src/interface/handler.h b/src/interface/handler.h new file mode 100644 --- /dev/null +++ b/src/interface/handler.h @@ -0,0 +1,34 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_INTERFACE_HANDLER_H +#define BITCOIN_INTERFACE_HANDLER_H + +#include + +namespace boost { +namespace signals2 { + class connection; +} // namespace signals2 +} // namespace boost + +namespace interface { + +//! Generic interface for managing an event handler or callback function +//! registered with another interface. Has a single disconnect method to cancel +//! the registration and prevent any future notifications. +class Handler { +public: + virtual ~Handler() {} + + //! Disconnect the handler. + virtual void disconnect() = 0; +}; + +//! Return handler wrapping a boost signal connection. +std::unique_ptr MakeHandler(boost::signals2::connection connection); + +} // namespace interface + +#endif // BITCOIN_INTERFACE_HANDLER_H diff --git a/src/interface/handler.cpp b/src/interface/handler.cpp new file mode 100644 --- /dev/null +++ b/src/interface/handler.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include + +#include +#include +#include + +namespace interface { +namespace { + + class HandlerImpl : public Handler { + public: + HandlerImpl(boost::signals2::connection connection) + : m_connection(std::move(connection)) {} + + void disconnect() override { m_connection.disconnect(); } + + boost::signals2::scoped_connection m_connection; + }; + +} // namespace + +std::unique_ptr MakeHandler(boost::signals2::connection connection) { + return std::make_unique(std::move(connection)); +} + +} // namespace interface diff --git a/src/interface/node.h b/src/interface/node.h new file mode 100644 --- /dev/null +++ b/src/interface/node.h @@ -0,0 +1,67 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_INTERFACE_NODE_H +#define BITCOIN_INTERFACE_NODE_H + +#include +#include +#include + +class Config; +class HTTPRPCRequestProcessor; +class RPCServer; + +namespace interface { + +class Handler; + +//! Top-level interface for a bitcoin node (bitcoind process). +class Node { +public: + virtual ~Node() {} + + //! Set command line arguments. + virtual void parseParameters(int argc, const char *const argv[]) = 0; + + //! Load settings from configuration file. + virtual void readConfigFile(const std::string &conf_path) = 0; + + //! Choose network parameters. + virtual void selectParams(const std::string &network) = 0; + + //! Init logging. + virtual void initLogging() = 0; + + //! Init parameter interaction. + virtual void initParameterInteraction() = 0; + + //! Get warnings. + virtual std::string getWarnings(const std::string &type) = 0; + + //! Initialize app dependencies. + virtual bool baseInitialize(Config &config, RPCServer &rpcServer) = 0; + + //! Start node. + virtual bool + appInitMain(Config &config, + HTTPRPCRequestProcessor &httpRPCRequestProcessor) = 0; + + //! Stop node. + virtual void appShutdown() = 0; + + //! Start shutdown. + virtual void startShutdown() = 0; + + //! Register handler for init messages. + using InitMessageFn = std::function; + virtual std::unique_ptr handleInitMessage(InitMessageFn fn) = 0; +}; + +//! Return implementation of Node interface. +std::unique_ptr MakeNode(); + +} // namespace interface + +#endif // BITCOIN_INTERFACE_NODE_H diff --git a/src/interface/node.cpp b/src/interface/node.cpp new file mode 100644 --- /dev/null +++ b/src/interface/node.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class HTTPRPCRequestProcessor; + +namespace interface { +namespace { + + class NodeImpl : public Node { + void parseParameters(int argc, const char *const argv[]) override { + gArgs.ParseParameters(argc, argv); + } + void readConfigFile(const std::string &conf_path) override { + gArgs.ReadConfigFile(conf_path); + } + void selectParams(const std::string &network) override { + SelectParams(network); + } + void initLogging() override { InitLogging(); } + void initParameterInteraction() override { InitParameterInteraction(); } + std::string getWarnings(const std::string &type) override { + return GetWarnings(type); + } + bool baseInitialize(Config &config, RPCServer &rpcServer) override { + return AppInitBasicSetup() && + AppInitParameterInteraction(config, rpcServer) && + AppInitSanityChecks() && AppInitLockDataDirectory(); + } + bool + appInitMain(Config &config, + HTTPRPCRequestProcessor &httpRPCRequestProcessor) override { + return AppInitMain(config, httpRPCRequestProcessor); + } + void appShutdown() override { + Interrupt(); + Shutdown(); + } + void startShutdown() override { StartShutdown(); } + std::unique_ptr handleInitMessage(InitMessageFn fn) override { + return MakeHandler(::uiInterface.InitMessage.connect(fn)); + } + }; + +} // namespace + +std::unique_ptr MakeNode() { + return std::make_unique(); +} + +} // namespace interface diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -28,6 +28,8 @@ #endif #include "init.h" +#include "interface/handler.h" +#include "interface/node.h" #include "rpc/server.h" #include "ui_interface.h" #include "uint256.h" @@ -173,13 +175,7 @@ class BitcoinABC : public QObject { Q_OBJECT public: - explicit BitcoinABC(); - - /** - * Basic initialization, before starting initialization/shutdown thread. - * Return true on success. - */ - static bool baseInitialize(Config &config, RPCServer &rpcServer); + explicit BitcoinABC(interface::Node &node); public Q_SLOTS: void initialize(Config *config, @@ -194,13 +190,15 @@ private: /// Pass fatal exception message to UI thread void handleRunawayException(const std::exception *e); + + interface::Node &m_node; }; /** Main Bitcoin application object */ class BitcoinApplication : public QApplication { Q_OBJECT public: - explicit BitcoinApplication(int &argc, char **argv); + explicit BitcoinApplication(interface::Node &node, int &argc, char **argv); ~BitcoinApplication(); #ifdef ENABLE_WALLET @@ -244,6 +242,7 @@ private: QThread *coreThread; + interface::Node &m_node; OptionsModel *optionsModel; ClientModel *clientModel; BitcoinGUI *window; @@ -261,27 +260,11 @@ #include "bitcoin.moc" -BitcoinABC::BitcoinABC() : QObject() {} +BitcoinABC::BitcoinABC(interface::Node &node) : QObject(), m_node(node) {} void BitcoinABC::handleRunawayException(const std::exception *e) { PrintExceptionContinue(e, "Runaway exception"); - Q_EMIT runawayException(QString::fromStdString(GetWarnings("gui"))); -} - -bool BitcoinABC::baseInitialize(Config &config, RPCServer &rpcServer) { - if (!AppInitBasicSetup()) { - return false; - } - if (!AppInitParameterInteraction(config, rpcServer)) { - return false; - } - if (!AppInitSanityChecks()) { - return false; - } - if (!AppInitLockDataDirectory()) { - return false; - } - return true; + Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings("gui"))); } void BitcoinABC::initialize(Config *cfg, @@ -289,7 +272,7 @@ Config &config(*cfg); try { qDebug() << __func__ << ": Running initialization in thread"; - bool rv = AppInitMain(config, *httpRPCRequestProcessor); + bool rv = m_node.appInitMain(config, *httpRPCRequestProcessor); Q_EMIT initializeResult(rv); } catch (const std::exception &e) { handleRunawayException(&e); @@ -301,8 +284,7 @@ void BitcoinABC::shutdown() { try { qDebug() << __func__ << ": Running Shutdown in thread"; - Interrupt(); - Shutdown(); + m_node.appShutdown(); qDebug() << __func__ << ": Shutdown finished"; Q_EMIT shutdownResult(); } catch (const std::exception &e) { @@ -312,9 +294,10 @@ } } -BitcoinApplication::BitcoinApplication(int &argc, char **argv) - : QApplication(argc, argv), coreThread(0), optionsModel(0), clientModel(0), - window(0), pollShutdownTimer(0), +BitcoinApplication::BitcoinApplication(interface::Node &node, int &argc, + char **argv) + : QApplication(argc, argv), coreThread(0), m_node(node), optionsModel(0), + clientModel(0), window(0), pollShutdownTimer(0), #ifdef ENABLE_WALLET paymentServer(0), m_wallet_models(), #endif @@ -390,7 +373,7 @@ return; } coreThread = new QThread(this); - BitcoinABC *executor = new BitcoinABC(); + BitcoinABC *executor = new BitcoinABC(m_node); executor->moveToThread(coreThread); /* communication to and from thread */ @@ -426,8 +409,8 @@ } void BitcoinApplication::parameterSetup() { - InitLogging(); - InitParameterInteraction(); + m_node.initLogging(); + m_node.initParameterInteraction(); } void BitcoinApplication::requestInitialize( @@ -462,7 +445,7 @@ delete clientModel; clientModel = 0; - StartShutdown(); + m_node.startShutdown(); // Request shutdown from core thread Q_EMIT requestedShutdown(); @@ -598,9 +581,11 @@ int main(int argc, char *argv[]) { SetupEnvironment(); + std::unique_ptr node = interface::MakeNode(); + /// 1. Parse command-line options. These take precedence over anything else. // Command-line options take precedence: - gArgs.ParseParameters(argc, argv); + node->parseParameters(argc, argv); // Do not refer to data directory yet, this can be overridden by // Intro::pickDataDirectory @@ -610,7 +595,7 @@ Q_INIT_RESOURCE(bitcoin); Q_INIT_RESOURCE(bitcoin_locale); - BitcoinApplication app(argc, argv); + BitcoinApplication app(*node, argc, argv); #if QT_VERSION > 0x050100 // Generate high-dpi pixmaps QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); @@ -686,7 +671,7 @@ return EXIT_FAILURE; } try { - gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); + node->readConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); } catch (const std::exception &e) { QMessageBox::critical( 0, QObject::tr(PACKAGE_NAME), @@ -707,7 +692,7 @@ // Check for -testnet or -regtest parameter (Params() calls are only valid // after this clause) try { - SelectParams(gArgs.GetChainName()); + node->selectParams(gArgs.GetChainName()); } catch (std::exception &e) { QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: %1").arg(e.what())); @@ -765,7 +750,8 @@ app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false)); // Subscribe to global signals from core - uiInterface.InitMessage.connect(InitMessage); + std::unique_ptr handler = + node->handleInitMessage(InitMessage); // Get global config Config &config = const_cast(GetConfig()); @@ -784,7 +770,7 @@ // initialization/shutdown thread. This is acceptable because this // function only contains steps that are quick to execute, so the GUI // thread won't be held up. - if (!BitcoinABC::baseInitialize(config, rpcServer)) { + if (!node->baseInitialize(config, rpcServer)) { // A dialog with detailed error will have been shown by InitError() return EXIT_FAILURE; } @@ -801,10 +787,12 @@ return app.getReturnValue(); } catch (const std::exception &e) { PrintExceptionContinue(&e, "Runaway exception"); - app.handleRunawayException(QString::fromStdString(GetWarnings("gui"))); + app.handleRunawayException( + QString::fromStdString(node->getWarnings("gui"))); } catch (...) { PrintExceptionContinue(nullptr, "Runaway exception"); - app.handleRunawayException(QString::fromStdString(GetWarnings("gui"))); + app.handleRunawayException( + QString::fromStdString(node->getWarnings("gui"))); } return EXIT_FAILURE; }