diff --git a/src/logging.cpp b/src/logging.cpp index 94bdf4f36..d093d6cc7 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -1,315 +1,324 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Copyright (c) 2017-2019 The Bitcoin 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 bool fLogIPs = DEFAULT_LOGIPS; const char *const DEFAULT_DEBUGLOGFILE = "debug.log"; BCLog::Logger &LogInstance() { /** * NOTE: the logger instance is leaked on exit. This is ugly, but will be * cleaned up by the OS/libc. Defining a logger as a global object doesn't * work since the order of destruction of static/global objects is * undefined. Consider if the logger gets destroyed, and then some later * destructor calls LogPrintf, maybe indirectly, and you get a core dump at * shutdown trying to access the logger. When the shutdown sequence is fully * audited and tested, explicit destruction of these objects can be * implemented by changing this from a raw pointer to a std::unique_ptr. * Since the destructor is never called, the logger and all its members must * have a trivial destructor. * * This method of initialization was originally introduced in * ee3374234c60aba2cc4c5cd5cac1c0aefc2d817c. */ static BCLog::Logger *g_logger{new BCLog::Logger()}; return *g_logger; } static int FileWriteStr(const std::string &str, FILE *fp) { return fwrite(str.data(), 1, str.size(), fp); } bool BCLog::Logger::StartLogging() { std::lock_guard scoped_lock(m_cs); assert(m_buffering); assert(m_fileout == nullptr); if (m_print_to_file) { assert(!m_file_path.empty()); m_fileout = fsbridge::fopen(m_file_path, "a"); if (!m_fileout) { return false; } // Unbuffered. setbuf(m_fileout, nullptr); // Add newlines to the logfile to distinguish this execution from the // last one. FileWriteStr("\n\n\n\n\n", m_fileout); } // Dump buffered messages from before we opened the log. m_buffering = false; while (!m_msgs_before_open.empty()) { const std::string &s = m_msgs_before_open.front(); if (m_print_to_file) { FileWriteStr(s, m_fileout); } if (m_print_to_console) { fwrite(s.data(), 1, s.size(), stdout); } m_msgs_before_open.pop_front(); } if (m_print_to_console) { fflush(stdout); } return true; } +void BCLog::Logger::DisconnectTestLogger() { + std::lock_guard scoped_lock(m_cs); + m_buffering = true; + if (m_fileout != nullptr) { + fclose(m_fileout); + } + m_fileout = nullptr; +} + struct CLogCategoryDesc { BCLog::LogFlags flag; std::string category; }; const CLogCategoryDesc LogCategories[] = { {BCLog::NONE, "0"}, {BCLog::NONE, "none"}, {BCLog::NET, "net"}, {BCLog::TOR, "tor"}, {BCLog::MEMPOOL, "mempool"}, {BCLog::HTTP, "http"}, {BCLog::BENCH, "bench"}, {BCLog::ZMQ, "zmq"}, {BCLog::DB, "db"}, {BCLog::RPC, "rpc"}, {BCLog::ESTIMATEFEE, "estimatefee"}, {BCLog::ADDRMAN, "addrman"}, {BCLog::SELECTCOINS, "selectcoins"}, {BCLog::REINDEX, "reindex"}, {BCLog::CMPCTBLOCK, "cmpctblock"}, {BCLog::RAND, "rand"}, {BCLog::PRUNE, "prune"}, {BCLog::PROXY, "proxy"}, {BCLog::MEMPOOLREJ, "mempoolrej"}, {BCLog::LIBEVENT, "libevent"}, {BCLog::COINDB, "coindb"}, {BCLog::QT, "qt"}, {BCLog::LEVELDB, "leveldb"}, {BCLog::ALL, "1"}, {BCLog::ALL, "all"}, }; bool GetLogCategory(BCLog::LogFlags &flag, const std::string &str) { if (str == "") { flag = BCLog::ALL; return true; } for (const CLogCategoryDesc &category_desc : LogCategories) { if (category_desc.category == str) { flag = category_desc.flag; return true; } } return false; } std::string ListLogCategories() { std::string ret; int outcount = 0; for (const CLogCategoryDesc &category_desc : LogCategories) { // Omit the special cases. if (category_desc.flag != BCLog::NONE && category_desc.flag != BCLog::ALL) { if (outcount != 0) { ret += ", "; } ret += category_desc.category; outcount++; } } return ret; } std::vector ListActiveLogCategories() { std::vector ret; for (const CLogCategoryDesc &category_desc : LogCategories) { // Omit the special cases. if (category_desc.flag != BCLog::NONE && category_desc.flag != BCLog::ALL) { CLogCategoryActive catActive; catActive.category = category_desc.category; catActive.active = LogAcceptCategory(category_desc.flag); ret.push_back(catActive); } } return ret; } BCLog::Logger::~Logger() { if (m_fileout) { fclose(m_fileout); } } std::string BCLog::Logger::LogTimestampStr(const std::string &str) { std::string strStamped; if (!m_log_timestamps) { return str; } if (m_started_new_line) { int64_t nTimeMicros = GetTimeMicros(); strStamped = FormatISO8601DateTime(nTimeMicros / 1000000); if (m_log_time_micros) { strStamped.pop_back(); strStamped += strprintf(".%06dZ", nTimeMicros % 1000000); } int64_t mocktime = GetMockTime(); if (mocktime) { strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")"; } strStamped += ' ' + str; } else { strStamped = str; } return strStamped; } void BCLog::Logger::LogPrintStr(const std::string &str) { std::lock_guard scoped_lock(m_cs); std::string str_prefixed = str; if (m_log_threadnames && m_started_new_line) { str_prefixed.insert(0, "[" + util::ThreadGetInternalName() + "] "); } str_prefixed = LogTimestampStr(str_prefixed); m_started_new_line = !str.empty() && str[str.size() - 1] == '\n'; if (m_buffering) { // buffer if we haven't started logging yet m_msgs_before_open.push_back(str_prefixed); return; } if (m_print_to_console) { // Print to console. fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout); fflush(stdout); } if (m_print_to_file) { assert(m_fileout != nullptr); // Reopen the log file, if requested. if (m_reopen_file) { m_reopen_file = false; FILE *new_fileout = fsbridge::fopen(m_file_path, "a"); if (new_fileout) { // unbuffered. setbuf(m_fileout, nullptr); fclose(m_fileout); m_fileout = new_fileout; } } FileWriteStr(str_prefixed, m_fileout); } } void BCLog::Logger::ShrinkDebugFile() { // Amount of debug.log to save at end when shrinking (must fit in memory) constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000; assert(!m_file_path.empty()); // Scroll debug.log if it's getting too big. FILE *file = fsbridge::fopen(m_file_path, "r"); // Special files (e.g. device nodes) may not have a size. size_t log_size = 0; try { log_size = fs::file_size(m_file_path); } catch (const fs::filesystem_error &) { } // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes. if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) { // Restart the file with some of the end. std::vector vch(RECENT_DEBUG_HISTORY_SIZE, 0); if (fseek(file, -((long)vch.size()), SEEK_END)) { LogPrintf("Failed to shrink debug log file: fseek(...) failed\n"); fclose(file); return; } int nBytes = fread(vch.data(), 1, vch.size(), file); fclose(file); file = fsbridge::fopen(m_file_path, "w"); if (file) { fwrite(vch.data(), 1, nBytes, file); fclose(file); } } else if (file != nullptr) { fclose(file); } } void BCLog::Logger::EnableCategory(LogFlags category) { m_categories |= category; } bool BCLog::Logger::EnableCategory(const std::string &str) { BCLog::LogFlags flag; if (!GetLogCategory(flag, str)) { return false; } EnableCategory(flag); return true; } void BCLog::Logger::DisableCategory(LogFlags category) { m_categories &= ~category; } bool BCLog::Logger::DisableCategory(const std::string &str) { BCLog::LogFlags flag; if (!GetLogCategory(flag, str)) { return false; } DisableCategory(flag); return true; } bool BCLog::Logger::WillLogCategory(LogFlags category) const { // ALL is not meant to be used as a logging category, but only as a mask // representing all categories. if (category == BCLog::NONE || category == BCLog::ALL) { LogPrintf("Error trying to log using a category mask instead of an " "explicit category.\n"); return true; } return (m_categories.load(std::memory_order_relaxed) & category) != 0; } bool BCLog::Logger::DefaultShrinkDebugFile() const { return m_categories != BCLog::NONE; } diff --git a/src/logging.h b/src/logging.h index 25282d265..564e9d5da 100644 --- a/src/logging.h +++ b/src/logging.h @@ -1,181 +1,183 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Copyright (c) 2017-2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_LOGGING_H #define BITCOIN_LOGGING_H #include #include #include #include #include #include #include static const bool DEFAULT_LOGTIMEMICROS = false; static const bool DEFAULT_LOGIPS = false; static const bool DEFAULT_LOGTIMESTAMPS = true; static const bool DEFAULT_LOGTHREADNAMES = false; extern bool fLogIPs; extern const char *const DEFAULT_DEBUGLOGFILE; struct CLogCategoryActive { std::string category; bool active; }; namespace BCLog { enum LogFlags : uint32_t { NONE = 0, NET = (1 << 0), TOR = (1 << 1), MEMPOOL = (1 << 2), HTTP = (1 << 3), BENCH = (1 << 4), ZMQ = (1 << 5), DB = (1 << 6), RPC = (1 << 7), ESTIMATEFEE = (1 << 8), ADDRMAN = (1 << 9), SELECTCOINS = (1 << 10), REINDEX = (1 << 11), CMPCTBLOCK = (1 << 12), RAND = (1 << 13), PRUNE = (1 << 14), PROXY = (1 << 15), MEMPOOLREJ = (1 << 16), LIBEVENT = (1 << 17), COINDB = (1 << 18), QT = (1 << 19), LEVELDB = (1 << 20), ALL = ~uint32_t(0), }; class Logger { private: // Can not use Mutex from sync.h because in debug mode it would cause a // deadlock when a potential deadlock was detected mutable std::mutex m_cs; // GUARDED_BY(m_cs) FILE *m_fileout = nullptr; // GUARDED_BY(m_cs) std::list m_msgs_before_open; //! Buffer messages before logging can be started. GUARDED_BY(m_cs) bool m_buffering{true}; /** * m_started_new_line is a state variable that will suppress printing of the * timestamp when multiple calls are made that don't end in a newline. */ std::atomic_bool m_started_new_line{true}; /** * Log categories bitfield. */ std::atomic m_categories{0}; std::string LogTimestampStr(const std::string &str); public: bool m_print_to_console = false; bool m_print_to_file = false; bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS; bool m_log_time_micros = DEFAULT_LOGTIMEMICROS; bool m_log_threadnames = DEFAULT_LOGTHREADNAMES; fs::path m_file_path; std::atomic m_reopen_file{false}; ~Logger(); /** Send a string to the log output */ void LogPrintStr(const std::string &str); /** Returns whether logs will be written to any output */ bool Enabled() const { std::lock_guard scoped_lock(m_cs); return m_buffering || m_print_to_console || m_print_to_file; } /** Start logging (and flush all buffered messages) */ bool StartLogging(); + /** Only for testing */ + void DisconnectTestLogger(); void ShrinkDebugFile(); uint32_t GetCategoryMask() const { return m_categories.load(); } void EnableCategory(LogFlags category); bool EnableCategory(const std::string &str); void DisableCategory(LogFlags category); bool DisableCategory(const std::string &str); /** Return true if log accepts specified category */ bool WillLogCategory(LogFlags category) const; /** Default for whether ShrinkDebugFile should be run */ bool DefaultShrinkDebugFile() const; }; } // namespace BCLog BCLog::Logger &LogInstance(); /** Return true if log accepts specified category */ static inline bool LogAcceptCategory(BCLog::LogFlags category) { return LogInstance().WillLogCategory(category); } /** Returns a string with the log categories. */ std::string ListLogCategories(); /** Returns a vector of the active log categories. */ std::vector ListActiveLogCategories(); /** Return true if str parses as a log category and set the flag */ bool GetLogCategory(BCLog::LogFlags &flag, const std::string &str); // Be conservative when using LogPrintf/error or other things which // unconditionally log to debug.log! It should not be the case that an inbound // peer can fill up a user's disk with debug.log entries. template static inline void LogPrintf(const char *fmt, const Args &... args) { if (LogInstance().Enabled()) { std::string log_msg; try { log_msg = tfm::format(fmt, args...); } catch (tinyformat::format_error &fmterr) { /** * Original format string will have newline so don't add one here */ log_msg = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + fmt; } LogInstance().LogPrintStr(log_msg); } } // Use a macro instead of a function for conditional logging to prevent // evaluating arguments when logging for the category is not enabled. #define LogPrint(category, ...) \ do { \ if (LogAcceptCategory((category))) { \ LogPrintf(__VA_ARGS__); \ } \ } while (0) /** * These are aliases used to explicitly state that the message should not end * with a newline character. It allows for detecting the missing newlines that * could make the logs hard to read. */ #define LogPrintfToBeContinued LogPrintf #define LogPrintToBeContinued LogPrint #endif // BITCOIN_LOGGING_H diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp index ef7ef44e0..b66535873 100644 --- a/src/qt/test/apptests.cpp +++ b/src/qt/test/apptests.cpp @@ -1,140 +1,146 @@ // 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 #if defined(HAVE_CONFIG_H) #include #endif #ifdef ENABLE_WALLET #include #endif #include #include #include #include #include #include #include /* * FIXME: requires QT_WIDGETS_LIB to be defined prior * inclusion to export the QtTest::keyClicks symbol. * On some older Qt versions the definition end up being set by the inclusion of * . * This only occurs when building with autotools, as QMake and CMake define * QT_WIDGETS_LIB on the command line. As a workaround for autotools, * should be included before . * Also prevent the linter from sorting the includes. */ // clang-format off #include #include // clang-format on #include #include #include namespace { //! Call getblockchaininfo RPC and check first field of JSON output. void TestRpcCommand(RPCConsole *console) { QEventLoop loop; QTextEdit *messagesWidget = console->findChild("messagesWidget"); QObject::connect(messagesWidget, &QTextEdit::textChanged, &loop, &QEventLoop::quit); QLineEdit *lineEdit = console->findChild("lineEdit"); QTest::keyClicks(lineEdit, "getblockchaininfo"); QTest::keyClick(lineEdit, Qt::Key_Return); loop.exec(); QString output = messagesWidget->toPlainText(); UniValue value; value.read( output .right(output.size() - output.lastIndexOf(QChar::ObjectReplacementCharacter) - 1) .toStdString()); QCOMPARE(value["chain"].get_str(), std::string("regtest")); } } // namespace //! Entry point for BitcoinApplication tests. void AppTests::appTests() { #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 AppTests on mac build with 'minimal' platform set due " "to Qt bugs. To run AppTests, invoke " "with 'test_bitcoin-qt -platform cocoa' on mac, or else use a " "linux or windows build."); return; } #endif Config &config = const_cast(GetConfig()); + // Already started by the common test setup, so stop it to avoid + // interference + ECC_Stop(); + LogInstance().DisconnectTestLogger(); + m_app.parameterSetup(); m_app.createOptionsModel(true /* reset settings */); QScopedPointer style(NetworkStyle::instantiate( QString::fromStdString(Params().NetworkIDString()))); m_app.setupPlatformStyle(); m_app.createWindow(&config, style.data()); connect(&m_app, &BitcoinApplication::windowShown, this, &AppTests::guiTests); expectCallback("guiTests"); m_app.baseInitialize(config); RPCServer rpcServer; HTTPRPCRequestProcessor httpRPCRequestProcessor(config, rpcServer); m_app.requestInitialize(config, rpcServer, httpRPCRequestProcessor); m_app.exec(); m_app.requestShutdown(config); m_app.exec(); // Reset global state to avoid interfering with later tests. AbortShutdown(); UnloadBlockIndex(); } //! Entry point for BitcoinGUI tests. void AppTests::guiTests(BitcoinGUI *window) { HandleCallback callback{"guiTests", *this}; connect(window, &BitcoinGUI::consoleShown, this, &AppTests::consoleTests); expectCallback("consoleTests"); QAction *action = window->findChild("openRPCConsoleAction"); action->activate(QAction::Trigger); } //! Entry point for RPCConsole tests. void AppTests::consoleTests(RPCConsole *console) { HandleCallback callback{"consoleTests", *this}; TestRpcCommand(console); } //! Destructor to shut down after the last expected callback completes. AppTests::HandleCallback::~HandleCallback() { auto &callbacks = m_app_tests.m_callbacks; auto it = callbacks.find(m_callback); assert(it != callbacks.end()); callbacks.erase(it); if (callbacks.empty()) { m_app_tests.m_app.quit(); } } diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index e85eab5f4..a7da6fa35 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -1,197 +1,201 @@ // Copyright (c) 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. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static UniValue rpcNestedTest_rpc(const Config &config, const JSONRPCRequest &request) { if (request.fHelp) { return "help message"; } return request.params.write(0, 0); } static const ContextFreeRPCCommand vRPCCommands[] = { {"test", "rpcNestedTest", &rpcNestedTest_rpc, {}}, }; void RPCNestedTests::rpcNestedTests() { // do some test setup // could be moved to a more generic place when we add more tests on QT level tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]); // mempool.setSanityCheck(1.0); + // Already started by the common test setup, so stop it to avoid + // interference + ECC_Stop(); + LogInstance().DisconnectTestLogger(); TestingSetup test; std::string result; std::string result2; std::string filtered; auto node = interfaces::MakeNode(); // Simple result filtering with path. RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()[chain]", &filtered); QVERIFY(result == "main"); QVERIFY(filtered == "getblockchaininfo()[chain]"); // Simple 2 level nesting. RPCConsole::RPCExecuteCommandLine(*node, result, "getblock(getbestblockhash())"); RPCConsole::RPCExecuteCommandLine( *node, result, "getblock(getblock(getbestblockhash())[hash], true)"); // 4 level nesting with whitespace, filtering path and boolean parameter. RPCConsole::RPCExecuteCommandLine( *node, result, "getblock( getblock( getblock(getbestblockhash())[hash] " ")[hash], true)"); RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo"); QVERIFY(result.substr(0, 1) == "{"); RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()"); QVERIFY(result.substr(0, 1) == "{"); // Whitespace at the end will be tolerated. RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo "); QVERIFY(result.substr(0, 1) == "{"); // Quote path identifier are allowed, but look after a child containing the // quotes in the key. (RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()[\"chain\"]")); QVERIFY(result == "null"); // parameter not in brackets are allowed. (RPCConsole::RPCExecuteCommandLine(*node, result, "createrawtransaction [] {} 0")); // Parameter in brackets are allowed. (RPCConsole::RPCExecuteCommandLine(*node, result2, "createrawtransaction([],{},0)")); QVERIFY(result == result2); // Whitespace between parameters is allowed. (RPCConsole::RPCExecuteCommandLine( *node, result2, "createrawtransaction( [], {} , 0 )")); QVERIFY(result == result2); RPCConsole::RPCExecuteCommandLine( *node, result, "getblock(getbestblockhash())[tx][0]", &filtered); QVERIFY(result == "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"); QVERIFY(filtered == "getblock(getbestblockhash())[tx][0]"); RPCConsole::RPCParseCommandLine(nullptr, result, "importprivkey", false, &filtered); QVERIFY(filtered == "importprivkey(…)"); RPCConsole::RPCParseCommandLine( nullptr, result, "signmessagewithprivkey abc", false, &filtered); QVERIFY(filtered == "signmessagewithprivkey(…)"); RPCConsole::RPCParseCommandLine( nullptr, result, "signmessagewithprivkey abc,def", false, &filtered); QVERIFY(filtered == "signmessagewithprivkey(…)"); RPCConsole::RPCParseCommandLine( nullptr, result, "signrawtransactionwithkey(abc)", false, &filtered); QVERIFY(filtered == "signrawtransactionwithkey(…)"); RPCConsole::RPCParseCommandLine(nullptr, result, "walletpassphrase(help())", false, &filtered); QVERIFY(filtered == "walletpassphrase(…)"); RPCConsole::RPCParseCommandLine( nullptr, result, "walletpassphrasechange(help(walletpassphrasechange(abc)))", false, &filtered); QVERIFY(filtered == "walletpassphrasechange(…)"); RPCConsole::RPCParseCommandLine( nullptr, result, "help(encryptwallet(abc, def))", false, &filtered); QVERIFY(filtered == "help(encryptwallet(…))"); RPCConsole::RPCParseCommandLine(nullptr, result, "help(importprivkey())", false, &filtered); QVERIFY(filtered == "help(importprivkey(…))"); RPCConsole::RPCParseCommandLine( nullptr, result, "help(importprivkey(help()))", false, &filtered); QVERIFY(filtered == "help(importprivkey(…))"); RPCConsole::RPCParseCommandLine( nullptr, result, "help(importprivkey(abc), walletpassphrase(def))", false, &filtered); QVERIFY(filtered == "help(importprivkey(…), walletpassphrase(…))"); RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest"); QVERIFY(result == "[]"); RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest ''"); QVERIFY(result == "[\"\"]"); RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest \"\""); QVERIFY(result == "[\"\"]"); RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest '' abc"); QVERIFY(result == "[\"\",\"abc\"]"); RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc '' abc"); QVERIFY(result == "[\"abc\",\"\",\"abc\"]"); RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc abc"); QVERIFY(result == "[\"abc\",\"abc\"]"); RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc\t\tabc"); QVERIFY(result == "[\"abc\",\"abc\"]"); RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest(abc )"); QVERIFY(result == "[\"abc\"]"); RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest( abc )"); QVERIFY(result == "[\"abc\"]"); RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest( abc , cba )"); QVERIFY(result == "[\"abc\",\"cba\"]"); // invalid syntax QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine( *node, result, "getblockchaininfo() .\n"), std::runtime_error); // invalid syntax QVERIFY_EXCEPTION_THROWN( RPCConsole::RPCExecuteCommandLine( *node, result, "getblockchaininfo() getblockchaininfo()"), std::runtime_error); // tolerate non closing brackets if we have no arguments. (RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo(")); // tolerate non command brackts (RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()()()")); // invalid argument QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine( *node, result, "getblockchaininfo(True)"), UniValue); // method not found QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine( *node, result, "a(getblockchaininfo(True))"), UniValue); // don't tollerate empty arguments when using , QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine( *node, result, "rpcNestedTest abc,,abc"), std::runtime_error); // don't tollerate empty arguments when using , QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine( *node, result, "rpcNestedTest(abc,,abc)"), std::runtime_error); // don't tollerate empty arguments when using , QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine( *node, result, "rpcNestedTest(abc,,)"), std::runtime_error); } diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index c5b91fb21..712678c59 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -1,131 +1,121 @@ // 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 #endif #include #include #include #include -#include #include #include #include #include #include #include #include #ifdef ENABLE_WALLET #include #ifdef ENABLE_BIP70 #include #endif // ENABLE_BIP70 #include #endif // ENABLE_WALLET +#include + #include #include #include #ifdef ENABLE_BIP70 #include #endif #if defined(QT_STATICPLUGIN) #include #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 extern void noui_connect(); // This is all you need to run all the tests int main(int argc, char *argv[]) { - SetupEnvironment(); - SetupNetworking(); - SelectParams(CBaseChainParams::REGTEST); - noui_connect(); - ClearDatadirCache(); - fs::path pathTemp = - fs::temp_directory_path() / strprintf("test_bitcoin-qt_%lu_%i", - (unsigned long)GetTime(), - (int)GetRand(100000)); - fs::create_directories(pathTemp); - gArgs.ForceSetArg("-datadir", pathTemp.string()); + BasicTestingSetup test{CBaseChainParams::REGTEST}; + auto node = interfaces::MakeNode(); 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", 0); // Don't remove this, it's needed to access // QApplication:: and QCoreApplication:: in the tests BitcoinApplication app(*node, argc, argv); app.setApplicationName("BitcoinABC-Qt-test"); #ifdef ENABLE_BIP70 // This is necessary to initialize openssl on the test framework // (at least on Darwin). SSL_library_init(); #endif 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; 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; if (QTest::qExec(&test7) != 0) { fInvalid = true; } AddressBookTests test8; if (QTest::qExec(&test8) != 0) { fInvalid = true; } #endif - fs::remove_all(pathTemp); - return fInvalid; } diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 8e4fa63ea..77053399c 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -1,342 +1,344 @@ // Copyright (c) 2012-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 #include #include #include #include #include // Test if a string consists entirely of null characters static bool is_null_key(const std::vector &key) { bool isnull = true; for (unsigned int i = 0; i < key.size(); i++) { isnull &= (key[i] == '\x00'); } return isnull; } BOOST_FIXTURE_TEST_SUITE(dbwrapper_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(dbwrapper) { // Perform tests both obfuscated and non-obfuscated. for (const bool obfuscate : {false, true}) { - fs::path ph = SetDataDir( - std::string("dbwrapper").append(obfuscate ? "_true" : "_false")); + fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_obfuscate_true" + : "dbwrapper_obfuscate_false"); CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); char key = 'k'; uint256 in = InsecureRand256(); uint256 res; // Ensure that we're doing real obfuscation when obfuscate=true BOOST_CHECK(obfuscate != is_null_key(dbwrapper_private::GetObfuscateKey(dbw))); BOOST_CHECK(dbw.Write(key, in)); BOOST_CHECK(dbw.Read(key, res)); BOOST_CHECK_EQUAL(res.ToString(), in.ToString()); } } // Test batch operations BOOST_AUTO_TEST_CASE(dbwrapper_batch) { // Perform tests both obfuscated and non-obfuscated. for (const bool obfuscate : {false, true}) { - fs::path ph = SetDataDir(std::string("dbwrapper_batch") - .append(obfuscate ? "_true" : "_false")); + fs::path ph = + GetDataDir() / (obfuscate ? "dbwrapper_batch_obfuscate_true" + : "dbwrapper_batch_obfuscate_false"); CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); char key = 'i'; uint256 in = InsecureRand256(); char key2 = 'j'; uint256 in2 = InsecureRand256(); char key3 = 'k'; uint256 in3 = InsecureRand256(); uint256 res; CDBBatch batch(dbw); batch.Write(key, in); batch.Write(key2, in2); batch.Write(key3, in3); // Remove key3 before it's even been written batch.Erase(key3); dbw.WriteBatch(batch); BOOST_CHECK(dbw.Read(key, res)); BOOST_CHECK_EQUAL(res.ToString(), in.ToString()); BOOST_CHECK(dbw.Read(key2, res)); BOOST_CHECK_EQUAL(res.ToString(), in2.ToString()); // key3 should've never been written BOOST_CHECK(dbw.Read(key3, res) == false); } } BOOST_AUTO_TEST_CASE(dbwrapper_iterator) { // Perform tests both obfuscated and non-obfuscated. for (const bool obfuscate : {false, true}) { - fs::path ph = SetDataDir(std::string("dbwrapper_iterator") - .append(obfuscate ? "_true" : "_false")); + fs::path ph = + GetDataDir() / (obfuscate ? "dbwrapper_iterator_obfuscate_true" + : "dbwrapper_iterator_obfuscate_false"); CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); // The two keys are intentionally chosen for ordering char key = 'j'; uint256 in = InsecureRand256(); BOOST_CHECK(dbw.Write(key, in)); char key2 = 'k'; uint256 in2 = InsecureRand256(); BOOST_CHECK(dbw.Write(key2, in2)); std::unique_ptr it( const_cast(dbw).NewIterator()); // Be sure to seek past the obfuscation key (if it exists) it->Seek(key); char key_res; uint256 val_res; it->GetKey(key_res); it->GetValue(val_res); BOOST_CHECK_EQUAL(key_res, key); BOOST_CHECK_EQUAL(val_res.ToString(), in.ToString()); it->Next(); it->GetKey(key_res); it->GetValue(val_res); BOOST_CHECK_EQUAL(key_res, key2); BOOST_CHECK_EQUAL(val_res.ToString(), in2.ToString()); it->Next(); BOOST_CHECK_EQUAL(it->Valid(), false); } } // Test that we do not obfuscation if there is existing data. BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate) { // We're going to share this fs::path between two wrappers - fs::path ph = SetDataDir("existing_data_no_obfuscate"); + fs::path ph = GetDataDir() / "existing_data_no_obfuscate"; create_directories(ph); // Set up a non-obfuscated wrapper to write some initial data. std::unique_ptr dbw = std::make_unique(ph, (1 << 10), false, false, false); char key = 'k'; uint256 in = InsecureRand256(); uint256 res; BOOST_CHECK(dbw->Write(key, in)); BOOST_CHECK(dbw->Read(key, res)); BOOST_CHECK_EQUAL(res.ToString(), in.ToString()); // Call the destructor to free leveldb LOCK dbw.reset(); // Now, set up another wrapper that wants to obfuscate the same directory CDBWrapper odbw(ph, (1 << 10), false, false, true); // Check that the key/val we wrote with unobfuscated wrapper exists and // is readable. uint256 res2; BOOST_CHECK(odbw.Read(key, res2)); BOOST_CHECK_EQUAL(res2.ToString(), in.ToString()); // There should be existing data BOOST_CHECK(!odbw.IsEmpty()); // The key should be an empty string BOOST_CHECK(is_null_key(dbwrapper_private::GetObfuscateKey(odbw))); uint256 in2 = InsecureRand256(); uint256 res3; // Check that we can write successfully BOOST_CHECK(odbw.Write(key, in2)); BOOST_CHECK(odbw.Read(key, res3)); BOOST_CHECK_EQUAL(res3.ToString(), in2.ToString()); } // Ensure that we start obfuscating during a reindex. BOOST_AUTO_TEST_CASE(existing_data_reindex) { // We're going to share this fs::path between two wrappers - fs::path ph = SetDataDir("existing_data_reindex"); + fs::path ph = GetDataDir() / "existing_data_reindex"; create_directories(ph); // Set up a non-obfuscated wrapper to write some initial data. std::unique_ptr dbw = std::make_unique(ph, (1 << 10), false, false, false); char key = 'k'; uint256 in = InsecureRand256(); uint256 res; BOOST_CHECK(dbw->Write(key, in)); BOOST_CHECK(dbw->Read(key, res)); BOOST_CHECK_EQUAL(res.ToString(), in.ToString()); // Call the destructor to free leveldb LOCK dbw.reset(); // Simulate a -reindex by wiping the existing data store CDBWrapper odbw(ph, (1 << 10), false, true, true); // Check that the key/val we wrote with unobfuscated wrapper doesn't exist uint256 res2; BOOST_CHECK(!odbw.Read(key, res2)); BOOST_CHECK(!is_null_key(dbwrapper_private::GetObfuscateKey(odbw))); uint256 in2 = InsecureRand256(); uint256 res3; // Check that we can write successfully BOOST_CHECK(odbw.Write(key, in2)); BOOST_CHECK(odbw.Read(key, res3)); BOOST_CHECK_EQUAL(res3.ToString(), in2.ToString()); } BOOST_AUTO_TEST_CASE(iterator_ordering) { - fs::path ph = SetDataDir("iterator_ordering"); + fs::path ph = GetDataDir() / "iterator_ordering"; CDBWrapper dbw(ph, (1 << 20), true, false, false); for (int x = 0x00; x < 256; ++x) { uint8_t key = x; uint32_t value = x * x; if (!(x & 1)) { BOOST_CHECK(dbw.Write(key, value)); } } // Check that creating an iterator creates a snapshot std::unique_ptr it( const_cast(dbw).NewIterator()); for (unsigned int x = 0x00; x < 256; ++x) { uint8_t key = x; uint32_t value = x * x; if (x & 1) { BOOST_CHECK(dbw.Write(key, value)); } } for (const int seek_start : {0x00, 0x80}) { it->Seek((uint8_t)seek_start); for (unsigned int x = seek_start; x < 255; ++x) { uint8_t key; uint32_t value; BOOST_CHECK(it->Valid()); // Avoid spurious errors about invalid iterator's key and value in // case of failure if (!it->Valid()) { break; } BOOST_CHECK(it->GetKey(key)); if (x & 1) { BOOST_CHECK_EQUAL(key, x + 1); continue; } BOOST_CHECK(it->GetValue(value)); BOOST_CHECK_EQUAL(key, x); BOOST_CHECK_EQUAL(value, x * x); it->Next(); } BOOST_CHECK(!it->Valid()); } } struct StringContentsSerializer { // Used to make two serialized objects the same while letting them have // different lengths. This is a terrible idea. std::string str; StringContentsSerializer() {} explicit StringContentsSerializer(const std::string &inp) : str(inp) {} StringContentsSerializer &operator+=(const std::string &s) { str += s; return *this; } StringContentsSerializer &operator+=(const StringContentsSerializer &s) { return *this += s.str; } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { if (ser_action.ForRead()) { str.clear(); char c = 0; while (true) { try { READWRITE(c); str.push_back(c); } catch (const std::ios_base::failure &) { break; } } } else { for (size_t i = 0; i < str.size(); i++) { READWRITE(str[i]); } } } }; BOOST_AUTO_TEST_CASE(iterator_string_ordering) { char buf[10]; - fs::path ph = SetDataDir("iterator_string_ordering"); + fs::path ph = GetDataDir() / "iterator_string_ordering"; CDBWrapper dbw(ph, (1 << 20), true, false, false); for (int x = 0x00; x < 10; ++x) { for (int y = 0; y < 10; y++) { snprintf(buf, sizeof(buf), "%d", x); StringContentsSerializer key(buf); for (int z = 0; z < y; z++) { key += key; } uint32_t value = x * x; BOOST_CHECK(dbw.Write(key, value)); } } std::unique_ptr it( const_cast(dbw).NewIterator()); for (const int seek_start : {0, 5}) { snprintf(buf, sizeof(buf), "%d", seek_start); StringContentsSerializer seek_key(buf); it->Seek(seek_key); for (unsigned int x = seek_start; x < 10; ++x) { for (int y = 0; y < 10; y++) { snprintf(buf, sizeof(buf), "%d", x); std::string exp_key(buf); for (int z = 0; z < y; z++) { exp_key += exp_key; } StringContentsSerializer key; uint32_t value; BOOST_CHECK(it->Valid()); // Avoid spurious errors about invalid iterator's key and value // in case of failure if (!it->Valid()) { break; } BOOST_CHECK(it->GetKey(key)); BOOST_CHECK(it->GetValue(value)); BOOST_CHECK_EQUAL(key.str, exp_key); BOOST_CHECK_EQUAL(value, x * x); it->Next(); } } BOOST_CHECK(!it->Valid()); } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/flatfile_tests.cpp b/src/test/flatfile_tests.cpp index 87ead7ad9..59b686cd6 100644 --- a/src/test/flatfile_tests.cpp +++ b/src/test/flatfile_tests.cpp @@ -1,133 +1,133 @@ // Copyright (c) 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 #include #include #include #include BOOST_FIXTURE_TEST_SUITE(flatfile_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(flatfile_filename) { - auto data_dir = SetDataDir("flatfile_test"); + const auto data_dir = GetDataDir(); FlatFilePos pos(456, 789); FlatFileSeq seq1(data_dir, "a", 16 * 1024); BOOST_CHECK_EQUAL(seq1.FileName(pos), data_dir / "a00456.dat"); FlatFileSeq seq2(data_dir / "a", "b", 16 * 1024); BOOST_CHECK_EQUAL(seq2.FileName(pos), data_dir / "a" / "b00456.dat"); } BOOST_AUTO_TEST_CASE(flatfile_open) { - auto data_dir = SetDataDir("flatfile_test"); + const auto data_dir = GetDataDir(); FlatFileSeq seq(data_dir, "a", 16 * 1024); std::string line1( "A purely peer-to-peer version of electronic cash would allow online " "payments to be sent directly from one party to another without going " "through a financial institution."); std::string line2("Digital signatures provide part of the solution, but " "the main benefits are lost if a trusted third party is " "still required to prevent double-spending."); size_t pos1 = 0; size_t pos2 = pos1 + GetSerializeSize(line1, CLIENT_VERSION); // Write first line to file. { CAutoFile file(seq.Open(FlatFilePos(0, pos1)), SER_DISK, CLIENT_VERSION); file << LIMITED_STRING(line1, 256); } // Attempt to append to file opened in read-only mode. { CAutoFile file(seq.Open(FlatFilePos(0, pos2), true), SER_DISK, CLIENT_VERSION); BOOST_CHECK_THROW(file << LIMITED_STRING(line2, 256), std::ios_base::failure); } // Append second line to file. { CAutoFile file(seq.Open(FlatFilePos(0, pos2)), SER_DISK, CLIENT_VERSION); file << LIMITED_STRING(line2, 256); } // Read text from file in read-only mode. { std::string text; CAutoFile file(seq.Open(FlatFilePos(0, pos1), true), SER_DISK, CLIENT_VERSION); file >> LIMITED_STRING(text, 256); BOOST_CHECK_EQUAL(text, line1); file >> LIMITED_STRING(text, 256); BOOST_CHECK_EQUAL(text, line2); } // Read text from file with position offset. { std::string text; CAutoFile file(seq.Open(FlatFilePos(0, pos2)), SER_DISK, CLIENT_VERSION); file >> LIMITED_STRING(text, 256); BOOST_CHECK_EQUAL(text, line2); } // Ensure another file in the sequence has no data. { std::string text; CAutoFile file(seq.Open(FlatFilePos(1, pos2)), SER_DISK, CLIENT_VERSION); BOOST_CHECK_THROW(file >> LIMITED_STRING(text, 256), std::ios_base::failure); } } BOOST_AUTO_TEST_CASE(flatfile_allocate) { - auto data_dir = SetDataDir("flatfile_test"); + const auto data_dir = GetDataDir(); FlatFileSeq seq(data_dir, "a", 100); bool out_of_space; BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 0), 1, out_of_space), 100); BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 0))), 100); BOOST_CHECK(!out_of_space); BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 99), 1, out_of_space), 0); BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 99))), 100); BOOST_CHECK(!out_of_space); BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 99), 2, out_of_space), 101); BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 99))), 200); BOOST_CHECK(!out_of_space); } BOOST_AUTO_TEST_CASE(flatfile_flush) { - auto data_dir = SetDataDir("flatfile_test"); + const auto data_dir = GetDataDir(); FlatFileSeq seq(data_dir, "a", 100); bool out_of_space; seq.Allocate(FlatFilePos(0, 0), 1, out_of_space); // Flush without finalize should not truncate file. seq.Flush(FlatFilePos(0, 1)); BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 1))), 100); // Flush with finalize should truncate file. seq.Flush(FlatFilePos(0, 1), true); BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 1))), 1); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp index 21b004675..ce93d9b29 100644 --- a/src/test/fs_tests.cpp +++ b/src/test/fs_tests.cpp @@ -1,59 +1,59 @@ // 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 #include #include BOOST_FIXTURE_TEST_SUITE(fs_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(fsbridge_fstream) { - fs::path tmpfolder = SetDataDir("fsbridge_fstream"); + fs::path tmpfolder = GetDataDir(); // tmpfile1 should be the same as tmpfile2 fs::path tmpfile1 = tmpfolder / "fs_tests_₿_🏃"; fs::path tmpfile2 = tmpfolder / L"fs_tests_₿_🏃"; { fsbridge::ofstream file(tmpfile1); file << "bitcoin"; } { fsbridge::ifstream file(tmpfile2); std::string input_buffer; file >> input_buffer; BOOST_CHECK_EQUAL(input_buffer, "bitcoin"); } { fsbridge::ifstream file(tmpfile1, std::ios_base::in | std::ios_base::ate); std::string input_buffer; file >> input_buffer; BOOST_CHECK_EQUAL(input_buffer, ""); } { fsbridge::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::app); file << "tests"; } { fsbridge::ifstream file(tmpfile1); std::string input_buffer; file >> input_buffer; BOOST_CHECK_EQUAL(input_buffer, "bitcointests"); } { fsbridge::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::trunc); file << "bitcoin"; } { fsbridge::ifstream file(tmpfile1); std::string input_buffer; file >> input_buffer; BOOST_CHECK_EQUAL(input_buffer, "bitcoin"); } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index bd4e8fad3..cf21bfdec 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -1,343 +1,341 @@ // Copyright (c) 2012-2019 The Bitcoin Core developers // Copyright (c) 2017-2019 The Bitcoin 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 class CAddrManSerializationMock : public CAddrMan { public: virtual void Serialize(CDataStream &s) const = 0; //! Ensure that bucket placement is always the same for testing purposes. void MakeDeterministic() { nKey.SetNull(); insecure_rand = FastRandomContext(true); } }; class CAddrManUncorrupted : public CAddrManSerializationMock { public: void Serialize(CDataStream &s) const override { CAddrMan::Serialize(s); } }; class CAddrManCorrupted : public CAddrManSerializationMock { public: void Serialize(CDataStream &s) const override { // Produces corrupt output that claims addrman has 20 addrs when it only // has one addr. uint8_t nVersion = 1; s << nVersion; s << uint8_t(32); s << nKey; s << 10; // nNew s << 10; // nTried int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30); s << nUBuckets; CService serv; Lookup("252.1.1.1", serv, 7777, false); CAddress addr = CAddress(serv, NODE_NONE); CNetAddr resolved; LookupHost("252.2.2.2", resolved, false); CAddrInfo info = CAddrInfo(addr, resolved); s << info; } }; class NetTestConfig : public DummyConfig { public: bool SetMaxBlockSize(uint64_t maxBlockSize) override { nMaxBlockSize = maxBlockSize; return true; } uint64_t GetMaxBlockSize() const override { return nMaxBlockSize; } private: uint64_t nMaxBlockSize; }; static CDataStream AddrmanToStream(CAddrManSerializationMock &_addrman) { CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION); ssPeersIn << Params().DiskMagic(); ssPeersIn << _addrman; std::string str = ssPeersIn.str(); std::vector vchData(str.begin(), str.end()); return CDataStream(vchData, SER_DISK, CLIENT_VERSION); } BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(cnode_listen_port) { // test default unsigned short port = GetListenPort(); BOOST_CHECK(port == Params().GetDefaultPort()); // test set port unsigned short altPort = 12345; gArgs.SoftSetArg("-port", std::to_string(altPort)); port = GetListenPort(); BOOST_CHECK(port == altPort); } BOOST_AUTO_TEST_CASE(caddrdb_read) { - SetDataDir("caddrdb_read"); CAddrManUncorrupted addrmanUncorrupted; addrmanUncorrupted.MakeDeterministic(); CService addr1, addr2, addr3; Lookup("250.7.1.1", addr1, 8333, false); Lookup("250.7.2.2", addr2, 9999, false); Lookup("250.7.3.3", addr3, 9999, false); // Add three addresses to new table. CService source; Lookup("252.5.1.1", source, 8333, false); addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), source); addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), source); addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), source); // Test that the de-serialization does not throw an exception. CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted); bool exceptionThrown = false; CAddrMan addrman1; BOOST_CHECK(addrman1.size() == 0); try { uint8_t pchMsgTmp[4]; ssPeers1 >> pchMsgTmp; ssPeers1 >> addrman1; } catch (const std::exception &) { exceptionThrown = true; } BOOST_CHECK(addrman1.size() == 3); BOOST_CHECK(exceptionThrown == false); // Test that CAddrDB::Read creates an addrman with the correct number of // addrs. CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted); CAddrMan addrman2; CAddrDB adb(Params()); BOOST_CHECK(addrman2.size() == 0); adb.Read(addrman2, ssPeers2); BOOST_CHECK(addrman2.size() == 3); } BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted) { - SetDataDir("caddrdb_read_corrupted"); CAddrManCorrupted addrmanCorrupted; addrmanCorrupted.MakeDeterministic(); // Test that the de-serialization of corrupted addrman throws an exception. CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted); bool exceptionThrown = false; CAddrMan addrman1; BOOST_CHECK(addrman1.size() == 0); try { uint8_t pchMsgTmp[4]; ssPeers1 >> pchMsgTmp; ssPeers1 >> addrman1; } catch (const std::exception &) { exceptionThrown = true; } // Even through de-serialization failed addrman is not left in a clean // state. BOOST_CHECK(addrman1.size() == 1); BOOST_CHECK(exceptionThrown); // Test that CAddrDB::Read leaves addrman in a clean state if // de-serialization fails. CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted); CAddrMan addrman2; CAddrDB adb(Params()); BOOST_CHECK(addrman2.size() == 0); adb.Read(addrman2, ssPeers2); BOOST_CHECK(addrman2.size() == 0); } BOOST_AUTO_TEST_CASE(cnode_simple_test) { SOCKET hSocket = INVALID_SOCKET; NodeId id = 0; int height = 0; in_addr ipv4Addr; ipv4Addr.s_addr = 0xa0b0c001; CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK); std::string pszDest; bool fInboundIn = false; // Test that fFeeler is false by default. auto pnode1 = std::make_unique(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, CAddress(), pszDest, fInboundIn); BOOST_CHECK(pnode1->fInbound == false); BOOST_CHECK(pnode1->fFeeler == false); fInboundIn = true; auto pnode2 = std::make_unique(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, CAddress(), pszDest, fInboundIn); BOOST_CHECK(pnode2->fInbound == true); BOOST_CHECK(pnode2->fFeeler == false); } BOOST_AUTO_TEST_CASE(test_getSubVersionEB) { BOOST_CHECK_EQUAL(getSubVersionEB(13800000000), "13800.0"); BOOST_CHECK_EQUAL(getSubVersionEB(3800000000), "3800.0"); BOOST_CHECK_EQUAL(getSubVersionEB(14000000), "14.0"); BOOST_CHECK_EQUAL(getSubVersionEB(1540000), "1.5"); BOOST_CHECK_EQUAL(getSubVersionEB(1560000), "1.5"); BOOST_CHECK_EQUAL(getSubVersionEB(210000), "0.2"); BOOST_CHECK_EQUAL(getSubVersionEB(10000), "0.0"); BOOST_CHECK_EQUAL(getSubVersionEB(0), "0.0"); } BOOST_AUTO_TEST_CASE(test_userAgent) { NetTestConfig config; config.SetMaxBlockSize(8000000); const std::string uacomment = "A very nice comment"; gArgs.ForceSetMultiArg("-uacomment", uacomment); const std::string versionMessage = "/Bitcoin ABC:" + std::to_string(CLIENT_VERSION_MAJOR) + "." + std::to_string(CLIENT_VERSION_MINOR) + "." + std::to_string(CLIENT_VERSION_REVISION) + "(EB8.0; " + uacomment + ")/"; BOOST_CHECK_EQUAL(userAgent(config), versionMessage); } BOOST_AUTO_TEST_CASE(LimitedAndReachable_Network) { BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), true); BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), true); BOOST_CHECK_EQUAL(IsReachable(NET_ONION), true); SetReachable(NET_IPV4, false); SetReachable(NET_IPV6, false); SetReachable(NET_ONION, false); BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), false); BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), false); BOOST_CHECK_EQUAL(IsReachable(NET_ONION), false); SetReachable(NET_IPV4, true); SetReachable(NET_IPV6, true); SetReachable(NET_ONION, true); BOOST_CHECK_EQUAL(IsReachable(NET_IPV4), true); BOOST_CHECK_EQUAL(IsReachable(NET_IPV6), true); BOOST_CHECK_EQUAL(IsReachable(NET_ONION), true); } BOOST_AUTO_TEST_CASE(LimitedAndReachable_NetworkCaseUnroutableAndInternal) { BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true); BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true); SetReachable(NET_UNROUTABLE, false); SetReachable(NET_INTERNAL, false); // Ignored for both networks BOOST_CHECK_EQUAL(IsReachable(NET_UNROUTABLE), true); BOOST_CHECK_EQUAL(IsReachable(NET_INTERNAL), true); } CNetAddr UtilBuildAddress(uint8_t p1, uint8_t p2, uint8_t p3, uint8_t p4) { uint8_t ip[] = {p1, p2, p3, p4}; struct sockaddr_in sa; // initialize the memory block memset(&sa, 0, sizeof(sockaddr_in)); memcpy(&(sa.sin_addr), &ip, sizeof(ip)); return CNetAddr(sa.sin_addr); } BOOST_AUTO_TEST_CASE(LimitedAndReachable_CNetAddr) { // 1.1.1.1 CNetAddr addr = UtilBuildAddress(0x001, 0x001, 0x001, 0x001); SetReachable(NET_IPV4, true); BOOST_CHECK_EQUAL(IsReachable(addr), true); SetReachable(NET_IPV4, false); BOOST_CHECK_EQUAL(IsReachable(addr), false); // have to reset this, because this is stateful. SetReachable(NET_IPV4, true); } BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle) { // 2.1.1.1:1000 CService addr = CService(UtilBuildAddress(0x002, 0x001, 0x001, 0x001), 1000); SetReachable(NET_IPV4, true); BOOST_CHECK_EQUAL(IsLocal(addr), false); BOOST_CHECK_EQUAL(AddLocal(addr, 1000), true); BOOST_CHECK_EQUAL(IsLocal(addr), true); RemoveLocal(addr); BOOST_CHECK_EQUAL(IsLocal(addr), false); } // prior to PR #14728, this test triggers an undefined behavior BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test) { // set up local addresses; all that's necessary to reproduce the bug is // that a normal IPv4 address is among the entries, but if this address is // !IsRoutable the undefined behavior is easier to trigger deterministically { LOCK(cs_mapLocalHost); in_addr ipv4AddrLocal; ipv4AddrLocal.s_addr = 0x0100007f; CNetAddr addr = CNetAddr(ipv4AddrLocal); LocalServiceInfo lsi; lsi.nScore = 23; lsi.nPort = 42; mapLocalHost[addr] = lsi; } // create a peer with an IPv4 address in_addr ipv4AddrPeer; ipv4AddrPeer.s_addr = 0xa0b0c001; CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK); std::unique_ptr pnode = std::make_unique(0, NODE_NETWORK, 0, INVALID_SOCKET, addr, 0, 0, CAddress{}, std::string{}, false); pnode->fSuccessfullyConnected.store(true); // the peer claims to be reaching us via IPv6 in6_addr ipv6AddrLocal; memset(ipv6AddrLocal.s6_addr, 0, 16); ipv6AddrLocal.s6_addr[0] = 0xcc; CAddress addrLocal = CAddress(CService(ipv6AddrLocal, 7777), NODE_NETWORK); pnode->SetAddrLocal(addrLocal); // before patch, this causes undefined behavior detectable with clang's // -fsanitize=memory AdvertiseLocal(&*pnode); // suppress no-checks-run warning; if this test fails, it's by triggering a // sanitizer BOOST_CHECK(1); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/setup_common.cpp b/src/test/setup_common.cpp index eae0ca457..e7a01343c 100644 --- a/src/test/setup_common.cpp +++ b/src/test/setup_common.cpp @@ -1,323 +1,321 @@ // 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 #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include