diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -132,6 +132,9 @@ compat/glibcxx_sanity.cpp compat/strnlen.cpp fs.cpp + interface/handler.cpp + interface/node.cpp + interface/wallet.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 @@ -7,6 +7,7 @@ AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(SANITIZER_LDFLAGS) AM_CXXFLAGS = $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(SANITIZER_CXXFLAGS) AM_CPPFLAGS = $(DEBUG_CPPFLAGS) $(HARDENED_CPPFLAGS) +AM_LIBTOOLFLAGS = --preserve-dup-deps EXTRA_LIBRARIES = if EMBEDDED_UNIVALUE @@ -140,6 +141,9 @@ httpserver.h \ indirectmap.h \ init.h \ + interface/handler.h \ + interface/node.h \ + interface/wallet.h \ key.h \ keystore.h \ dbwrapper.h \ @@ -292,6 +296,7 @@ libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_wallet_a_SOURCES = \ + interface/wallet.cpp \ wallet/crypter.cpp \ wallet/db.cpp \ wallet/finaltx.cpp \ @@ -430,6 +435,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.qt.include b/src/Makefile.qt.include --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -399,7 +399,7 @@ endif qt_bitcoin_qt_LDADD = qt/libbitcoinqt.a $(LIBBITCOIN_SERVER) if ENABLE_WALLET -qt_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET) +qt_bitcoin_qt_LDADD += $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET) endif if ENABLE_ZMQ qt_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) @@ -408,7 +408,7 @@ $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) qt_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -qt_bitcoin_qt_LIBTOOLFLAGS = --tag CXX +qt_bitcoin_qt_LIBTOOLFLAGS = $(AM_LIBTOOLFLAGS) --tag CXX #locale/foo.ts -> locale/foo.qm QT_QM=$(QT_TS:.ts=.qm) diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -58,7 +58,7 @@ qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER) if ENABLE_WALLET -qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET) +qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET) endif if ENABLE_ZMQ qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) 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,258 @@ +// 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 // For banmap_t +#include // For Amount +#include // For HelpMessageMode +#include // For CConnman::NumConnections +#include // For Network + +#include +#include +#include +#include +#include +#include +#include + +class CCoinControl; +class Coin; +class Config; +class CFeeRate; +struct CNodeStateStats; +struct CNodeStats; +class HTTPRPCRequestProcessor; +class proxyType; +class RPCServer; +class RPCTimerInterface; +class UniValue; + +namespace interface { + +class Handler; +class Wallet; + +//! 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; + + //! Set a command line argument if it doesn't already have a value + virtual bool softSetArg(const std::string &arg, + const std::string &value) = 0; + + //! Set a command line boolean argument if it doesn't already have a value + virtual bool softSetBoolArg(const std::string &arg, bool value) = 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; + + //! Get network name. + virtual std::string getNetwork() = 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; + + //! Return whether shutdown was requested. + virtual bool shutdownRequested() = 0; + + //! Get help message string. + virtual std::string helpMessage(HelpMessageMode mode) = 0; + + //! Map port. + virtual void mapPort(bool use_upnp) = 0; + + //! Get proxy. + virtual bool getProxy(Network net, proxyType &proxy_info) = 0; + + //! Get number of connections. + virtual size_t getNodeCount(CConnman::NumConnections flags) = 0; + + //! Get stats for connected nodes. + using NodesStats = + std::vector>; + virtual bool getNodesStats(NodesStats &stats) = 0; + + //! Get ban map entries. + virtual bool getBanned(banmap_t &banmap) = 0; + + //! Ban node. + virtual bool ban(const CNetAddr &net_addr, BanReason reason, + int64_t ban_time_offset) = 0; + + //! Unban node. + virtual bool unban(const CSubNet &ip) = 0; + + //! Disconnect node. + virtual bool disconnect(NodeId id) = 0; + + //! Get total bytes recv. + virtual int64_t getTotalBytesRecv() = 0; + + //! Get total bytes sent. + virtual int64_t getTotalBytesSent() = 0; + + //! Get mempool size. + virtual size_t getMempoolSize() = 0; + + //! Get mempool dynamic usage. + virtual size_t getMempoolDynamicUsage() = 0; + + //! Get header tip height and time. + virtual bool getHeaderTip(int &height, int64_t &block_time) = 0; + + //! Get num blocks. + virtual int getNumBlocks() = 0; + + //! Get last block time. + virtual int64_t getLastBlockTime() = 0; + + //! Get verification progress. + virtual double getVerificationProgress() = 0; + + //! Is initial block download. + virtual bool isInitialBlockDownload() = 0; + + //! Get reindex. + virtual bool getReindex() = 0; + + //! Get importing. + virtual bool getImporting() = 0; + + //! Set network active. + virtual void setNetworkActive(bool active) = 0; + + //! Get network active. + virtual bool getNetworkActive() = 0; + + //! Get minimum fee. + virtual Amount getMinimumFee(unsigned int tx_bytes, + const CCoinControl &coin_control) = 0; + + //! Get max tx fee. + virtual Amount getMaxTxFee() = 0; + + //! Get dust relay fee. + virtual CFeeRate getDustRelayFee() = 0; + + //! Get pay tx fee. + virtual CFeeRate getPayTxFee() = 0; + + //! Execute rpc command. + virtual UniValue executeRpc(const std::string &command, + const UniValue ¶ms, + const std::string &uri) = 0; + + //! List rpc commands. + virtual std::vector listRpcCommands() = 0; + + //! Set RPC timer interface if unset. + virtual void rpcSetTimerInterfaceIfUnset(RPCTimerInterface *iface) = 0; + + //! Unset RPC timer interface. + virtual void rpcUnsetTimerInterface(RPCTimerInterface *iface) = 0; + + //! Get unspent outputs associated with a transaction. + virtual bool getUnspentOutput(const COutPoint &output, Coin &coin) = 0; + + //! Return interfaces for accessing wallets (if any). + virtual std::vector> getWallets() = 0; + + //! Register handler for init messages. + using InitMessageFn = std::function; + virtual std::unique_ptr handleInitMessage(InitMessageFn fn) = 0; + + //! Register handler for message box messages. + using MessageBoxFn = + std::function; + virtual std::unique_ptr handleMessageBox(MessageBoxFn fn) = 0; + + //! Register handler for question messages. + using QuestionFn = std::function; + virtual std::unique_ptr handleQuestion(QuestionFn fn) = 0; + + //! Register handler for progress messages. + using ShowProgressFn = std::function; + virtual std::unique_ptr handleShowProgress(ShowProgressFn fn) = 0; + + //! Register handler for load wallet messages. + using LoadWalletFn = std::function wallet)>; + virtual std::unique_ptr handleLoadWallet(LoadWalletFn fn) = 0; + + //! Register handler for number of connections changed messages. + using NotifyNumConnectionsChangedFn = + std::function; + virtual std::unique_ptr + handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) = 0; + + //! Register handler for network active messages. + using NotifyNetworkActiveChangedFn = + std::function; + virtual std::unique_ptr + handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) = 0; + + //! Register handler for notify alert messages. + using NotifyAlertChangedFn = std::function; + virtual std::unique_ptr + handleNotifyAlertChanged(NotifyAlertChangedFn fn) = 0; + + //! Register handler for ban list messages. + using BannedListChangedFn = std::function; + virtual std::unique_ptr + handleBannedListChanged(BannedListChangedFn fn) = 0; + + //! Register handler for block tip messages. + using NotifyBlockTipFn = + std::function; + virtual std::unique_ptr + handleNotifyBlockTip(NotifyBlockTipFn fn) = 0; + + //! Register handler for header tip messages. + using NotifyHeaderTipFn = + std::function; + virtual std::unique_ptr + handleNotifyHeaderTip(NotifyHeaderTipFn 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,320 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_CONFIG_H) +#include +#endif +#ifdef ENABLE_WALLET +#include +#include +#define CHECK_WALLET(x) x +#else +#define CHECK_WALLET(x) \ + throw std::logic_error("Wallet function called in non-wallet build.") +#endif + +#include +#include +#include + +class CWallet; +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); + } + bool softSetArg(const std::string &arg, + const std::string &value) override { + return gArgs.SoftSetArg(arg, value); + } + bool softSetBoolArg(const std::string &arg, bool value) override { + return gArgs.SoftSetBoolArg(arg, value); + } + void selectParams(const std::string &network) override { + SelectParams(network); + } + std::string getNetwork() override { return Params().NetworkIDString(); } + 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(); } + bool shutdownRequested() override { return ShutdownRequested(); } + void mapPort(bool use_upnp) override { + if (use_upnp) { + StartMapPort(); + } else { + InterruptMapPort(); + StopMapPort(); + } + } + std::string helpMessage(HelpMessageMode mode) override { + return HelpMessage(mode); + } + bool getProxy(Network net, proxyType &proxy_info) override { + return GetProxy(net, proxy_info); + } + size_t getNodeCount(CConnman::NumConnections flags) override { + return g_connman ? g_connman->GetNodeCount(flags) : 0; + } + bool getNodesStats(NodesStats &stats) override { + stats.clear(); + + if (g_connman) { + std::vector stats_temp; + g_connman->GetNodeStats(stats_temp); + + stats.reserve(stats_temp.size()); + for (auto &node_stats_temp : stats_temp) { + stats.emplace_back(std::move(node_stats_temp), false, + CNodeStateStats()); + } + + // Try to retrieve the CNodeStateStats for each node. + TRY_LOCK(::cs_main, lockMain); + if (lockMain) { + for (auto &node_stats : stats) { + std::get<1>(node_stats) = + GetNodeStateStats(std::get<0>(node_stats).nodeid, + std::get<2>(node_stats)); + } + } + return true; + } + return false; + } + bool getBanned(banmap_t &banmap) override { + if (g_connman) { + g_connman->GetBanned(banmap); + return true; + } + return false; + } + bool ban(const CNetAddr &net_addr, BanReason reason, + int64_t ban_time_offset) override { + if (g_connman) { + g_connman->Ban(net_addr, reason, ban_time_offset); + return true; + } + return false; + } + bool unban(const CSubNet &ip) override { + if (g_connman) { + g_connman->Unban(ip); + return true; + } + return false; + } + bool disconnect(NodeId id) override { + if (g_connman) { + return g_connman->DisconnectNode(id); + } + return false; + } + int64_t getTotalBytesRecv() override { + return g_connman ? g_connman->GetTotalBytesRecv() : 0; + } + int64_t getTotalBytesSent() override { + return g_connman ? g_connman->GetTotalBytesSent() : 0; + } + size_t getMempoolSize() override { return g_mempool.size(); } + size_t getMempoolDynamicUsage() override { + return g_mempool.DynamicMemoryUsage(); + } + bool getHeaderTip(int &height, int64_t &block_time) override { + LOCK(::cs_main); + if (::pindexBestHeader) { + height = ::pindexBestHeader->nHeight; + block_time = ::pindexBestHeader->GetBlockTime(); + return true; + } + return false; + } + int getNumBlocks() override { + LOCK(::cs_main); + return ::chainActive.Height(); + } + int64_t getLastBlockTime() override { + LOCK(::cs_main); + if (::chainActive.Tip()) { + return ::chainActive.Tip()->GetBlockTime(); + } + return Params() + .GenesisBlock() + .GetBlockTime(); // Genesis block's time of current network + } + double getVerificationProgress() override { + const CBlockIndex *tip; + { + LOCK(::cs_main); + tip = ::chainActive.Tip(); + } + return GuessVerificationProgress(Params().TxData(), tip); + } + bool isInitialBlockDownload() override { + return IsInitialBlockDownload(); + } + bool getReindex() override { return ::fReindex; } + bool getImporting() override { return ::fImporting; } + void setNetworkActive(bool active) override { + if (g_connman) { + g_connman->SetNetworkActive(active); + } + } + bool getNetworkActive() override { + return g_connman && g_connman->GetNetworkActive(); + } + Amount getMinimumFee(unsigned int tx_bytes, + const CCoinControl &coin_control) override { + Amount result; + CHECK_WALLET(result = + GetMinimumFee(tx_bytes, g_mempool, coin_control)); + return result; + } + Amount getMaxTxFee() override { return ::maxTxFee; } + CFeeRate getDustRelayFee() override { return ::dustRelayFee; } + CFeeRate getPayTxFee() override { CHECK_WALLET(return ::payTxFee); } + UniValue executeRpc(const std::string &command, const UniValue ¶ms, + const std::string &uri) override { + JSONRPCRequest req; + req.params = params; + req.strMethod = command; + req.URI = uri; + GlobalConfig config; + return ::tableRPC.execute(config, req); + } + std::vector listRpcCommands() override { + return ::tableRPC.listCommands(); + } + void rpcSetTimerInterfaceIfUnset(RPCTimerInterface *iface) override { + RPCSetTimerInterfaceIfUnset(iface); + } + void rpcUnsetTimerInterface(RPCTimerInterface *iface) override { + RPCUnsetTimerInterface(iface); + } + bool getUnspentOutput(const COutPoint &output, Coin &coin) override { + LOCK(::cs_main); + return ::pcoinsTip->GetCoin(output, coin); + } + std::vector> getWallets() override { +#ifdef ENABLE_WALLET + std::vector> wallets; + for (CWalletRef wallet : ::vpwallets) { + wallets.emplace_back(MakeWallet(*wallet)); + } + return wallets; +#else + throw std::logic_error( + "Node::getWallets() called in non-wallet build."); +#endif + } + std::unique_ptr handleInitMessage(InitMessageFn fn) override { + return MakeHandler(::uiInterface.InitMessage.connect(fn)); + } + std::unique_ptr handleMessageBox(MessageBoxFn fn) override { + return MakeHandler(::uiInterface.ThreadSafeMessageBox.connect(fn)); + } + std::unique_ptr handleQuestion(QuestionFn fn) override { + return MakeHandler(::uiInterface.ThreadSafeQuestion.connect(fn)); + } + std::unique_ptr + handleShowProgress(ShowProgressFn fn) override { + return MakeHandler(::uiInterface.ShowProgress.connect(fn)); + } + std::unique_ptr handleLoadWallet(LoadWalletFn fn) override { + CHECK_WALLET(return MakeHandler(::uiInterface.LoadWallet.connect( + [fn](CWallet *wallet) { fn(MakeWallet(*wallet)); }))); + } + std::unique_ptr handleNotifyNumConnectionsChanged( + NotifyNumConnectionsChangedFn fn) override { + return MakeHandler( + ::uiInterface.NotifyNumConnectionsChanged.connect(fn)); + } + std::unique_ptr handleNotifyNetworkActiveChanged( + NotifyNetworkActiveChangedFn fn) override { + return MakeHandler( + ::uiInterface.NotifyNetworkActiveChanged.connect(fn)); + } + std::unique_ptr + handleNotifyAlertChanged(NotifyAlertChangedFn fn) override { + return MakeHandler(::uiInterface.NotifyAlertChanged.connect(fn)); + } + std::unique_ptr + handleBannedListChanged(BannedListChangedFn fn) override { + return MakeHandler(::uiInterface.BannedListChanged.connect(fn)); + } + std::unique_ptr + handleNotifyBlockTip(NotifyBlockTipFn fn) override { + return MakeHandler(::uiInterface.NotifyBlockTip.connect( + [fn](bool initial_download, const CBlockIndex *block) { + fn(initial_download, block->nHeight, block->GetBlockTime(), + GuessVerificationProgress(Params().TxData(), block)); + })); + } + std::unique_ptr + handleNotifyHeaderTip(NotifyHeaderTipFn fn) override { + return MakeHandler(::uiInterface.NotifyHeaderTip.connect( + [fn](bool initial_download, const CBlockIndex *block) { + fn(initial_download, block->nHeight, block->GetBlockTime(), + GuessVerificationProgress(Params().TxData(), block)); + })); + } + }; + +} // namespace + +std::unique_ptr MakeNode() { + return std::make_unique(); +} + +} // namespace interface diff --git a/src/interface/wallet.h b/src/interface/wallet.h new file mode 100644 --- /dev/null +++ b/src/interface/wallet.h @@ -0,0 +1,337 @@ +// 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_WALLET_H +#define BITCOIN_INTERFACE_WALLET_H + +#include // For Amount +#include // For CTxOut +#include // For CTxDestination (CKeyID and CScriptID) +#include