diff options
39 files changed, 883 insertions, 617 deletions
diff --git a/.github/ISSUE_TEMPLATE/good_first_issue.md b/.github/ISSUE_TEMPLATE/good_first_issue.md index 8be78a1f6e..d32e22d360 100644 --- a/.github/ISSUE_TEMPLATE/good_first_issue.md +++ b/.github/ISSUE_TEMPLATE/good_first_issue.md @@ -2,11 +2,13 @@ name: Good first issue about: '(Regular devs only): Suggest a new good first issue' title: '' -labels: good first issue +labels: '' assignees: '' --- +<!-- Needs the label "good first issue" assigned manually before or after opening --> + <!-- A good first issue is an uncontroversial issue, that has a relatively unique and obvious solution --> <!-- Motivate the issue and explain the solution briefly --> diff --git a/build-aux/m4/ax_boost_thread.m4 b/build-aux/m4/ax_boost_thread.m4 index e9dea43535..75e80e6e75 100644 --- a/build-aux/m4/ax_boost_thread.m4 +++ b/build-aux/m4/ax_boost_thread.m4 @@ -30,7 +30,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 32 +#serial 33 AC_DEFUN([AX_BOOST_THREAD], [ @@ -67,13 +67,24 @@ AC_DEFUN([AX_BOOST_THREAD], [AC_LANG_PUSH([C++]) CXXFLAGS_SAVE=$CXXFLAGS - if test "x$host_os" = "xsolaris" ; then - CXXFLAGS="-pthreads $CXXFLAGS" - elif test "x$host_os" = "xmingw32" ; then - CXXFLAGS="-mthreads $CXXFLAGS" - else - CXXFLAGS="-pthread $CXXFLAGS" - fi + case "x$host_os" in + xsolaris ) + CXXFLAGS="-pthreads $CXXFLAGS" + break; + ;; + xmingw32 ) + CXXFLAGS="-mthreads $CXXFLAGS" + break; + ;; + *android* ) + break; + ;; + * ) + CXXFLAGS="-pthread $CXXFLAGS" + break; + ;; + esac + AC_COMPILE_IFELSE([ AC_LANG_PROGRAM( [[@%:@include <boost/thread/thread.hpp>]], @@ -84,13 +95,23 @@ AC_DEFUN([AX_BOOST_THREAD], AC_LANG_POP([C++]) ]) if test "x$ax_cv_boost_thread" = "xyes"; then - if test "x$host_os" = "xsolaris" ; then - BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" - elif test "x$host_os" = "xmingw32" ; then - BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" - else - BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" - fi + case "x$host_os" in + xsolaris ) + BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" + break; + ;; + xmingw32 ) + BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" + break; + ;; + *android* ) + break; + ;; + * ) + BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" + break; + ;; + esac AC_SUBST(BOOST_CPPFLAGS) @@ -148,6 +169,9 @@ AC_DEFUN([AX_BOOST_THREAD], xmingw32 ) break; ;; + *android* ) + break; + ;; * ) BOOST_THREAD_LIB="$BOOST_THREAD_LIB -lpthread" break; diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4 index e171238cbc..6c7665830b 100644 --- a/build-aux/m4/bitcoin_qt.m4 +++ b/build-aux/m4/bitcoin_qt.m4 @@ -72,18 +72,32 @@ AC_DEFUN([BITCOIN_QT_INIT],[ AC_ARG_WITH([qtdbus], [AS_HELP_STRING([--with-qtdbus], - [enable DBus support (default is yes if qt is enabled and QtDBus is found)])], + [enable DBus support (default is yes if qt is enabled and QtDBus is found, except on Android)])], [use_dbus=$withval], [use_dbus=auto]) + dnl Android doesn't support D-Bus and certainly doesn't use it for notifications + case $host in + *android*) + if test "x$use_dbus" != xyes; then + use_dbus=no + fi + ;; + esac + AC_SUBST(QT_TRANSLATION_DIR,$qt_translation_path) ]) dnl Find Qt libraries and includes. +dnl +dnl BITCOIN_QT_CONFIGURE([MINIMUM-VERSION]) +dnl dnl Outputs: See _BITCOIN_QT_FIND_LIBS dnl Outputs: Sets variables for all qt-related tools. dnl Outputs: bitcoin_enable_qt, bitcoin_enable_qt_dbus, bitcoin_enable_qt_test AC_DEFUN([BITCOIN_QT_CONFIGURE],[ + qt_version=">= $1" + qt_lib_prefix="Qt5" BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS]) dnl This is ugly and complicated. Yuck. Works as follows: @@ -221,7 +235,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ bitcoin_enable_qt=no ]) if test x$bitcoin_enable_qt = xyes; then - AC_MSG_RESULT([$bitcoin_enable_qt ($QT_LIB_PREFIX)]) + AC_MSG_RESULT([$bitcoin_enable_qt ($qt_lib_prefix)]) else AC_MSG_RESULT([$bitcoin_enable_qt]) fi @@ -295,25 +309,19 @@ AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[ if test -d "$qt_plugin_path/platforms/android"; then QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms/android -lqtfreetype -lEGL" fi - m4_ifdef([PKG_CHECK_MODULES],[ - if test x$bitcoin_cv_qt58 = xno; then - PKG_CHECK_MODULES([QTPLATFORM], [Qt5PlatformSupport], [QT_LIBS="$QTPLATFORM_LIBS $QT_LIBS"]) - else - PKG_CHECK_MODULES([QTFONTDATABASE], [Qt5FontDatabaseSupport], [QT_LIBS="-lQt5FontDatabaseSupport $QT_LIBS"]) - PKG_CHECK_MODULES([QTEVENTDISPATCHER], [Qt5EventDispatcherSupport], [QT_LIBS="-lQt5EventDispatcherSupport $QT_LIBS"]) - PKG_CHECK_MODULES([QTTHEME], [Qt5ThemeSupport], [QT_LIBS="-lQt5ThemeSupport $QT_LIBS"]) - PKG_CHECK_MODULES([QTDEVICEDISCOVERY], [Qt5DeviceDiscoverySupport], [QT_LIBS="-lQt5DeviceDiscoverySupport $QT_LIBS"]) - PKG_CHECK_MODULES([QTACCESSIBILITY], [Qt5AccessibilitySupport], [QT_LIBS="-lQt5AccessibilitySupport $QT_LIBS"]) - PKG_CHECK_MODULES([QTFB], [Qt5FbSupport], [QT_LIBS="-lQt5FbSupport $QT_LIBS"]) - fi - if test "x$TARGET_OS" = xlinux; then - PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"]) - elif test "x$TARGET_OS" = xdarwin; then - PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport], [QT_LIBS="-lQt5ClipboardSupport $QT_LIBS"]) - PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport], [QT_LIBS="-lQt5GraphicsSupport $QT_LIBS"]) - PKG_CHECK_MODULES([QTCGL], [Qt5CglSupport], [QT_LIBS="-lQt5CglSupport $QT_LIBS"]) - fi - ]) + PKG_CHECK_MODULES([QTFONTDATABASE], [Qt5FontDatabaseSupport], [QT_LIBS="-lQt5FontDatabaseSupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTEVENTDISPATCHER], [Qt5EventDispatcherSupport], [QT_LIBS="-lQt5EventDispatcherSupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTTHEME], [Qt5ThemeSupport], [QT_LIBS="-lQt5ThemeSupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTDEVICEDISCOVERY], [Qt5DeviceDiscoverySupport], [QT_LIBS="-lQt5DeviceDiscoverySupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTACCESSIBILITY], [Qt5AccessibilitySupport], [QT_LIBS="-lQt5AccessibilitySupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTFB], [Qt5FbSupport], [QT_LIBS="-lQt5FbSupport $QT_LIBS"]) + if test "x$TARGET_OS" = xlinux; then + PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"]) + elif test "x$TARGET_OS" = xdarwin; then + PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport], [QT_LIBS="-lQt5ClipboardSupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport], [QT_LIBS="-lQt5GraphicsSupport $QT_LIBS"]) + PKG_CHECK_MODULES([QTCGL], [Qt5CglSupport], [QT_LIBS="-lQt5CglSupport $QT_LIBS"]) + fi fi ]) @@ -321,23 +329,29 @@ dnl Internal. Find Qt libraries using pkg-config. dnl Outputs: All necessary QT_* variables are set. dnl Outputs: have_qt_test and have_qt_dbus are set (if applicable) to yes|no. AC_DEFUN([_BITCOIN_QT_FIND_LIBS],[ - m4_ifdef([PKG_CHECK_MODULES],[ - QT_LIB_PREFIX=Qt5 - qt5_modules="Qt5Core Qt5Gui Qt5Network Qt5Widgets" - BITCOIN_QT_CHECK([ - PKG_CHECK_MODULES([QT5], [$qt5_modules], [QT_INCLUDES="$QT5_CFLAGS"; QT_LIBS="$QT5_LIBS" have_qt=yes],[have_qt=no]) + BITCOIN_QT_CHECK([ + PKG_CHECK_MODULES([QT_CORE], [${qt_lib_prefix}Core $qt_version], [], + [BITCOIN_QT_FAIL([${qt_lib_prefix}Core $qt_version not found])]) + ]) + BITCOIN_QT_CHECK([ + PKG_CHECK_MODULES([QT_GUI], [${qt_lib_prefix}Gui $qt_version], [], + [BITCOIN_QT_FAIL([${qt_lib_prefix}Gui $qt_version not found])]) + ]) + BITCOIN_QT_CHECK([ + PKG_CHECK_MODULES([QT_WIDGETS], [${qt_lib_prefix}Widgets $qt_version], [], + [BITCOIN_QT_FAIL([${qt_lib_prefix}Widgets $qt_version not found])]) + ]) + BITCOIN_QT_CHECK([ + PKG_CHECK_MODULES([QT_NETWORK], [${qt_lib_prefix}Network $qt_version], [], + [BITCOIN_QT_FAIL([${qt_lib_prefix}Network $qt_version not found])]) + ]) + QT_INCLUDES="$QT_CORE_CFLAGS $QT_GUI_CFLAGS $QT_WIDGETS_CFLAGS $QT_NETWORK_CFLAGS" + QT_LIBS="$QT_CORE_LIBS $QT_GUI_LIBS $QT_WIDGETS_LIBS $QT_NETWORK_LIBS" - if test "x$have_qt" != xyes; then - have_qt=no - BITCOIN_QT_FAIL([Qt dependencies not found]) - fi - ]) - BITCOIN_QT_CHECK([ - PKG_CHECK_MODULES([QT_TEST], [${QT_LIB_PREFIX}Test], [QT_TEST_INCLUDES="$QT_TEST_CFLAGS"; have_qt_test=yes], [have_qt_test=no]) - if test "x$use_dbus" != xno; then - PKG_CHECK_MODULES([QT_DBUS], [${QT_LIB_PREFIX}DBus], [QT_DBUS_INCLUDES="$QT_DBUS_CFLAGS"; have_qt_dbus=yes], [have_qt_dbus=no]) - fi - ]) + BITCOIN_QT_CHECK([ + PKG_CHECK_MODULES([QT_TEST], [${qt_lib_prefix}Test $qt_version], [QT_TEST_INCLUDES="$QT_TEST_CFLAGS"; have_qt_test=yes], [have_qt_test=no]) + if test "x$use_dbus" != xno; then + PKG_CHECK_MODULES([QT_DBUS], [${qt_lib_prefix}DBus $qt_version], [QT_DBUS_INCLUDES="$QT_DBUS_CFLAGS"; have_qt_dbus=yes], [have_qt_dbus=no]) + fi ]) - true; dnl ]) diff --git a/build_msvc/common.init.vcxproj b/build_msvc/common.init.vcxproj index 4fd516fff5..a080fd2aa4 100644 --- a/build_msvc/common.init.vcxproj +++ b/build_msvc/common.init.vcxproj @@ -110,7 +110,7 @@ <AdditionalOptions>/utf-8 /std:c++17 %(AdditionalOptions)</AdditionalOptions> <DisableSpecificWarnings>4018;4221;4244;4267;4334;4715;4805;4834</DisableSpecificWarnings> <TreatWarningAsError>true</TreatWarningAsError> - <PreprocessorDefinitions>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING;ZMQ_STATIC;NOMINMAX;WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING;ZMQ_STATIC;NOMINMAX;WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;_WIN32_IE=0x0501;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\secp256k1\include;..\..\src\leveldb\include;..\..\src\leveldb\helpers\memenv;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link> diff --git a/configure.ac b/configure.ac index 3ab97eb531..acd4e0cf6c 100644 --- a/configure.ac +++ b/configure.ac @@ -604,7 +604,7 @@ case $host in AC_MSG_ERROR("windres not found") fi - CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -D_WIN32_WINNT=0x0601" + CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -D_WIN32_WINNT=0x0601 -D_WIN32_IE=0x0501 -DWIN32_LEAN_AND_MEAN" dnl libtool insists upon adding -nostdlib and a list of objects/libs to link against. dnl That breaks our ability to build dll's with static libgcc/libstdc++/libssp. Override @@ -1161,7 +1161,7 @@ else BITCOIN_QT_INIT dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus - BITCOIN_QT_CONFIGURE + BITCOIN_QT_CONFIGURE([5.5.1]) fi if test x$enable_wallet != xno; then diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk index 6f35ede248..ce1b25bcf1 100644 --- a/depends/packages/zeromq.mk +++ b/depends/packages/zeromq.mk @@ -3,7 +3,6 @@ $(package)_version=4.3.1 $(package)_download_path=https://github.com/zeromq/libzmq/releases/download/v$($(package)_version)/ $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=bcbabe1e2c7d0eec4ed612e10b94b112dd5f06fcefa994a0c79a45d835cd21eb -$(package)_patches=0001-fix-build-with-older-mingw64.patch 0002-disable-pthread_set_name_np.patch define $(package)_set_vars $(package)_config_opts=--without-docs --disable-shared --disable-curve --disable-curve-keygen --disable-perf @@ -16,8 +15,6 @@ define $(package)_set_vars endef define $(package)_preprocess_cmds - patch -p1 < $($(package)_patch_dir)/0001-fix-build-with-older-mingw64.patch && \ - patch -p1 < $($(package)_patch_dir)/0002-disable-pthread_set_name_np.patch && \ cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub config endef diff --git a/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch b/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch deleted file mode 100644 index b911ac5672..0000000000 --- a/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch +++ /dev/null @@ -1,30 +0,0 @@ -From f6866b0f166ad168618aae64c7fbee8775d3eb23 Mon Sep 17 00:00:00 2001 -From: mruddy <6440430+mruddy@users.noreply.github.com> -Date: Sat, 30 Jun 2018 09:44:58 -0400 -Subject: [PATCH] fix build with older mingw64 - ---- - src/windows.hpp | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/src/windows.hpp b/src/windows.hpp -index 6c3839fd..2c32ec79 100644 ---- a/src/windows.hpp -+++ b/src/windows.hpp -@@ -58,6 +58,13 @@ - #include <winsock2.h> - #include <windows.h> - #include <mswsock.h> -+ -+#if defined __MINGW64_VERSION_MAJOR && __MINGW64_VERSION_MAJOR < 4 -+// Workaround for mingw-w64 < v4.0 which did not include ws2ipdef.h in iphlpapi.h. -+// Fixed in mingw-w64 by 9bd8fe9148924840d315b4c915dd099955ea89d1. -+#include <ws2def.h> -+#include <ws2ipdef.h> -+#endif - #include <iphlpapi.h> - - #if !defined __MINGW32__ --- -2.17.1 - diff --git a/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch b/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch deleted file mode 100644 index b1c6f78a70..0000000000 --- a/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch +++ /dev/null @@ -1,35 +0,0 @@ -From c9bbdd6581d07acfe8971e4bcebe278a3676cf03 Mon Sep 17 00:00:00 2001 -From: mruddy <6440430+mruddy@users.noreply.github.com> -Date: Sat, 30 Jun 2018 09:57:18 -0400 -Subject: [PATCH] disable pthread_set_name_np - -pthread_set_name_np adds a Glibc requirement on >= 2.12. ---- - src/thread.cpp | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/src/thread.cpp b/src/thread.cpp -index a1086b0c..9943f354 100644 ---- a/src/thread.cpp -+++ b/src/thread.cpp -@@ -308,7 +308,7 @@ void zmq::thread_t::setThreadName (const char *name_) - */ - if (!name_) - return; -- -+#if 0 - #if defined(ZMQ_HAVE_PTHREAD_SETNAME_1) - int rc = pthread_setname_np (name_); - if (rc) -@@ -324,6 +324,8 @@ void zmq::thread_t::setThreadName (const char *name_) - #elif defined(ZMQ_HAVE_PTHREAD_SET_NAME) - pthread_set_name_np (_descriptor, name_); - #endif -+#endif -+ return; - } - - #endif --- -2.17.1 - diff --git a/doc/release-notes-19731.md b/doc/release-notes-19731.md new file mode 100644 index 0000000000..abe38e06af --- /dev/null +++ b/doc/release-notes-19731.md @@ -0,0 +1,6 @@ +Updated RPCs +------------ + +- The `getpeerinfo` RPC now has additional `last_block` and `last_transaction` + fields that return the UNIX epoch time of the last block and the last valid + transaction received from each peer. (#19731) diff --git a/doc/release-notes.md b/doc/release-notes.md index 4656963f5a..a8bd68370d 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -139,6 +139,10 @@ Updated settings in future releases. Refer to the help of the affected settings `-whitebind` and `-whitelist` for more details. (#19191) +- Netmasks that contain 1-bits after 0-bits (the 1-bits are not contiguous on + the left side, e.g. 255.0.255.255) are no longer accepted. They are invalid + according to RFC 4632. + Changes to Wallet or GUI related settings can be found in the GUI or Wallet section below. Tools and Utilities diff --git a/src/compat.h b/src/compat.h index 68f6eb692c..0be02cae03 100644 --- a/src/compat.h +++ b/src/compat.h @@ -11,9 +11,6 @@ #endif #ifdef WIN32 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN 1 -#endif #ifndef NOMINMAX #define NOMINMAX #endif diff --git a/src/hash.cpp b/src/hash.cpp index 4c09f5f646..83b90ae063 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -77,3 +77,10 @@ void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char he num[3] = (nChild >> 0) & 0xFF; CHMAC_SHA512(chainCode.begin(), chainCode.size()).Write(&header, 1).Write(data, 32).Write(num, 4).Finalize(output); } + +uint256 SHA256Uint256(const uint256& input) +{ + uint256 result; + CSHA256().Write(input.begin(), 32).Finalize(result.begin()); + return result; +} diff --git a/src/hash.h b/src/hash.h index 71806483ff..c16bbb48ce 100644 --- a/src/hash.h +++ b/src/hash.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_HASH_H #define BITCOIN_HASH_H +#include <attributes.h> #include <crypto/common.h> #include <crypto/ripemd160.h> #include <crypto/sha256.h> @@ -98,7 +99,7 @@ inline uint160 Hash160(const T1& in1) class CHashWriter { private: - CHash256 ctx; + CSHA256 ctx; const int nType; const int nVersion; @@ -110,13 +111,27 @@ public: int GetVersion() const { return nVersion; } void write(const char *pch, size_t size) { - ctx.Write({(const unsigned char*)pch, size}); + ctx.Write((const unsigned char*)pch, size); } - // invalidates the object + /** Compute the double-SHA256 hash of all data written to this object. + * + * Invalidates this object. + */ uint256 GetHash() { uint256 result; - ctx.Finalize(result); + ctx.Finalize(result.begin()); + ctx.Reset().Write(result.begin(), CSHA256::OUTPUT_SIZE).Finalize(result.begin()); + return result; + } + + /** Compute the SHA256 hash of all data written to this object. + * + * Invalidates this object. + */ + uint256 GetSHA256() { + uint256 result; + ctx.Finalize(result.begin()); return result; } @@ -124,9 +139,8 @@ public: * Returns the first 64 bits from the resulting hash. */ inline uint64_t GetCheapHash() { - unsigned char result[CHash256::OUTPUT_SIZE]; - ctx.Finalize(result); - return ReadLE64(result); + uint256 result = GetHash(); + return ReadLE64(result.begin()); } template<typename T> @@ -181,6 +195,9 @@ uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL return ss.GetHash(); } +/** Single-SHA256 a 32-byte input (represented as uint256). */ +NODISCARD uint256 SHA256Uint256(const uint256& input); + unsigned int MurmurHash3(unsigned int nHashSeed, Span<const unsigned char> vDataToHash); void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]); diff --git a/src/init.cpp b/src/init.cpp index eddb3ae9a0..ecd57960ad 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1371,7 +1371,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA node.chainman = &g_chainman; ChainstateManager& chainman = *Assert(node.chainman); - node.peer_logic.reset(new PeerLogicValidation(node.connman.get(), node.banman.get(), *node.scheduler, chainman, *node.mempool)); + node.peer_logic.reset(new PeerLogicValidation(*node.connman, node.banman.get(), *node.scheduler, chainman, *node.mempool)); RegisterValidationInterface(node.peer_logic.get()); // sanitize comments per BIP-0014, format user agent and check total size diff --git a/src/net.cpp b/src/net.cpp index 6c1980735c..883e57bdf0 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -530,6 +530,8 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap) } X(nLastSend); X(nLastRecv); + X(nLastTXTime); + X(nLastBlockTime); X(nTimeConnected); X(nTimeOffset); stats.addrName = GetAddrName(); @@ -619,6 +619,8 @@ public: bool fRelayTxes; int64_t nLastSend; int64_t nLastRecv; + int64_t nLastTXTime; + int64_t nLastBlockTime; int64_t nTimeConnected; int64_t nTimeOffset; std::string addrName; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 7b83583e41..60bdfbe9f5 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -844,7 +844,7 @@ void PeerLogicValidation::InitializeNode(CNode *pnode) { mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName), pnode->IsInboundConn(), pnode->IsManualConn())); } if(!pnode->IsInboundConn()) - PushNodeVersion(*pnode, *connman, GetTime()); + PushNodeVersion(*pnode, m_connman, GetTime()); } void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const @@ -855,7 +855,7 @@ void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const // Sanity check: all unbroadcast txns should exist in the mempool if (m_mempool.exists(elem.first)) { LOCK(cs_main); - RelayTransaction(elem.first, elem.second, *connman); + RelayTransaction(elem.first, elem.second, m_connman); } else { m_mempool.RemoveUnbroadcastTx(elem.first, true); } @@ -1197,8 +1197,8 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT); } -PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool) - : connman(connmanIn), +PeerLogicValidation::PeerLogicValidation(CConnman& connman, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool) + : m_connman(connman), m_banman(banman), m_chainman(chainman), m_mempool(pool), @@ -1326,7 +1326,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std: fWitnessesPresentInMostRecentCompactBlock = fWitnessEnabled; } - connman->ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) { + m_connman.ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) { AssertLockHeld(cs_main); // TODO: Avoid the repeated-serialization here @@ -1341,7 +1341,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std: LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerLogicValidation::NewPoWValidBlock", hashBlock.ToString(), pnode->GetId()); - connman->PushMessage(pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock)); + m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock)); state.pindexBestHeaderSent = pindex; } }); @@ -1353,7 +1353,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std: */ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { const int nNewHeight = pindexNew->nHeight; - connman->SetBestHeight(nNewHeight); + m_connman.SetBestHeight(nNewHeight); SetServiceFlagsIBDCache(!fInitialDownload); if (!fInitialDownload) { @@ -1370,7 +1370,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB } } // Relay inventory, but don't relay old inventory during initial block download. - connman->ForEachNode([nNewHeight, &vHashes](CNode* pnode) { + m_connman.ForEachNode([nNewHeight, &vHashes](CNode* pnode) { LOCK(pnode->cs_inventory); if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) { for (const uint256& hash : reverse_iterate(vHashes)) { @@ -1378,7 +1378,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB } } }); - connman->WakeMessageHandler(); + m_connman.WakeMessageHandler(); } } @@ -1409,7 +1409,7 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidatio !::ChainstateActive().IsInitialBlockDownload() && mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) { if (it != mapBlockSource.end()) { - MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, *connman); + MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, m_connman); } } if (it != mapBlockSource.end()) @@ -2302,17 +2302,9 @@ static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainPar connman.PushMessage(&peer, std::move(msg)); } -void ProcessMessage( - CNode& pfrom, - const std::string& msg_type, - CDataStream& vRecv, - const std::chrono::microseconds time_received, - const CChainParams& chainparams, - ChainstateManager& chainman, - CTxMemPool& mempool, - CConnman& connman, - BanMan* banman, - const std::atomic<bool>& interruptMsgProc) +void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, + const std::chrono::microseconds time_received, + const CChainParams& chainparams, const std::atomic<bool>& interruptMsgProc) { LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom.GetId()); if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0) @@ -2348,7 +2340,7 @@ void ProcessMessage( nServices = ServiceFlags(nServiceInt); if (!pfrom.IsInboundConn()) { - connman.SetServices(pfrom.addr, nServices); + m_connman.SetServices(pfrom.addr, nServices); } if (pfrom.ExpectServicesFromConn() && !HasAllDesirableServiceFlags(nServices)) { @@ -2377,7 +2369,7 @@ void ProcessMessage( if (!vRecv.empty()) vRecv >> fRelay; // Disconnect if we connected to ourself - if (pfrom.IsInboundConn() && !connman.CheckIncomingNonce(nNonce)) + if (pfrom.IsInboundConn() && !m_connman.CheckIncomingNonce(nNonce)) { LogPrintf("connected to self at %s, disconnecting\n", pfrom.addr.ToString()); pfrom.fDisconnect = true; @@ -2391,13 +2383,13 @@ void ProcessMessage( // Be shy and don't send version until we hear if (pfrom.IsInboundConn()) - PushNodeVersion(pfrom, connman, GetAdjustedTime()); + PushNodeVersion(pfrom, m_connman, GetAdjustedTime()); if (nVersion >= WTXID_RELAY_VERSION) { - connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::WTXIDRELAY)); + m_connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::WTXIDRELAY)); } - connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK)); + m_connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK)); pfrom.nServices = nServices; pfrom.SetAddrLocal(addrMe); @@ -2453,9 +2445,9 @@ void ProcessMessage( } // Get recent addresses - connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR)); + m_connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR)); pfrom.fGetAddr = true; - connman.MarkAddressGood(pfrom.addr); + m_connman.MarkAddressGood(pfrom.addr); } std::string remoteAddr; @@ -2474,7 +2466,7 @@ void ProcessMessage( // If the peer is old enough to have the old alert system, send it the final alert. if (pfrom.nVersion <= 70012) { CDataStream finalAlert(ParseHex("60010000000000000000000000ffffff7f00000000ffffff7ffeffff7f01ffffff7f00000000ffffff7f00ffffff7f002f555247454e543a20416c657274206b657920636f6d70726f6d697365642c2075706772616465207265717569726564004630440220653febd6410f470f6bae11cad19c48413becb1ac2c17f908fd0fd53bdc3abd5202206d0e9c96fe88d4a0f01ed9dedae2b6f9e00da94cad0fecaae66ecf689bf71b50"), SER_NETWORK, PROTOCOL_VERSION); - connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert)); + m_connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert)); } // Feeler connections exist only to verify if address is online. @@ -2513,7 +2505,7 @@ void ProcessMessage( // We send this to non-NODE NETWORK peers as well, because even // non-NODE NETWORK peers can announce blocks (such as pruning // nodes) - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDHEADERS)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDHEADERS)); } if (pfrom.nVersion >= SHORT_IDS_BLOCKS_VERSION) { // Tell our peer we are willing to provide version 1 or 2 cmpctblocks @@ -2524,9 +2516,9 @@ void ProcessMessage( bool fAnnounceUsingCMPCTBLOCK = false; uint64_t nCMPCTBLOCKVersion = 2; if (pfrom.GetLocalServices() & NODE_WITNESS) - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); nCMPCTBLOCKVersion = 1; - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); } pfrom.fSuccessfullyConnected = true; return; @@ -2590,7 +2582,7 @@ void ProcessMessage( if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; pfrom.AddAddressKnown(addr); - if (banman && (banman->IsDiscouraged(addr) || banman->IsBanned(addr))) { + if (m_banman && (m_banman->IsDiscouraged(addr) || m_banman->IsBanned(addr))) { // Do not process banned/discouraged addresses beyond remembering we received them continue; } @@ -2598,13 +2590,13 @@ void ProcessMessage( if (addr.nTime > nSince && !pfrom.fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) { // Relay to a limited number of other nodes - RelayAddress(addr, fReachable, connman); + RelayAddress(addr, fReachable, m_connman); } // Do not store addresses outside our network if (fReachable) vAddrOk.push_back(addr); } - connman.AddNewAddresses(vAddrOk, pfrom.addr, 2 * 60 * 60); + m_connman.AddNewAddresses(vAddrOk, pfrom.addr, 2 * 60 * 60); if (vAddr.size() < 1000) pfrom.fGetAddr = false; if (pfrom.IsAddrFetchConn()) @@ -2680,7 +2672,7 @@ void ProcessMessage( if (inv.IsMsgWtx()) continue; } - bool fAlreadyHave = AlreadyHave(inv, mempool); + bool fAlreadyHave = AlreadyHave(inv, m_mempool); LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId()); if (inv.IsMsgTx()) { @@ -2703,14 +2695,14 @@ void ProcessMessage( LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom.GetId()); pfrom.fDisconnect = true; return; - } else if (!fAlreadyHave && !chainman.ActiveChainstate().IsInitialBlockDownload()) { + } else if (!fAlreadyHave && !m_chainman.ActiveChainstate().IsInitialBlockDownload()) { RequestTx(State(pfrom.GetId()), ToGenTxid(inv), current_time); } } } if (best_block != nullptr) { - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), *best_block)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), *best_block)); LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, best_block->ToString(), pfrom.GetId()); } @@ -2734,7 +2726,7 @@ void ProcessMessage( } pfrom.vRecvGetData.insert(pfrom.vRecvGetData.end(), vInv.begin(), vInv.end()); - ProcessGetData(pfrom, chainparams, connman, mempool, interruptMsgProc); + ProcessGetData(pfrom, chainparams, m_connman, m_mempool, interruptMsgProc); return; } @@ -2818,7 +2810,7 @@ void ProcessMessage( // Unlock cs_most_recent_block to avoid cs_main lock inversion } if (recent_block) { - SendBlockTransactions(*recent_block, req, pfrom, connman); + SendBlockTransactions(*recent_block, req, pfrom, m_connman); return; } @@ -2851,7 +2843,7 @@ void ProcessMessage( bool ret = ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()); assert(ret); - SendBlockTransactions(block, req, pfrom, connman); + SendBlockTransactions(block, req, pfrom, m_connman); return; } @@ -2918,7 +2910,7 @@ void ProcessMessage( // will re-announce the new block via headers (or compact blocks again) // in the SendMessages logic. nodestate->pindexBestHeaderSent = pindex ? pindex : ::ChainActive().Tip(); - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); return; } @@ -2977,10 +2969,10 @@ void ProcessMessage( // already; and an adversary can already relay us old transactions // (older than our recency filter) if trying to DoS us, without any need // for witness malleation. - if (!AlreadyHave(CInv(MSG_WTX, wtxid), mempool) && - AcceptToMemoryPool(mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) { - mempool.check(&::ChainstateActive().CoinsTip()); - RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), connman); + if (!AlreadyHave(CInv(MSG_WTX, wtxid), m_mempool) && + AcceptToMemoryPool(m_mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) { + m_mempool.check(&::ChainstateActive().CoinsTip()); + RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman); for (unsigned int i = 0; i < tx.vout.size(); i++) { auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(txid, i)); if (it_by_prev != mapOrphanTransactionsByPrev.end()) { @@ -2995,10 +2987,10 @@ void ProcessMessage( LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", pfrom.GetId(), tx.GetHash().ToString(), - mempool.size(), mempool.DynamicMemoryUsage() / 1000); + m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000); // Recursively process any orphan transactions that depended on this one - ProcessOrphanTx(connman, mempool, pfrom.orphan_work_set, lRemovedTxn); + ProcessOrphanTx(m_connman, m_mempool, pfrom.orphan_work_set, lRemovedTxn); } else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { @@ -3032,7 +3024,7 @@ void ProcessMessage( // protocol for getting all unconfirmed parents. CInv _inv(MSG_TX | nFetchFlags, parent_txid); pfrom.AddKnownTx(parent_txid); - if (!AlreadyHave(_inv, mempool)) RequestTx(State(pfrom.GetId()), ToGenTxid(_inv), current_time); + if (!AlreadyHave(_inv, m_mempool)) RequestTx(State(pfrom.GetId()), ToGenTxid(_inv), current_time); } AddOrphanTx(ptx, pfrom.GetId()); @@ -3093,11 +3085,11 @@ void ProcessMessage( // if they were already in the mempool, // allowing the node to function as a gateway for // nodes hidden behind it. - if (!mempool.exists(tx.GetHash())) { + if (!m_mempool.exists(tx.GetHash())) { LogPrintf("Not relaying non-mempool transaction %s from forcerelay peer=%d\n", tx.GetHash().ToString(), pfrom.GetId()); } else { LogPrintf("Force relaying tx %s from peer=%d\n", tx.GetHash().ToString(), pfrom.GetId()); - RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), connman); + RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman); } } } @@ -3150,7 +3142,7 @@ void ProcessMessage( if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) { // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers if (!::ChainstateActive().IsInitialBlockDownload()) - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256())); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256())); return; } @@ -3161,7 +3153,7 @@ void ProcessMessage( const CBlockIndex *pindex = nullptr; BlockValidationState state; - if (!chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) { + if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) { if (state.IsInvalid()) { MaybePunishNodeForBlock(pfrom.GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock"); return; @@ -3211,7 +3203,7 @@ void ProcessMessage( // so we just grab the block via normal getdata std::vector<CInv> vInv(1); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash()); - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); } return; } @@ -3232,9 +3224,9 @@ void ProcessMessage( if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) || (fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) { std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr; - if (!MarkBlockAsInFlight(mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) { + if (!MarkBlockAsInFlight(m_mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) { if (!(*queuedBlockIt)->partialBlock) - (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&mempool)); + (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&m_mempool)); else { // The block was already in flight using compact blocks from the same peer LogPrint(BCLog::NET, "Peer sent us compact block we were already syncing!\n"); @@ -3252,7 +3244,7 @@ void ProcessMessage( // Duplicate txindexes, the block is now in-flight, so just request it std::vector<CInv> vInv(1); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash()); - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); return; } @@ -3269,7 +3261,7 @@ void ProcessMessage( fProcessBLOCKTXN = true; } else { req.blockhash = pindex->GetBlockHash(); - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req)); } } else { // This block is either already in flight from a different @@ -3277,7 +3269,7 @@ void ProcessMessage( // download from. // Optimistically try to reconstruct anyway since we might be // able to without any round trips. - PartiallyDownloadedBlock tempBlock(&mempool); + PartiallyDownloadedBlock tempBlock(&m_mempool); ReadStatus status = tempBlock.InitData(cmpctblock, vExtraTxnForCompact); if (status != READ_STATUS_OK) { // TODO: don't ignore failures @@ -3295,7 +3287,7 @@ void ProcessMessage( // mempool will probably be useless - request the block normally std::vector<CInv> vInv(1); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash()); - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); return; } else { // If this was an announce-cmpctblock, we want the same treatment as a header message @@ -3305,7 +3297,7 @@ void ProcessMessage( } // cs_main if (fProcessBLOCKTXN) - return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, chainparams, chainman, mempool, connman, banman, interruptMsgProc); + return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, chainparams, interruptMsgProc); if (fRevertToHeaderProcessing) { // Headers received from HB compact block peers are permitted to be @@ -3313,7 +3305,7 @@ void ProcessMessage( // the peer if the header turns out to be for an invalid block. // Note that if a peer tries to build on an invalid chain, that // will be detected and the peer will be disconnected/discouraged. - return ProcessHeadersMessage(pfrom, connman, chainman, mempool, {cmpctblock.header}, chainparams, /*via_compact_block=*/true); + return ProcessHeadersMessage(pfrom, m_connman, m_chainman, m_mempool, {cmpctblock.header}, chainparams, /*via_compact_block=*/true); } if (fBlockReconstructed) { @@ -3333,7 +3325,7 @@ void ProcessMessage( // we have a chain with at least nMinimumChainWork), and we ignore // compact blocks with less work than our tip, it is safe to treat // reconstructed compact blocks as having been requested. - chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); + m_chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); if (fNewBlock) { pfrom.nLastBlockTime = GetTime(); } else { @@ -3385,7 +3377,7 @@ void ProcessMessage( // Might have collided, fall back to getdata now :( std::vector<CInv> invs; invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom), resp.blockhash)); - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs)); } else { // Block is either okay, or possibly we received // READ_STATUS_CHECKBLOCK_FAILED. @@ -3423,7 +3415,7 @@ void ProcessMessage( // disk-space attacks), but this should be safe due to the // protections in the compact block handler -- see related comment // in compact block optimistic reconstruction handling. - chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); + m_chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); if (fNewBlock) { pfrom.nLastBlockTime = GetTime(); } else { @@ -3457,7 +3449,7 @@ void ProcessMessage( ReadCompactSize(vRecv); // ignore tx count; assume it is 0. } - return ProcessHeadersMessage(pfrom, connman, chainman, mempool, headers, chainparams, /*via_compact_block=*/false); + return ProcessHeadersMessage(pfrom, m_connman, m_chainman, m_mempool, headers, chainparams, /*via_compact_block=*/false); } if (msg_type == NetMsgType::BLOCK) @@ -3486,7 +3478,7 @@ void ProcessMessage( mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true)); } bool fNewBlock = false; - chainman.ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock); + m_chainman.ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock); if (fNewBlock) { pfrom.nLastBlockTime = GetTime(); } else { @@ -3522,9 +3514,9 @@ void ProcessMessage( pfrom.vAddrToSend.clear(); std::vector<CAddress> vAddr; if (pfrom.HasPermission(PF_ADDR)) { - vAddr = connman.GetAddresses(MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND); + vAddr = m_connman.GetAddresses(MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND); } else { - vAddr = connman.GetAddresses(pfrom.addr.GetNetwork(), MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND); + vAddr = m_connman.GetAddresses(pfrom.addr.GetNetwork(), MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND); } FastRandomContext insecure_rand; for (const CAddress &addr : vAddr) { @@ -3544,7 +3536,7 @@ void ProcessMessage( return; } - if (connman.OutboundTargetReached(false) && !pfrom.HasPermission(PF_MEMPOOL)) + if (m_connman.OutboundTargetReached(false) && !pfrom.HasPermission(PF_MEMPOOL)) { if (!pfrom.HasPermission(PF_NOBAN)) { @@ -3577,7 +3569,7 @@ void ProcessMessage( // it, if the remote node sends a ping once per second and this node takes 5 // seconds to respond to each, the 5th ping the remote sends would appear to // return very quickly. - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::PONG, nonce)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::PONG, nonce)); } return; } @@ -3717,17 +3709,17 @@ void ProcessMessage( } if (msg_type == NetMsgType::GETCFILTERS) { - ProcessGetCFilters(pfrom, vRecv, chainparams, connman); + ProcessGetCFilters(pfrom, vRecv, chainparams, m_connman); return; } if (msg_type == NetMsgType::GETCFHEADERS) { - ProcessGetCFHeaders(pfrom, vRecv, chainparams, connman); + ProcessGetCFHeaders(pfrom, vRecv, chainparams, m_connman); return; } if (msg_type == NetMsgType::GETCFCHECKPT) { - ProcessGetCFCheckPt(pfrom, vRecv, chainparams, connman); + ProcessGetCFCheckPt(pfrom, vRecv, chainparams, m_connman); return; } @@ -3802,7 +3794,7 @@ bool PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode& pnode) // Normal case: Disconnect the peer and discourage all nodes sharing the address LogPrintf("Disconnecting and discouraging peer %d!\n", peer_id); if (m_banman) m_banman->Discourage(pnode.addr); - connman->DisconnectNode(pnode.addr); + m_connman.DisconnectNode(pnode.addr); return true; } @@ -3820,12 +3812,12 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter bool fMoreWork = false; if (!pfrom->vRecvGetData.empty()) - ProcessGetData(*pfrom, chainparams, *connman, m_mempool, interruptMsgProc); + ProcessGetData(*pfrom, chainparams, m_connman, m_mempool, interruptMsgProc); if (!pfrom->orphan_work_set.empty()) { std::list<CTransactionRef> removed_txn; LOCK2(cs_main, g_cs_orphans); - ProcessOrphanTx(*connman, m_mempool, pfrom->orphan_work_set, removed_txn); + ProcessOrphanTx(m_connman, m_mempool, pfrom->orphan_work_set, removed_txn); for (const CTransactionRef& removedTx : removed_txn) { AddToCompactExtraTransactions(removedTx); } @@ -3851,7 +3843,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter // Just take one message msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin()); pfrom->nProcessQueueSize -= msgs.front().m_raw_message_size; - pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman->GetReceiveFloodSize(); + pfrom->fPauseRecv = pfrom->nProcessQueueSize > m_connman.GetReceiveFloodSize(); fMoreWork = !pfrom->vProcessMsg.empty(); } CNetMessage& msg(msgs.front()); @@ -3885,7 +3877,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter } try { - ProcessMessage(*pfrom, msg_type, vRecv, msg.m_time, chainparams, m_chainman, m_mempool, *connman, m_banman, interruptMsgProc); + ProcessMessage(*pfrom, msg_type, vRecv, msg.m_time, chainparams, interruptMsgProc); if (interruptMsgProc) return false; if (!pfrom->vRecvGetData.empty()) @@ -3938,7 +3930,7 @@ void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds) } else { assert(state.m_chain_sync.m_work_header); LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString()); - connman->PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256())); + m_connman.PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256())); state.m_chain_sync.m_sent_getheaders = true; constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes // Bump the timeout to allow a response, which could clear the timeout @@ -3955,7 +3947,7 @@ void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds) void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds) { // Check whether we have too many outbound peers - int extra_peers = connman->GetExtraOutboundCount(); + int extra_peers = m_connman.GetExtraOutboundCount(); if (extra_peers > 0) { // If we have more outbound peers than we target, disconnect one. // Pick the outbound peer that least recently announced @@ -3964,7 +3956,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds) NodeId worst_peer = -1; int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max(); - connman->ForEachNode([&](CNode* pnode) { + m_connman.ForEachNode([&](CNode* pnode) { AssertLockHeld(cs_main); // Ignore non-outbound peers, or nodes marked for disconnect already @@ -3981,7 +3973,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds) } }); if (worst_peer != -1) { - bool disconnected = connman->ForNode(worst_peer, [&](CNode *pnode) { + bool disconnected = m_connman.ForNode(worst_peer, [&](CNode *pnode) { AssertLockHeld(cs_main); // Only disconnect a peer that has been connected to us for @@ -4005,7 +3997,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds) // detected a stale tip. Don't try any more extra peers until // we next detect a stale tip, to limit the load we put on the // network from these extra connections. - connman->SetTryNewOutboundPeer(false); + m_connman.SetTryNewOutboundPeer(false); } } } @@ -4015,8 +4007,6 @@ void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params { LOCK(cs_main); - if (connman == nullptr) return; - int64_t time_in_seconds = GetTime(); EvictExtraOutboundPeers(time_in_seconds); @@ -4024,11 +4014,11 @@ void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params if (time_in_seconds > m_stale_tip_check_time) { // Check whether our tip is stale, and if so, allow using an extra // outbound peer - if (!fImporting && !fReindex && connman->GetNetworkActive() && connman->GetUseAddrmanOutgoing() && TipMayBeStale(consensusParams)) { + if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale(consensusParams)) { LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - g_last_tip_update); - connman->SetTryNewOutboundPeer(true); - } else if (connman->GetTryNewOutboundPeer()) { - connman->SetTryNewOutboundPeer(false); + m_connman.SetTryNewOutboundPeer(true); + } else if (m_connman.GetTryNewOutboundPeer()) { + m_connman.SetTryNewOutboundPeer(false); } m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL; } @@ -4091,11 +4081,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto) pto->m_ping_start = GetTime<std::chrono::microseconds>(); if (pto->nVersion > BIP0031_VERSION) { pto->nPingNonceSent = nonce; - connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce)); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce)); } else { // Peer is too old to support ping command with nonce, pong will never arrive. pto->nPingNonceSent = 0; - connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING)); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING)); } } @@ -4130,14 +4120,14 @@ bool PeerLogicValidation::SendMessages(CNode* pto) // receiver rejects addr messages larger than MAX_ADDR_TO_SEND if (vAddr.size() >= MAX_ADDR_TO_SEND) { - connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); vAddr.clear(); } } } pto->vAddrToSend.clear(); if (!vAddr.empty()) - connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); // we only send the big addr message once if (pto->vAddrToSend.capacity() > 40) pto->vAddrToSend.shrink_to_fit(); @@ -4164,7 +4154,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) if (pindexStart->pprev) pindexStart = pindexStart->pprev; LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), pto->nStartingHeight); - connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexStart), uint256())); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexStart), uint256())); } } @@ -4248,10 +4238,10 @@ bool PeerLogicValidation::SendMessages(CNode* pto) LOCK(cs_most_recent_block); if (most_recent_block_hash == pBestIndex->GetBlockHash()) { if (state.fWantsCmpctWitness || !fWitnessesPresentInMostRecentCompactBlock) - connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block)); + m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block)); else { CBlockHeaderAndShortTxIDs cmpctblock(*most_recent_block, state.fWantsCmpctWitness); - connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); } fGotBlockFromCache = true; } @@ -4261,7 +4251,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) bool ret = ReadBlockFromDisk(block, pBestIndex, consensusParams); assert(ret); CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness); - connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); } state.pindexBestHeaderSent = pBestIndex; } else if (state.fPreferHeaders) { @@ -4274,7 +4264,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) LogPrint(BCLog::NET, "%s: sending header %s to peer=%d\n", __func__, vHeaders.front().GetHash().ToString(), pto->GetId()); } - connman->PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); state.pindexBestHeaderSent = pBestIndex; } else fRevertToInv = true; @@ -4319,7 +4309,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) for (const uint256& hash : pto->vInventoryBlockToSend) { vInv.push_back(CInv(MSG_BLOCK, hash)); if (vInv.size() == MAX_INV_SZ) { - connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } } @@ -4332,7 +4322,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) if (pto->m_tx_relay->nNextInvSend < current_time) { fSendTrickle = true; if (pto->IsInboundConn()) { - pto->m_tx_relay->nNextInvSend = std::chrono::microseconds{connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL)}; + pto->m_tx_relay->nNextInvSend = std::chrono::microseconds{m_connman.PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL)}; } else { // Use half the delay for outbound peers, as there is less privacy concern for them. pto->m_tx_relay->nNextInvSend = PoissonNextSend(current_time, std::chrono::seconds{INVENTORY_BROADCAST_INTERVAL >> 1}); @@ -4372,7 +4362,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) // Responses to MEMPOOL requests bypass the m_recently_announced_invs filter. vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { - connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } } @@ -4448,7 +4438,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) } } if (vInv.size() == MAX_INV_SZ) { - connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } pto->m_tx_relay->filterInventoryKnown.insert(hash); @@ -4465,7 +4455,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) } } if (!vInv.empty()) - connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); // Detect whether we're stalling current_time = GetTime<std::chrono::microseconds>(); @@ -4593,7 +4583,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) LogPrint(BCLog::NET, "Requesting %s peer=%d\n", inv.ToString(), pto->GetId()); vGetData.push_back(inv); if (vGetData.size() >= MAX_GETDATA_SZ) { - connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); vGetData.clear(); } UpdateTxRequestTime(gtxid, current_time); @@ -4623,7 +4613,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) if (!vGetData.empty()) - connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); // // Message: feefilter @@ -4651,7 +4641,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) // We always have a fee filter of at least minRelayTxFee filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK()); if (filterToSend != pto->m_tx_relay->lastSentFeeFilter) { - connman->PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend)); + m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend)); pto->m_tx_relay->lastSentFeeFilter = filterToSend; } pto->m_tx_relay->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL); diff --git a/src/net_processing.h b/src/net_processing.h index 2d98714122..74d6603747 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -11,6 +11,7 @@ #include <sync.h> #include <validationinterface.h> +class CChainParams; class CTxMemPool; class ChainstateManager; @@ -28,7 +29,7 @@ static const int DISCOURAGEMENT_THRESHOLD{100}; class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface { private: - CConnman* const connman; + CConnman& m_connman; /** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */ BanMan* const m_banman; ChainstateManager& m_chainman; @@ -37,7 +38,7 @@ private: bool MaybeDiscourageAndDisconnect(CNode& pnode); public: - PeerLogicValidation(CConnman* connman, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool); + PeerLogicValidation(CConnman& connman, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool); /** * Overridden from CValidationInterface. @@ -85,8 +86,14 @@ public: /** Retrieve unbroadcast transactions from the mempool and reattempt sending to peers */ void ReattemptInitialBroadcast(CScheduler& scheduler) const; + /** Process a single message from a peer. Public for fuzz testing */ + void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, + const std::chrono::microseconds time_received, const CChainParams& chainparams, + const std::atomic<bool>& interruptMsgProc); + private: int64_t m_stale_tip_check_time; //!< Next time to check for stale tip + }; struct CNodeStateStats { diff --git a/src/netaddress.cpp b/src/netaddress.cpp index d29aed6c8b..cb874e5e31 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -3,79 +3,90 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <cstdint> #include <netaddress.h> #include <hash.h> #include <util/strencodings.h> #include <util/asmap.h> #include <tinyformat.h> -static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; -static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43}; +#include <algorithm> +#include <array> +#include <cstdint> +#include <iterator> +#include <tuple> -// 0xFD + sha256("bitcoin")[0:5] -static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 }; +constexpr size_t CNetAddr::V1_SERIALIZATION_SIZE; /** * Construct an unspecified IPv6 network address (::/128). * * @note This address is considered invalid by CNetAddr::IsValid() */ -CNetAddr::CNetAddr() -{ - memset(ip, 0, sizeof(ip)); -} +CNetAddr::CNetAddr() {} void CNetAddr::SetIP(const CNetAddr& ipIn) { + // Size check. + switch (ipIn.m_net) { + case NET_IPV4: + assert(ipIn.m_addr.size() == ADDR_IPV4_SIZE); + break; + case NET_IPV6: + assert(ipIn.m_addr.size() == ADDR_IPV6_SIZE); + break; + case NET_ONION: + assert(ipIn.m_addr.size() == ADDR_TORV2_SIZE); + break; + case NET_INTERNAL: + assert(ipIn.m_addr.size() == ADDR_INTERNAL_SIZE); + break; + case NET_UNROUTABLE: + case NET_MAX: + assert(false); + } // no default case, so the compiler can warn about missing cases + m_net = ipIn.m_net; - memcpy(ip, ipIn.ip, sizeof(ip)); + m_addr = ipIn.m_addr; } -void CNetAddr::SetLegacyIPv6(const uint8_t ipv6[16]) +template <typename T1, size_t PREFIX_LEN> +inline bool HasPrefix(const T1& obj, const std::array<uint8_t, PREFIX_LEN>& prefix) { - if (memcmp(ipv6, pchIPv4, sizeof(pchIPv4)) == 0) { + return obj.size() >= PREFIX_LEN && + std::equal(std::begin(prefix), std::end(prefix), std::begin(obj)); +} + +void CNetAddr::SetLegacyIPv6(Span<const uint8_t> ipv6) +{ + assert(ipv6.size() == ADDR_IPV6_SIZE); + + size_t skip{0}; + + if (HasPrefix(ipv6, IPV4_IN_IPV6_PREFIX)) { + // IPv4-in-IPv6 m_net = NET_IPV4; - } else if (memcmp(ipv6, pchOnionCat, sizeof(pchOnionCat)) == 0) { + skip = sizeof(IPV4_IN_IPV6_PREFIX); + } else if (HasPrefix(ipv6, TORV2_IN_IPV6_PREFIX)) { + // TORv2-in-IPv6 m_net = NET_ONION; - } else if (memcmp(ipv6, g_internal_prefix, sizeof(g_internal_prefix)) == 0) { + skip = sizeof(TORV2_IN_IPV6_PREFIX); + } else if (HasPrefix(ipv6, INTERNAL_IN_IPV6_PREFIX)) { + // Internal-in-IPv6 m_net = NET_INTERNAL; + skip = sizeof(INTERNAL_IN_IPV6_PREFIX); } else { + // IPv6 m_net = NET_IPV6; } - memcpy(ip, ipv6, 16); -} -void CNetAddr::SetRaw(Network network, const uint8_t *ip_in) -{ - switch(network) - { - case NET_IPV4: - m_net = NET_IPV4; - memcpy(ip, pchIPv4, 12); - memcpy(ip+12, ip_in, 4); - break; - case NET_IPV6: - SetLegacyIPv6(ip_in); - break; - default: - assert(!"invalid network"); - } + m_addr.assign(ipv6.begin() + skip, ipv6.end()); } /** - * Try to make this a dummy address that maps the specified name into IPv6 like - * so: (0xFD + %sha256("bitcoin")[0:5]) + %sha256(name)[0:10]. Such dummy - * addresses have a prefix of fd6b:88c0:8724::/48 and are guaranteed to not be - * publicly routable as it falls under RFC4193's fc00::/7 subnet allocated to - * unique-local addresses. - * - * CAddrMan uses these fake addresses to keep track of which DNS seeds were - * used. - * + * Create an "internal" address that represents a name or FQDN. CAddrMan uses + * these fake addresses to keep track of which DNS seeds were used. * @returns Whether or not the operation was successful. - * - * @see CNetAddr::IsInternal(), CNetAddr::IsRFC4193() + * @see NET_INTERNAL, INTERNAL_IN_IPV6_PREFIX, CNetAddr::IsInternal(), CNetAddr::IsRFC4193() */ bool CNetAddr::SetInternal(const std::string &name) { @@ -85,31 +96,26 @@ bool CNetAddr::SetInternal(const std::string &name) m_net = NET_INTERNAL; unsigned char hash[32] = {}; CSHA256().Write((const unsigned char*)name.data(), name.size()).Finalize(hash); - memcpy(ip, g_internal_prefix, sizeof(g_internal_prefix)); - memcpy(ip + sizeof(g_internal_prefix), hash, sizeof(ip) - sizeof(g_internal_prefix)); + m_addr.assign(hash, hash + ADDR_INTERNAL_SIZE); return true; } /** - * Try to make this a dummy address that maps the specified onion address into - * IPv6 using OnionCat's range and encoding. Such dummy addresses have a prefix - * of fd87:d87e:eb43::/48 and are guaranteed to not be publicly routable as they - * fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses. + * Parse a TORv2 address and set this object to it. * * @returns Whether or not the operation was successful. * - * @see CNetAddr::IsTor(), CNetAddr::IsRFC4193() + * @see CNetAddr::IsTor() */ bool CNetAddr::SetSpecial(const std::string &strName) { if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") { std::vector<unsigned char> vchAddr = DecodeBase32(strName.substr(0, strName.size() - 6).c_str()); - if (vchAddr.size() != 16-sizeof(pchOnionCat)) + if (vchAddr.size() != ADDR_TORV2_SIZE) { return false; + } m_net = NET_ONION; - memcpy(ip, pchOnionCat, sizeof(pchOnionCat)); - for (unsigned int i=0; i<16-sizeof(pchOnionCat); i++) - ip[i + sizeof(pchOnionCat)] = vchAddr[i]; + m_addr.assign(vchAddr.begin(), vchAddr.end()); return true; } return false; @@ -117,28 +123,23 @@ bool CNetAddr::SetSpecial(const std::string &strName) CNetAddr::CNetAddr(const struct in_addr& ipv4Addr) { - SetRaw(NET_IPV4, (const uint8_t*)&ipv4Addr); + m_net = NET_IPV4; + const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&ipv4Addr); + m_addr.assign(ptr, ptr + ADDR_IPV4_SIZE); } CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr, const uint32_t scope) { - SetRaw(NET_IPV6, (const uint8_t*)&ipv6Addr); + SetLegacyIPv6(Span<const uint8_t>(reinterpret_cast<const uint8_t*>(&ipv6Addr), sizeof(ipv6Addr))); scopeId = scope; } -unsigned int CNetAddr::GetByte(int n) const -{ - return ip[15-n]; -} - bool CNetAddr::IsBindAny() const { - const int cmplen = IsIPv4() ? 4 : 16; - for (int i = 0; i < cmplen; ++i) { - if (GetByte(i)) return false; + if (!IsIPv4() && !IsIPv6()) { + return false; } - - return true; + return std::all_of(m_addr.begin(), m_addr.end(), [](uint8_t b) { return b == 0; }); } bool CNetAddr::IsIPv4() const { return m_net == NET_IPV4; } @@ -148,88 +149,88 @@ bool CNetAddr::IsIPv6() const { return m_net == NET_IPV6; } bool CNetAddr::IsRFC1918() const { return IsIPv4() && ( - GetByte(3) == 10 || - (GetByte(3) == 192 && GetByte(2) == 168) || - (GetByte(3) == 172 && (GetByte(2) >= 16 && GetByte(2) <= 31))); + m_addr[0] == 10 || + (m_addr[0] == 192 && m_addr[1] == 168) || + (m_addr[0] == 172 && m_addr[1] >= 16 && m_addr[1] <= 31)); } bool CNetAddr::IsRFC2544() const { - return IsIPv4() && GetByte(3) == 198 && (GetByte(2) == 18 || GetByte(2) == 19); + return IsIPv4() && m_addr[0] == 198 && (m_addr[1] == 18 || m_addr[1] == 19); } bool CNetAddr::IsRFC3927() const { - return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254); + return IsIPv4() && HasPrefix(m_addr, std::array<uint8_t, 2>{169, 254}); } bool CNetAddr::IsRFC6598() const { - return IsIPv4() && GetByte(3) == 100 && GetByte(2) >= 64 && GetByte(2) <= 127; + return IsIPv4() && m_addr[0] == 100 && m_addr[1] >= 64 && m_addr[1] <= 127; } bool CNetAddr::IsRFC5737() const { - return IsIPv4() && ((GetByte(3) == 192 && GetByte(2) == 0 && GetByte(1) == 2) || - (GetByte(3) == 198 && GetByte(2) == 51 && GetByte(1) == 100) || - (GetByte(3) == 203 && GetByte(2) == 0 && GetByte(1) == 113)); + return IsIPv4() && (HasPrefix(m_addr, std::array<uint8_t, 3>{192, 0, 2}) || + HasPrefix(m_addr, std::array<uint8_t, 3>{198, 51, 100}) || + HasPrefix(m_addr, std::array<uint8_t, 3>{203, 0, 113})); } bool CNetAddr::IsRFC3849() const { - return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 && - GetByte(13) == 0x0D && GetByte(12) == 0xB8; + return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 4>{0x20, 0x01, 0x0D, 0xB8}); } bool CNetAddr::IsRFC3964() const { - return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x02; + return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 2>{0x20, 0x02}); } bool CNetAddr::IsRFC6052() const { - static const unsigned char pchRFC6052[] = {0,0x64,0xFF,0x9B,0,0,0,0,0,0,0,0}; - return IsIPv6() && memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0; + return IsIPv6() && + HasPrefix(m_addr, std::array<uint8_t, 12>{0x00, 0x64, 0xFF, 0x9B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); } bool CNetAddr::IsRFC4380() const { - return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 && - GetByte(12) == 0; + return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 4>{0x20, 0x01, 0x00, 0x00}); } bool CNetAddr::IsRFC4862() const { - static const unsigned char pchRFC4862[] = {0xFE,0x80,0,0,0,0,0,0}; - return IsIPv6() && memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0; + return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 8>{0xFE, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}); } bool CNetAddr::IsRFC4193() const { - return IsIPv6() && (GetByte(15) & 0xFE) == 0xFC; + return IsIPv6() && (m_addr[0] & 0xFE) == 0xFC; } bool CNetAddr::IsRFC6145() const { - static const unsigned char pchRFC6145[] = {0,0,0,0,0,0,0,0,0xFF,0xFF,0,0}; - return IsIPv6() && memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0; + return IsIPv6() && + HasPrefix(m_addr, std::array<uint8_t, 12>{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00}); } bool CNetAddr::IsRFC4843() const { - return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 && - GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10; + return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 3>{0x20, 0x01, 0x00}) && + (m_addr[3] & 0xF0) == 0x10; } bool CNetAddr::IsRFC7343() const { - return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 && - GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20; + return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 3>{0x20, 0x01, 0x00}) && + (m_addr[3] & 0xF0) == 0x20; } bool CNetAddr::IsHeNet() const { - return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70); + return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 4>{0x20, 0x01, 0x04, 0x70}); } /** @@ -243,13 +244,15 @@ bool CNetAddr::IsTor() const { return m_net == NET_ONION; } bool CNetAddr::IsLocal() const { // IPv4 loopback (127.0.0.0/8 or 0.0.0.0/8) - if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0)) + if (IsIPv4() && (m_addr[0] == 127 || m_addr[0] == 0)) { return true; + } // IPv6 loopback (::1/128) static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; - if (IsIPv6() && memcmp(ip, pchLocal, 16) == 0) + if (IsIPv6() && memcmp(m_addr.data(), pchLocal, sizeof(pchLocal)) == 0) { return true; + } return false; } @@ -272,13 +275,16 @@ bool CNetAddr::IsValid() const // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26... // so if the first length field is garbled, it reads the second batch // of addr misaligned by 3 bytes. - if (IsIPv6() && memcmp(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0) + if (IsIPv6() && memcmp(m_addr.data(), IPV4_IN_IPV6_PREFIX.data() + 3, + sizeof(IPV4_IN_IPV6_PREFIX) - 3) == 0) { return false; + } // unspecified IPv6 address (::/128) unsigned char ipNone6[16] = {}; - if (IsIPv6() && memcmp(ip, ipNone6, 16) == 0) + if (IsIPv6() && memcmp(m_addr.data(), ipNone6, sizeof(ipNone6)) == 0) { return false; + } // documentation IPv6 address if (IsRFC3849()) @@ -287,17 +293,11 @@ bool CNetAddr::IsValid() const if (IsInternal()) return false; - if (IsIPv4()) - { - // INADDR_NONE - uint32_t ipNone = INADDR_NONE; - if (memcmp(ip+12, &ipNone, 4) == 0) - return false; - - // 0 - ipNone = 0; - if (memcmp(ip+12, &ipNone, 4) == 0) + if (IsIPv4()) { + const uint32_t addr = ReadBE32(m_addr.data()); + if (addr == INADDR_ANY || addr == INADDR_NONE) { return false; + } } return true; @@ -318,7 +318,7 @@ bool CNetAddr::IsRoutable() const } /** - * @returns Whether or not this is a dummy address that maps a name into IPv6. + * @returns Whether or not this is a dummy address that represents a name. * * @see CNetAddr::SetInternal(const std::string &) */ @@ -341,9 +341,9 @@ enum Network CNetAddr::GetNetwork() const std::string CNetAddr::ToStringIP() const { if (IsTor()) - return EncodeBase32(&ip[6], 10) + ".onion"; + return EncodeBase32(m_addr.data(), m_addr.size()) + ".onion"; if (IsInternal()) - return EncodeBase32(ip + sizeof(g_internal_prefix), sizeof(ip) - sizeof(g_internal_prefix)) + ".internal"; + return EncodeBase32(m_addr.data(), m_addr.size()) + ".internal"; CService serv(*this, 0); struct sockaddr_storage sockaddr; socklen_t socklen = sizeof(sockaddr); @@ -353,13 +353,13 @@ std::string CNetAddr::ToStringIP() const return std::string(name); } if (IsIPv4()) - return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0)); - else - return strprintf("%x:%x:%x:%x:%x:%x:%x:%x", - GetByte(15) << 8 | GetByte(14), GetByte(13) << 8 | GetByte(12), - GetByte(11) << 8 | GetByte(10), GetByte(9) << 8 | GetByte(8), - GetByte(7) << 8 | GetByte(6), GetByte(5) << 8 | GetByte(4), - GetByte(3) << 8 | GetByte(2), GetByte(1) << 8 | GetByte(0)); + return strprintf("%u.%u.%u.%u", m_addr[0], m_addr[1], m_addr[2], m_addr[3]); + assert(IsIPv6()); + return strprintf("%x:%x:%x:%x:%x:%x:%x:%x", + m_addr[0] << 8 | m_addr[1], m_addr[2] << 8 | m_addr[3], + m_addr[4] << 8 | m_addr[5], m_addr[6] << 8 | m_addr[7], + m_addr[8] << 8 | m_addr[9], m_addr[10] << 8 | m_addr[11], + m_addr[12] << 8 | m_addr[13], m_addr[14] << 8 | m_addr[15]); } std::string CNetAddr::ToString() const @@ -369,12 +369,12 @@ std::string CNetAddr::ToString() const bool operator==(const CNetAddr& a, const CNetAddr& b) { - return a.m_net == b.m_net && memcmp(a.ip, b.ip, 16) == 0; + return a.m_net == b.m_net && a.m_addr == b.m_addr; } bool operator<(const CNetAddr& a, const CNetAddr& b) { - return a.m_net < b.m_net || (a.m_net == b.m_net && memcmp(a.ip, b.ip, 16) < 0); + return std::tie(a.m_net, a.m_addr) < std::tie(b.m_net, b.m_addr); } /** @@ -391,7 +391,8 @@ bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const { if (!IsIPv4()) return false; - memcpy(pipv4Addr, ip+12, 4); + assert(sizeof(*pipv4Addr) == m_addr.size()); + memcpy(pipv4Addr, m_addr.data(), m_addr.size()); return true; } @@ -410,7 +411,8 @@ bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const if (!IsIPv6()) { return false; } - memcpy(pipv6Addr, ip, 16); + assert(sizeof(*pipv6Addr) == m_addr.size()); + memcpy(pipv6Addr, m_addr.data(), m_addr.size()); return true; } @@ -421,15 +423,17 @@ bool CNetAddr::HasLinkedIPv4() const uint32_t CNetAddr::GetLinkedIPv4() const { - if (IsIPv4() || IsRFC6145() || IsRFC6052()) { - // IPv4, mapped IPv4, SIIT translated IPv4: the IPv4 address is the last 4 bytes of the address - return ReadBE32(ip + 12); + if (IsIPv4()) { + return ReadBE32(m_addr.data()); + } else if (IsRFC6052() || IsRFC6145()) { + // mapped IPv4, SIIT translated IPv4: the IPv4 address is the last 4 bytes of the address + return ReadBE32(MakeSpan(m_addr).last(ADDR_IPV4_SIZE).data()); } else if (IsRFC3964()) { // 6to4 tunneled IPv4: the IPv4 address is in bytes 2-6 - return ReadBE32(ip + 2); + return ReadBE32(MakeSpan(m_addr).subspan(2, ADDR_IPV4_SIZE).data()); } else if (IsRFC4380()) { // Teredo tunneled IPv4: the IPv4 address is in the last 4 bytes of the address, but bitflipped - return ~ReadBE32(ip + 12); + return ~ReadBE32(MakeSpan(m_addr).last(ADDR_IPV4_SIZE).data()); } assert(false); } @@ -458,10 +462,10 @@ uint32_t CNetAddr::GetMappedAS(const std::vector<bool> &asmap) const { } std::vector<bool> ip_bits(128); if (HasLinkedIPv4()) { - // For lookup, treat as if it was just an IPv4 address (pchIPv4 prefix + IPv4 bits) + // For lookup, treat as if it was just an IPv4 address (IPV4_IN_IPV6_PREFIX + IPv4 bits) for (int8_t byte_i = 0; byte_i < 12; ++byte_i) { for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) { - ip_bits[byte_i * 8 + bit_i] = (pchIPv4[byte_i] >> (7 - bit_i)) & 1; + ip_bits[byte_i * 8 + bit_i] = (IPV4_IN_IPV6_PREFIX[byte_i] >> (7 - bit_i)) & 1; } } uint32_t ipv4 = GetLinkedIPv4(); @@ -470,8 +474,9 @@ uint32_t CNetAddr::GetMappedAS(const std::vector<bool> &asmap) const { } } else { // Use all 128 bits of the IPv6 address otherwise + assert(IsIPv6()); for (int8_t byte_i = 0; byte_i < 16; ++byte_i) { - uint8_t cur_byte = GetByte(15 - byte_i); + uint8_t cur_byte = m_addr[byte_i]; for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) { ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1; } @@ -507,19 +512,15 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co } vchRet.push_back(net_class); - int nStartByte = 0; - int nBits = 16; + int nBits{0}; if (IsLocal()) { // all local addresses belong to the same group - nBits = 0; } else if (IsInternal()) { // all internal-usage addresses get their own group - nStartByte = sizeof(g_internal_prefix); - nBits = (sizeof(ip) - sizeof(g_internal_prefix)) * 8; + nBits = ADDR_INTERNAL_SIZE * 8; } else if (!IsRoutable()) { // all other unroutable addresses belong to the same group - nBits = 0; } else if (HasLinkedIPv4()) { // IPv4 addresses (and mapped IPv4 addresses) use /16 groups uint32_t ipv4 = GetLinkedIPv4(); @@ -527,7 +528,6 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co vchRet.push_back((ipv4 >> 16) & 0xFF); return vchRet; } else if (IsTor()) { - nStartByte = 6; nBits = 4; } else if (IsHeNet()) { // for he.net, use /36 groups @@ -537,23 +537,29 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co nBits = 32; } - // push our ip onto vchRet byte by byte... - while (nBits >= 8) - { - vchRet.push_back(GetByte(15 - nStartByte)); - nStartByte++; - nBits -= 8; - } + // Push our address onto vchRet. + const size_t num_bytes = nBits / 8; + vchRet.insert(vchRet.end(), m_addr.begin(), m_addr.begin() + num_bytes); + nBits %= 8; // ...for the last byte, push nBits and for the rest of the byte push 1's - if (nBits > 0) - vchRet.push_back(GetByte(15 - nStartByte) | ((1 << (8 - nBits)) - 1)); + if (nBits > 0) { + assert(num_bytes < m_addr.size()); + vchRet.push_back(m_addr[num_bytes] | ((1 << (8 - nBits)) - 1)); + } return vchRet; } +std::vector<unsigned char> CNetAddr::GetAddrBytes() const +{ + uint8_t serialized[V1_SERIALIZATION_SIZE]; + SerializeV1Array(serialized); + return {std::begin(serialized), std::end(serialized)}; +} + uint64_t CNetAddr::GetHash() const { - uint256 hash = Hash(ip); + uint256 hash = Hash(m_addr); uint64_t nRet; memcpy(&nRet, &hash, sizeof(nRet)); return nRet; @@ -764,53 +770,89 @@ CSubNet::CSubNet(): memset(netmask, 0, sizeof(netmask)); } -CSubNet::CSubNet(const CNetAddr &addr, int32_t mask) +CSubNet::CSubNet(const CNetAddr& addr, uint8_t mask) : CSubNet() { - valid = true; + valid = (addr.IsIPv4() && mask <= ADDR_IPV4_SIZE * 8) || + (addr.IsIPv6() && mask <= ADDR_IPV6_SIZE * 8); + if (!valid) { + return; + } + + assert(mask <= sizeof(netmask) * 8); + network = addr; - // Default to /32 (IPv4) or /128 (IPv6), i.e. match single address - memset(netmask, 255, sizeof(netmask)); - - // IPv4 addresses start at offset 12, and first 12 bytes must match, so just offset n - const int astartofs = network.IsIPv4() ? 12 : 0; - - int32_t n = mask; - if(n >= 0 && n <= (128 - astartofs*8)) // Only valid if in range of bits of address - { - n += astartofs*8; - // Clear bits [n..127] - for (; n < 128; ++n) - netmask[n>>3] &= ~(1<<(7-(n&7))); - } else - valid = false; - // Normalize network according to netmask - for(int x=0; x<16; ++x) - network.ip[x] &= netmask[x]; + uint8_t n = mask; + for (size_t i = 0; i < network.m_addr.size(); ++i) { + const uint8_t bits = n < 8 ? n : 8; + netmask[i] = (uint8_t)((uint8_t)0xFF << (8 - bits)); // Set first bits. + network.m_addr[i] &= netmask[i]; // Normalize network according to netmask. + n -= bits; + } +} + +/** + * @returns The number of 1-bits in the prefix of the specified subnet mask. If + * the specified subnet mask is not a valid one, -1. + */ +static inline int NetmaskBits(uint8_t x) +{ + switch(x) { + case 0x00: return 0; + case 0x80: return 1; + case 0xc0: return 2; + case 0xe0: return 3; + case 0xf0: return 4; + case 0xf8: return 5; + case 0xfc: return 6; + case 0xfe: return 7; + case 0xff: return 8; + default: return -1; + } } -CSubNet::CSubNet(const CNetAddr &addr, const CNetAddr &mask) +CSubNet::CSubNet(const CNetAddr& addr, const CNetAddr& mask) : CSubNet() { - valid = true; - network = addr; - // Default to /32 (IPv4) or /128 (IPv6), i.e. match single address - memset(netmask, 255, sizeof(netmask)); + valid = (addr.IsIPv4() || addr.IsIPv6()) && addr.m_net == mask.m_net; + if (!valid) { + return; + } + // Check if `mask` contains 1-bits after 0-bits (which is an invalid netmask). + bool zeros_found = false; + for (auto b : mask.m_addr) { + const int num_bits = NetmaskBits(b); + if (num_bits == -1 || (zeros_found && num_bits != 0)) { + valid = false; + return; + } + if (num_bits < 8) { + zeros_found = true; + } + } - // IPv4 addresses start at offset 12, and first 12 bytes must match, so just offset n - const int astartofs = network.IsIPv4() ? 12 : 0; + assert(mask.m_addr.size() <= sizeof(netmask)); - for(int x=astartofs; x<16; ++x) - netmask[x] = mask.ip[x]; + memcpy(netmask, mask.m_addr.data(), mask.m_addr.size()); + + network = addr; // Normalize network according to netmask - for(int x=0; x<16; ++x) - network.ip[x] &= netmask[x]; + for (size_t x = 0; x < network.m_addr.size(); ++x) { + network.m_addr[x] &= netmask[x]; + } } -CSubNet::CSubNet(const CNetAddr &addr): - valid(addr.IsValid()) +CSubNet::CSubNet(const CNetAddr& addr) : CSubNet() { - memset(netmask, 255, sizeof(netmask)); + valid = addr.IsIPv4() || addr.IsIPv6(); + if (!valid) { + return; + } + + assert(addr.m_addr.size() <= sizeof(netmask)); + + memset(netmask, 0xFF, addr.m_addr.size()); + network = addr; } @@ -822,68 +864,29 @@ bool CSubNet::Match(const CNetAddr &addr) const { if (!valid || !addr.IsValid() || network.m_net != addr.m_net) return false; - for(int x=0; x<16; ++x) - if ((addr.ip[x] & netmask[x]) != network.ip[x]) + assert(network.m_addr.size() == addr.m_addr.size()); + for (size_t x = 0; x < addr.m_addr.size(); ++x) { + if ((addr.m_addr[x] & netmask[x]) != network.m_addr[x]) { return false; - return true; -} - -/** - * @returns The number of 1-bits in the prefix of the specified subnet mask. If - * the specified subnet mask is not a valid one, -1. - */ -static inline int NetmaskBits(uint8_t x) -{ - switch(x) { - case 0x00: return 0; - case 0x80: return 1; - case 0xc0: return 2; - case 0xe0: return 3; - case 0xf0: return 4; - case 0xf8: return 5; - case 0xfc: return 6; - case 0xfe: return 7; - case 0xff: return 8; - default: return -1; + } } + return true; } std::string CSubNet::ToString() const { - /* Parse binary 1{n}0{N-n} to see if mask can be represented as /n */ - int cidr = 0; - bool valid_cidr = true; - int n = network.IsIPv4() ? 12 : 0; - for (; n < 16 && netmask[n] == 0xff; ++n) - cidr += 8; - if (n < 16) { - int bits = NetmaskBits(netmask[n]); - if (bits < 0) - valid_cidr = false; - else - cidr += bits; - ++n; - } - for (; n < 16 && valid_cidr; ++n) - if (netmask[n] != 0x00) - valid_cidr = false; - - /* Format output */ - std::string strNetmask; - if (valid_cidr) { - strNetmask = strprintf("%u", cidr); - } else { - if (network.IsIPv4()) - strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]); - else - strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x", - netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3], - netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7], - netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11], - netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]); + assert(network.m_addr.size() <= sizeof(netmask)); + + uint8_t cidr = 0; + + for (size_t i = 0; i < network.m_addr.size(); ++i) { + if (netmask[i] == 0x00) { + break; + } + cidr += NetmaskBits(netmask[i]); } - return network.ToString() + "/" + strNetmask; + return network.ToString() + strprintf("/%u", cidr); } bool CSubNet::IsValid() const diff --git a/src/netaddress.h b/src/netaddress.h index 0365907d44..d00f5a6f55 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -9,9 +9,12 @@ #include <config/bitcoin-config.h> #endif +#include <attributes.h> #include <compat.h> +#include <prevector.h> #include <serialize.h> +#include <array> #include <cstdint> #include <string> #include <vector> @@ -39,16 +42,49 @@ enum Network /// TORv2 NET_ONION, - /// A set of dummy addresses that map a name to an IPv6 address. These - /// addresses belong to RFC4193's fc00::/7 subnet (unique-local addresses). - /// We use them to map a string or FQDN to an IPv6 address in CAddrMan to - /// keep track of which DNS seeds were used. + /// A set of addresses that represent the hash of a string or FQDN. We use + /// them in CAddrMan to keep track of which DNS seeds were used. NET_INTERNAL, /// Dummy value to indicate the number of NET_* constants. NET_MAX, }; +/// Prefix of an IPv6 address when it contains an embedded IPv4 address. +/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155). +static const std::array<uint8_t, 12> IPV4_IN_IPV6_PREFIX{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF +}; + +/// Prefix of an IPv6 address when it contains an embedded TORv2 address. +/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155). +/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they +/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses. +static const std::array<uint8_t, 6> TORV2_IN_IPV6_PREFIX{ + 0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43 +}; + +/// Prefix of an IPv6 address when it contains an embedded "internal" address. +/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155). +/// The prefix comes from 0xFD + SHA256("bitcoin")[0:5]. +/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they +/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses. +static const std::array<uint8_t, 6> INTERNAL_IN_IPV6_PREFIX{ + 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 // 0xFD + sha256("bitcoin")[0:5]. +}; + +/// Size of IPv4 address (in bytes). +static constexpr size_t ADDR_IPV4_SIZE = 4; + +/// Size of IPv6 address (in bytes). +static constexpr size_t ADDR_IPV6_SIZE = 16; + +/// Size of TORv2 address (in bytes). +static constexpr size_t ADDR_TORV2_SIZE = 10; + +/// Size of "internal" (NET_INTERNAL) address (in bytes). +static constexpr size_t ADDR_INTERNAL_SIZE = 10; + /** * Network address. */ @@ -56,11 +92,16 @@ class CNetAddr { protected: /** + * Raw representation of the network address. + * In network byte order (big endian) for IPv4 and IPv6. + */ + prevector<ADDR_IPV6_SIZE, uint8_t> m_addr{ADDR_IPV6_SIZE, 0x0}; + + /** * Network to which this address belongs. */ Network m_net{NET_IPV6}; - unsigned char ip[16]; // in network byte order uint32_t scopeId{0}; // for scoped/link-local ipv6 addresses public: @@ -74,13 +115,7 @@ class CNetAddr * (e.g. IPv4) disguised as IPv6. This encoding is used in the legacy * `addr` encoding. */ - void SetLegacyIPv6(const uint8_t ipv6[16]); - - /** - * Set raw IPv4 or IPv6 address (in network byte order) - * @note Only NET_IPV4 and NET_IPV6 are allowed for network. - */ - void SetRaw(Network network, const uint8_t *data); + void SetLegacyIPv6(Span<const uint8_t> ipv6); bool SetInternal(const std::string& name); @@ -111,7 +146,6 @@ class CNetAddr enum Network GetNetwork() const; std::string ToString() const; std::string ToStringIP() const; - unsigned int GetByte(int n) const; uint64_t GetHash() const; bool GetInAddr(struct in_addr* pipv4Addr) const; uint32_t GetNetClass() const; @@ -127,7 +161,7 @@ class CNetAddr uint32_t GetMappedAS(const std::vector<bool> &asmap) const; std::vector<unsigned char> GetGroup(const std::vector<bool> &asmap) const; - std::vector<unsigned char> GetAddrBytes() const { return {std::begin(ip), std::end(ip)}; } + std::vector<unsigned char> GetAddrBytes() const; int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const; explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0); @@ -143,7 +177,7 @@ class CNetAddr template <typename Stream> void Serialize(Stream& s) const { - s << ip; + SerializeV1Stream(s); } /** @@ -152,14 +186,92 @@ class CNetAddr template <typename Stream> void Unserialize(Stream& s) { - unsigned char ip_temp[sizeof(ip)]; - s >> ip_temp; + UnserializeV1Stream(s); + } + + friend class CSubNet; + + private: + /** + * Size of CNetAddr when serialized as ADDRv1 (pre-BIP155) (in bytes). + */ + static constexpr size_t V1_SERIALIZATION_SIZE = ADDR_IPV6_SIZE; + + /** + * Serialize in pre-ADDRv2/BIP155 format to an array. + * Some addresses (e.g. TORv3) cannot be serialized in pre-BIP155 format. + */ + void SerializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE]) const + { + size_t prefix_size; + + switch (m_net) { + case NET_IPV6: + assert(m_addr.size() == sizeof(arr)); + memcpy(arr, m_addr.data(), m_addr.size()); + return; + case NET_IPV4: + prefix_size = sizeof(IPV4_IN_IPV6_PREFIX); + assert(prefix_size + m_addr.size() == sizeof(arr)); + memcpy(arr, IPV4_IN_IPV6_PREFIX.data(), prefix_size); + memcpy(arr + prefix_size, m_addr.data(), m_addr.size()); + return; + case NET_ONION: + prefix_size = sizeof(TORV2_IN_IPV6_PREFIX); + assert(prefix_size + m_addr.size() == sizeof(arr)); + memcpy(arr, TORV2_IN_IPV6_PREFIX.data(), prefix_size); + memcpy(arr + prefix_size, m_addr.data(), m_addr.size()); + return; + case NET_INTERNAL: + prefix_size = sizeof(INTERNAL_IN_IPV6_PREFIX); + assert(prefix_size + m_addr.size() == sizeof(arr)); + memcpy(arr, INTERNAL_IN_IPV6_PREFIX.data(), prefix_size); + memcpy(arr + prefix_size, m_addr.data(), m_addr.size()); + return; + case NET_UNROUTABLE: + case NET_MAX: + assert(false); + } // no default case, so the compiler can warn about missing cases + + assert(false); + } + + /** + * Serialize in pre-ADDRv2/BIP155 format to a stream. + * Some addresses (e.g. TORv3) cannot be serialized in pre-BIP155 format. + */ + template <typename Stream> + void SerializeV1Stream(Stream& s) const + { + uint8_t serialized[V1_SERIALIZATION_SIZE]; + + SerializeV1Array(serialized); + + s << serialized; + } + + /** + * Unserialize from a pre-ADDRv2/BIP155 format from an array. + */ + void UnserializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE]) + { // Use SetLegacyIPv6() so that m_net is set correctly. For example // ::FFFF:0102:0304 should be set as m_net=NET_IPV4 (1.2.3.4). - SetLegacyIPv6(ip_temp); + SetLegacyIPv6(arr); } - friend class CSubNet; + /** + * Unserialize from a pre-ADDRv2/BIP155 format from a stream. + */ + template <typename Stream> + void UnserializeV1Stream(Stream& s) + { + uint8_t serialized[V1_SERIALIZATION_SIZE]; + + s >> serialized; + + UnserializeV1Array(serialized); + } }; class CSubNet @@ -174,11 +286,11 @@ class CSubNet public: CSubNet(); - CSubNet(const CNetAddr &addr, int32_t mask); - CSubNet(const CNetAddr &addr, const CNetAddr &mask); + CSubNet(const CNetAddr& addr, uint8_t mask); + CSubNet(const CNetAddr& addr, const CNetAddr& mask); //constructor for single ip subnet (<ipv4>/32 or <ipv6>/128) - explicit CSubNet(const CNetAddr &addr); + explicit CSubNet(const CNetAddr& addr); bool Match(const CNetAddr &addr) const; diff --git a/src/netbase.cpp b/src/netbase.cpp index 3a3b5f3e66..0273839017 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -13,6 +13,7 @@ #include <atomic> #include <cstdint> +#include <limits> #ifndef WIN32 #include <fcntl.h> @@ -838,8 +839,8 @@ bool LookupSubNet(const std::string& strSubnet, CSubNet& ret) if (slash != strSubnet.npos) { std::string strNetmask = strSubnet.substr(slash + 1); - int32_t n; - if (ParseInt32(strNetmask, &n)) { + uint8_t n; + if (ParseUInt8(strNetmask, &n)) { // If valid number, assume CIDR variable-length subnet masking ret = CSubNet(network, n); return ret.IsValid(); diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 6e34aca0eb..bab17562a6 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -21,11 +21,6 @@ #include <util/system.h> #ifdef WIN32 -#ifdef _WIN32_IE -#undef _WIN32_IE -#endif -#define _WIN32_IE 0x0501 -#define WIN32_LEAN_AND_MEAN 1 #ifndef NOMINMAX #define NOMINMAX #endif diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 9bd7c15992..e9343b3348 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -100,6 +100,8 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) {RPCResult::Type::BOOL, "relaytxes", "Whether peer has asked us to relay transactions to it"}, {RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"}, {RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"}, + {RPCResult::Type::NUM_TIME, "last_transaction", "The " + UNIX_EPOCH_TIME + " of the last valid transaction received from this peer"}, + {RPCResult::Type::NUM_TIME, "last_block", "The " + UNIX_EPOCH_TIME + " of the last block received from this peer"}, {RPCResult::Type::NUM, "bytessent", "The total bytes sent"}, {RPCResult::Type::NUM, "bytesrecv", "The total bytes received"}, {RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"}, @@ -169,6 +171,8 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) obj.pushKV("relaytxes", stats.fRelayTxes); obj.pushKV("lastsend", stats.nLastSend); obj.pushKV("lastrecv", stats.nLastRecv); + obj.pushKV("last_transaction", stats.nLastTXTime); + obj.pushKV("last_block", stats.nLastBlockTime); obj.pushKV("bytessent", stats.nSendBytes); obj.pushKV("bytesrecv", stats.nRecvBytes); obj.pushKV("conntime", stats.nTimeConnected); diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 39feb4ccc9..7b2457a5e3 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1258,34 +1258,37 @@ public: } }; +/** Compute the (single) SHA256 of the concatenation of all prevouts of a tx. */ template <class T> -uint256 GetPrevoutHash(const T& txTo) +uint256 GetPrevoutsSHA256(const T& txTo) { CHashWriter ss(SER_GETHASH, 0); for (const auto& txin : txTo.vin) { ss << txin.prevout; } - return ss.GetHash(); + return ss.GetSHA256(); } +/** Compute the (single) SHA256 of the concatenation of all nSequences of a tx. */ template <class T> -uint256 GetSequenceHash(const T& txTo) +uint256 GetSequencesSHA256(const T& txTo) { CHashWriter ss(SER_GETHASH, 0); for (const auto& txin : txTo.vin) { ss << txin.nSequence; } - return ss.GetHash(); + return ss.GetSHA256(); } +/** Compute the (single) SHA256 of the concatenation of all txouts of a tx. */ template <class T> -uint256 GetOutputsHash(const T& txTo) +uint256 GetOutputsSHA256(const T& txTo) { CHashWriter ss(SER_GETHASH, 0); for (const auto& txout : txTo.vout) { ss << txout; } - return ss.GetHash(); + return ss.GetSHA256(); } } // namespace @@ -1297,9 +1300,9 @@ void PrecomputedTransactionData::Init(const T& txTo) // Cache is calculated only for transactions with witness if (txTo.HasWitness()) { - hashPrevouts = GetPrevoutHash(txTo); - hashSequence = GetSequenceHash(txTo); - hashOutputs = GetOutputsHash(txTo); + hashPrevouts = SHA256Uint256(GetPrevoutsSHA256(txTo)); + hashSequence = SHA256Uint256(GetSequencesSHA256(txTo)); + hashOutputs = SHA256Uint256(GetOutputsSHA256(txTo)); } m_ready = true; @@ -1329,16 +1332,16 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn const bool cacheready = cache && cache->m_ready; if (!(nHashType & SIGHASH_ANYONECANPAY)) { - hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo); + hashPrevouts = cacheready ? cache->hashPrevouts : SHA256Uint256(GetPrevoutsSHA256(txTo)); } if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - hashSequence = cacheready ? cache->hashSequence : GetSequenceHash(txTo); + hashSequence = cacheready ? cache->hashSequence : SHA256Uint256(GetSequencesSHA256(txTo)); } if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo); + hashOutputs = cacheready ? cache->hashOutputs : SHA256Uint256(GetOutputsSHA256(txTo)); } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { CHashWriter ss(SER_GETHASH, 0); ss << txTo.vout[nIn]; diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index b4f392116c..26de780f29 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -10,7 +10,6 @@ #endif #ifdef WIN32 -#define WIN32_LEAN_AND_MEAN 1 #ifndef NOMINMAX #define NOMINMAX #endif diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index 0115803e58..c0a2fca9ca 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -80,7 +80,7 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup) BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction) { auto connman = MakeUnique<CConnman>(0x1337, 0x1337); - auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool); + auto peerLogic = MakeUnique<PeerLogicValidation>(*connman, nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool); // Mock an outbound peer CAddress addr1(ip(0xa0b0c001), NODE_NONE); @@ -150,7 +150,7 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidat BOOST_AUTO_TEST_CASE(stale_tip_peer_management) { auto connman = MakeUnique<CConnmanTest>(0x1337, 0x1337); - auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool); + auto peerLogic = MakeUnique<PeerLogicValidation>(*connman, nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool); const Consensus::Params& consensusParams = Params().GetConsensus(); constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS; @@ -223,7 +223,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement) { auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto connman = MakeUnique<CConnman>(0x1337, 0x1337); - auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool); + auto peerLogic = MakeUnique<PeerLogicValidation>(*connman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool); banman->ClearBanned(); CAddress addr1(ip(0xa0b0c001), NODE_NONE); @@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) { auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto connman = MakeUnique<CConnman>(0x1337, 0x1337); - auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool); + auto peerLogic = MakeUnique<PeerLogicValidation>(*connman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool); banman->ClearBanned(); int64_t nStartTime = GetTime(); diff --git a/src/test/fuzz/asmap.cpp b/src/test/fuzz/asmap.cpp index 40ca01bd9f..e3aefa18a3 100644 --- a/src/test/fuzz/asmap.cpp +++ b/src/test/fuzz/asmap.cpp @@ -33,7 +33,7 @@ void test_one_input(const std::vector<uint8_t>& buffer) if (buffer.size() < 1 + 3 + 4) return; int asmap_size = 3 + (buffer[0] & 127); bool ipv6 = buffer[0] & 128; - int addr_size = ipv6 ? 16 : 4; + const size_t addr_size = ipv6 ? ADDR_IPV6_SIZE : ADDR_IPV4_SIZE; if (buffer.size() < size_t(1 + asmap_size + addr_size)) return; std::vector<bool> asmap = ipv6 ? IPV6_PREFIX_ASMAP : IPV4_PREFIX_ASMAP; asmap.reserve(asmap.size() + 8 * asmap_size); @@ -43,7 +43,17 @@ void test_one_input(const std::vector<uint8_t>& buffer) } } if (!SanityCheckASMap(asmap)) return; + + const uint8_t* addr_data = buffer.data() + 1 + asmap_size; CNetAddr net_addr; - net_addr.SetRaw(ipv6 ? NET_IPV6 : NET_IPV4, buffer.data() + 1 + asmap_size); + if (ipv6) { + assert(addr_size == ADDR_IPV6_SIZE); + net_addr.SetLegacyIPv6(Span<const uint8_t>(addr_data, addr_size)); + } else { + assert(addr_size == ADDR_IPV4_SIZE); + in_addr ipv4; + memcpy(&ipv4, addr_data, addr_size); + net_addr.SetIP(CNetAddr{ipv4}); + } (void)net_addr.GetMappedAS(asmap); } diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp index 2901c704f6..8252f38726 100644 --- a/src/test/fuzz/netaddress.cpp +++ b/src/test/fuzz/netaddress.cpp @@ -17,9 +17,6 @@ void test_one_input(const std::vector<uint8_t>& buffer) FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const CNetAddr net_addr = ConsumeNetAddr(fuzzed_data_provider); - for (int i = 0; i < 15; ++i) { - (void)net_addr.GetByte(i); - } (void)net_addr.GetHash(); (void)net_addr.GetNetClass(); if (net_addr.GetNetwork() == Network::NET_IPV4) { @@ -78,7 +75,7 @@ void test_one_input(const std::vector<uint8_t>& buffer) (void)net_addr.ToString(); (void)net_addr.ToStringIP(); - const CSubNet sub_net{net_addr, fuzzed_data_provider.ConsumeIntegral<int32_t>()}; + const CSubNet sub_net{net_addr, fuzzed_data_provider.ConsumeIntegral<uint8_t>()}; (void)sub_net.IsValid(); (void)sub_net.ToString(); diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp index 677b87a47a..ec09acc6c6 100644 --- a/src/test/fuzz/process_message.cpp +++ b/src/test/fuzz/process_message.cpp @@ -30,18 +30,6 @@ #include <string> #include <vector> -void ProcessMessage( - CNode& pfrom, - const std::string& msg_type, - CDataStream& vRecv, - const std::chrono::microseconds time_received, - const CChainParams& chainparams, - ChainstateManager& chainman, - CTxMemPool& mempool, - CConnman& connman, - BanMan* banman, - const std::atomic<bool>& interruptMsgProc); - namespace { #ifdef MESSAGE_TYPE @@ -87,10 +75,9 @@ void test_one_input(const std::vector<uint8_t>& buffer) connman.AddTestNode(p2p_node); g_setup->m_node.peer_logic->InitializeNode(&p2p_node); try { - ProcessMessage(p2p_node, random_message_type, random_bytes_data_stream, GetTime<std::chrono::microseconds>(), - Params(), *g_setup->m_node.chainman, *g_setup->m_node.mempool, - *g_setup->m_node.connman, g_setup->m_node.banman.get(), - std::atomic<bool>{false}); + g_setup->m_node.peer_logic->ProcessMessage(p2p_node, random_message_type, random_bytes_data_stream, + GetTime<std::chrono::microseconds>(), Params(), + std::atomic<bool>{false}); } catch (const std::ios_base::failure&) { } SyncWithValidationInterfaceQueue(); diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index 9f9552edb9..ed6093a8a8 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -257,7 +257,7 @@ CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept { - return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int32_t>()}; + return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()}; } void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST) diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 317000c771..917ae571f5 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -13,8 +13,10 @@ #include <streams.h> #include <test/util/setup_common.h> #include <util/memory.h> +#include <util/strencodings.h> #include <util/string.h> #include <util/system.h> +#include <version.h> #include <boost/test/unit_test.hpp> @@ -188,6 +190,78 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) BOOST_CHECK(pnode2->IsInboundConn() == true); } +BOOST_AUTO_TEST_CASE(cnetaddr_basic) +{ + CNetAddr addr; + + // IPv4, INADDR_ANY + BOOST_REQUIRE(LookupHost("0.0.0.0", addr, false)); + BOOST_REQUIRE(!addr.IsValid()); + BOOST_REQUIRE(addr.IsIPv4()); + + BOOST_CHECK(addr.IsBindAny()); + BOOST_CHECK_EQUAL(addr.ToString(), "0.0.0.0"); + + // IPv4, INADDR_NONE + BOOST_REQUIRE(LookupHost("255.255.255.255", addr, false)); + BOOST_REQUIRE(!addr.IsValid()); + BOOST_REQUIRE(addr.IsIPv4()); + + BOOST_CHECK(!addr.IsBindAny()); + BOOST_CHECK_EQUAL(addr.ToString(), "255.255.255.255"); + + // IPv4, casual + BOOST_REQUIRE(LookupHost("12.34.56.78", addr, false)); + BOOST_REQUIRE(addr.IsValid()); + BOOST_REQUIRE(addr.IsIPv4()); + + BOOST_CHECK(!addr.IsBindAny()); + BOOST_CHECK_EQUAL(addr.ToString(), "12.34.56.78"); + + // IPv6, in6addr_any + BOOST_REQUIRE(LookupHost("::", addr, false)); + BOOST_REQUIRE(!addr.IsValid()); + BOOST_REQUIRE(addr.IsIPv6()); + + BOOST_CHECK(addr.IsBindAny()); + BOOST_CHECK_EQUAL(addr.ToString(), "::"); + + // IPv6, casual + BOOST_REQUIRE(LookupHost("1122:3344:5566:7788:9900:aabb:ccdd:eeff", addr, false)); + BOOST_REQUIRE(addr.IsValid()); + BOOST_REQUIRE(addr.IsIPv6()); + + BOOST_CHECK(!addr.IsBindAny()); + BOOST_CHECK_EQUAL(addr.ToString(), "1122:3344:5566:7788:9900:aabb:ccdd:eeff"); + + // TORv2 + addr.SetSpecial("6hzph5hv6337r6p2.onion"); + BOOST_REQUIRE(addr.IsValid()); + BOOST_REQUIRE(addr.IsTor()); + + BOOST_CHECK(!addr.IsBindAny()); + BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion"); + + // Internal + addr.SetInternal("esffpp"); + BOOST_REQUIRE(!addr.IsValid()); // "internal" is considered invalid + BOOST_REQUIRE(addr.IsInternal()); + + BOOST_CHECK(!addr.IsBindAny()); + BOOST_CHECK_EQUAL(addr.ToString(), "esffpvrt3wpeaygy.internal"); +} + +BOOST_AUTO_TEST_CASE(cnetaddr_serialize) +{ + CNetAddr addr; + CDataStream s(SER_NETWORK, PROTOCOL_VERSION); + + addr.SetInternal("a"); + s << addr; + BOOST_CHECK_EQUAL(HexStr(s), "fd6b88c08724ca978112ca1bbdcafac2"); + s.clear(); +} + // prior to PR #14728, this test triggers an undefined behavior BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test) { diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index 49073ea657..6681c92bb5 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -185,6 +185,7 @@ BOOST_AUTO_TEST_CASE(subnet_test) BOOST_CHECK(!ResolveSubNet("1.2.3.0/-1").IsValid()); BOOST_CHECK(ResolveSubNet("1.2.3.0/32").IsValid()); BOOST_CHECK(!ResolveSubNet("1.2.3.0/33").IsValid()); + BOOST_CHECK(!ResolveSubNet("1.2.3.0/300").IsValid()); BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:8/0").IsValid()); BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:8/33").IsValid()); BOOST_CHECK(!ResolveSubNet("1:2:3:4:5:6:7:8/-1").IsValid()); @@ -216,6 +217,11 @@ BOOST_AUTO_TEST_CASE(subnet_test) BOOST_CHECK(CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).Match(ResolveIP("1:2:3:4:5:6:7:8"))); BOOST_CHECK(!CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).Match(ResolveIP("1:2:3:4:5:6:7:9"))); BOOST_CHECK(CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).ToString() == "1:2:3:4:5:6:7:8/128"); + // IPv4 address with IPv6 netmask or the other way around. + BOOST_CHECK(!CSubNet(ResolveIP("1.1.1.1"), ResolveIP("ffff::")).IsValid()); + BOOST_CHECK(!CSubNet(ResolveIP("::1"), ResolveIP("255.0.0.0")).IsValid()); + // Can't subnet TOR (or any other non-IPv4 and non-IPv6 network). + BOOST_CHECK(!CSubNet(ResolveIP("5wyqrzbvrdsumnok.onion"), ResolveIP("255.0.0.0")).IsValid()); subnet = ResolveSubNet("1.2.3.4/255.255.255.255"); BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32"); @@ -290,11 +296,13 @@ BOOST_AUTO_TEST_CASE(subnet_test) BOOST_CHECK_EQUAL(subnet.ToString(), "1::/16"); subnet = ResolveSubNet("1:2:3:4:5:6:7:8/0000:0000:0000:0000:0000:0000:0000:0000"); BOOST_CHECK_EQUAL(subnet.ToString(), "::/0"); + // Invalid netmasks (with 1-bits after 0-bits) subnet = ResolveSubNet("1.2.3.4/255.255.232.0"); - BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/255.255.232.0"); + BOOST_CHECK(!subnet.IsValid()); + subnet = ResolveSubNet("1.2.3.4/255.0.255.255"); + BOOST_CHECK(!subnet.IsValid()); subnet = ResolveSubNet("1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f"); - BOOST_CHECK_EQUAL(subnet.ToString(), "1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f"); - + BOOST_CHECK(!subnet.IsValid()); } BOOST_AUTO_TEST_CASE(netbase_getgroup) @@ -428,7 +436,8 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters) BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0", 11), ret)); BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0example.com", 22), ret)); BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0example.com\0", 23), ret)); - BOOST_CHECK(LookupSubNet(std::string("5wyqrzbvrdsumnok.onion", 22), ret)); + // We only do subnetting for IPv4 and IPv6 + BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion", 22), ret)); BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0", 23), ret)); BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com", 34), ret)); BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com\0", 35), ret)); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 0143b08be4..d9a00c2205 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -168,7 +168,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const m_node.mempool->setSanityCheck(1.0); m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests. - m_node.peer_logic = MakeUnique<PeerLogicValidation>(m_node.connman.get(), m_node.banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool); + m_node.peer_logic = MakeUnique<PeerLogicValidation>(*m_node.connman, m_node.banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool); { CConnman::Options options; options.m_msgproc = m_node.peer_logic.get(); @@ -196,49 +196,34 @@ TestingSetup::~TestingSetup() TestChain100Setup::TestChain100Setup() { - // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests. - // TODO: fix the code to support SegWit blocks. - gArgs.ForceSetArg("-segwitheight", "432"); - // Need to recreate chainparams - SelectParams(CBaseChainParams::REGTEST); - // Generate a 100-block chain: coinbaseKey.MakeNewKey(true); - CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; - for (int i = 0; i < COINBASE_MATURITY; i++) - { + 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 CChainParams& chainparams = Params(); - std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(*m_node.mempool, chainparams).CreateNewBlock(scriptPubKey); - CBlock& block = pblocktemplate->block; + CTxMemPool empty_pool; + CBlock block = BlockAssembler(empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block; - // Replace mempool-selected txns with just coinbase plus passed-in txns: - block.vtx.resize(1); - for (const CMutableTransaction& tx : txns) + Assert(block.vtx.size() == 1); + for (const CMutableTransaction& tx : txns) { block.vtx.push_back(MakeTransactionRef(tx)); - // IncrementExtraNonce creates a valid coinbase and merkleRoot - { - LOCK(cs_main); - unsigned int extraNonce = 0; - IncrementExtraNonce(&block, ::ChainActive().Tip(), extraNonce); } + RegenerateCommitments(block); while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce; std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block); Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr); - CBlock result = block; - return result; + return block; } TestChain100Setup::~TestChain100Setup() @@ -246,8 +231,8 @@ TestChain100Setup::~TestChain100Setup() gArgs.ForceSetArg("-segwitheight", "0"); } - -CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) { +CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) +{ return FromTx(MakeTransactionRef(tx)); } diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index 78b279e42a..22f5d6d936 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -102,15 +102,16 @@ class CBlock; struct CMutableTransaction; class CScript; -// -// Testing fixture that pre-creates a -// 100-block REGTEST-mode block chain -// +/** + * Testing fixture that pre-creates a 100-block REGTEST-mode block chain + */ struct TestChain100Setup : public RegTestingSetup { TestChain100Setup(); - // Create a new block with just given transactions, coinbase paying to - // scriptPubKey, and try to add it to the current chain. + /** + * Create a new block with just given transactions, coinbase paying to + * scriptPubKey, and try to add it to the current chain. + */ CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey); diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index 4d51b9045e..6d93029737 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -318,6 +318,18 @@ bool ParseInt64(const std::string& str, int64_t *out) n <= std::numeric_limits<int64_t>::max(); } +bool ParseUInt8(const std::string& str, uint8_t *out) +{ + uint32_t u32; + if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint8_t>::max()) { + return false; + } + if (out != nullptr) { + *out = static_cast<uint8_t>(u32); + } + return true; +} + bool ParseUInt32(const std::string& str, uint32_t *out) { if (!ParsePrechecks(str)) diff --git a/src/util/strencodings.h b/src/util/strencodings.h index b4bbaeebf6..4c7a2694a9 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -99,6 +99,13 @@ NODISCARD bool ParseInt32(const std::string& str, int32_t *out); NODISCARD bool ParseInt64(const std::string& str, int64_t *out); /** + * Convert decimal string to unsigned 8-bit integer with strict parse error feedback. + * @returns true if the entire string could be parsed as valid integer, + * false if not the entire string could be parsed or when overflow or underflow occurred. + */ +NODISCARD bool ParseUInt8(const std::string& str, uint8_t *out); + +/** * Convert decimal string to unsigned 32-bit integer with strict parse error feedback. * @returns true if the entire string could be parsed as valid integer, * false if not the entire string could be parsed or when overflow or underflow occurred. diff --git a/src/util/system.cpp b/src/util/system.cpp index 7b74789b32..00aa53df70 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -48,12 +48,6 @@ #pragma warning(disable:4717) #endif -#ifdef _WIN32_IE -#undef _WIN32_IE -#endif -#define _WIN32_IE 0x0501 - -#define WIN32_LEAN_AND_MEAN 1 #ifndef NOMINMAX #define NOMINMAX #endif diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index 192b60e5d2..9b8e585b49 100755 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -8,9 +8,12 @@ Tests correspond to code in rpc/net.cpp. """ from decimal import Decimal +from itertools import product +import time from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( + assert_approx, assert_equal, assert_greater_than_or_equal, assert_greater_than, @@ -48,25 +51,27 @@ class NetTest(BitcoinTestFramework): self.supports_cli = False def run_test(self): - self.log.info('Get out of IBD for the minfeefilter test') - self.nodes[0].generate(1) - self.log.info('Connect nodes both way') + # Get out of IBD for the minfeefilter and getpeerinfo tests. + self.nodes[0].generate(101) + # Connect nodes both ways. connect_nodes(self.nodes[0], 1) connect_nodes(self.nodes[1], 0) - self._test_connection_count() - self._test_getnettotals() - self._test_getnetworkinfo() - self._test_getaddednodeinfo() - self._test_getpeerinfo() + self.test_connection_count() + self.test_getpeerinfo() + self.test_getnettotals() + self.test_getnetworkinfo() + self.test_getaddednodeinfo() self.test_service_flags() - self._test_getnodeaddresses() + self.test_getnodeaddresses() - def _test_connection_count(self): - # connect_nodes connects each node to the other + def test_connection_count(self): + self.log.info("Test getconnectioncount") + # After using `connect_nodes` to connect nodes 0 and 1 to each other. assert_equal(self.nodes[0].getconnectioncount(), 2) - def _test_getnettotals(self): + def test_getnettotals(self): + self.log.info("Test getnettotals") # getnettotals totalbytesrecv and totalbytessent should be # consistent with getpeerinfo. Since the RPC calls are not atomic, # and messages might have been recvd or sent between RPC calls, call @@ -96,7 +101,8 @@ class NetTest(BitcoinTestFramework): assert_greater_than_or_equal(after['bytesrecv_per_msg'].get('pong', 0), before['bytesrecv_per_msg'].get('pong', 0) + 32) assert_greater_than_or_equal(after['bytessent_per_msg'].get('ping', 0), before['bytessent_per_msg'].get('ping', 0) + 32) - def _test_getnetworkinfo(self): + def test_getnetworkinfo(self): + self.log.info("Test getnetworkinfo") assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True) assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2) @@ -108,7 +114,7 @@ class NetTest(BitcoinTestFramework): with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']): self.nodes[0].setnetworkactive(state=True) - self.log.info('Connect nodes both way') + # Connect nodes both ways. connect_nodes(self.nodes[0], 1) connect_nodes(self.nodes[1], 0) @@ -120,7 +126,8 @@ class NetTest(BitcoinTestFramework): for info in network_info: assert_net_servicesnames(int(info["localservices"], 0x10), info["localservicesnames"]) - def _test_getaddednodeinfo(self): + def test_getaddednodeinfo(self): + self.log.info("Test getaddednodeinfo") assert_equal(self.nodes[0].getaddednodeinfo(), []) # add a node (node2) to node0 ip_port = "127.0.0.1:{}".format(p2p_port(2)) @@ -139,8 +146,20 @@ class NetTest(BitcoinTestFramework): # check that a non-existent node returns an error assert_raises_rpc_error(-24, "Node has not been added", self.nodes[0].getaddednodeinfo, '1.1.1.1') - def _test_getpeerinfo(self): + def test_getpeerinfo(self): + self.log.info("Test getpeerinfo") + # Create a few getpeerinfo last_block/last_transaction values. + if self.is_wallet_compiled(): + self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1) + self.nodes[1].generate(1) + self.sync_all() + time_now = int(time.time()) peer_info = [x.getpeerinfo() for x in self.nodes] + # Verify last_block and last_transaction keys/values. + for node, peer, field in product(range(self.num_nodes), range(2), ['last_block', 'last_transaction']): + assert field in peer_info[node][peer].keys() + if peer_info[node][peer][field] != 0: + assert_approx(peer_info[node][peer][field], time_now, vspan=60) # check both sides of bidirectional connection between nodes # the address bound to on one side will be the source address for the other node assert_equal(peer_info[0][0]['addrbind'], peer_info[1][0]['addr']) @@ -152,11 +171,13 @@ class NetTest(BitcoinTestFramework): assert_net_servicesnames(int(info[0]["services"], 0x10), info[0]["servicesnames"]) def test_service_flags(self): + self.log.info("Test service flags") self.nodes[0].add_p2p_connection(P2PInterface(), services=(1 << 4) | (1 << 63)) assert_equal(['UNKNOWN[2^4]', 'UNKNOWN[2^63]'], self.nodes[0].getpeerinfo()[-1]['servicesnames']) self.nodes[0].disconnect_p2ps() - def _test_getnodeaddresses(self): + def test_getnodeaddresses(self): + self.log.info("Test getnodeaddresses") self.nodes[0].add_p2p_connection(P2PInterface()) # Add some addresses to the Address Manager over RPC. Due to the way diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py index 56b18752ec..c7895edbcc 100755 --- a/test/fuzz/test_runner.py +++ b/test/fuzz/test_runner.py @@ -56,6 +56,14 @@ def main(): '--m_dir', help='Merge inputs from this directory into the seed_dir. Needs /target subdirectory.', ) + parser.add_argument( + '-g', + '--generate', + action='store_true', + help='Create new corpus seeds (or extend the existing ones) by running' + ' the given targets for a finite number of times. Outputs them to' + ' the passed seed_dir.' + ) args = parser.parse_args() @@ -100,19 +108,20 @@ def main(): logging.info("{} of {} detected fuzz target(s) selected: {}".format(len(test_list_selection), len(test_list_all), " ".join(test_list_selection))) - test_list_seedless = [] - for t in test_list_selection: - corpus_path = os.path.join(args.seed_dir, t) - if not os.path.exists(corpus_path) or len(os.listdir(corpus_path)) == 0: - test_list_seedless.append(t) - test_list_seedless.sort() - if test_list_seedless: - logging.info( - "Fuzzing harnesses lacking a seed corpus: {}".format( - " ".join(test_list_seedless) + if not args.generate: + test_list_seedless = [] + for t in test_list_selection: + corpus_path = os.path.join(args.seed_dir, t) + if not os.path.exists(corpus_path) or len(os.listdir(corpus_path)) == 0: + test_list_seedless.append(t) + test_list_seedless.sort() + if test_list_seedless: + logging.info( + "Fuzzing harnesses lacking a seed corpus: {}".format( + " ".join(test_list_seedless) + ) ) - ) - logging.info("Please consider adding a fuzz seed corpus at https://github.com/bitcoin-core/qa-assets") + logging.info("Please consider adding a fuzz seed corpus at https://github.com/bitcoin-core/qa-assets") try: help_output = subprocess.run( @@ -133,6 +142,14 @@ def main(): sys.exit(1) with ThreadPoolExecutor(max_workers=args.par) as fuzz_pool: + if args.generate: + return generate_corpus_seeds( + fuzz_pool=fuzz_pool, + build_dir=config["environment"]["BUILDDIR"], + seed_dir=args.seed_dir, + targets=test_list_selection, + ) + if args.m_dir: merge_inputs( fuzz_pool=fuzz_pool, @@ -152,6 +169,37 @@ def main(): ) +def generate_corpus_seeds(*, fuzz_pool, build_dir, seed_dir, targets): + """Generates new corpus seeds. + + Run {targets} without input, and outputs the generated corpus seeds to + {seed_dir}. + """ + logging.info("Generating corpus seeds to {}".format(seed_dir)) + + def job(command): + logging.debug("Running '{}'\n".format(" ".join(command))) + logging.debug("Command '{}' output:\n'{}'\n".format( + ' '.join(command), + subprocess.run(command, check=True, stderr=subprocess.PIPE, + universal_newlines=True).stderr + )) + + futures = [] + for target in targets: + target_seed_dir = os.path.join(seed_dir, target) + os.makedirs(target_seed_dir, exist_ok=True) + command = [ + os.path.join(build_dir, "src", "test", "fuzz", target), + "-runs=100000", + target_seed_dir, + ] + futures.append(fuzz_pool.submit(job, command)) + + for future in as_completed(futures): + future.result() + + def merge_inputs(*, fuzz_pool, corpus, test_list, build_dir, merge_dir): logging.info("Merge the inputs in the passed dir into the seed_dir. Passed dir {}".format(merge_dir)) jobs = [] |