diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 5ad17a73b..be0d2aa49 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -1,376 +1,387 @@ // 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 <interfaces/node.h> #include <addrdb.h> #include <banman.h> #include <chain.h> #include <chainparams.h> #include <config.h> #include <init.h> #include <interfaces/chain.h> #include <interfaces/handler.h> #include <interfaces/wallet.h> #include <net.h> #include <net_processing.h> #include <netaddress.h> #include <netbase.h> #include <node/context.h> #include <policy/fees.h> #include <policy/settings.h> #include <primitives/block.h> #include <rpc/server.h> #include <shutdown.h> #include <support/allocators/secure.h> #include <sync.h> #include <txmempool.h> #include <ui_interface.h> #include <util/ref.h> #include <util/system.h> #include <util/translation.h> #include <validation.h> #include <warnings.h> #if defined(HAVE_CONFIG_H) #include <config/bitcoin-config.h> #endif #include <univalue.h> class HTTPRPCRequestProcessor; class CWallet; fs::path GetWalletDir(); std::vector<fs::path> ListWalletDir(); std::vector<std::shared_ptr<CWallet>> GetWallets(); std::shared_ptr<CWallet> LoadWallet(const CChainParams &chainParams, interfaces::Chain &chain, const std::string &name, bilingual_str &error, std::vector<bilingual_str> &warnings); WalletCreationStatus CreateWallet(const CChainParams ¶ms, interfaces::Chain &chain, const SecureString &passphrase, uint64_t wallet_creation_flags, const std::string &name, bilingual_str &error, std::vector<bilingual_str> &warnings, std::shared_ptr<CWallet> &result); std::unique_ptr<interfaces::Handler> HandleLoadWallet(interfaces::Node::LoadWalletFn load_wallet); namespace interfaces { namespace { class NodeImpl : public Node { public: + NodeImpl(NodeContext *context) { setContext(context); } void initError(const std::string &message) override { InitError(Untranslated(message)); } bool parseParameters(int argc, const char *const argv[], std::string &error) override { return gArgs.ParseParameters(argc, argv, error); } bool readConfigFiles(std::string &error) override { return gArgs.ReadConfigFiles(error, true); } void forceSetArg(const std::string &arg, const std::string &value) override { gArgs.ForceSetArg(arg, value); } 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); } uint64_t getAssumedBlockchainSize() override { return Params().AssumedBlockchainSize(); } uint64_t getAssumedChainStateSize() override { return Params().AssumedChainStateSize(); } 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) override { return AppInitBasicSetup() && AppInitParameterInteraction(config) && AppInitSanityChecks() && AppInitLockDataDirectory(); } bool appInitMain(Config &config, RPCServer &rpcServer, HTTPRPCRequestProcessor &httpRPCRequestProcessor) override { - m_context.chain = MakeChain(m_context, config.GetChainParams()); + m_context->chain = MakeChain(*m_context, config.GetChainParams()); return AppInitMain(config, rpcServer, httpRPCRequestProcessor, - m_context); + *m_context); } void appShutdown() override { - Interrupt(m_context); - Shutdown(m_context); + Interrupt(*m_context); + Shutdown(*m_context); } void startShutdown() override { StartShutdown(); } bool shutdownRequested() override { return ShutdownRequested(); } void mapPort(bool use_upnp) override { if (use_upnp) { StartMapPort(); } else { InterruptMapPort(); StopMapPort(); } } + // PR18571: return SetupServerArgs(*m_context) + // See https://reviews.bitcoinabc.org/D7993 void setupServerArgs() override { return SetupServerArgs(); } bool getProxy(Network net, proxyType &proxy_info) override { return GetProxy(net, proxy_info); } size_t getNodeCount(CConnman::NumConnections flags) override { - return m_context.connman ? m_context.connman->GetNodeCount(flags) - : 0; + return m_context->connman ? m_context->connman->GetNodeCount(flags) + : 0; } bool getNodesStats(NodesStats &stats) override { stats.clear(); - if (m_context.connman) { + if (m_context->connman) { std::vector<CNodeStats> stats_temp; - m_context.connman->GetNodeStats(stats_temp); + m_context->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 (m_context.banman) { - m_context.banman->GetBanned(banmap); + if (m_context->banman) { + m_context->banman->GetBanned(banmap); return true; } return false; } bool ban(const CNetAddr &net_addr, int64_t ban_time_offset) override { - if (m_context.banman) { - m_context.banman->Ban(net_addr, ban_time_offset); + if (m_context->banman) { + m_context->banman->Ban(net_addr, ban_time_offset); return true; } return false; } bool unban(const CSubNet &ip) override { - if (m_context.banman) { - m_context.banman->Unban(ip); + if (m_context->banman) { + m_context->banman->Unban(ip); return true; } return false; } bool disconnect(const CNetAddr &net_addr) override { - if (m_context.connman) { - return m_context.connman->DisconnectNode(net_addr); + if (m_context->connman) { + return m_context->connman->DisconnectNode(net_addr); } return false; } bool disconnect(NodeId id) override { - if (m_context.connman) { - return m_context.connman->DisconnectNode(id); + if (m_context->connman) { + return m_context->connman->DisconnectNode(id); } return false; } int64_t getTotalBytesRecv() override { - return m_context.connman ? m_context.connman->GetTotalBytesRecv() - : 0; + return m_context->connman ? m_context->connman->GetTotalBytesRecv() + : 0; } int64_t getTotalBytesSent() override { - return m_context.connman ? m_context.connman->GetTotalBytesSent() - : 0; + return m_context->connman ? m_context->connman->GetTotalBytesSent() + : 0; } size_t getMempoolSize() override { - return m_context.mempool ? m_context.mempool->size() : 0; + return m_context->mempool ? m_context->mempool->size() : 0; } size_t getMempoolDynamicUsage() override { - return m_context.mempool ? m_context.mempool->DynamicMemoryUsage() - : 0; + return m_context->mempool ? m_context->mempool->DynamicMemoryUsage() + : 0; } 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(); } // Genesis block's time of current network return Params().GenesisBlock().GetBlockTime(); } double getVerificationProgress() override { const CBlockIndex *tip; { LOCK(::cs_main); tip = ::ChainActive().Tip(); } return GuessVerificationProgress(Params().TxData(), tip); } bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); } bool getReindex() override { return ::fReindex; } bool getImporting() override { return ::fImporting; } void setNetworkActive(bool active) override { - if (m_context.connman) { - m_context.connman->SetNetworkActive(active); + if (m_context->connman) { + m_context->connman->SetNetworkActive(active); } } bool getNetworkActive() override { - return m_context.connman && m_context.connman->GetNetworkActive(); + return m_context->connman && m_context->connman->GetNetworkActive(); } CFeeRate estimateSmartFee() override { - return m_context.mempool ? m_context.mempool->estimateFee() - : CFeeRate(); + return m_context->mempool ? m_context->mempool->estimateFee() + : CFeeRate(); } CFeeRate getDustRelayFee() override { return ::dustRelayFee; } UniValue executeRpc(Config &config, const std::string &command, const UniValue ¶ms, const std::string &uri) override { JSONRPCRequest req(m_context_ref); req.params = params; req.strMethod = command; req.URI = uri; return ::tableRPC.execute(config, req); } std::vector<std::string> 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 ::ChainstateActive().CoinsTip().GetCoin(output, coin); } std::string getWalletDir() override { return GetWalletDir().string(); } std::vector<std::string> listWalletDir() override { std::vector<std::string> paths; for (auto &path : ListWalletDir()) { paths.push_back(path.string()); } return paths; } std::vector<std::unique_ptr<Wallet>> getWallets() override { std::vector<std::unique_ptr<Wallet>> wallets; - for (auto &client : m_context.chain_clients) { + for (auto &client : m_context->chain_clients) { auto client_wallets = client->getWallets(); std::move(client_wallets.begin(), client_wallets.end(), std::back_inserter(wallets)); } return wallets; } std::unique_ptr<Wallet> loadWallet(const CChainParams ¶ms, const std::string &name, bilingual_str &error, std::vector<bilingual_str> &warnings) const override { return MakeWallet( - LoadWallet(params, *m_context.chain, name, error, warnings)); + LoadWallet(params, *m_context->chain, name, error, warnings)); } WalletCreationStatus createWallet(const CChainParams ¶ms, const SecureString &passphrase, uint64_t wallet_creation_flags, const std::string &name, bilingual_str &error, std::vector<bilingual_str> &warnings, std::unique_ptr<Wallet> &result) override { std::shared_ptr<CWallet> wallet; WalletCreationStatus status = CreateWallet( - params, *m_context.chain, passphrase, wallet_creation_flags, + params, *m_context->chain, passphrase, wallet_creation_flags, name, error, warnings, wallet); result = MakeWallet(wallet); return status; } std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override { return MakeHandler(::uiInterface.InitMessage_connect(fn)); } std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) override { return MakeHandler(::uiInterface.ThreadSafeMessageBox_connect(fn)); } std::unique_ptr<Handler> handleQuestion(QuestionFn fn) override { return MakeHandler(::uiInterface.ThreadSafeQuestion_connect(fn)); } std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override { return MakeHandler(::uiInterface.ShowProgress_connect(fn)); } std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override { return HandleLoadWallet(std::move(fn)); } std::unique_ptr<Handler> handleNotifyNumConnectionsChanged( NotifyNumConnectionsChangedFn fn) override { return MakeHandler( ::uiInterface.NotifyNumConnectionsChanged_connect(fn)); } std::unique_ptr<Handler> handleNotifyNetworkActiveChanged( NotifyNetworkActiveChangedFn fn) override { return MakeHandler( ::uiInterface.NotifyNetworkActiveChanged_connect(fn)); } std::unique_ptr<Handler> handleNotifyAlertChanged(NotifyAlertChangedFn fn) override { return MakeHandler(::uiInterface.NotifyAlertChanged_connect(fn)); } std::unique_ptr<Handler> handleBannedListChanged(BannedListChangedFn fn) override { return MakeHandler(::uiInterface.BannedListChanged_connect(fn)); } std::unique_ptr<Handler> 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<Handler> 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)); })); } - NodeContext *context() override { return &m_context; } - NodeContext m_context; + NodeContext *context() override { return m_context; } + void setContext(NodeContext *context) override { + m_context = context; + if (context) { + m_context_ref.Set(*context); + } else { + m_context_ref.Clear(); + } + } + NodeContext *m_context{nullptr}; util::Ref m_context_ref{m_context}; }; } // namespace -std::unique_ptr<Node> MakeNode() { - return std::make_unique<NodeImpl>(); +std::unique_ptr<Node> MakeNode(NodeContext *context) { + return std::make_unique<NodeImpl>(context); } } // namespace interfaces diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 2f1320ed7..ba8108090 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -1,296 +1,298 @@ // 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_INTERFACES_NODE_H #define BITCOIN_INTERFACES_NODE_H #include <amount.h> // For Amount #include <net.h> // For CConnman::NumConnections #include <net_types.h> // For banmap_t #include <netaddress.h> // For Network #include <support/allocators/secure.h> // For SecureString #include <cstddef> #include <cstdint> #include <functional> #include <memory> #include <string> #include <tuple> #include <vector> class BanMan; class CCoinControl; class CFeeRate; struct CNodeStateStats; struct CNodeStats; class Coin; class Config; class HTTPRPCRequestProcessor; struct NodeContext; class proxyType; class RPCServer; class RPCTimerInterface; class UniValue; enum class WalletCreationStatus; struct bilingual_str; namespace interfaces { class Handler; class Wallet; //! Top-level interface for a bitcoin node (bitcoind process). class Node { public: virtual ~Node() {} //! Send init error. virtual void initError(const std::string &message) = 0; //! Set command line arguments. virtual bool parseParameters(int argc, const char *const argv[], std::string &error) = 0; //! Set a command line argument virtual void forceSetArg(const std::string &arg, const std::string &value) = 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 bool readConfigFiles(std::string &error) = 0; //! Choose network parameters. virtual void selectParams(const std::string &network) = 0; //! Get the (assumed) blockchain size. virtual uint64_t getAssumedBlockchainSize() = 0; //! Get the (assumed) chain state size. virtual uint64_t getAssumedChainStateSize() = 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) = 0; //! Start node. virtual bool appInitMain(Config &config, RPCServer &rpcServer, HTTPRPCRequestProcessor &httpRPCRequestProcessor) = 0; //! Stop node. virtual void appShutdown() = 0; //! Start shutdown. virtual void startShutdown() = 0; //! Return whether shutdown was requested. virtual bool shutdownRequested() = 0; //! Setup arguments virtual void setupServerArgs() = 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<std::tuple<CNodeStats, bool, CNodeStateStats>>; 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, int64_t ban_time_offset) = 0; //! Unban node. virtual bool unban(const CSubNet &ip) = 0; //! Disconnect node by address. virtual bool disconnect(const CNetAddr &net_addr) = 0; //! Disconnect node by id. 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; //! Estimate smart fee. virtual CFeeRate estimateSmartFee() = 0; //! Get dust relay fee. virtual CFeeRate getDustRelayFee() = 0; //! Execute rpc command. virtual UniValue executeRpc(Config &config, const std::string &command, const UniValue ¶ms, const std::string &uri) = 0; //! List rpc commands. virtual std::vector<std::string> 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 default wallet directory. virtual std::string getWalletDir() = 0; //! Return available wallets in wallet directory. virtual std::vector<std::string> listWalletDir() = 0; //! Return interfaces for accessing wallets (if any). virtual std::vector<std::unique_ptr<Wallet>> 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<Wallet> loadWallet(const CChainParams ¶ms, const std::string &name, bilingual_str &error, std::vector<bilingual_str> &warnings) const = 0; //! Create a wallet from file virtual WalletCreationStatus createWallet(const CChainParams ¶ms, const SecureString &passphrase, uint64_t wallet_creation_flags, const std::string &name, bilingual_str &error, std::vector<bilingual_str> &warnings, std::unique_ptr<Wallet> &result) = 0; //! Register handler for init messages. using InitMessageFn = std::function<void(const std::string &message)>; virtual std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) = 0; //! Register handler for message box messages. using MessageBoxFn = std::function<bool(const bilingual_str &message, const std::string &caption, unsigned int style)>; virtual std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) = 0; //! Register handler for question messages. using QuestionFn = std::function<bool(const bilingual_str &message, const std::string &non_interactive_message, const std::string &caption, unsigned int style)>; virtual std::unique_ptr<Handler> handleQuestion(QuestionFn fn) = 0; //! Register handler for progress messages. using ShowProgressFn = std::function<void( const std::string &title, int progress, bool resume_possible)>; virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0; //! Register handler for load wallet messages. using LoadWalletFn = std::function<void(std::unique_ptr<Wallet> wallet)>; virtual std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) = 0; //! Register handler for number of connections changed messages. using NotifyNumConnectionsChangedFn = std::function<void(int new_num_connections)>; virtual std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) = 0; //! Register handler for network active messages. using NotifyNetworkActiveChangedFn = std::function<void(bool network_active)>; virtual std::unique_ptr<Handler> handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) = 0; //! Register handler for notify alert messages. using NotifyAlertChangedFn = std::function<void()>; virtual std::unique_ptr<Handler> handleNotifyAlertChanged(NotifyAlertChangedFn fn) = 0; //! Register handler for ban list messages. using BannedListChangedFn = std::function<void()>; virtual std::unique_ptr<Handler> handleBannedListChanged(BannedListChangedFn fn) = 0; //! Register handler for block tip messages. using NotifyBlockTipFn = std::function<void(bool initial_download, int height, int64_t block_time, double verification_progress)>; virtual std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) = 0; //! Register handler for header tip messages. using NotifyHeaderTipFn = std::function<void(bool initial_download, int height, int64_t block_time, double verification_progress)>; virtual std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) = 0; - //! Return pointer to internal chain interface, useful for testing. + //! Get and set internal node context. Useful for testing, but not + //! accessible across processes. virtual NodeContext *context() { return nullptr; } + virtual void setContext(NodeContext *context) {} }; //! Return implementation of Node interface. -std::unique_ptr<Node> MakeNode(); +std::unique_ptr<Node> MakeNode(NodeContext *context = nullptr); } // namespace interfaces #endif // BITCOIN_INTERFACES_NODE_H diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 7a8c39439..e0bce4797 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -1,765 +1,768 @@ // Copyright (c) 2011-2019 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 <qt/bitcoin.h> #include <chainparams.h> #include <config.h> #include <httprpc.h> #include <interfaces/handler.h> #include <interfaces/node.h> +#include <node/context.h> #include <noui.h> #include <qt/bitcoingui.h> #include <qt/clientmodel.h> #include <qt/guiconstants.h> #include <qt/guiutil.h> #include <qt/intro.h> #include <qt/networkstyle.h> #include <qt/optionsmodel.h> #include <qt/platformstyle.h> #include <qt/splashscreen.h> #include <qt/utilitydialog.h> #include <qt/winshutdownmonitor.h> #include <ui_interface.h> #include <uint256.h> #include <util/ref.h> #include <util/system.h> #include <util/threadnames.h> #ifdef ENABLE_WALLET #include <qt/paymentserver.h> #include <qt/walletcontroller.h> #include <qt/walletmodel.h> #endif // ENABLE_WALLET #include <QDebug> #include <QLibraryInfo> #include <QLocale> #include <QMessageBox> #include <QSettings> #include <QStringList> #include <QThread> #include <QTimer> #include <QTranslator> #if defined(QT_STATICPLUGIN) #include <QtPlugin> #if defined(QT_QPA_PLATFORM_XCB) Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); #elif defined(QT_QPA_PLATFORM_WINDOWS) Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); #elif defined(QT_QPA_PLATFORM_COCOA) Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); #endif #endif // Declare meta types used for QMetaObject::invokeMethod Q_DECLARE_METATYPE(bool *) Q_DECLARE_METATYPE(Amount) Q_DECLARE_METATYPE(uint256) // Config is non-copyable so we can only register pointers to it Q_DECLARE_METATYPE(Config *) static QString GetLangTerritory() { QSettings settings; // Get desired locale (e.g. "de_DE") // 1) System default language QString lang_territory = QLocale::system().name(); // 2) Language from QSettings QString lang_territory_qsettings = settings.value("language", "").toString(); if (!lang_territory_qsettings.isEmpty()) { lang_territory = lang_territory_qsettings; } // 3) -lang command line argument lang_territory = QString::fromStdString( gArgs.GetArg("-lang", lang_territory.toStdString())); return lang_territory; } /** Set up translations */ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator) { // Remove old translators QApplication::removeTranslator(&qtTranslatorBase); QApplication::removeTranslator(&qtTranslator); QApplication::removeTranslator(&translatorBase); QApplication::removeTranslator(&translator); // Get desired locale (e.g. "de_DE") // 1) System default language QString lang_territory = GetLangTerritory(); // Convert to "de" only by truncating "_DE" QString lang = lang_territory; lang.truncate(lang_territory.lastIndexOf('_')); // Load language files for configured locale: // - First load the translator for the base language, without territory // - Then load the more specific locale translator // Load e.g. qt_de.qm if (qtTranslatorBase.load( "qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { QApplication::installTranslator(&qtTranslatorBase); } // Load e.g. qt_de_DE.qm if (qtTranslator.load( "qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { QApplication::installTranslator(&qtTranslator); } // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in // bitcoin.qrc) if (translatorBase.load(lang, ":/translations/")) { QApplication::installTranslator(&translatorBase); } // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in // bitcoin.qrc) if (translator.load(lang_territory, ":/translations/")) { QApplication::installTranslator(&translator); } } /* qDebug() message handler --> debug.log */ void DebugMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { Q_UNUSED(context); if (type == QtDebugMsg) { LogPrint(BCLog::QT, "GUI: %s\n", msg.toStdString()); } else { LogPrintf("GUI: %s\n", msg.toStdString()); } } BitcoinABC::BitcoinABC(interfaces::Node &node) : QObject(), m_node(node) {} void BitcoinABC::handleRunawayException(const std::exception *e) { PrintExceptionContinue(e, "Runaway exception"); Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings("gui"))); } void BitcoinABC::initialize(Config *config, RPCServer *rpcServer, HTTPRPCRequestProcessor *httpRPCRequestProcessor) { try { qDebug() << __func__ << ": Running initialization in thread"; util::ThreadRename("qt-init"); bool rv = m_node.appInitMain(*config, *rpcServer, *httpRPCRequestProcessor); Q_EMIT initializeResult(rv); } catch (const std::exception &e) { handleRunawayException(&e); } catch (...) { handleRunawayException(nullptr); } } void BitcoinABC::shutdown() { try { qDebug() << __func__ << ": Running Shutdown in thread"; m_node.appShutdown(); qDebug() << __func__ << ": Shutdown finished"; Q_EMIT shutdownResult(); } catch (const std::exception &e) { handleRunawayException(&e); } catch (...) { handleRunawayException(nullptr); } } static int qt_argc = 1; static const char *qt_argv = "bitcoin-qt"; BitcoinApplication::BitcoinApplication(interfaces::Node &node) : QApplication(qt_argc, const_cast<char **>(&qt_argv)), coreThread(nullptr), m_node(node), optionsModel(nullptr), clientModel(nullptr), window(nullptr), pollShutdownTimer(nullptr), returnValue(0), platformStyle(nullptr) { setQuitOnLastWindowClosed(false); } void BitcoinApplication::setupPlatformStyle() { // UI per-platform customization // This must be done inside the BitcoinApplication constructor, or after it, // because PlatformStyle::instantiate requires a QApplication. std::string platformName; platformName = gArgs.GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM); platformStyle = PlatformStyle::instantiate(QString::fromStdString(platformName)); // Fall back to "other" if specified name not found. if (!platformStyle) { platformStyle = PlatformStyle::instantiate("other"); } assert(platformStyle); } BitcoinApplication::~BitcoinApplication() { if (coreThread) { qDebug() << __func__ << ": Stopping thread"; coreThread->quit(); coreThread->wait(); qDebug() << __func__ << ": Stopped thread"; } delete window; window = nullptr; delete optionsModel; optionsModel = nullptr; delete platformStyle; platformStyle = nullptr; } #ifdef ENABLE_WALLET void BitcoinApplication::createPaymentServer() { paymentServer = new PaymentServer(this); } #endif void BitcoinApplication::createOptionsModel(bool resetSettings) { optionsModel = new OptionsModel(m_node, nullptr, resetSettings); } void BitcoinApplication::createWindow(const Config *config, const NetworkStyle *networkStyle) { window = new BitcoinGUI(m_node, config, platformStyle, networkStyle, nullptr); pollShutdownTimer = new QTimer(window); connect(pollShutdownTimer, &QTimer::timeout, window, &BitcoinGUI::detectShutdown); } void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle) { SplashScreen *splash = new SplashScreen(m_node, networkStyle); // We don't hold a direct pointer to the splash screen after creation, but // the splash screen will take care of deleting itself when finish() // happens. splash->show(); connect(this, &BitcoinApplication::splashFinished, splash, &SplashScreen::finish); connect(this, &BitcoinApplication::requestedShutdown, splash, &QWidget::close); } bool BitcoinApplication::baseInitialize(Config &config) { return m_node.baseInitialize(config); } void BitcoinApplication::startThread() { if (coreThread) { return; } coreThread = new QThread(this); BitcoinABC *executor = new BitcoinABC(m_node); executor->moveToThread(coreThread); /* communication to and from thread */ connect(executor, &BitcoinABC::initializeResult, this, &BitcoinApplication::initializeResult); connect(executor, &BitcoinABC::shutdownResult, this, &BitcoinApplication::shutdownResult); connect(executor, &BitcoinABC::runawayException, this, &BitcoinApplication::handleRunawayException); // Note on how Qt works: it tries to directly invoke methods if the signal // is emitted on the same thread that the target object 'lives' on. // But if the target object 'lives' on another thread (executor here does) // the SLOT will be invoked asynchronously at a later time in the thread // of the target object. So.. we pass a pointer around. If you pass // a reference around (even if it's non-const) you'll get Qt generating // code to copy-construct the parameter in question (Q_DECLARE_METATYPE // and qRegisterMetaType generate this code). For the Config class, // which is noncopyable, we can't do this. So.. we have to pass // pointers to Config around. Make sure Config &/Config * isn't a // temporary (eg it lives somewhere aside from the stack) or this will // crash because initialize() gets executed in another thread at some // unspecified time (after) requestedInitialize() is emitted! connect(this, &BitcoinApplication::requestedInitialize, executor, &BitcoinABC::initialize); connect(this, &BitcoinApplication::requestedShutdown, executor, &BitcoinABC::shutdown); /* make sure executor object is deleted in its own thread */ connect(coreThread, &QThread::finished, executor, &QObject::deleteLater); coreThread->start(); } void BitcoinApplication::parameterSetup() { // Default printtoconsole to false for the GUI. GUI programs should not // print to the console unnecessarily. gArgs.SoftSetBoolArg("-printtoconsole", false); m_node.initLogging(); m_node.initParameterInteraction(); } void BitcoinApplication::SetPrune(bool prune, bool force) { optionsModel->SetPrune(prune, force); } void BitcoinApplication::requestInitialize( Config &config, RPCServer &rpcServer, HTTPRPCRequestProcessor &httpRPCRequestProcessor) { qDebug() << __func__ << ": Requesting initialize"; startThread(); // IMPORTANT: config must NOT be a reference to a temporary because below // signal may be connected to a slot that will be executed as a queued // connection in another thread! Q_EMIT requestedInitialize(&config, &rpcServer, &httpRPCRequestProcessor); } void BitcoinApplication::requestShutdown(Config &config) { // Show a simple window indicating shutdown status. Do this first as some of // the steps may take some time below, for example the RPC console may still // be executing a command. shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window)); qDebug() << __func__ << ": Requesting shutdown"; startThread(); window->hide(); // Must disconnect node signals otherwise current thread can deadlock since // no event loop is running. window->unsubscribeFromCoreSignals(); // Request node shutdown, which can interrupt long operations, like // rescanning a wallet. m_node.startShutdown(); // Unsetting the client model can cause the current thread to wait for node // to complete an operation, like wait for a RPC execution to complate. window->setClientModel(nullptr); pollShutdownTimer->stop(); delete clientModel; clientModel = nullptr; // Request shutdown from core thread Q_EMIT requestedShutdown(); } void BitcoinApplication::initializeResult(bool success) { qDebug() << __func__ << ": Initialization result: " << success; returnValue = success ? EXIT_SUCCESS : EXIT_FAILURE; if (!success) { // Make sure splash screen doesn't stick around during shutdown. Q_EMIT splashFinished(); // Exit first main loop invocation. quit(); return; } // Log this only after AppInitMain finishes, as then logging setup is // guaranteed complete. qWarning() << "Platform customization:" << platformStyle->getName(); clientModel = new ClientModel(m_node, optionsModel); window->setClientModel(clientModel); #ifdef ENABLE_WALLET if (WalletModel::isWalletEnabled()) { m_wallet_controller = new WalletController(m_node, platformStyle, optionsModel, this); window->setWalletController(m_wallet_controller); if (paymentServer) { paymentServer->setOptionsModel(optionsModel); #ifdef ENABLE_BIP70 PaymentServer::LoadRootCAs(); connect(m_wallet_controller, &WalletController::coinsSent, paymentServer, &PaymentServer::fetchPaymentACK); #endif } } #endif // ENABLE_WALLET // If -min option passed, start window minimized(iconified) // or minimized to tray if (!gArgs.GetBoolArg("-min", false)) { window->show(); } else if (clientModel->getOptionsModel()->getMinimizeToTray() && window->hasTrayIcon()) { // do nothing as the window is managed by the tray icon } else { window->showMinimized(); } Q_EMIT splashFinished(); Q_EMIT windowShown(window); #ifdef ENABLE_WALLET // Now that initialization/startup is done, process any command-line // bitcoincash: URIs or payment requests: if (paymentServer) { connect(paymentServer, &PaymentServer::receivedPaymentRequest, window, &BitcoinGUI::handlePaymentRequest); connect(window, &BitcoinGUI::receivedURI, paymentServer, &PaymentServer::handleURIOrFile); connect(paymentServer, &PaymentServer::message, [this](const QString &title, const QString &message, unsigned int style) { window->message(title, message, style); }); QTimer::singleShot(100, paymentServer, &PaymentServer::uiReady); } #endif pollShutdownTimer->start(200); } void BitcoinApplication::shutdownResult() { // Exit second main loop invocation after shutdown finished. quit(); } void BitcoinApplication::handleRunawayException(const QString &message) { QMessageBox::critical( nullptr, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. Bitcoin can no longer continue " "safely and will quit.") + QString("\n\n") + message); ::exit(EXIT_FAILURE); } WId BitcoinApplication::getMainWinId() const { if (!window) { return 0; } return window->winId(); } static void SetupUIArgs() { #if defined(ENABLE_WALLET) && defined(ENABLE_BIP70) gArgs.AddArg("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %d)", DEFAULT_SELFSIGNED_ROOTCERTS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::GUI); #endif gArgs.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %d)", DEFAULT_CHOOSE_DATADIR), ArgsManager::ALLOW_ANY, OptionsCategory::GUI); gArgs.AddArg("-lang=<lang>", "Set language, for example \"de_DE\" (default: system locale)", ArgsManager::ALLOW_ANY, OptionsCategory::GUI); gArgs.AddArg("-min", "Start minimized", ArgsManager::ALLOW_ANY, OptionsCategory::GUI); gArgs.AddArg( "-rootcertificates=<file>", "Set SSL root certificates for payment request (default: -system-)", ArgsManager::ALLOW_ANY, OptionsCategory::GUI); gArgs.AddArg("-splash", strprintf("Show splash screen on startup (default: %d)", DEFAULT_SPLASHSCREEN), ArgsManager::ALLOW_ANY, OptionsCategory::GUI); gArgs.AddArg("-resetguisettings", "Reset all settings changed in the GUI", ArgsManager::ALLOW_ANY, OptionsCategory::GUI); gArgs.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of " "windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::GUI); } static void MigrateSettings() { assert(!QApplication::applicationName().isEmpty()); static const QString legacyAppName("Bitcoin-Qt"), #ifdef Q_OS_DARWIN // Macs and/or iOS et al use a domain-style name for Settings // files. All other platforms use a simple orgname. This // difference is documented in the QSettings class documentation. legacyOrg("bitcoin.org"); #else legacyOrg("Bitcoin"); #endif QSettings // below picks up settings file location based on orgname,appname legacy(legacyOrg, legacyAppName), // default c'tor below picks up settings file location based on // QApplication::applicationName(), et al -- which was already set // in main() abc; #ifdef Q_OS_DARWIN // Disable bogus OSX keys from MacOS system-wide prefs that may cloud our // judgement ;) (this behavior is also documented in QSettings docs) legacy.setFallbacksEnabled(false); abc.setFallbacksEnabled(false); #endif const QStringList legacyKeys(legacy.allKeys()); // We only migrate settings if we have Core settings but no Bitcoin-ABC // settings if (!legacyKeys.isEmpty() && abc.allKeys().isEmpty()) { for (const QString &key : legacyKeys) { // now, copy settings over abc.setValue(key, legacy.value(key)); } } } int GuiMain(int argc, char *argv[]) { #ifdef WIN32 util::WinCmdLineArgs winArgs; std::tie(argc, argv) = winArgs.get(); #endif SetupEnvironment(); util::ThreadSetInternalName("main"); - std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(); + NodeContext node_context; + std::unique_ptr<interfaces::Node> node = + interfaces::MakeNode(&node_context); // Subscribe to global signals from core std::unique_ptr<interfaces::Handler> handler_message_box = node->handleMessageBox(noui_ThreadSafeMessageBox); std::unique_ptr<interfaces::Handler> handler_question = node->handleQuestion(noui_ThreadSafeQuestion); std::unique_ptr<interfaces::Handler> handler_init_message = node->handleInitMessage(noui_InitMessage); // Do not refer to data directory yet, this can be overridden by // Intro::pickDataDirectory /// 1. Basic Qt initialization (not dependent on parameters or /// configuration) Q_INIT_RESOURCE(bitcoin); Q_INIT_RESOURCE(bitcoin_locale); // Generate high-dpi pixmaps QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #ifdef Q_OS_MAC QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); #endif BitcoinApplication app(*node); // Register meta types used for QMetaObject::invokeMethod qRegisterMetaType<bool *>(); #ifdef ENABLE_WALLET qRegisterMetaType<WalletModel *>(); #endif // Need to pass name here as Amount is a typedef (see // http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType) // IMPORTANT if it is no longer a typedef use the normal variant above qRegisterMetaType<Amount>("Amount"); qRegisterMetaType<std::function<void()>>("std::function<void()>"); qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon"); // Need to register any types Qt doesn't know about if you intend // to use them with the signal/slot mechanism Qt provides. Even pointers. // Note that class Config is noncopyable and so we can't register a // non-pointer version of it with Qt, because Qt expects to be able to // copy-construct non-pointers to objects for invoking slots // behind-the-scenes in the 'Queued' connection case. qRegisterMetaType<Config *>(); /// 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(); SetupUIArgs(); std::string error; if (!node->parseParameters(argc, argv, error)) { node->initError( strprintf("Error parsing command line arguments: %s\n", error)); // Create a message box, because the gui has neither been created nor // has subscribed to core signals QMessageBox::critical( nullptr, PACKAGE_NAME, // message can not be translated because translations have not been // initialized QString::fromStdString("Error parsing command line arguments: %1.") .arg(QString::fromStdString(error))); return EXIT_FAILURE; } // Now that the QApplication is setup and we have parsed our parameters, we // can set the platform style app.setupPlatformStyle(); /// 3. Application identification // must be set before OptionsModel is initialized or translations are // loaded, as it is used to locate QSettings. // Note: If you move these calls somewhere else, be sure to bring // MigrateSettings() below along for the ride. QApplication::setOrganizationName(QAPP_ORG_NAME); QApplication::setOrganizationDomain(QAPP_ORG_DOMAIN); QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT); // Migrate settings from core's/our old GUI settings to Bitcoin ABC // only if core's exist but Bitcoin ABC's doesn't. // NOTE -- this function needs to be called *after* the above 3 lines // that set the app orgname and app name! If you move the above 3 lines // to elsewhere, take this call with you! MigrateSettings(); /// 4. Initialization of translations, so that intro dialog is in user's /// language. Now that QSettings are accessible, initialize translations. QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator; initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); // Show help message immediately after parsing command-line options (for // "-lang") and setting locale, but before showing splash screen. if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { HelpMessageDialog help(*node, nullptr, gArgs.IsArgSet("-version")); help.showOrPrint(); return EXIT_SUCCESS; } /// 5. Now that settings and translations are available, ask user for data /// directory. User language is set up: pick a data directory. bool did_show_intro = false; // Intro dialog prune check box bool prune = false; // Gracefully exit if the user cancels if (!Intro::showIfNeeded(*node, did_show_intro, prune)) { return EXIT_SUCCESS; } /// 6. Determine availability of data directory and parse /// bitcoin.conf /// - Do not call GetDataDir(true) before this step finishes. if (!CheckDataDirOption()) { node->initError( strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""))); QMessageBox::critical( nullptr, PACKAGE_NAME, QObject::tr( "Error: Specified data directory \"%1\" does not exist.") .arg(QString::fromStdString(gArgs.GetArg("-datadir", "")))); return EXIT_FAILURE; } if (!node->readConfigFiles(error)) { node->initError( strprintf("Error reading configuration file: %s\n", error)); QMessageBox::critical( nullptr, PACKAGE_NAME, QObject::tr("Error: Cannot parse configuration file: %1.") .arg(QString::fromStdString(error))); return EXIT_FAILURE; } /// 7. Determine network (and switch to network specific options) // - Do not call Params() before this step. // - Do this after parsing the configuration file, as the network can be // switched there. // - QSettings() will use the new application name after this, resulting in // network-specific settings. // - Needs to be done before createOptionsModel. // Check for -chain, -testnet or -regtest parameter (Params() calls are only // valid after this clause) try { node->selectParams(gArgs.GetChainName()); } catch (std::exception &e) { node->initError(strprintf("%s\n", e.what())); QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: %1").arg(e.what())); return EXIT_FAILURE; } #ifdef ENABLE_WALLET // Parse URIs on command line -- this can affect Params() PaymentServer::ipcParseCommandLine(*node, argc, argv); #endif QScopedPointer<const NetworkStyle> networkStyle( NetworkStyle::instantiate(Params().NetworkIDString())); assert(!networkStyle.isNull()); // Allow for separate UI settings for testnets QApplication::setApplicationName(networkStyle->getAppName()); // Re-initialize translations after changing application name (language in // network-specific settings can be different) initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); #ifdef ENABLE_WALLET /// 8. URI IPC sending // - Do this early as we don't want to bother initializing if we are just // calling IPC // - Do this *after* setting up the data directory, as the data directory // hash is used in the name // of the server. // - Do this after creating app and setting up translations, so errors are // translated properly. if (PaymentServer::ipcSendCommandLine()) { exit(EXIT_SUCCESS); } // Start up the payment server early, too, so impatient users that click on // bitcoincash: links repeatedly have their payment requests routed to this // process: if (WalletModel::isWalletEnabled()) { app.createPaymentServer(); } #endif // ENABLE_WALLET /// 9. Main GUI initialization // Install global event filter that makes sure that long tooltips can be // word-wrapped. app.installEventFilter( new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); #if defined(Q_OS_WIN) // Install global event filter for processing Windows session related // Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION) qApp->installNativeEventFilter(new WinShutdownMonitor()); #endif // Install qDebug() message handler to route to debug.log qInstallMessageHandler(DebugMessageHandler); // Allow parameter interaction before we create the options model app.parameterSetup(); // Load GUI settings from QSettings app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false)); if (did_show_intro) { // Store intro dialog settings other than datadir (network specific) app.SetPrune(prune, true); } // Get global config Config &config = const_cast<Config &>(GetConfig()); if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false)) { app.createSplashScreen(networkStyle.data()); } RPCServer rpcServer; util::Ref context{node}; HTTPRPCRequestProcessor httpRPCRequestProcessor(config, rpcServer, context); try { app.createWindow(&config, networkStyle.data()); // 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 (!app.baseInitialize(config)) { // A dialog with detailed error will have been shown by InitError() return EXIT_FAILURE; } app.requestInitialize(config, rpcServer, httpRPCRequestProcessor); #if defined(Q_OS_WIN) WinShutdownMonitor::registerShutdownBlockReason( QObject::tr("%1 didn't yet exit safely...").arg(PACKAGE_NAME), (HWND)app.getMainWinId()); #endif app.exec(); app.requestShutdown(config); app.exec(); return app.getReturnValue(); } catch (const std::exception &e) { PrintExceptionContinue(&e, "Runaway exception"); app.handleRunawayException( QString::fromStdString(node->getWarnings("gui"))); } catch (...) { PrintExceptionContinue(nullptr, "Runaway exception"); app.handleRunawayException( QString::fromStdString(node->getWarnings("gui"))); } return EXIT_FAILURE; } diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp index d53236286..5dff53320 100644 --- a/src/qt/test/addressbooktests.cpp +++ b/src/qt/test/addressbooktests.cpp @@ -1,166 +1,168 @@ #include <qt/test/addressbooktests.h> #include <test/util/setup_common.h> #include <interfaces/chain.h> #include <interfaces/node.h> #include <qt/editaddressdialog.h> #include <qt/optionsmodel.h> #include <qt/platformstyle.h> #include <qt/qvalidatedlineedit.h> #include <qt/test/util.h> #include <qt/walletmodel.h> #include <cashaddrenc.h> #include <key.h> #include <key_io.h> #include <wallet/wallet.h> +#include <walletinitinterface.h> #include <QApplication> #include <QMessageBox> #include <QTimer> #include <memory> namespace { /** * Fill the edit address dialog box with data, submit it, and ensure that * the resulting message meets expectations. */ void EditAddressAndSubmit(EditAddressDialog *dialog, const QString &label, const QString &address, QString expected_msg) { QString warning_text; dialog->findChild<QLineEdit *>("labelEdit")->setText(label); dialog->findChild<QValidatedLineEdit *>("addressEdit")->setText(address); ConfirmMessage(&warning_text, 5); dialog->accept(); QCOMPARE(warning_text, expected_msg); } /** * Test adding various send addresses to the address book. * * There are three cases tested: * * - new_address: a new address which should add as a send address * successfully. * - existing_s_address: an existing sending address which won't add * successfully. * - existing_r_address: an existing receiving address which won't add * successfully. * * In each case, verify the resulting state of the address book and optionally * the warning message presented to the user. */ void TestAddAddressesToSendBook(interfaces::Node &node) { TestChain100Setup test; + node.setContext(&test.m_node); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>( Params(), node.context()->chain.get(), WalletLocation(), WalletDatabase::CreateMock()); wallet->SetupLegacyScriptPubKeyMan(); bool firstRun; wallet->LoadWallet(firstRun); auto build_address = [&wallet]() { CKey key; key.MakeNewKey(true); CTxDestination dest(GetDestinationForKey( key.GetPubKey(), wallet->m_default_address_type)); return std::make_pair( dest, QString::fromStdString(EncodeCashAddr(dest, Params()))); }; CTxDestination r_key_dest, s_key_dest; // Add a preexisting "receive" entry in the address book. QString preexisting_r_address; QString r_label("already here (r)"); // Add a preexisting "send" entry in the address book. QString preexisting_s_address; QString s_label("already here (s)"); // Define a new address (which should add to the address book successfully). QString new_address; std::tie(r_key_dest, preexisting_r_address) = build_address(); std::tie(s_key_dest, preexisting_s_address) = build_address(); std::tie(std::ignore, new_address) = build_address(); { LOCK(wallet->cs_wallet); wallet->SetAddressBook(r_key_dest, r_label.toStdString(), "receive"); wallet->SetAddressBook(s_key_dest, s_label.toStdString(), "send"); } auto check_addbook_size = [&wallet](int expected_size) { LOCK(wallet->cs_wallet); QCOMPARE(static_cast<int>(wallet->m_address_book.size()), expected_size); }; // We should start with the two addresses we added earlier and nothing else. check_addbook_size(2); // Initialize relevant QT models. std::unique_ptr<const PlatformStyle> platformStyle( PlatformStyle::instantiate("other")); OptionsModel optionsModel(node); AddWallet(wallet); WalletModel walletModel(interfaces::MakeWallet(wallet), node, platformStyle.get(), &optionsModel); RemoveWallet(wallet); EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress); editAddressDialog.setModel(walletModel.getAddressTableModel()); EditAddressAndSubmit( &editAddressDialog, QString("uhoh"), preexisting_r_address, QString( "Address \"%1\" already exists as a receiving address with label " "\"%2\" and so cannot be added as a sending address.") .arg(preexisting_r_address) .arg(r_label)); check_addbook_size(2); EditAddressAndSubmit( &editAddressDialog, QString("uhoh, different"), preexisting_s_address, QString( "The entered address \"%1\" is already in the address book with " "label \"%2\".") .arg(preexisting_s_address) .arg(s_label)); check_addbook_size(2); // Submit a new address which should add successfully - we expect the // warning message to be blank. EditAddressAndSubmit(&editAddressDialog, QString("new"), new_address, QString("")); check_addbook_size(3); } } // namespace void AddressBookTests::addressBookTests() { #ifdef Q_OS_MAC if (QApplication::platformName() == "minimal") { // Disable for mac on "minimal" platform to avoid crashes inside the Qt // framework when it tries to look up unimplemented cocoa functions, // and fails to handle returned nulls // (https://bugreports.qt.io/browse/QTBUG-49686). QWARN("Skipping AddressBookTests on mac build with 'minimal' platform " "set due to Qt bugs. To run AppTests, invoke with " "'QT_QPA_PLATFORM=cocoa test_bitcoin-qt' on mac, or else use a " "linux or windows build."); return; } #endif TestAddAddressesToSendBook(m_node); } diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index d39442436..e1c1792ee 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -1,114 +1,116 @@ // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include <config/bitcoin-config.h> #endif #include <compat/setenv.h> #include <interfaces/node.h> #include <key.h> #include <qt/bitcoin.h> #include <qt/test/apptests.h> #include <qt/test/bitcoinaddressvalidatortests.h> #include <qt/test/compattests.h> #include <qt/test/guiutiltests.h> #include <qt/test/rpcnestedtests.h> #include <qt/test/uritests.h> #ifdef ENABLE_WALLET #include <qt/test/addressbooktests.h> #ifdef ENABLE_BIP70 #include <qt/test/paymentservertests.h> #endif // ENABLE_BIP70 #include <qt/test/wallettests.h> #endif // ENABLE_WALLET #include <test/util/setup_common.h> #include <QApplication> #include <QObject> #include <QTest> #if defined(QT_STATICPLUGIN) #include <QtPlugin> #if defined(QT_QPA_PLATFORM_MINIMAL) Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin); #endif #if defined(QT_QPA_PLATFORM_XCB) Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); #elif defined(QT_QPA_PLATFORM_WINDOWS) Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); #elif defined(QT_QPA_PLATFORM_COCOA) Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); #endif #endif // This is all you need to run all the tests int main(int argc, char *argv[]) { // Initialize persistent globals with the testing setup state for sanity. // E.g. -datadir in gArgs is set to a temp directory dummy value (instead // of defaulting to the default datadir), or globalChainParams is set to // regtest params. // // All tests must use their own testing setup (if needed). { BasicTestingSetup dummy{CBaseChainParams::REGTEST}; } - std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(); + NodeContext node_context; + std::unique_ptr<interfaces::Node> node = + interfaces::MakeNode(&node_context); bool fInvalid = false; // Prefer the "minimal" platform for the test instead of the normal default // platform ("xcb", "windows", or "cocoa") so tests can't unintentionally // interfere with any background GUIs and don't require extra resources. setenv("QT_QPA_PLATFORM", "minimal", /* overwrite */ 0); // Don't remove this, it's needed to access // QApplication:: and QCoreApplication:: in the tests BitcoinApplication app(*node); app.setApplicationName("BitcoinABC-Qt-test"); AppTests app_tests(app); if (QTest::qExec(&app_tests) != 0) { fInvalid = true; } URITests test1; if (QTest::qExec(&test1) != 0) { fInvalid = true; } #if defined(ENABLE_WALLET) && defined(ENABLE_BIP70) PaymentServerTests test2; if (QTest::qExec(&test2) != 0) { fInvalid = true; } #endif RPCNestedTests test3(*node); if (QTest::qExec(&test3) != 0) { fInvalid = true; } CompatTests test4; if (QTest::qExec(&test4) != 0) { fInvalid = true; } GUIUtilTests test5; if (QTest::qExec(&test5) != 0) { fInvalid = true; } BitcoinAddressValidatorTests test6; if (QTest::qExec(&test6) != 0) { fInvalid = true; } #ifdef ENABLE_WALLET WalletTests test7(*node); if (QTest::qExec(&test7) != 0) { fInvalid = true; } AddressBookTests test8(*node); if (QTest::qExec(&test8) != 0) { fInvalid = true; } #endif return fInvalid; } diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index dbf3624e7..6342ffa28 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -1,263 +1,262 @@ #include <qt/test/util.h> #include <qt/test/wallettests.h> #include <cashaddrenc.h> #include <chain.h> #include <chainparams.h> #include <interfaces/chain.h> #include <interfaces/node.h> #include <key_io.h> #include <qt/bitcoinamountfield.h> #include <qt/optionsmodel.h> #include <qt/overviewpage.h> #include <qt/platformstyle.h> #include <qt/qvalidatedlineedit.h> #include <qt/receivecoinsdialog.h> #include <qt/receiverequestdialog.h> #include <qt/recentrequeststablemodel.h> #include <qt/sendcoinsdialog.h> #include <qt/sendcoinsentry.h> #include <qt/transactiontablemodel.h> #include <qt/walletmodel.h> #include <validation.h> #include <wallet/wallet.h> #include <test/util/setup_common.h> #include <QAbstractButton> #include <QApplication> #include <QDialogButtonBox> #include <QListView> #include <QPushButton> #include <QTextEdit> #include <QTimer> #include <QVBoxLayout> #include <memory> namespace { //! Press "Yes" or "Cancel" buttons in modal send confirmation dialog. void ConfirmSend(QString *text = nullptr, bool cancel = false) { QTimer::singleShot(0, Qt::PreciseTimer, [text, cancel]() { for (QWidget *widget : QApplication::topLevelWidgets()) { if (widget->inherits("SendConfirmationDialog")) { SendConfirmationDialog *dialog = qobject_cast<SendConfirmationDialog *>(widget); if (text) { *text = dialog->text(); } QAbstractButton *button = dialog->button( cancel ? QMessageBox::Cancel : QMessageBox::Yes); button->setEnabled(true); button->click(); } } }); } //! Send coins to address and return txid. TxId SendCoins(CWallet &wallet, SendCoinsDialog &sendCoinsDialog, const CTxDestination &address, Amount amount) { QVBoxLayout *entries = sendCoinsDialog.findChild<QVBoxLayout *>("entries"); SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(entries->itemAt(0)->widget()); entry->findChild<QValidatedLineEdit *>("payTo")->setText( QString::fromStdString(EncodeCashAddr(address, Params()))); entry->findChild<BitcoinAmountField *>("payAmount")->setValue(amount); TxId txid; boost::signals2::scoped_connection c = wallet.NotifyTransactionChanged.connect( [&txid](CWallet *, const TxId &hash, ChangeType status) { if (status == CT_NEW) { txid = hash; } }); ConfirmSend(); bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked"); assert(invoked); return txid; } //! Find index of txid in transaction list. QModelIndex FindTx(const QAbstractItemModel &model, const uint256 &txid) { QString hash = QString::fromStdString(txid.ToString()); int rows = model.rowCount({}); for (int row = 0; row < rows; ++row) { QModelIndex index = model.index(row, 0, {}); if (model.data(index, TransactionTableModel::TxHashRole) == hash) { return index; } } return {}; } //! Simple qt wallet tests. // // Test widgets can be debugged interactively calling show() on them and // manually running the event loop, e.g.: // // sendCoinsDialog.show(); // QEventLoop().exec(); // // This also requires overriding the default minimal Qt platform: // // QT_QPA_PLATFORM=xcb src/qt/test/test_bitcoin-qt # Linux // QT_QPA_PLATFORM=windows src/qt/test/test_bitcoin-qt # Windows // QT_QPA_PLATFORM=cocoa src/qt/test/test_bitcoin-qt # macOS void TestGUI(interfaces::Node &node) { // Set up wallet and chain with 105 blocks (5 mature blocks for spending). TestChain100Setup test; for (int i = 0; i < 5; ++i) { test.CreateAndProcessBlock( {}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); } - node.context()->connman = std::move(test.m_node.connman); - node.context()->mempool = std::move(test.m_node.mempool); + node.setContext(&test.m_node); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>( Params(), node.context()->chain.get(), WalletLocation(), WalletDatabase::CreateMock()); bool firstRun; wallet->LoadWallet(firstRun); { auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan(); LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore); wallet->SetAddressBook( GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive"); spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); wallet->SetLastBlockProcessed(105, ::ChainActive().Tip()->GetBlockHash()); } { WalletRescanReserver reserver(*wallet); reserver.reserve(); CWallet::ScanResult result = wallet->ScanForWalletTransactions( Params().GetConsensus().hashGenesisBlock, 0 /* block height */, {} /* max height */, reserver, true /* fUpdate */); QCOMPARE(result.status, CWallet::ScanResult::SUCCESS); QCOMPARE(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash()); QVERIFY(result.last_failed_block.IsNull()); } wallet->SetBroadcastTransactions(true); // Create widgets for sending coins and listing transactions. std::unique_ptr<const PlatformStyle> platformStyle( PlatformStyle::instantiate("other")); OptionsModel optionsModel(node); AddWallet(wallet); WalletModel walletModel(interfaces::MakeWallet(wallet), node, platformStyle.get(), &optionsModel); RemoveWallet(wallet); // Send two transactions, and verify they are added to transaction list. SendCoinsDialog sendCoinsDialog(platformStyle.get(), &walletModel); TransactionTableModel *transactionTableModel = walletModel.getTransactionTableModel(); QCOMPARE(transactionTableModel->rowCount({}), 105); TxId txid1 = SendCoins(*wallet.get(), sendCoinsDialog, CTxDestination(PKHash()), 5 * COIN); TxId txid2 = SendCoins(*wallet.get(), sendCoinsDialog, CTxDestination(PKHash()), 10 * COIN); QCOMPARE(transactionTableModel->rowCount({}), 107); QVERIFY(FindTx(*transactionTableModel, txid1).isValid()); QVERIFY(FindTx(*transactionTableModel, txid2).isValid()); // Check current balance on OverviewPage OverviewPage overviewPage(platformStyle.get()); overviewPage.setWalletModel(&walletModel); QLabel *balanceLabel = overviewPage.findChild<QLabel *>("labelBalance"); QString balanceText = balanceLabel->text(); int unit = walletModel.getOptionsModel()->getDisplayUnit(); Amount balance = walletModel.wallet().getBalance(); QString balanceComparison = BitcoinUnits::formatWithUnit( unit, balance, false, BitcoinUnits::separatorAlways); QCOMPARE(balanceText, balanceComparison); // Check Request Payment button ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get()); receiveCoinsDialog.setModel(&walletModel); RecentRequestsTableModel *requestTableModel = walletModel.getRecentRequestsTableModel(); // Label input QLineEdit *labelInput = receiveCoinsDialog.findChild<QLineEdit *>("reqLabel"); labelInput->setText("TEST_LABEL_1"); // Amount input BitcoinAmountField *amountInput = receiveCoinsDialog.findChild<BitcoinAmountField *>("reqAmount"); amountInput->setValue(1 * SATOSHI); // Message input QLineEdit *messageInput = receiveCoinsDialog.findChild<QLineEdit *>("reqMessage"); messageInput->setText("TEST_MESSAGE_1"); int initialRowCount = requestTableModel->rowCount({}); QPushButton *requestPaymentButton = receiveCoinsDialog.findChild<QPushButton *>("receiveButton"); requestPaymentButton->click(); for (QWidget *widget : QApplication::topLevelWidgets()) { if (widget->inherits("ReceiveRequestDialog")) { ReceiveRequestDialog *receiveRequestDialog = qobject_cast<ReceiveRequestDialog *>(widget); QTextEdit *rlist = receiveRequestDialog->QObject::findChild<QTextEdit *>("outUri"); QString paymentText = rlist->toPlainText(); QStringList paymentTextList = paymentText.split('\n'); QCOMPARE(paymentTextList.at(0), QString("Payment information")); QVERIFY(paymentTextList.at(1).indexOf(QString("URI: bchreg:")) != -1); QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1); QCOMPARE(paymentTextList.at(3), QString("Amount: 0.00000001 ") + QString::fromStdString(CURRENCY_UNIT)); QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1")); QCOMPARE(paymentTextList.at(5), QString("Message: TEST_MESSAGE_1")); } } // Clear button QPushButton *clearButton = receiveCoinsDialog.findChild<QPushButton *>("clearButton"); clearButton->click(); QCOMPARE(labelInput->text(), QString("")); QCOMPARE(amountInput->value(), Amount::zero()); QCOMPARE(messageInput->text(), QString("")); // Check addition to history int currentRowCount = requestTableModel->rowCount({}); QCOMPARE(currentRowCount, initialRowCount + 1); // Check Remove button QTableView *table = receiveCoinsDialog.findChild<QTableView *>("recentRequestsView"); table->selectRow(currentRowCount - 1); QPushButton *removeRequestButton = receiveCoinsDialog.findChild<QPushButton *>("removeRequestButton"); removeRequestButton->click(); QCOMPARE(requestTableModel->rowCount({}), currentRowCount - 1); } } // namespace void WalletTests::walletTests() { #ifdef Q_OS_MAC if (QApplication::platformName() == "minimal") { // Disable for mac on "minimal" platform to avoid crashes inside the Qt // framework when it tries to look up unimplemented cocoa functions, // and fails to handle returned nulls // (https://bugreports.qt.io/browse/QTBUG-49686). QWARN( "Skipping WalletTests on mac build with 'minimal' platform set due " "to Qt bugs. To run AppTests, invoke with 'QT_QPA_PLATFORM=cocoa " "test_bitcoin-qt' on mac, or else use a linux or windows build."); return; } #endif TestGUI(m_node); } diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 5edb85eb6..0ee456687 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -1,366 +1,371 @@ // Copyright (c) 2011-2019 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 <test/util/setup_common.h> #include <banman.h> #include <chainparams.h> #include <config.h> #include <consensus/consensus.h> #include <consensus/validation.h> #include <crypto/sha256.h> #include <init.h> +#include <interfaces/chain.h> #include <logging.h> #include <miner.h> #include <net.h> #include <noui.h> #include <pow/pow.h> #include <rpc/blockchain.h> #include <rpc/register.h> #include <rpc/server.h> #include <script/script_error.h> #include <script/scriptcache.h> #include <script/sigcache.h> #include <streams.h> #include <txdb.h> #include <txmempool.h> #include <util/strencodings.h> #include <util/time.h> #include <util/translation.h> #include <util/validation.h> #include <validation.h> #include <validationinterface.h> +#include <walletinitinterface.h> #include <functional> #include <memory> const std::function<std::string(const char *)> G_TRANSLATION_FUN = nullptr; FastRandomContext g_insecure_rand_ctx; /** * Random context to get unique temp data dirs. Separate from * g_insecure_rand_ctx, which can be seeded from a const env var */ static FastRandomContext g_insecure_rand_ctx_temp_path; /** * Return the unsigned from the environment var if available, * otherwise 0 */ static uint256 GetUintFromEnv(const std::string &env_name) { const char *num = std::getenv(env_name.c_str()); if (!num) { return {}; } return uint256S(num); } void Seed(FastRandomContext &ctx) { // Should be enough to get the seed once for the process static uint256 seed{}; static const std::string RANDOM_CTX_SEED{"RANDOM_CTX_SEED"}; if (seed.IsNull()) { seed = GetUintFromEnv(RANDOM_CTX_SEED); } if (seed.IsNull()) { seed = GetRandHash(); } LogPrintf("%s: Setting random seed for current tests to %s=%s\n", __func__, RANDOM_CTX_SEED, seed.GetHex()); ctx = FastRandomContext(seed); } std::ostream &operator<<(std::ostream &os, const uint256 &num) { os << num.ToString(); return os; } std::ostream &operator<<(std::ostream &os, const ScriptError &err) { os << ScriptErrorString(err); return os; } BasicTestingSetup::BasicTestingSetup(const std::string &chainName) : m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / std::to_string(g_insecure_rand_ctx_temp_path.rand32())} { SetMockTime(0); fs::create_directories(m_path_root); gArgs.ForceSetArg("-datadir", m_path_root.string()); ClearDatadirCache(); SelectParams(chainName); SeedInsecureRand(); gArgs.ForceSetArg("-printtoconsole", "0"); InitLogging(); LogInstance().StartLogging(); SHA256AutoDetect(); ECC_Start(); SetupEnvironment(); SetupNetworking(); InitSignatureCache(); InitScriptExecutionCache(); + m_node.chain = interfaces::MakeChain(m_node, Params()); + g_wallet_init_interface.Construct(m_node); + fCheckBlockIndex = true; static bool noui_connected = false; if (!noui_connected) { noui_connect(); noui_connected = true; } } BasicTestingSetup::~BasicTestingSetup() { LogInstance().DisconnectTestLogger(); fs::remove_all(m_path_root); ECC_Stop(); } TestingSetup::TestingSetup(const std::string &chainName) : BasicTestingSetup(chainName) { const Config &config = GetConfig(); const CChainParams &chainparams = config.GetChainParams(); // Ideally we'd move all the RPC tests to the functional testing framework // instead of unit tests, but for now we need these here. RPCServer rpcServer; RegisterAllRPCCommands(config, rpcServer, tableRPC); /** * RPC does not come out of the warmup state on its own. Normally, this is * handled in bitcoind's init path, but unit tests do not trigger this * codepath, so we call it explicitly as part of setup. */ std::string rpcWarmupStatus; if (RPCIsInWarmup(&rpcWarmupStatus)) { SetRPCWarmupFinished(); } m_node.scheduler = std::make_unique<CScheduler>(); // We have to run a scheduler thread to prevent ActivateBestChain // from blocking due to queue overrun. threadGroup.create_thread([&] { m_node.scheduler->serviceQueue(); }); GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler); pblocktree.reset(new CBlockTreeDB(1 << 20, true)); g_chainstate = std::make_unique<CChainState>(); ::ChainstateActive().InitCoinsDB( /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false); assert(!::ChainstateActive().CanFlushToDisk()); ::ChainstateActive().InitCoinsCache(); assert(::ChainstateActive().CanFlushToDisk()); if (!LoadGenesisBlock(chainparams)) { throw std::runtime_error("LoadGenesisBlock failed."); } { BlockValidationState state; if (!ActivateBestChain(config, state)) { throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state))); } } constexpr int script_check_threads = 2; for (int i = 0; i < script_check_threads; ++i) { threadGroup.create_thread([i]() { return ThreadScriptCheck(i); }); } m_node.mempool = &::g_mempool; m_node.mempool->setSanityCheck(1.0); m_node.banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", chainparams, nullptr, DEFAULT_MISBEHAVING_BANTIME); // Deterministic randomness for tests. m_node.connman = std::make_unique<CConnman>(config, 0x1337, 0x1337); } TestingSetup::~TestingSetup() { if (m_node.scheduler) { m_node.scheduler->stop(); } threadGroup.interrupt_all(); threadGroup.join_all(); GetMainSignals().FlushBackgroundCallbacks(); GetMainSignals().UnregisterBackgroundSignalScheduler(); m_node.connman.reset(); m_node.banman.reset(); m_node.mempool = nullptr; m_node.scheduler.reset(); UnloadBlockIndex(); g_chainstate.reset(); pblocktree.reset(); } TestChain100Setup::TestChain100Setup() { // Generate a 100-block chain: coinbaseKey.MakeNewKey(true); CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; for (int i = 0; i < COINBASE_MATURITY; i++) { std::vector<CMutableTransaction> noTxns; CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey); m_coinbase_txns.push_back(b.vtx[0]); } } // // Create a new block with just given transactions, coinbase paying to // scriptPubKey, and try to add it to the current chain. // CBlock TestChain100Setup::CreateAndProcessBlock( const std::vector<CMutableTransaction> &txns, const CScript &scriptPubKey) { const Config &config = GetConfig(); std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(config, *m_node.mempool).CreateNewBlock(scriptPubKey); CBlock &block = pblocktemplate->block; // Replace mempool-selected txns with just coinbase plus passed-in txns: block.vtx.resize(1); for (const CMutableTransaction &tx : txns) { block.vtx.push_back(MakeTransactionRef(tx)); } // Order transactions by canonical order std::sort(std::begin(block.vtx) + 1, std::end(block.vtx), [](const std::shared_ptr<const CTransaction> &txa, const std::shared_ptr<const CTransaction> &txb) -> bool { return txa->GetId() < txb->GetId(); }); // IncrementExtraNonce creates a valid coinbase and merkleRoot { LOCK(cs_main); unsigned int extraNonce = 0; IncrementExtraNonce(&block, ::ChainActive().Tip(), config.GetMaxBlockSize(), extraNonce); } const Consensus::Params ¶ms = config.GetChainParams().GetConsensus(); while (!CheckProofOfWork(block.GetHash(), block.nBits, params)) { ++block.nNonce; } std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block); ProcessNewBlock(config, shared_pblock, true, nullptr); CBlock result = block; return result; } TestChain100Setup::~TestChain100Setup() {} CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) { return FromTx(MakeTransactionRef(tx)); } CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef &tx) { return CTxMemPoolEntry(tx, nFee, nTime, nHeight, spendsCoinbase, nSigOpCount, LockPoints()); } /** * @returns a real block * (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af) with 9 * txs. */ CBlock getBlock13b8a() { CBlock block; CDataStream stream( ParseHex( "0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb680000000" "0000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558" "da2fdb261b4d4c86041b1ab1bf9309010000000100000000000000000000000000" "00000000000000000000000000000000000000ffffffff07044c86041b0146ffff" "ffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf2" "54bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c" "4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada9027" "80da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100da" "b24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd0221" "00fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb4" "01ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369e" "d2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98e" "c706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad" "2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21ad" "c6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e86" "25a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d8982" "35e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff02" "80969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388" "ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd3" "88ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c522" "92d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3" "889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2f" "afd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d3" "1b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83aba" "f975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cb" "cba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044" "022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae7406056" "58022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e0100" "3614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c342" "3e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc" "2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13" "fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021" "e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91" "c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b4830" "4502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9" "236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddc" "ce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d78990" "4f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8" "ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942" "fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad5652937" "1864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd9" "0111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37" "f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b" "25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d0014104" "43bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf" "7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffff" "ffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a1232" "2d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70a" "e67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176" "f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf" "062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b956" "00db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd40" "67b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c6" "9b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e" "58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b217901000000" "8b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3c" "a2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b49" "1d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33" "d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312e" "f1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144a" "f553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd" "48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b" "659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0" "aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc3" "1895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9" "944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830" "450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1a" "f03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d9" "8a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d78990" "4f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8" "ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a03" "8fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261" "b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f6351285" "0811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa4207" "0082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0" "c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c0000000000" "1976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000" "000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed" "8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc" "87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded" "4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e" "3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29" "934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695" "a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93" "376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e9" "1349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49" "304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654" "d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66" "ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e88" "60c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8" "fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb604203" "4aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75e" "ef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION); stream >> block; return block; }