diff --git a/src/Makefile.am b/src/Makefile.am index 72e5cdb95d..affafaa5f9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -104,6 +104,8 @@ BITCOIN_CORE_H = \ httpserver.h \ indirectmap.h \ init.h \ + interface/handler.h \ + interface/node.h \ key.h \ key_io.h \ keystore.h \ @@ -357,6 +359,8 @@ libbitcoin_util_a_SOURCES = \ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ fs.cpp \ + interface/handler.cpp \ + interface/node.cpp \ random.cpp \ rpc/protocol.cpp \ rpc/util.cpp \ diff --git a/src/interface/handler.cpp b/src/interface/handler.cpp new file mode 100644 index 0000000000..4b27f374f4 --- /dev/null +++ b/src/interface/handler.cpp @@ -0,0 +1,33 @@ +// 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 MakeUnique(std::move(connection)); +} + +} // namespace interface diff --git a/src/interface/handler.h b/src/interface/handler.h new file mode 100644 index 0000000000..a76334bfbf --- /dev/null +++ b/src/interface/handler.h @@ -0,0 +1,35 @@ +// 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/node.cpp b/src/interface/node.cpp new file mode 100644 index 0000000000..4e3fa6ceb9 --- /dev/null +++ b/src/interface/node.cpp @@ -0,0 +1,53 @@ +// 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 + +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() override + { + return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() && + AppInitLockDataDirectory(); + } + bool appInitMain() override { return AppInitMain(); } + 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 MakeUnique(); } + +} // namespace interface diff --git a/src/interface/node.h b/src/interface/node.h new file mode 100644 index 0000000000..b69ef160a3 --- /dev/null +++ b/src/interface/node.h @@ -0,0 +1,62 @@ +// 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 + +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() = 0; + + //! Start node. + virtual bool appInitMain() = 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/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 140e56123f..4ab5a891e3 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -27,6 +27,8 @@ #endif #include +#include +#include #include #include #include @@ -180,11 +182,7 @@ class BitcoinCore: public QObject { Q_OBJECT public: - explicit BitcoinCore(); - /** Basic initialization, before starting initialization/shutdown thread. - * Return true on success. - */ - static bool baseInitialize(); + explicit BitcoinCore(interface::Node& node); public Q_SLOTS: void initialize(); @@ -196,9 +194,10 @@ Q_SIGNALS: void runawayException(const QString &message); private: - /// Pass fatal exception message to UI thread void handleRunawayException(const std::exception *e); + + interface::Node& m_node; }; /** Main Bitcoin application object */ @@ -206,7 +205,7 @@ 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 @@ -247,6 +246,7 @@ Q_SIGNALS: private: QThread *coreThread; + interface::Node& m_node; OptionsModel *optionsModel; ClientModel *clientModel; BitcoinGUI *window; @@ -264,36 +264,15 @@ private: #include -BitcoinCore::BitcoinCore(): - QObject() +BitcoinCore::BitcoinCore(interface::Node& node) : + QObject(), m_node(node) { } void BitcoinCore::handleRunawayException(const std::exception *e) { PrintExceptionContinue(e, "Runaway exception"); - Q_EMIT runawayException(QString::fromStdString(GetWarnings("gui"))); -} - -bool BitcoinCore::baseInitialize() -{ - if (!AppInitBasicSetup()) - { - return false; - } - if (!AppInitParameterInteraction()) - { - return false; - } - if (!AppInitSanityChecks()) - { - return false; - } - if (!AppInitLockDataDirectory()) - { - return false; - } - return true; + Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings("gui"))); } void BitcoinCore::initialize() @@ -301,7 +280,7 @@ void BitcoinCore::initialize() try { qDebug() << __func__ << ": Running initialization in thread"; - bool rv = AppInitMain(); + bool rv = m_node.appInitMain(); Q_EMIT initializeResult(rv); } catch (const std::exception& e) { handleRunawayException(&e); @@ -315,8 +294,7 @@ void BitcoinCore::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) { @@ -326,9 +304,10 @@ void BitcoinCore::shutdown() } } -BitcoinApplication::BitcoinApplication(int &argc, char **argv): +BitcoinApplication::BitcoinApplication(interface::Node& node, int &argc, char **argv): QApplication(argc, argv), coreThread(0), + m_node(node), optionsModel(0), clientModel(0), window(0), @@ -409,7 +388,7 @@ void BitcoinApplication::startThread() if(coreThread) return; coreThread = new QThread(this); - BitcoinCore *executor = new BitcoinCore(); + BitcoinCore *executor = new BitcoinCore(m_node); executor->moveToThread(coreThread); /* communication to and from thread */ @@ -427,8 +406,8 @@ void BitcoinApplication::startThread() void BitcoinApplication::parameterSetup() { - InitLogging(); - InitParameterInteraction(); + m_node.initLogging(); + m_node.initParameterInteraction(); } void BitcoinApplication::requestInitialize() @@ -461,7 +440,7 @@ void BitcoinApplication::requestShutdown() delete clientModel; clientModel = 0; - StartShutdown(); + m_node.startShutdown(); // Request shutdown from core thread Q_EMIT requestedShutdown(); @@ -555,9 +534,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 @@ -571,7 +552,7 @@ int main(int argc, char *argv[]) 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); @@ -633,7 +614,7 @@ int main(int argc, char *argv[]) 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), QObject::tr("Error: Cannot parse configuration file: %1. Only use key=value syntax.").arg(e.what())); @@ -648,7 +629,7 @@ int main(int argc, char *argv[]) // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) try { - SelectParams(ChainNameFromCommandLine()); + node->selectParams(ChainNameFromCommandLine()); } catch(std::exception &e) { QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: %1").arg(e.what())); return EXIT_FAILURE; @@ -705,7 +686,7 @@ int main(int argc, char *argv[]) app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false)); // Subscribe to global signals from core - uiInterface.InitMessage.connect(InitMessage); + std::unique_ptr handler = node->handleInitMessage(InitMessage); if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false)) app.createSplashScreen(networkStyle.data()); @@ -717,7 +698,7 @@ int main(int argc, char *argv[]) // Perform base initialization before spinning up 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 (BitcoinCore::baseInitialize()) { + if (node->baseInitialize()) { app.requestInitialize(); #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId()); @@ -732,10 +713,10 @@ int main(int argc, char *argv[]) } } 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 rv; }