From bac78d0273ec147b24250c7afa9089fd0b9819aa Mon Sep 17 00:00:00 2001 From: cryptocode Date: Thu, 27 Feb 2020 14:53:56 +0100 Subject: [PATCH 01/43] Utility: nano::optional_ptr (#2605) * optional_ptr * Implement feedback from Wesley/Guilherme --- nano/core_test/utility.cpp | 28 ++++++++++++ nano/lib/CMakeLists.txt | 1 + nano/lib/optional_ptr.hpp | 93 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 nano/lib/optional_ptr.hpp diff --git a/nano/core_test/utility.cpp b/nano/core_test/utility.cpp index de7a91a681..4ef4614cf6 100644 --- a/nano/core_test/utility.cpp +++ b/nano/core_test/utility.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -7,6 +8,33 @@ #include +TEST (optional_ptr, basic) +{ + struct valtype + { + int64_t x{ 1 }; + int64_t y{ 2 }; + int64_t z{ 3 }; + }; + + nano::optional_ptr opt; + ASSERT_FALSE (opt); + ASSERT_FALSE (opt.is_initialized ()); + + { + auto val = valtype{}; + opt = val; + ASSERT_LT (sizeof (opt), sizeof (val)); + std::unique_ptr uptr; + ASSERT_EQ (sizeof (opt), sizeof (uptr)); + } + ASSERT_TRUE (opt); + ASSERT_TRUE (opt.is_initialized ()); + ASSERT_EQ (opt->x, 1); + ASSERT_EQ (opt->y, 2); + ASSERT_EQ (opt->z, 3); +} + TEST (thread, worker) { std::atomic passed_sleep{ false }; diff --git a/nano/lib/CMakeLists.txt b/nano/lib/CMakeLists.txt index 5729a3a0ee..69e9afd269 100644 --- a/nano/lib/CMakeLists.txt +++ b/nano/lib/CMakeLists.txt @@ -41,6 +41,7 @@ add_library (nano_lib memory.cpp numbers.hpp numbers.cpp + optional_ptr.hpp rep_weights.hpp rep_weights.cpp rocksdbconfig.hpp diff --git a/nano/lib/optional_ptr.hpp b/nano/lib/optional_ptr.hpp new file mode 100644 index 0000000000..7c7ab903c8 --- /dev/null +++ b/nano/lib/optional_ptr.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include + +#include +#include + +namespace nano +{ +/** + * A space efficient optional which does heap allocation when needed. + * This is an alternative to boost/std::optional when the value type is + * large and often not present. + * + * optional_ptr is similar to using std::unique_ptr as an optional, with the + * main difference being that it's copyable. + */ +template +class optional_ptr +{ + static_assert (sizeof (T) > alignof (std::max_align_t), "Use [std|boost]::optional"); + +public: + optional_ptr () = default; + + optional_ptr (T const & value) : + ptr (new T{ value }) + { + } + + optional_ptr (optional_ptr const & other) + { + if (other && other.ptr) + { + ptr = std::make_unique (*other.ptr); + } + } + + optional_ptr & operator= (optional_ptr const & other) + { + if (other && other.ptr) + { + ptr = std::make_unique (*other.ptr); + } + return *this; + } + + T & operator* () + { + return *ptr; + } + + T const & operator* () const + { + return *ptr; + } + + T * const operator-> () + { + return ptr.operator-> (); + } + + T const * const operator-> () const + { + return ptr.operator-> (); + } + + T const * const get () const + { + debug_assert (is_initialized ()); + return ptr.get (); + } + + T * const get () + { + debug_assert (is_initialized ()); + return ptr.get (); + } + + explicit operator bool () const + { + return static_cast (ptr); + } + + bool is_initialized () const + { + return static_cast (ptr); + } + +private: + std::unique_ptr ptr{ nullptr }; +}; +} From 81d92057d5260664938c61a16f422f5290883b42 Mon Sep 17 00:00:00 2001 From: Russel Waters Date: Thu, 27 Feb 2020 21:37:34 -0500 Subject: [PATCH 02/43] CMakeLists backwards compatibility (#2607) use version checks supported on older cmakes use atleast 3.11 FindBoost.cmake as import targets are not properly exposed on previous versions --- CMakeLists.txt | 10 +- cmake/legacyModules/FindBoost.cmake | 2098 +++++++++++++++++++++++++++ 2 files changed, 2106 insertions(+), 2 deletions(-) create mode 100644 cmake/legacyModules/FindBoost.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index e19ba83d6a..5fbed76a89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,19 @@ cmake_minimum_required (VERSION 3.4) -if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.12") +if (CMAKE_VERSION VERSION_GREATER 3.12 OR CMAKE_VERSION VERSION_EQUAL 3.12) #find_package uses _ROOT variables cmake_policy(SET CMP0074 NEW) endif() -if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13") +if (CMAKE_VERSION VERSION_GREATER 3.13 OR CMAKE_VERSION VERSION_EQUAL 3.13) #option honors normal variables cmake_policy(SET CMP0077 NEW) endif() + +if (CMAKE_VERSION VERSION_LESS 3.11) + # compatibility for boost import targets use bundled 3.11 FindBoost.cmake + list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/legacyModules") +endif () + # compatibility for osx sierra and on # needs to be set before project set (CMAKE_OSX_DEPLOYMENT_TARGET 10.12 CACHE STRING "") diff --git a/cmake/legacyModules/FindBoost.cmake b/cmake/legacyModules/FindBoost.cmake new file mode 100644 index 0000000000..8d44aee0b9 --- /dev/null +++ b/cmake/legacyModules/FindBoost.cmake @@ -0,0 +1,2098 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#.rst: +# FindBoost +# --------- +# +# Find Boost include dirs and libraries +# +# Use this module by invoking find_package with the form:: +# +# find_package(Boost +# [version] [EXACT] # Minimum or EXACT version e.g. 1.67.0 +# [REQUIRED] # Fail with error if Boost is not found +# [COMPONENTS ...] # Boost libraries by their canonical name +# # e.g. "date_time" for "libboost_date_time" +# [OPTIONAL_COMPONENTS ...] +# # Optional Boost libraries by their canonical name) +# ) # e.g. "date_time" for "libboost_date_time" +# +# This module finds headers and requested component libraries OR a CMake +# package configuration file provided by a "Boost CMake" build. For the +# latter case skip to the "Boost CMake" section below. For the former +# case results are reported in variables:: +# +# Boost_FOUND - True if headers and requested libraries were found +# Boost_INCLUDE_DIRS - Boost include directories +# Boost_LIBRARY_DIRS - Link directories for Boost libraries +# Boost_LIBRARIES - Boost component libraries to be linked +# Boost__FOUND - True if component was found ( is upper-case) +# Boost__LIBRARY - Libraries to link for component (may include +# target_link_libraries debug/optimized keywords) +# Boost_VERSION - BOOST_VERSION value from boost/version.hpp +# Boost_LIB_VERSION - Version string appended to library filenames +# Boost_MAJOR_VERSION - Boost major version number (X in X.y.z) +# Boost_MINOR_VERSION - Boost minor version number (Y in x.Y.z) +# Boost_SUBMINOR_VERSION - Boost subminor version number (Z in x.y.Z) +# Boost_LIB_DIAGNOSTIC_DEFINITIONS (Windows) +# - Pass to add_definitions() to have diagnostic +# information about Boost's automatic linking +# displayed during compilation +# +# Note that Boost Python components require a Python version suffix +# (Boost 1.67 and later), e.g. ``python36`` or ``python27`` for the +# versions built against Python 3.6 and 2.7, respectively. This also +# applies to additional components using Python including +# ``mpi_python`` and ``numpy``. Earlier Boost releases may use +# distribution-specific suffixes such as ``2``, ``3`` or ``2.7``. +# These may also be used as suffixes, but note that they are not +# portable. +# +# This module reads hints about search locations from variables:: +# +# BOOST_ROOT - Preferred installation prefix +# (or BOOSTROOT) +# BOOST_INCLUDEDIR - Preferred include directory e.g. /include +# BOOST_LIBRARYDIR - Preferred library directory e.g. /lib +# Boost_NO_SYSTEM_PATHS - Set to ON to disable searching in locations not +# specified by these hint variables. Default is OFF. +# Boost_ADDITIONAL_VERSIONS +# - List of Boost versions not known to this module +# (Boost install locations may contain the version) +# +# and saves search results persistently in CMake cache entries:: +# +# Boost_INCLUDE_DIR - Directory containing Boost headers +# Boost_LIBRARY_DIR_RELEASE - Directory containing release Boost libraries +# Boost_LIBRARY_DIR_DEBUG - Directory containing debug Boost libraries +# Boost__LIBRARY_DEBUG - Component library debug variant +# Boost__LIBRARY_RELEASE - Component library release variant +# +# The following :prop_tgt:`IMPORTED` targets are also defined:: +# +# Boost::boost - Target for header-only dependencies +# (Boost include directory) +# Boost:: - Target for specific component dependency +# (shared or static library); is lower- +# case +# Boost::diagnostic_definitions - interface target to enable diagnostic +# information about Boost's automatic linking +# during compilation (adds BOOST_LIB_DIAGNOSTIC) +# Boost::disable_autolinking - interface target to disable automatic +# linking with MSVC (adds BOOST_ALL_NO_LIB) +# Boost::dynamic_linking - interface target to enable dynamic linking +# linking with MSVC (adds BOOST_ALL_DYN_LINK) +# +# Implicit dependencies such as Boost::filesystem requiring +# Boost::system will be automatically detected and satisfied, even +# if system is not specified when using find_package and if +# Boost::system is not added to target_link_libraries. If using +# Boost::thread, then Threads::Threads will also be added automatically. +# +# It is important to note that the imported targets behave differently +# than variables created by this module: multiple calls to +# find_package(Boost) in the same directory or sub-directories with +# different options (e.g. static or shared) will not override the +# values of the targets created by the first call. +# +# Users may set these hints or results as cache entries. Projects +# should not read these entries directly but instead use the above +# result variables. Note that some hint names start in upper-case +# "BOOST". One may specify these as environment variables if they are +# not specified as CMake variables or cache entries. +# +# This module first searches for the Boost header files using the above +# hint variables (excluding BOOST_LIBRARYDIR) and saves the result in +# Boost_INCLUDE_DIR. Then it searches for requested component libraries +# using the above hints (excluding BOOST_INCLUDEDIR and +# Boost_ADDITIONAL_VERSIONS), "lib" directories near Boost_INCLUDE_DIR, +# and the library name configuration settings below. It saves the +# library directories in Boost_LIBRARY_DIR_DEBUG and +# Boost_LIBRARY_DIR_RELEASE and individual library +# locations in Boost__LIBRARY_DEBUG and Boost__LIBRARY_RELEASE. +# When one changes settings used by previous searches in the same build +# tree (excluding environment variables) this module discards previous +# search results affected by the changes and searches again. +# +# Boost libraries come in many variants encoded in their file name. +# Users or projects may tell this module which variant to find by +# setting variables:: +# +# Boost_USE_DEBUG_LIBS - Set to ON or OFF to specify whether to search +# and use the debug libraries. Default is ON. +# Boost_USE_RELEASE_LIBS - Set to ON or OFF to specify whether to search +# and use the release libraries. Default is ON. +# Boost_USE_MULTITHREADED - Set to OFF to use the non-multithreaded +# libraries ('mt' tag). Default is ON. +# Boost_USE_STATIC_LIBS - Set to ON to force the use of the static +# libraries. Default is OFF. +# Boost_USE_STATIC_RUNTIME - Set to ON or OFF to specify whether to use +# libraries linked statically to the C++ runtime +# ('s' tag). Default is platform dependent. +# Boost_USE_DEBUG_RUNTIME - Set to ON or OFF to specify whether to use +# libraries linked to the MS debug C++ runtime +# ('g' tag). Default is ON. +# Boost_USE_DEBUG_PYTHON - Set to ON to use libraries compiled with a +# debug Python build ('y' tag). Default is OFF. +# Boost_USE_STLPORT - Set to ON to use libraries compiled with +# STLPort ('p' tag). Default is OFF. +# Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS +# - Set to ON to use libraries compiled with +# STLPort deprecated "native iostreams" +# ('n' tag). Default is OFF. +# Boost_COMPILER - Set to the compiler-specific library suffix +# (e.g. "-gcc43"). Default is auto-computed +# for the C++ compiler in use. A list may be +# used if multiple compatible suffixes should +# be tested for, in decreasing order of +# preference. +# Boost_THREADAPI - Suffix for "thread" component library name, +# such as "pthread" or "win32". Names with +# and without this suffix will both be tried. +# Boost_NAMESPACE - Alternate namespace used to build boost with +# e.g. if set to "myboost", will search for +# myboost_thread instead of boost_thread. +# +# Other variables one may set to control this module are:: +# +# Boost_DEBUG - Set to ON to enable debug output from FindBoost. +# Please enable this before filing any bug report. +# Boost_DETAILED_FAILURE_MSG +# - Set to ON to add detailed information to the +# failure message even when the REQUIRED option +# is not given to the find_package call. +# Boost_REALPATH - Set to ON to resolve symlinks for discovered +# libraries to assist with packaging. For example, +# the "system" component library may be resolved to +# "/usr/lib/libboost_system.so.1.67.0" instead of +# "/usr/lib/libboost_system.so". This does not +# affect linking and should not be enabled unless +# the user needs this information. +# Boost_LIBRARY_DIR - Default value for Boost_LIBRARY_DIR_RELEASE and +# Boost_LIBRARY_DIR_DEBUG. +# +# On Visual Studio and Borland compilers Boost headers request automatic +# linking to corresponding libraries. This requires matching libraries +# to be linked explicitly or available in the link library search path. +# In this case setting Boost_USE_STATIC_LIBS to OFF may not achieve +# dynamic linking. Boost automatic linking typically requests static +# libraries with a few exceptions (such as Boost.Python). Use:: +# +# add_definitions(${Boost_LIB_DIAGNOSTIC_DEFINITIONS}) +# +# to ask Boost to report information about automatic linking requests. +# +# Example to find Boost headers only:: +# +# find_package(Boost 1.36.0) +# if(Boost_FOUND) +# include_directories(${Boost_INCLUDE_DIRS}) +# add_executable(foo foo.cc) +# endif() +# +# Example to find Boost libraries and use imported targets:: +# +# find_package(Boost 1.56 REQUIRED COMPONENTS +# date_time filesystem iostreams) +# add_executable(foo foo.cc) +# target_link_libraries(foo Boost::date_time Boost::filesystem +# Boost::iostreams) +# +# Example to find Boost Python 3.6 libraries and use imported targets:: +# +# find_package(Boost 1.67 REQUIRED COMPONENTS +# python36 numpy36) +# add_executable(foo foo.cc) +# target_link_libraries(foo Boost::python36 Boost::numpy36) +# +# Example to find Boost headers and some *static* (release only) libraries:: +# +# set(Boost_USE_STATIC_LIBS ON) # only find static libs +# set(Boost_USE_DEBUG_LIBS OFF) # ignore debug libs and +# set(Boost_USE_RELEASE_LIBS ON) # only find release libs +# set(Boost_USE_MULTITHREADED ON) +# set(Boost_USE_STATIC_RUNTIME OFF) +# find_package(Boost 1.66.0 COMPONENTS date_time filesystem system ...) +# if(Boost_FOUND) +# include_directories(${Boost_INCLUDE_DIRS}) +# add_executable(foo foo.cc) +# target_link_libraries(foo ${Boost_LIBRARIES}) +# endif() +# +# Boost CMake +# ^^^^^^^^^^^ +# +# If Boost was built using the boost-cmake project it provides a package +# configuration file for use with find_package's Config mode. This +# module looks for the package configuration file called +# BoostConfig.cmake or boost-config.cmake and stores the result in cache +# entry "Boost_DIR". If found, the package configuration file is loaded +# and this module returns with no further action. See documentation of +# the Boost CMake package configuration for details on what it provides. +# +# Set Boost_NO_BOOST_CMAKE to ON to disable the search for boost-cmake. + +# Save project's policies +cmake_policy(PUSH) +cmake_policy(SET CMP0057 NEW) # if IN_LIST + +#------------------------------------------------------------------------------- +# Before we go searching, check whether boost-cmake is available, unless the +# user specifically asked NOT to search for boost-cmake. +# +# If Boost_DIR is set, this behaves as any find_package call would. If not, +# it looks at BOOST_ROOT and BOOSTROOT to find Boost. +# +if (NOT Boost_NO_BOOST_CMAKE) + # If Boost_DIR is not set, look for BOOSTROOT and BOOST_ROOT as alternatives, + # since these are more conventional for Boost. + if ("$ENV{Boost_DIR}" STREQUAL "") + if (NOT "$ENV{BOOST_ROOT}" STREQUAL "") + set(ENV{Boost_DIR} $ENV{BOOST_ROOT}) + elseif (NOT "$ENV{BOOSTROOT}" STREQUAL "") + set(ENV{Boost_DIR} $ENV{BOOSTROOT}) + endif() + endif() + + # Do the same find_package call but look specifically for the CMake version. + # Note that args are passed in the Boost_FIND_xxxxx variables, so there is no + # need to delegate them to this find_package call. + find_package(Boost QUIET NO_MODULE) + mark_as_advanced(Boost_DIR) + + # If we found boost-cmake, then we're done. Print out what we found. + # Otherwise let the rest of the module try to find it. + if (Boost_FOUND) + message(STATUS "Boost ${Boost_FIND_VERSION} found.") + if (Boost_FIND_COMPONENTS) + message(STATUS "Found Boost components:\n ${Boost_FIND_COMPONENTS}") + endif() + # Restore project's policies + cmake_policy(POP) + return() + endif() +endif() + + +#------------------------------------------------------------------------------- +# FindBoost functions & macros +# + +############################################ +# +# Check the existence of the libraries. +# +############################################ +# This macro was taken directly from the FindQt4.cmake file that is included +# with the CMake distribution. This is NOT my work. All work was done by the +# original authors of the FindQt4.cmake file. Only minor modifications were +# made to remove references to Qt and make this file more generally applicable +# And ELSE/ENDIF pairs were removed for readability. +######################################################################### + +macro(_Boost_ADJUST_LIB_VARS basename) + if(Boost_INCLUDE_DIR ) + if(Boost_${basename}_LIBRARY_DEBUG AND Boost_${basename}_LIBRARY_RELEASE) + # if the generator is multi-config or if CMAKE_BUILD_TYPE is set for + # single-config generators, set optimized and debug libraries + get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(_isMultiConfig OR CMAKE_BUILD_TYPE) + set(Boost_${basename}_LIBRARY optimized ${Boost_${basename}_LIBRARY_RELEASE} debug ${Boost_${basename}_LIBRARY_DEBUG}) + else() + # For single-config generators where CMAKE_BUILD_TYPE has no value, + # just use the release libraries + set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE} ) + endif() + # FIXME: This probably should be set for both cases + set(Boost_${basename}_LIBRARIES optimized ${Boost_${basename}_LIBRARY_RELEASE} debug ${Boost_${basename}_LIBRARY_DEBUG}) + endif() + + # if only the release version was found, set the debug variable also to the release version + if(Boost_${basename}_LIBRARY_RELEASE AND NOT Boost_${basename}_LIBRARY_DEBUG) + set(Boost_${basename}_LIBRARY_DEBUG ${Boost_${basename}_LIBRARY_RELEASE}) + set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE}) + set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_RELEASE}) + endif() + + # if only the debug version was found, set the release variable also to the debug version + if(Boost_${basename}_LIBRARY_DEBUG AND NOT Boost_${basename}_LIBRARY_RELEASE) + set(Boost_${basename}_LIBRARY_RELEASE ${Boost_${basename}_LIBRARY_DEBUG}) + set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_DEBUG}) + set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_DEBUG}) + endif() + + # If the debug & release library ends up being the same, omit the keywords + if("${Boost_${basename}_LIBRARY_RELEASE}" STREQUAL "${Boost_${basename}_LIBRARY_DEBUG}") + set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE} ) + set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_RELEASE} ) + endif() + + if(Boost_${basename}_LIBRARY AND Boost_${basename}_HEADER) + set(Boost_${basename}_FOUND ON) + if("x${basename}" STREQUAL "xTHREAD" AND NOT TARGET Threads::Threads) + string(APPEND Boost_ERROR_REASON_THREAD " (missing dependency: Threads)") + set(Boost_THREAD_FOUND OFF) + endif() + endif() + + endif() + # Make variables changeable to the advanced user + mark_as_advanced( + Boost_${basename}_LIBRARY_RELEASE + Boost_${basename}_LIBRARY_DEBUG + ) +endmacro() + +# Detect changes in used variables. +# Compares the current variable value with the last one. +# In short form: +# v != v_LAST -> CHANGED = 1 +# v is defined, v_LAST not -> CHANGED = 1 +# v is not defined, but v_LAST is -> CHANGED = 1 +# otherwise -> CHANGED = 0 +# CHANGED is returned in variable named ${changed_var} +macro(_Boost_CHANGE_DETECT changed_var) + set(${changed_var} 0) + foreach(v ${ARGN}) + if(DEFINED _Boost_COMPONENTS_SEARCHED) + if(${v}) + if(_${v}_LAST) + string(COMPARE NOTEQUAL "${${v}}" "${_${v}_LAST}" _${v}_CHANGED) + else() + set(_${v}_CHANGED 1) + endif() + elseif(_${v}_LAST) + set(_${v}_CHANGED 1) + endif() + if(_${v}_CHANGED) + set(${changed_var} 1) + endif() + else() + set(_${v}_CHANGED 0) + endif() + endforeach() +endmacro() + +# +# Find the given library (var). +# Use 'build_type' to support different lib paths for RELEASE or DEBUG builds +# +macro(_Boost_FIND_LIBRARY var build_type) + + find_library(${var} ${ARGN}) + + if(${var}) + # If this is the first library found then save Boost_LIBRARY_DIR_[RELEASE,DEBUG]. + if(NOT Boost_LIBRARY_DIR_${build_type}) + get_filename_component(_dir "${${var}}" PATH) + set(Boost_LIBRARY_DIR_${build_type} "${_dir}" CACHE PATH "Boost library directory ${build_type}" FORCE) + endif() + elseif(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT) + # Try component-specific hints but do not save Boost_LIBRARY_DIR_[RELEASE,DEBUG]. + find_library(${var} HINTS ${_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT} ${ARGN}) + endif() + + # If Boost_LIBRARY_DIR_[RELEASE,DEBUG] is known then search only there. + if(Boost_LIBRARY_DIR_${build_type}) + set(_boost_LIBRARY_SEARCH_DIRS_${build_type} ${Boost_LIBRARY_DIR_${build_type}} NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " Boost_LIBRARY_DIR_${build_type} = ${Boost_LIBRARY_DIR_${build_type}}" + " _boost_LIBRARY_SEARCH_DIRS_${build_type} = ${_boost_LIBRARY_SEARCH_DIRS_${build_type}}") + endif() + endif() +endmacro() + +#------------------------------------------------------------------------------- + +# +# Runs compiler with "-dumpversion" and parses major/minor +# version with a regex. +# +function(_Boost_COMPILER_DUMPVERSION _OUTPUT_VERSION) + string(REGEX REPLACE "([0-9]+)\\.([0-9]+)(\\.[0-9]+)?" "\\1\\2" + _boost_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION}) + + set(${_OUTPUT_VERSION} ${_boost_COMPILER_VERSION} PARENT_SCOPE) +endfunction() + +# +# Take a list of libraries with "thread" in it +# and prepend duplicates with "thread_${Boost_THREADAPI}" +# at the front of the list +# +function(_Boost_PREPEND_LIST_WITH_THREADAPI _output) + set(_orig_libnames ${ARGN}) + string(REPLACE "thread" "thread_${Boost_THREADAPI}" _threadapi_libnames "${_orig_libnames}") + set(${_output} ${_threadapi_libnames} ${_orig_libnames} PARENT_SCOPE) +endfunction() + +# +# If a library is found, replace its cache entry with its REALPATH +# +function(_Boost_SWAP_WITH_REALPATH _library _docstring) + if(${_library}) + get_filename_component(_boost_filepathreal ${${_library}} REALPATH) + unset(${_library} CACHE) + set(${_library} ${_boost_filepathreal} CACHE FILEPATH "${_docstring}") + endif() +endfunction() + +function(_Boost_CHECK_SPELLING _var) + if(${_var}) + string(TOUPPER ${_var} _var_UC) + message(FATAL_ERROR "ERROR: ${_var} is not the correct spelling. The proper spelling is ${_var_UC}.") + endif() +endfunction() + +# Guesses Boost's compiler prefix used in built library names +# Returns the guess by setting the variable pointed to by _ret +function(_Boost_GUESS_COMPILER_PREFIX _ret) + if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xIntel") + if(WIN32) + set (_boost_COMPILER "-iw") + else() + set (_boost_COMPILER "-il") + endif() + elseif (GHSMULTI) + set(_boost_COMPILER "-ghs") + elseif("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC") + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10) + set(_boost_COMPILER "-vc141;-vc140") + elseif (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19) + set(_boost_COMPILER "-vc140") + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18) + set(_boost_COMPILER "-vc120") + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 17) + set(_boost_COMPILER "-vc110") + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16) + set(_boost_COMPILER "-vc100") + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15) + set(_boost_COMPILER "-vc90") + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14) + set(_boost_COMPILER "-vc80") + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.10) + set(_boost_COMPILER "-vc71") + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13) # Good luck! + set(_boost_COMPILER "-vc7") # yes, this is correct + else() # VS 6.0 Good luck! + set(_boost_COMPILER "-vc6") # yes, this is correct + endif() + elseif (BORLAND) + set(_boost_COMPILER "-bcb") + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "SunPro") + set(_boost_COMPILER "-sw") + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "XL") + set(_boost_COMPILER "-xlc") + elseif (MINGW) + if(${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION} VERSION_LESS 1.34) + set(_boost_COMPILER "-mgw") # no GCC version encoding prior to 1.34 + else() + _Boost_COMPILER_DUMPVERSION(_boost_COMPILER_VERSION) + set(_boost_COMPILER "-mgw${_boost_COMPILER_VERSION}") + endif() + elseif (UNIX) + if (CMAKE_COMPILER_IS_GNUCXX) + if(${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION} VERSION_LESS 1.34) + set(_boost_COMPILER "-gcc") # no GCC version encoding prior to 1.34 + else() + _Boost_COMPILER_DUMPVERSION(_boost_COMPILER_VERSION) + # Determine which version of GCC we have. + if(APPLE) + if(Boost_MINOR_VERSION) + if(${Boost_MINOR_VERSION} GREATER 35) + # In Boost 1.36.0 and newer, the mangled compiler name used + # on Mac OS X/Darwin is "xgcc". + set(_boost_COMPILER "-xgcc${_boost_COMPILER_VERSION}") + else() + # In Boost <= 1.35.0, there is no mangled compiler name for + # the Mac OS X/Darwin version of GCC. + set(_boost_COMPILER "") + endif() + else() + # We don't know the Boost version, so assume it's + # pre-1.36.0. + set(_boost_COMPILER "") + endif() + else() + set(_boost_COMPILER "-gcc${_boost_COMPILER_VERSION}") + endif() + endif() + endif () + else() + # TODO at least Boost_DEBUG here? + set(_boost_COMPILER "") + endif() + set(${_ret} ${_boost_COMPILER} PARENT_SCOPE) +endfunction() + +# +# Get component dependencies. Requires the dependencies to have been +# defined for the Boost release version. +# +# component - the component to check +# _ret - list of library dependencies +# +function(_Boost_COMPONENT_DEPENDENCIES component _ret) + # Note: to add a new Boost release, run + # + # % cmake -DBOOST_DIR=/path/to/boost/source -P Utilities/Scripts/BoostScanDeps.cmake + # + # The output may be added in a new block below. If it's the same as + # the previous release, simply update the version range of the block + # for the previous release. Also check if any new components have + # been added, and add any new components to + # _Boost_COMPONENT_HEADERS. + # + # This information was originally generated by running + # BoostScanDeps.cmake against every boost release to date supported + # by FindBoost: + # + # % for version in /path/to/boost/sources/* + # do + # cmake -DBOOST_DIR=$version -P Utilities/Scripts/BoostScanDeps.cmake + # done + # + # The output was then updated by search and replace with these regexes: + # + # - Strip message(STATUS) prefix dashes + # s;^-- ;; + # - Indent + # s;^set(; set(;; + # - Add conditionals + # s;Scanning /path/to/boost/sources/boost_\(.*\)_\(.*\)_\(.*); elseif(NOT Boost_VERSION VERSION_LESS \10\20\3 AND Boost_VERSION VERSION_LESS xxxx); + # + # This results in the logic seen below, but will require the xxxx + # replacing with the following Boost release version (or the next + # minor version to be released, e.g. 1.59 was the latest at the time + # of writing, making 1.60 the next, so 106000 is the needed version + # number). Identical consecutive releases were then merged together + # by updating the end range of the first block and removing the + # following redundant blocks. + # + # Running the script against all historical releases should be + # required only if the BoostScanDeps.cmake script logic is changed. + # The addition of a new release should only require it to be run + # against the new release. + + # Handle Python version suffixes + if(component MATCHES "^(python|mpi_python|numpy)([0-9][0-9]?|[0-9]\\.[0-9])\$") + set(component "${CMAKE_MATCH_1}") + set(component_python_version "${CMAKE_MATCH_2}") + endif() + + set(_Boost_IMPORTED_TARGETS TRUE) + if(Boost_VERSION VERSION_LESS 103300) + message(WARNING "Imported targets and dependency information not available for Boost version ${Boost_VERSION} (all versions older than 1.33)") + set(_Boost_IMPORTED_TARGETS FALSE) + elseif(NOT Boost_VERSION VERSION_LESS 103300 AND Boost_VERSION VERSION_LESS 103500) + set(_Boost_IOSTREAMS_DEPENDENCIES regex thread) + set(_Boost_REGEX_DEPENDENCIES thread) + set(_Boost_WAVE_DEPENDENCIES filesystem thread) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 103500 AND Boost_VERSION VERSION_LESS 103600) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_WAVE_DEPENDENCIES filesystem system thread) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 103600 AND Boost_VERSION VERSION_LESS 103800) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_WAVE_DEPENDENCIES filesystem system thread) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 103800 AND Boost_VERSION VERSION_LESS 104300) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_THREAD_DEPENDENCIES date_time) + set(_Boost_WAVE_DEPENDENCIES filesystem system thread date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 104300 AND Boost_VERSION VERSION_LESS 104400) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_THREAD_DEPENDENCIES date_time) + set(_Boost_WAVE_DEPENDENCIES filesystem system thread date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 104400 AND Boost_VERSION VERSION_LESS 104500) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l random serialization) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_THREAD_DEPENDENCIES date_time) + set(_Boost_WAVE_DEPENDENCIES serialization filesystem system thread date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 104500 AND Boost_VERSION VERSION_LESS 104700) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_THREAD_DEPENDENCIES date_time) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 104700 AND Boost_VERSION VERSION_LESS 104800) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_THREAD_DEPENDENCIES date_time) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 104800 AND Boost_VERSION VERSION_LESS 105000) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_THREAD_DEPENDENCIES date_time) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 105000 AND Boost_VERSION VERSION_LESS 105300) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l regex random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 105300 AND Boost_VERSION VERSION_LESS 105400) + set(_Boost_ATOMIC_DEPENDENCIES thread chrono system date_time) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l regex random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 105400 AND Boost_VERSION VERSION_LESS 105500) + set(_Boost_ATOMIC_DEPENDENCIES thread chrono system date_time) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES log_setup date_time system filesystem thread regex chrono) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l regex random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 105500 AND Boost_VERSION VERSION_LESS 105600) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES log_setup date_time system filesystem thread regex chrono) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l regex random) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 105600 AND Boost_VERSION VERSION_LESS 105900) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES log_setup date_time system filesystem thread regex chrono) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 105900 AND Boost_VERSION VERSION_LESS 106000) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES log_setup date_time system filesystem thread regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 106000 AND Boost_VERSION VERSION_LESS 106100) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 106100 AND Boost_VERSION VERSION_LESS 106200) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 106200 AND Boost_VERSION VERSION_LESS 106300) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 106300 AND Boost_VERSION VERSION_LESS 106500) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_COROUTINE2_DEPENDENCIES context fiber thread chrono system date_time) + set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + elseif(NOT Boost_VERSION VERSION_LESS 106500 AND Boost_VERSION VERSION_LESS 106700) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python mpi serialization) + set(_Boost_NUMPY_DEPENDENCIES python) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + else() + if(NOT Boost_VERSION VERSION_LESS 106700) + set(_Boost_CHRONO_DEPENDENCIES system) + set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) + set(_Boost_COROUTINE_DEPENDENCIES context system) + set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time) + set(_Boost_FILESYSTEM_DEPENDENCIES system) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization) + set(_Boost_NUMPY_DEPENDENCIES python${component_python_version}) + set(_Boost_RANDOM_DEPENDENCIES system) + set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) + set(_Boost_TIMER_DEPENDENCIES chrono system) + set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + endif() + if(NOT Boost_VERSION VERSION_LESS 106800) + message(WARNING "New Boost version may have incorrect or missing dependencies and imported targets") + endif() + endif() + + string(TOUPPER ${component} uppercomponent) + set(${_ret} ${_Boost_${uppercomponent}_DEPENDENCIES} PARENT_SCOPE) + set(_Boost_IMPORTED_TARGETS ${_Boost_IMPORTED_TARGETS} PARENT_SCOPE) + + string(REGEX REPLACE ";" " " _boost_DEPS_STRING "${_Boost_${uppercomponent}_DEPENDENCIES}") + if (NOT _boost_DEPS_STRING) + set(_boost_DEPS_STRING "(none)") + endif() + # message(STATUS "Dependencies for Boost::${component}: ${_boost_DEPS_STRING}") +endfunction() + +# +# Get component headers. This is the primary header (or headers) for +# a given component, and is used to check that the headers are present +# as well as the library itself as an extra sanity check of the build +# environment. +# +# component - the component to check +# _hdrs +# +function(_Boost_COMPONENT_HEADERS component _hdrs) + # Handle Python version suffixes + if(component MATCHES "^(python|mpi_python|numpy)([0-9][0-9]?|[0-9]\\.[0-9])\$") + set(component "${CMAKE_MATCH_1}") + set(component_python_version "${CMAKE_MATCH_2}") + endif() + + # Note: new boost components will require adding here. The header + # must be present in all versions of Boost providing a library. + set(_Boost_ATOMIC_HEADERS "boost/atomic.hpp") + set(_Boost_CHRONO_HEADERS "boost/chrono.hpp") + set(_Boost_CONTAINER_HEADERS "boost/container/container_fwd.hpp") + set(_Boost_CONTEXT_HEADERS "boost/context/all.hpp") + set(_Boost_COROUTINE_HEADERS "boost/coroutine/all.hpp") + set(_Boost_DATE_TIME_HEADERS "boost/date_time/date.hpp") + set(_Boost_EXCEPTION_HEADERS "boost/exception/exception.hpp") + set(_Boost_FIBER_HEADERS "boost/fiber/all.hpp") + set(_Boost_FILESYSTEM_HEADERS "boost/filesystem/path.hpp") + set(_Boost_GRAPH_HEADERS "boost/graph/adjacency_list.hpp") + set(_Boost_GRAPH_PARALLEL_HEADERS "boost/graph/adjacency_list.hpp") + set(_Boost_IOSTREAMS_HEADERS "boost/iostreams/stream.hpp") + set(_Boost_LOCALE_HEADERS "boost/locale.hpp") + set(_Boost_LOG_HEADERS "boost/log/core.hpp") + set(_Boost_LOG_SETUP_HEADERS "boost/log/detail/setup_config.hpp") + set(_Boost_MATH_HEADERS "boost/math_fwd.hpp") + set(_Boost_MATH_C99_HEADERS "boost/math/tr1.hpp") + set(_Boost_MATH_C99F_HEADERS "boost/math/tr1.hpp") + set(_Boost_MATH_C99L_HEADERS "boost/math/tr1.hpp") + set(_Boost_MATH_TR1_HEADERS "boost/math/tr1.hpp") + set(_Boost_MATH_TR1F_HEADERS "boost/math/tr1.hpp") + set(_Boost_MATH_TR1L_HEADERS "boost/math/tr1.hpp") + set(_Boost_MPI_HEADERS "boost/mpi.hpp") + set(_Boost_MPI_PYTHON_HEADERS "boost/mpi/python/config.hpp") + set(_Boost_NUMPY_HEADERS "boost/python/numpy.hpp") + set(_Boost_PRG_EXEC_MONITOR_HEADERS "boost/test/prg_exec_monitor.hpp") + set(_Boost_PROGRAM_OPTIONS_HEADERS "boost/program_options.hpp") + set(_Boost_PYTHON_HEADERS "boost/python.hpp") + set(_Boost_RANDOM_HEADERS "boost/random.hpp") + set(_Boost_REGEX_HEADERS "boost/regex.hpp") + set(_Boost_SERIALIZATION_HEADERS "boost/serialization/serialization.hpp") + set(_Boost_SIGNALS_HEADERS "boost/signals.hpp") + set(_Boost_SYSTEM_HEADERS "boost/system/config.hpp") + set(_Boost_TEST_EXEC_MONITOR_HEADERS "boost/test/test_exec_monitor.hpp") + set(_Boost_THREAD_HEADERS "boost/thread.hpp") + set(_Boost_TIMER_HEADERS "boost/timer.hpp") + set(_Boost_TYPE_ERASURE_HEADERS "boost/type_erasure/config.hpp") + set(_Boost_UNIT_TEST_FRAMEWORK_HEADERS "boost/test/framework.hpp") + set(_Boost_WAVE_HEADERS "boost/wave.hpp") + set(_Boost_WSERIALIZATION_HEADERS "boost/archive/text_wiarchive.hpp") + if(WIN32) + set(_Boost_BZIP2_HEADERS "boost/iostreams/filter/bzip2.hpp") + set(_Boost_ZLIB_HEADERS "boost/iostreams/filter/zlib.hpp") + endif() + + string(TOUPPER ${component} uppercomponent) + set(${_hdrs} ${_Boost_${uppercomponent}_HEADERS} PARENT_SCOPE) + + string(REGEX REPLACE ";" " " _boost_HDRS_STRING "${_Boost_${uppercomponent}_HEADERS}") + if (NOT _boost_HDRS_STRING) + set(_boost_HDRS_STRING "(none)") + endif() + # message(STATUS "Headers for Boost::${component}: ${_boost_HDRS_STRING}") +endfunction() + +# +# Determine if any missing dependencies require adding to the component list. +# +# Sets _Boost_${COMPONENT}_DEPENDENCIES for each required component, +# plus _Boost_IMPORTED_TARGETS (TRUE if imported targets should be +# defined; FALSE if dependency information is unavailable). +# +# componentvar - the component list variable name +# extravar - the indirect dependency list variable name +# +# +function(_Boost_MISSING_DEPENDENCIES componentvar extravar) + # _boost_unprocessed_components - list of components requiring processing + # _boost_processed_components - components already processed (or currently being processed) + # _boost_new_components - new components discovered for future processing + # + list(APPEND _boost_unprocessed_components ${${componentvar}}) + + while(_boost_unprocessed_components) + list(APPEND _boost_processed_components ${_boost_unprocessed_components}) + foreach(component ${_boost_unprocessed_components}) + string(TOUPPER ${component} uppercomponent) + set(${_ret} ${_Boost_${uppercomponent}_DEPENDENCIES} PARENT_SCOPE) + _Boost_COMPONENT_DEPENDENCIES("${component}" _Boost_${uppercomponent}_DEPENDENCIES) + set(_Boost_${uppercomponent}_DEPENDENCIES ${_Boost_${uppercomponent}_DEPENDENCIES} PARENT_SCOPE) + set(_Boost_IMPORTED_TARGETS ${_Boost_IMPORTED_TARGETS} PARENT_SCOPE) + foreach(componentdep ${_Boost_${uppercomponent}_DEPENDENCIES}) + if (NOT ("${componentdep}" IN_LIST _boost_processed_components OR "${componentdep}" IN_LIST _boost_new_components)) + list(APPEND _boost_new_components ${componentdep}) + endif() + endforeach() + endforeach() + set(_boost_unprocessed_components ${_boost_new_components}) + unset(_boost_new_components) + endwhile() + set(_boost_extra_components ${_boost_processed_components}) + if(_boost_extra_components AND ${componentvar}) + list(REMOVE_ITEM _boost_extra_components ${${componentvar}}) + endif() + set(${componentvar} ${_boost_processed_components} PARENT_SCOPE) + set(${extravar} ${_boost_extra_components} PARENT_SCOPE) +endfunction() + +# +# Some boost libraries may require particular set of compler features. +# The very first one was `boost::fiber` introduced in Boost 1.62. +# One can check required compiler features of it in +# `${Boost_ROOT}/libs/fiber/build/Jamfile.v2`. +# +function(_Boost_COMPILER_FEATURES component _ret) + # Boost >= 1.62 and < 1.67 + if(NOT Boost_VERSION VERSION_LESS 106200 AND Boost_VERSION VERSION_LESS 106700) + set(_Boost_FIBER_COMPILER_FEATURES + cxx_alias_templates + cxx_auto_type + cxx_constexpr + cxx_defaulted_functions + cxx_final + cxx_lambdas + cxx_noexcept + cxx_nullptr + cxx_rvalue_references + cxx_thread_local + cxx_variadic_templates + ) + endif() + string(TOUPPER ${component} uppercomponent) + set(${_ret} ${_Boost_${uppercomponent}_COMPILER_FEATURES} PARENT_SCOPE) +endfunction() + +# +# Update library search directory hint variable with paths used by prebuilt boost binaries. +# +# Prebuilt windows binaries (https://sourceforge.net/projects/boost/files/boost-binaries/) +# have library directories named using MSVC compiler version and architecture. +# This function would append corresponding directories if MSVC is a current compiler, +# so having `BOOST_ROOT` would be enough to specify to find everything. +# +function(_Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS componentlibvar basedir) + if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC") + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_arch_suffix 64) + else() + set(_arch_suffix 32) + endif() + if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10) + list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-14.1) + list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-14.0) + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19) + list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-14.0) + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18) + list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-12.0) + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 17) + list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-11.0) + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16) + list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-10.0) + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15) + list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-9.0) + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14) + list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-8.0) + endif() + set(${componentlibvar} ${${componentlibvar}} PARENT_SCOPE) + endif() +endfunction() + +# +# End functions/macros +# +#------------------------------------------------------------------------------- + +#------------------------------------------------------------------------------- +# main. +#------------------------------------------------------------------------------- + + +# If the user sets Boost_LIBRARY_DIR, use it as the default for both +# configurations. +if(NOT Boost_LIBRARY_DIR_RELEASE AND Boost_LIBRARY_DIR) + set(Boost_LIBRARY_DIR_RELEASE "${Boost_LIBRARY_DIR}") +endif() +if(NOT Boost_LIBRARY_DIR_DEBUG AND Boost_LIBRARY_DIR) + set(Boost_LIBRARY_DIR_DEBUG "${Boost_LIBRARY_DIR}") +endif() + +if(NOT DEFINED Boost_USE_DEBUG_LIBS) + set(Boost_USE_DEBUG_LIBS TRUE) +endif() +if(NOT DEFINED Boost_USE_RELEASE_LIBS) + set(Boost_USE_RELEASE_LIBS TRUE) +endif() +if(NOT DEFINED Boost_USE_MULTITHREADED) + set(Boost_USE_MULTITHREADED TRUE) +endif() +if(NOT DEFINED Boost_USE_DEBUG_RUNTIME) + set(Boost_USE_DEBUG_RUNTIME TRUE) +endif() + +# Check the version of Boost against the requested version. +if(Boost_FIND_VERSION AND NOT Boost_FIND_VERSION_MINOR) + message(SEND_ERROR "When requesting a specific version of Boost, you must provide at least the major and minor version numbers, e.g., 1.34") +endif() + +if(Boost_FIND_VERSION_EXACT) + # The version may appear in a directory with or without the patch + # level, even when the patch level is non-zero. + set(_boost_TEST_VERSIONS + "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}.${Boost_FIND_VERSION_PATCH}" + "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") +else() + # The user has not requested an exact version. Among known + # versions, find those that are acceptable to the user request. + # + # Note: When adding a new Boost release, also update the dependency + # information in _Boost_COMPONENT_DEPENDENCIES and + # _Boost_COMPONENT_HEADERS. See the instructions at the top of + # _Boost_COMPONENT_DEPENDENCIES. + set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS} + "1.67.0" "1.67" "1.66.0" "1.66" "1.65.1" "1.65.0" "1.65" + "1.64.0" "1.64" "1.63.0" "1.63" "1.62.0" "1.62" "1.61.0" "1.61" "1.60.0" "1.60" + "1.59.0" "1.59" "1.58.0" "1.58" "1.57.0" "1.57" "1.56.0" "1.56" "1.55.0" "1.55" + "1.54.0" "1.54" "1.53.0" "1.53" "1.52.0" "1.52" "1.51.0" "1.51" + "1.50.0" "1.50" "1.49.0" "1.49" "1.48.0" "1.48" "1.47.0" "1.47" "1.46.1" + "1.46.0" "1.46" "1.45.0" "1.45" "1.44.0" "1.44" "1.43.0" "1.43" "1.42.0" "1.42" + "1.41.0" "1.41" "1.40.0" "1.40" "1.39.0" "1.39" "1.38.0" "1.38" "1.37.0" "1.37" + "1.36.1" "1.36.0" "1.36" "1.35.1" "1.35.0" "1.35" "1.34.1" "1.34.0" + "1.34" "1.33.1" "1.33.0" "1.33") + + set(_boost_TEST_VERSIONS) + if(Boost_FIND_VERSION) + set(_Boost_FIND_VERSION_SHORT "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") + # Select acceptable versions. + foreach(version ${_Boost_KNOWN_VERSIONS}) + if(NOT "${version}" VERSION_LESS "${Boost_FIND_VERSION}") + # This version is high enough. + list(APPEND _boost_TEST_VERSIONS "${version}") + elseif("${version}.99" VERSION_EQUAL "${_Boost_FIND_VERSION_SHORT}.99") + # This version is a short-form for the requested version with + # the patch level dropped. + list(APPEND _boost_TEST_VERSIONS "${version}") + endif() + endforeach() + else() + # Any version is acceptable. + set(_boost_TEST_VERSIONS "${_Boost_KNOWN_VERSIONS}") + endif() +endif() + +# The reason that we failed to find Boost. This will be set to a +# user-friendly message when we fail to find some necessary piece of +# Boost. +set(Boost_ERROR_REASON) + +if(Boost_DEBUG) + # Output some of their choices + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "_boost_TEST_VERSIONS = ${_boost_TEST_VERSIONS}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Boost_USE_MULTITHREADED = ${Boost_USE_MULTITHREADED}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Boost_USE_STATIC_LIBS = ${Boost_USE_STATIC_LIBS}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Boost_USE_STATIC_RUNTIME = ${Boost_USE_STATIC_RUNTIME}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Boost_ADDITIONAL_VERSIONS = ${Boost_ADDITIONAL_VERSIONS}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Boost_NO_SYSTEM_PATHS = ${Boost_NO_SYSTEM_PATHS}") +endif() + +# Supply Boost_LIB_DIAGNOSTIC_DEFINITIONS as a convenience target. It +# will only contain any interface definitions on WIN32, but is created +# on all platforms to keep end user code free from platform dependent +# code. Also provide convenience targets to disable autolinking and +# enable dynamic linking. +if(NOT TARGET Boost::diagnostic_definitions) + add_library(Boost::diagnostic_definitions INTERFACE IMPORTED) + add_library(Boost::disable_autolinking INTERFACE IMPORTED) + add_library(Boost::dynamic_linking INTERFACE IMPORTED) +endif() +if(WIN32) + # In windows, automatic linking is performed, so you do not have + # to specify the libraries. If you are linking to a dynamic + # runtime, then you can choose to link to either a static or a + # dynamic Boost library, the default is to do a static link. You + # can alter this for a specific library "whatever" by defining + # BOOST_WHATEVER_DYN_LINK to force Boost library "whatever" to be + # linked dynamically. Alternatively you can force all Boost + # libraries to dynamic link by defining BOOST_ALL_DYN_LINK. + + # This feature can be disabled for Boost library "whatever" by + # defining BOOST_WHATEVER_NO_LIB, or for all of Boost by defining + # BOOST_ALL_NO_LIB. + + # If you want to observe which libraries are being linked against + # then defining BOOST_LIB_DIAGNOSTIC will cause the auto-linking + # code to emit a #pragma message each time a library is selected + # for linking. + set(Boost_LIB_DIAGNOSTIC_DEFINITIONS "-DBOOST_LIB_DIAGNOSTIC") + set_target_properties(Boost::diagnostic_definitions PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "BOOST_LIB_DIAGNOSTIC") + set_target_properties(Boost::disable_autolinking PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "BOOST_ALL_NO_LIB") + set_target_properties(Boost::dynamic_linking PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "BOOST_ALL_DYN_LINK") +endif() + +_Boost_CHECK_SPELLING(Boost_ROOT) +_Boost_CHECK_SPELLING(Boost_LIBRARYDIR) +_Boost_CHECK_SPELLING(Boost_INCLUDEDIR) + +# Collect environment variable inputs as hints. Do not consider changes. +foreach(v BOOSTROOT BOOST_ROOT BOOST_INCLUDEDIR BOOST_LIBRARYDIR) + set(_env $ENV{${v}}) + if(_env) + file(TO_CMAKE_PATH "${_env}" _ENV_${v}) + else() + set(_ENV_${v} "") + endif() +endforeach() +if(NOT _ENV_BOOST_ROOT AND _ENV_BOOSTROOT) + set(_ENV_BOOST_ROOT "${_ENV_BOOSTROOT}") +endif() + +# Collect inputs and cached results. Detect changes since the last run. +if(NOT BOOST_ROOT AND BOOSTROOT) + set(BOOST_ROOT "${BOOSTROOT}") +endif() +set(_Boost_VARS_DIR + BOOST_ROOT + Boost_NO_SYSTEM_PATHS + ) + +if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Declared as CMake or Environmental Variables:") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " BOOST_ROOT = ${BOOST_ROOT}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " BOOST_INCLUDEDIR = ${BOOST_INCLUDEDIR}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " BOOST_LIBRARYDIR = ${BOOST_LIBRARYDIR}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "_boost_TEST_VERSIONS = ${_boost_TEST_VERSIONS}") +endif() + +# ------------------------------------------------------------------------ +# Search for Boost include DIR +# ------------------------------------------------------------------------ + +set(_Boost_VARS_INC BOOST_INCLUDEDIR Boost_INCLUDE_DIR Boost_ADDITIONAL_VERSIONS) +_Boost_CHANGE_DETECT(_Boost_CHANGE_INCDIR ${_Boost_VARS_DIR} ${_Boost_VARS_INC}) +# Clear Boost_INCLUDE_DIR if it did not change but other input affecting the +# location did. We will find a new one based on the new inputs. +if(_Boost_CHANGE_INCDIR AND NOT _Boost_INCLUDE_DIR_CHANGED) + unset(Boost_INCLUDE_DIR CACHE) +endif() + +if(NOT Boost_INCLUDE_DIR) + set(_boost_INCLUDE_SEARCH_DIRS "") + if(BOOST_INCLUDEDIR) + list(APPEND _boost_INCLUDE_SEARCH_DIRS ${BOOST_INCLUDEDIR}) + elseif(_ENV_BOOST_INCLUDEDIR) + list(APPEND _boost_INCLUDE_SEARCH_DIRS ${_ENV_BOOST_INCLUDEDIR}) + endif() + + if( BOOST_ROOT ) + list(APPEND _boost_INCLUDE_SEARCH_DIRS ${BOOST_ROOT}/include ${BOOST_ROOT}) + elseif( _ENV_BOOST_ROOT ) + list(APPEND _boost_INCLUDE_SEARCH_DIRS ${_ENV_BOOST_ROOT}/include ${_ENV_BOOST_ROOT}) + endif() + + if( Boost_NO_SYSTEM_PATHS) + list(APPEND _boost_INCLUDE_SEARCH_DIRS NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH) + else() + if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC") + foreach(ver ${_Boost_KNOWN_VERSIONS}) + string(REPLACE "." "_" ver "${ver}") + list(APPEND _boost_INCLUDE_SEARCH_DIRS PATHS "C:/local/boost_${ver}") + endforeach() + endif() + list(APPEND _boost_INCLUDE_SEARCH_DIRS PATHS + C:/boost/include + C:/boost + /sw/local/include + ) + endif() + + # Try to find Boost by stepping backwards through the Boost versions + # we know about. + # Build a list of path suffixes for each version. + set(_boost_PATH_SUFFIXES) + foreach(_boost_VER ${_boost_TEST_VERSIONS}) + # Add in a path suffix, based on the required version, ideally + # we could read this from version.hpp, but for that to work we'd + # need to know the include dir already + set(_boost_BOOSTIFIED_VERSION) + + # Transform 1.35 => 1_35 and 1.36.0 => 1_36_0 + if(_boost_VER MATCHES "([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(_boost_BOOSTIFIED_VERSION + "${CMAKE_MATCH_1}_${CMAKE_MATCH_2}_${CMAKE_MATCH_3}") + elseif(_boost_VER MATCHES "([0-9]+)\\.([0-9]+)") + set(_boost_BOOSTIFIED_VERSION + "${CMAKE_MATCH_1}_${CMAKE_MATCH_2}") + endif() + + list(APPEND _boost_PATH_SUFFIXES + "boost-${_boost_BOOSTIFIED_VERSION}" + "boost_${_boost_BOOSTIFIED_VERSION}" + "boost/boost-${_boost_BOOSTIFIED_VERSION}" + "boost/boost_${_boost_BOOSTIFIED_VERSION}" + ) + + endforeach() + + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Include debugging info:") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " _boost_INCLUDE_SEARCH_DIRS = ${_boost_INCLUDE_SEARCH_DIRS}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " _boost_PATH_SUFFIXES = ${_boost_PATH_SUFFIXES}") + endif() + + # Look for a standard boost header file. + find_path(Boost_INCLUDE_DIR + NAMES boost/config.hpp + HINTS ${_boost_INCLUDE_SEARCH_DIRS} + PATH_SUFFIXES ${_boost_PATH_SUFFIXES} + ) +endif() + +# ------------------------------------------------------------------------ +# Extract version information from version.hpp +# ------------------------------------------------------------------------ + +# Set Boost_FOUND based only on header location and version. +# It will be updated below for component libraries. +if(Boost_INCLUDE_DIR) + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "location of version.hpp: ${Boost_INCLUDE_DIR}/boost/version.hpp") + endif() + + # Extract Boost_VERSION and Boost_LIB_VERSION from version.hpp + set(Boost_VERSION 0) + set(Boost_LIB_VERSION "") + file(STRINGS "${Boost_INCLUDE_DIR}/boost/version.hpp" _boost_VERSION_HPP_CONTENTS REGEX "#define BOOST_(LIB_)?VERSION ") + set(_Boost_VERSION_REGEX "([0-9]+)") + set(_Boost_LIB_VERSION_REGEX "\"([0-9_]+)\"") + foreach(v VERSION LIB_VERSION) + if("${_boost_VERSION_HPP_CONTENTS}" MATCHES "#define BOOST_${v} ${_Boost_${v}_REGEX}") + set(Boost_${v} "${CMAKE_MATCH_1}") + endif() + endforeach() + unset(_boost_VERSION_HPP_CONTENTS) + + math(EXPR Boost_MAJOR_VERSION "${Boost_VERSION} / 100000") + math(EXPR Boost_MINOR_VERSION "${Boost_VERSION} / 100 % 1000") + math(EXPR Boost_SUBMINOR_VERSION "${Boost_VERSION} % 100") + + string(APPEND Boost_ERROR_REASON + "Boost version: ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}\nBoost include path: ${Boost_INCLUDE_DIR}") + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "version.hpp reveals boost " + "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") + endif() + + if(Boost_FIND_VERSION) + # Set Boost_FOUND based on requested version. + set(_Boost_VERSION "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") + if("${_Boost_VERSION}" VERSION_LESS "${Boost_FIND_VERSION}") + set(Boost_FOUND 0) + set(_Boost_VERSION_AGE "old") + elseif(Boost_FIND_VERSION_EXACT AND + NOT "${_Boost_VERSION}" VERSION_EQUAL "${Boost_FIND_VERSION}") + set(Boost_FOUND 0) + set(_Boost_VERSION_AGE "new") + else() + set(Boost_FOUND 1) + endif() + if(NOT Boost_FOUND) + # State that we found a version of Boost that is too new or too old. + string(APPEND Boost_ERROR_REASON + "\nDetected version of Boost is too ${_Boost_VERSION_AGE}. Requested version was ${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") + if (Boost_FIND_VERSION_PATCH) + string(APPEND Boost_ERROR_REASON + ".${Boost_FIND_VERSION_PATCH}") + endif () + if (NOT Boost_FIND_VERSION_EXACT) + string(APPEND Boost_ERROR_REASON " (or newer)") + endif () + string(APPEND Boost_ERROR_REASON ".") + endif () + else() + # Caller will accept any Boost version. + set(Boost_FOUND 1) + endif() +else() + set(Boost_FOUND 0) + string(APPEND Boost_ERROR_REASON + "Unable to find the Boost header files. Please set BOOST_ROOT to the root directory containing Boost or BOOST_INCLUDEDIR to the directory containing Boost's headers.") +endif() + +# ------------------------------------------------------------------------ +# Prefix initialization +# ------------------------------------------------------------------------ + +set(Boost_LIB_PREFIX "") +if ( (GHSMULTI AND Boost_USE_STATIC_LIBS) OR + (WIN32 AND Boost_USE_STATIC_LIBS AND NOT CYGWIN) ) + set(Boost_LIB_PREFIX "lib") +endif() + +if ( NOT Boost_NAMESPACE ) + set(Boost_NAMESPACE "boost") +endif() + +# ------------------------------------------------------------------------ +# Suffix initialization and compiler suffix detection. +# ------------------------------------------------------------------------ + +set(_Boost_VARS_NAME + Boost_NAMESPACE + Boost_COMPILER + Boost_THREADAPI + Boost_USE_DEBUG_PYTHON + Boost_USE_MULTITHREADED + Boost_USE_STATIC_LIBS + Boost_USE_STATIC_RUNTIME + Boost_USE_STLPORT + Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS + ) +_Boost_CHANGE_DETECT(_Boost_CHANGE_LIBNAME ${_Boost_VARS_NAME}) + +# Setting some more suffixes for the library +if (Boost_COMPILER) + set(_boost_COMPILER ${Boost_COMPILER}) + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "using user-specified Boost_COMPILER = ${_boost_COMPILER}") + endif() +else() + # Attempt to guess the compiler suffix + # NOTE: this is not perfect yet, if you experience any issues + # please report them and use the Boost_COMPILER variable + # to work around the problems. + _Boost_GUESS_COMPILER_PREFIX(_boost_COMPILER) + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "guessed _boost_COMPILER = ${_boost_COMPILER}") + endif() +endif() + +set (_boost_MULTITHREADED "-mt") +if( NOT Boost_USE_MULTITHREADED ) + set (_boost_MULTITHREADED "") +endif() +if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "_boost_MULTITHREADED = ${_boost_MULTITHREADED}") +endif() + +#====================== +# Systematically build up the Boost ABI tag for the 'tagged' and 'versioned' layouts +# http://boost.org/doc/libs/1_66_0/more/getting_started/windows.html#library-naming +# http://boost.org/doc/libs/1_66_0/boost/config/auto_link.hpp +# http://boost.org/doc/libs/1_66_0/tools/build/src/tools/common.jam +# http://boost.org/doc/libs/1_66_0/boostcpp.jam +set( _boost_RELEASE_ABI_TAG "-") +set( _boost_DEBUG_ABI_TAG "-") +# Key Use this library when: +# s linking statically to the C++ standard library and +# compiler runtime support libraries. +if(Boost_USE_STATIC_RUNTIME) + set( _boost_RELEASE_ABI_TAG "${_boost_RELEASE_ABI_TAG}s") + set( _boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}s") +endif() +# g using debug versions of the standard and runtime +# support libraries +if(WIN32 AND Boost_USE_DEBUG_RUNTIME) + if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC" + OR "x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xClang" + OR "x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xIntel") + string(APPEND _boost_DEBUG_ABI_TAG "g") + endif() +endif() +# y using special debug build of python +if(Boost_USE_DEBUG_PYTHON) + string(APPEND _boost_DEBUG_ABI_TAG "y") +endif() +# d using a debug version of your code +string(APPEND _boost_DEBUG_ABI_TAG "d") +# p using the STLport standard library rather than the +# default one supplied with your compiler +if(Boost_USE_STLPORT) + string(APPEND _boost_RELEASE_ABI_TAG "p") + string(APPEND _boost_DEBUG_ABI_TAG "p") +endif() +# n using the STLport deprecated "native iostreams" feature +# removed from the documentation in 1.43.0 but still present in +# boost/config/auto_link.hpp +if(Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS) + string(APPEND _boost_RELEASE_ABI_TAG "n") + string(APPEND _boost_DEBUG_ABI_TAG "n") +endif() + +# -x86 Architecture and address model tag +# First character is the architecture, then word-size, either 32 or 64 +# Only used in 'versioned' layout, added in Boost 1.66.0 +set(_boost_ARCHITECTURE_TAG "") +# {CMAKE_CXX_COMPILER_ARCHITECTURE_ID} is not currently set for all compilers +if(NOT "x${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "x" AND NOT Boost_VERSION VERSION_LESS 106600) + string(APPEND _boost_ARCHITECTURE_TAG "-") + # This needs to be kept in-sync with the section of CMakePlatformId.h.in + # inside 'defined(_WIN32) && defined(_MSC_VER)' + if(${CMAKE_CXX_COMPILER_ARCHITECTURE_ID} STREQUAL "IA64") + string(APPEND _boost_ARCHITECTURE_TAG "i") + elseif(${CMAKE_CXX_COMPILER_ARCHITECTURE_ID} STREQUAL "X86" + OR ${CMAKE_CXX_COMPILER_ARCHITECTURE_ID} STREQUAL "x64") + string(APPEND _boost_ARCHITECTURE_TAG "x") + elseif(${CMAKE_CXX_COMPILER_ARCHITECTURE_ID} MATCHES "^ARM") + string(APPEND _boost_ARCHITECTURE_TAG "a") + elseif(${CMAKE_CXX_COMPILER_ARCHITECTURE_ID} STREQUAL "MIPS") + string(APPEND _boost_ARCHITECTURE_TAG "m") + endif() + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + string(APPEND _boost_ARCHITECTURE_TAG "64") + else() + string(APPEND _boost_ARCHITECTURE_TAG "32") + endif() +endif() + +if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "_boost_RELEASE_ABI_TAG = ${_boost_RELEASE_ABI_TAG}") + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "_boost_DEBUG_ABI_TAG = ${_boost_DEBUG_ABI_TAG}") +endif() + +# ------------------------------------------------------------------------ +# Begin finding boost libraries +# ------------------------------------------------------------------------ + +set(_Boost_VARS_LIB "") +foreach(c DEBUG RELEASE) + set(_Boost_VARS_LIB_${c} BOOST_LIBRARYDIR Boost_LIBRARY_DIR_${c}) + list(APPEND _Boost_VARS_LIB ${_Boost_VARS_LIB_${c}}) + _Boost_CHANGE_DETECT(_Boost_CHANGE_LIBDIR_${c} ${_Boost_VARS_DIR} ${_Boost_VARS_LIB_${c}} Boost_INCLUDE_DIR) + # Clear Boost_LIBRARY_DIR_${c} if it did not change but other input affecting the + # location did. We will find a new one based on the new inputs. + if(_Boost_CHANGE_LIBDIR_${c} AND NOT _Boost_LIBRARY_DIR_${c}_CHANGED) + unset(Boost_LIBRARY_DIR_${c} CACHE) + endif() + + # If Boost_LIBRARY_DIR_[RELEASE,DEBUG] is set, prefer its value. + if(Boost_LIBRARY_DIR_${c}) + set(_boost_LIBRARY_SEARCH_DIRS_${c} ${Boost_LIBRARY_DIR_${c}} NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + else() + set(_boost_LIBRARY_SEARCH_DIRS_${c} "") + if(BOOST_LIBRARYDIR) + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} ${BOOST_LIBRARYDIR}) + elseif(_ENV_BOOST_LIBRARYDIR) + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} ${_ENV_BOOST_LIBRARYDIR}) + endif() + + if(BOOST_ROOT) + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} ${BOOST_ROOT}/lib ${BOOST_ROOT}/stage/lib) + _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "${BOOST_ROOT}") + elseif(_ENV_BOOST_ROOT) + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} ${_ENV_BOOST_ROOT}/lib ${_ENV_BOOST_ROOT}/stage/lib) + _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "${_ENV_BOOST_ROOT}") + endif() + + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} + ${Boost_INCLUDE_DIR}/lib + ${Boost_INCLUDE_DIR}/../lib + ${Boost_INCLUDE_DIR}/stage/lib + ) + _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "${Boost_INCLUDE_DIR}/..") + _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "${Boost_INCLUDE_DIR}") + if( Boost_NO_SYSTEM_PATHS ) + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH) + else() + foreach(ver ${_Boost_KNOWN_VERSIONS}) + string(REPLACE "." "_" ver "${ver}") + _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "C:/local/boost_${ver}") + endforeach() + _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "C:/boost") + list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} PATHS + C:/boost/lib + C:/boost + /sw/local/lib + ) + endif() + endif() +endforeach() + +if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "_boost_LIBRARY_SEARCH_DIRS_RELEASE = ${_boost_LIBRARY_SEARCH_DIRS_RELEASE}" + "_boost_LIBRARY_SEARCH_DIRS_DEBUG = ${_boost_LIBRARY_SEARCH_DIRS_DEBUG}") +endif() + +# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES +if( Boost_USE_STATIC_LIBS ) + set( _boost_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + if(WIN32) + list(INSERT CMAKE_FIND_LIBRARY_SUFFIXES 0 .lib .a) + else() + set(CMAKE_FIND_LIBRARY_SUFFIXES .a) + endif() +endif() + +# We want to use the tag inline below without risking double dashes +if(_boost_RELEASE_ABI_TAG) + if(${_boost_RELEASE_ABI_TAG} STREQUAL "-") + set(_boost_RELEASE_ABI_TAG "") + endif() +endif() +if(_boost_DEBUG_ABI_TAG) + if(${_boost_DEBUG_ABI_TAG} STREQUAL "-") + set(_boost_DEBUG_ABI_TAG "") + endif() +endif() + +# The previous behavior of FindBoost when Boost_USE_STATIC_LIBS was enabled +# on WIN32 was to: +# 1. Search for static libs compiled against a SHARED C++ standard runtime library (use if found) +# 2. Search for static libs compiled against a STATIC C++ standard runtime library (use if found) +# We maintain this behavior since changing it could break people's builds. +# To disable the ambiguous behavior, the user need only +# set Boost_USE_STATIC_RUNTIME either ON or OFF. +set(_boost_STATIC_RUNTIME_WORKAROUND false) +if(WIN32 AND Boost_USE_STATIC_LIBS) + if(NOT DEFINED Boost_USE_STATIC_RUNTIME) + set(_boost_STATIC_RUNTIME_WORKAROUND TRUE) + endif() +endif() + +# On versions < 1.35, remove the System library from the considered list +# since it wasn't added until 1.35. +if(Boost_VERSION AND Boost_FIND_COMPONENTS) + if(Boost_VERSION LESS 103500) + list(REMOVE_ITEM Boost_FIND_COMPONENTS system) + endif() +endif() + +# Additional components may be required via component dependencies. +# Add any missing components to the list. +_Boost_MISSING_DEPENDENCIES(Boost_FIND_COMPONENTS _Boost_EXTRA_FIND_COMPONENTS) + +# If thread is required, get the thread libs as a dependency +if("thread" IN_LIST Boost_FIND_COMPONENTS) + if(Boost_FIND_QUIETLY) + set(_Boost_find_quiet QUIET) + else() + set(_Boost_find_quiet "") + endif() + find_package(Threads ${_Boost_find_quiet}) + unset(_Boost_find_quiet) +endif() + +# If the user changed any of our control inputs flush previous results. +if(_Boost_CHANGE_LIBDIR_DEBUG OR _Boost_CHANGE_LIBDIR_RELEASE OR _Boost_CHANGE_LIBNAME) + foreach(COMPONENT ${_Boost_COMPONENTS_SEARCHED}) + string(TOUPPER ${COMPONENT} UPPERCOMPONENT) + foreach(c DEBUG RELEASE) + set(_var Boost_${UPPERCOMPONENT}_LIBRARY_${c}) + unset(${_var} CACHE) + set(${_var} "${_var}-NOTFOUND") + endforeach() + endforeach() + set(_Boost_COMPONENTS_SEARCHED "") +endif() + +foreach(COMPONENT ${Boost_FIND_COMPONENTS}) + string(TOUPPER ${COMPONENT} UPPERCOMPONENT) + + set( _boost_docstring_release "Boost ${COMPONENT} library (release)") + set( _boost_docstring_debug "Boost ${COMPONENT} library (debug)") + + # Compute component-specific hints. + set(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT "") + if(${COMPONENT} STREQUAL "mpi" OR ${COMPONENT} STREQUAL "mpi_python" OR + ${COMPONENT} STREQUAL "graph_parallel") + foreach(lib ${MPI_CXX_LIBRARIES} ${MPI_C_LIBRARIES}) + if(IS_ABSOLUTE "${lib}") + get_filename_component(libdir "${lib}" PATH) + string(REPLACE "\\" "/" libdir "${libdir}") + list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT ${libdir}) + endif() + endforeach() + endif() + + # Handle Python version suffixes + unset(COMPONENT_PYTHON_VERSION_MAJOR) + unset(COMPONENT_PYTHON_VERSION_MINOR) + if(${COMPONENT} MATCHES "^(python|mpi_python|numpy)([0-9])\$") + set(COMPONENT_UNVERSIONED "${CMAKE_MATCH_1}") + set(COMPONENT_PYTHON_VERSION_MAJOR "${CMAKE_MATCH_2}") + elseif(${COMPONENT} MATCHES "^(python|mpi_python|numpy)([0-9])\\.?([0-9])\$") + set(COMPONENT_UNVERSIONED "${CMAKE_MATCH_1}") + set(COMPONENT_PYTHON_VERSION_MAJOR "${CMAKE_MATCH_2}") + set(COMPONENT_PYTHON_VERSION_MINOR "${CMAKE_MATCH_3}") + endif() + + unset(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME) + if (COMPONENT_PYTHON_VERSION_MINOR) + # Boost >= 1.67 + list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}") + # Debian/Ubuntu (Some versions omit the 2 and/or 3 from the suffix) + list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}${COMPONENT_PYTHON_VERSION_MAJOR}-py${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}") + list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-py${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}") + # Gentoo + list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}") + # RPMs + list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}") + endif() + if (COMPONENT_PYTHON_VERSION_MAJOR AND NOT COMPONENT_PYTHON_VERSION_MINOR) + # Boost < 1.67 + list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}${COMPONENT_PYTHON_VERSION_MAJOR}") + endif() + + # Consolidate and report component-specific hints. + if(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME) + list(REMOVE_DUPLICATES _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME) + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Component-specific library search names for ${COMPONENT_NAME}: " + "${_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME}") + endif() + endif() + if(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT) + list(REMOVE_DUPLICATES _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT) + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Component-specific library search paths for ${COMPONENT}: " + "${_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT}") + endif() + endif() + + # + # Find headers + # + _Boost_COMPONENT_HEADERS("${COMPONENT}" Boost_${UPPERCOMPONENT}_HEADER_NAME) + # Look for a standard boost header file. + if(Boost_${UPPERCOMPONENT}_HEADER_NAME) + if(EXISTS "${Boost_INCLUDE_DIR}/${Boost_${UPPERCOMPONENT}_HEADER_NAME}") + set(Boost_${UPPERCOMPONENT}_HEADER ON) + else() + set(Boost_${UPPERCOMPONENT}_HEADER OFF) + endif() + else() + set(Boost_${UPPERCOMPONENT}_HEADER ON) + message(WARNING "No header defined for ${COMPONENT}; skipping header check") + endif() + + # + # Find RELEASE libraries + # + unset(_boost_RELEASE_NAMES) + foreach(component IN LISTS _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME COMPONENT) + foreach(compiler IN LISTS _boost_COMPILER) + list(APPEND _boost_RELEASE_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG} ) + endforeach() + list(APPEND _boost_RELEASE_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component} ) + if(_boost_STATIC_RUNTIME_WORKAROUND) + set(_boost_RELEASE_STATIC_ABI_TAG "-s${_boost_RELEASE_ABI_TAG}") + foreach(compiler IN LISTS _boost_COMPILER) + list(APPEND _boost_RELEASE_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} ) + endforeach() + list(APPEND _boost_RELEASE_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} ) + endif() + endforeach() + if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread") + _Boost_PREPEND_LIST_WITH_THREADAPI(_boost_RELEASE_NAMES ${_boost_RELEASE_NAMES}) + endif() + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Searching for ${UPPERCOMPONENT}_LIBRARY_RELEASE: ${_boost_RELEASE_NAMES}") + endif() + + # if Boost_LIBRARY_DIR_RELEASE is not defined, + # but Boost_LIBRARY_DIR_DEBUG is, look there first for RELEASE libs + if(NOT Boost_LIBRARY_DIR_RELEASE AND Boost_LIBRARY_DIR_DEBUG) + list(INSERT _boost_LIBRARY_SEARCH_DIRS_RELEASE 0 ${Boost_LIBRARY_DIR_DEBUG}) + endif() + + # Avoid passing backslashes to _Boost_FIND_LIBRARY due to macro re-parsing. + string(REPLACE "\\" "/" _boost_LIBRARY_SEARCH_DIRS_tmp "${_boost_LIBRARY_SEARCH_DIRS_RELEASE}") + + if(Boost_USE_RELEASE_LIBS) + _Boost_FIND_LIBRARY(Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE RELEASE + NAMES ${_boost_RELEASE_NAMES} + HINTS ${_boost_LIBRARY_SEARCH_DIRS_tmp} + NAMES_PER_DIR + DOC "${_boost_docstring_release}" + ) + endif() + + # + # Find DEBUG libraries + # + unset(_boost_DEBUG_NAMES) + foreach(component IN LISTS _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME COMPONENT) + foreach(compiler IN LISTS _boost_COMPILER) + list(APPEND _boost_DEBUG_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG} ) + endforeach() + list(APPEND _boost_DEBUG_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component} ) + if(_boost_STATIC_RUNTIME_WORKAROUND) + set(_boost_DEBUG_STATIC_ABI_TAG "-s${_boost_DEBUG_ABI_TAG}") + foreach(compiler IN LISTS _boost_COMPILER) + list(APPEND _boost_DEBUG_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} ) + endforeach() + list(APPEND _boost_DEBUG_NAMES + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} ) + endif() + endforeach() + if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread") + _Boost_PREPEND_LIST_WITH_THREADAPI(_boost_DEBUG_NAMES ${_boost_DEBUG_NAMES}) + endif() + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + "Searching for ${UPPERCOMPONENT}_LIBRARY_DEBUG: ${_boost_DEBUG_NAMES}") + endif() + + # if Boost_LIBRARY_DIR_DEBUG is not defined, + # but Boost_LIBRARY_DIR_RELEASE is, look there first for DEBUG libs + if(NOT Boost_LIBRARY_DIR_DEBUG AND Boost_LIBRARY_DIR_RELEASE) + list(INSERT _boost_LIBRARY_SEARCH_DIRS_DEBUG 0 ${Boost_LIBRARY_DIR_RELEASE}) + endif() + + # Avoid passing backslashes to _Boost_FIND_LIBRARY due to macro re-parsing. + string(REPLACE "\\" "/" _boost_LIBRARY_SEARCH_DIRS_tmp "${_boost_LIBRARY_SEARCH_DIRS_DEBUG}") + + if(Boost_USE_DEBUG_LIBS) + _Boost_FIND_LIBRARY(Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG DEBUG + NAMES ${_boost_DEBUG_NAMES} + HINTS ${_boost_LIBRARY_SEARCH_DIRS_tmp} + NAMES_PER_DIR + DOC "${_boost_docstring_debug}" + ) + endif () + + if(Boost_REALPATH) + _Boost_SWAP_WITH_REALPATH(Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE "${_boost_docstring_release}") + _Boost_SWAP_WITH_REALPATH(Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG "${_boost_docstring_debug}" ) + endif() + + _Boost_ADJUST_LIB_VARS(${UPPERCOMPONENT}) + + # Check if component requires some compiler features + _Boost_COMPILER_FEATURES(${COMPONENT} _Boost_${UPPERCOMPONENT}_COMPILER_FEATURES) + +endforeach() + +# Restore the original find library ordering +if( Boost_USE_STATIC_LIBS ) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${_boost_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) +endif() + +# ------------------------------------------------------------------------ +# End finding boost libraries +# ------------------------------------------------------------------------ + +set(Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIR}) +set(Boost_LIBRARY_DIRS) +if(Boost_LIBRARY_DIR_RELEASE) + list(APPEND Boost_LIBRARY_DIRS ${Boost_LIBRARY_DIR_RELEASE}) +endif() +if(Boost_LIBRARY_DIR_DEBUG) + list(APPEND Boost_LIBRARY_DIRS ${Boost_LIBRARY_DIR_DEBUG}) +endif() +if(Boost_LIBRARY_DIRS) + list(REMOVE_DUPLICATES Boost_LIBRARY_DIRS) +endif() + +# The above setting of Boost_FOUND was based only on the header files. +# Update it for the requested component libraries. +if(Boost_FOUND) + # The headers were found. Check for requested component libs. + set(_boost_CHECKED_COMPONENT FALSE) + set(_Boost_MISSING_COMPONENTS "") + foreach(COMPONENT ${Boost_FIND_COMPONENTS}) + string(TOUPPER ${COMPONENT} UPPERCOMPONENT) + set(_boost_CHECKED_COMPONENT TRUE) + if(NOT Boost_${UPPERCOMPONENT}_FOUND AND Boost_FIND_REQUIRED_${COMPONENT}) + list(APPEND _Boost_MISSING_COMPONENTS ${COMPONENT}) + endif() + endforeach() + if(_Boost_MISSING_COMPONENTS AND _Boost_EXTRA_FIND_COMPONENTS) + # Optional indirect dependencies are not counted as missing. + list(REMOVE_ITEM _Boost_MISSING_COMPONENTS ${_Boost_EXTRA_FIND_COMPONENTS}) + endif() + + if(Boost_DEBUG) + message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] Boost_FOUND = ${Boost_FOUND}") + endif() + + if (_Boost_MISSING_COMPONENTS) + set(Boost_FOUND 0) + # We were unable to find some libraries, so generate a sensible + # error message that lists the libraries we were unable to find. + string(APPEND Boost_ERROR_REASON + "\nCould not find the following") + if(Boost_USE_STATIC_LIBS) + string(APPEND Boost_ERROR_REASON " static") + endif() + string(APPEND Boost_ERROR_REASON + " Boost libraries:\n") + foreach(COMPONENT ${_Boost_MISSING_COMPONENTS}) + string(TOUPPER ${COMPONENT} UPPERCOMPONENT) + string(APPEND Boost_ERROR_REASON + " ${Boost_NAMESPACE}_${COMPONENT}${Boost_ERROR_REASON_${UPPERCOMPONENT}}\n") + endforeach() + + list(LENGTH Boost_FIND_COMPONENTS Boost_NUM_COMPONENTS_WANTED) + list(LENGTH _Boost_MISSING_COMPONENTS Boost_NUM_MISSING_COMPONENTS) + if (${Boost_NUM_COMPONENTS_WANTED} EQUAL ${Boost_NUM_MISSING_COMPONENTS}) + string(APPEND Boost_ERROR_REASON + "No Boost libraries were found. You may need to set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.") + else () + string(APPEND Boost_ERROR_REASON + "Some (but not all) of the required Boost libraries were found. You may need to install these additional Boost libraries. Alternatively, set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.") + endif () + endif () + + if( NOT Boost_LIBRARY_DIRS AND NOT _boost_CHECKED_COMPONENT ) + # Compatibility Code for backwards compatibility with CMake + # 2.4's FindBoost module. + + # Look for the boost library path. + # Note that the user may not have installed any libraries + # so it is quite possible the Boost_LIBRARY_DIRS may not exist. + set(_boost_LIB_DIR ${Boost_INCLUDE_DIR}) + + if("${_boost_LIB_DIR}" MATCHES "boost-[0-9]+") + get_filename_component(_boost_LIB_DIR ${_boost_LIB_DIR} PATH) + endif() + + if("${_boost_LIB_DIR}" MATCHES "/include$") + # Strip off the trailing "/include" in the path. + get_filename_component(_boost_LIB_DIR ${_boost_LIB_DIR} PATH) + endif() + + if(EXISTS "${_boost_LIB_DIR}/lib") + string(APPEND _boost_LIB_DIR /lib) + elseif(EXISTS "${_boost_LIB_DIR}/stage/lib") + string(APPEND _boost_LIB_DIR "/stage/lib") + else() + set(_boost_LIB_DIR "") + endif() + + if(_boost_LIB_DIR AND EXISTS "${_boost_LIB_DIR}") + set(Boost_LIBRARY_DIRS ${_boost_LIB_DIR}) + endif() + + endif() +else() + # Boost headers were not found so no components were found. + foreach(COMPONENT ${Boost_FIND_COMPONENTS}) + string(TOUPPER ${COMPONENT} UPPERCOMPONENT) + set(Boost_${UPPERCOMPONENT}_FOUND 0) + endforeach() +endif() + +# ------------------------------------------------------------------------ +# Add imported targets +# ------------------------------------------------------------------------ + +if(Boost_FOUND) + # For header-only libraries + if(NOT TARGET Boost::boost) + add_library(Boost::boost INTERFACE IMPORTED) + if(Boost_INCLUDE_DIRS) + set_target_properties(Boost::boost PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}") + endif() + endif() + + foreach(COMPONENT ${Boost_FIND_COMPONENTS}) + if(_Boost_IMPORTED_TARGETS AND NOT TARGET Boost::${COMPONENT}) + string(TOUPPER ${COMPONENT} UPPERCOMPONENT) + if(Boost_${UPPERCOMPONENT}_FOUND) + if(Boost_USE_STATIC_LIBS) + add_library(Boost::${COMPONENT} STATIC IMPORTED) + else() + # Even if Boost_USE_STATIC_LIBS is OFF, we might have static + # libraries as a result. + add_library(Boost::${COMPONENT} UNKNOWN IMPORTED) + endif() + if(Boost_INCLUDE_DIRS) + set_target_properties(Boost::${COMPONENT} PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}") + endif() + if(EXISTS "${Boost_${UPPERCOMPONENT}_LIBRARY}") + set_target_properties(Boost::${COMPONENT} PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${Boost_${UPPERCOMPONENT}_LIBRARY}") + endif() + if(EXISTS "${Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE}") + set_property(TARGET Boost::${COMPONENT} APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(Boost::${COMPONENT} PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "CXX" + IMPORTED_LOCATION_RELEASE "${Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE}") + endif() + if(EXISTS "${Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG}") + set_property(TARGET Boost::${COMPONENT} APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties(Boost::${COMPONENT} PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "CXX" + IMPORTED_LOCATION_DEBUG "${Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG}") + endif() + if(_Boost_${UPPERCOMPONENT}_DEPENDENCIES) + unset(_Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES) + foreach(dep ${_Boost_${UPPERCOMPONENT}_DEPENDENCIES}) + list(APPEND _Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES Boost::${dep}) + endforeach() + if(COMPONENT STREQUAL "thread") + list(APPEND _Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES Threads::Threads) + endif() + set_target_properties(Boost::${COMPONENT} PROPERTIES + INTERFACE_LINK_LIBRARIES "${_Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES}") + endif() + if(_Boost_${UPPERCOMPONENT}_COMPILER_FEATURES) + set_target_properties(Boost::${COMPONENT} PROPERTIES + INTERFACE_COMPILE_FEATURES "${_Boost_${UPPERCOMPONENT}_COMPILER_FEATURES}") + endif() + endif() + endif() + endforeach() +endif() + +# ------------------------------------------------------------------------ +# Notification to end user about what was found +# ------------------------------------------------------------------------ + +set(Boost_LIBRARIES "") +if(Boost_FOUND) + if(NOT Boost_FIND_QUIETLY) + message(STATUS "Boost version: ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") + if(Boost_FIND_COMPONENTS) + message(STATUS "Found the following Boost libraries:") + endif() + endif() + foreach( COMPONENT ${Boost_FIND_COMPONENTS} ) + string( TOUPPER ${COMPONENT} UPPERCOMPONENT ) + if( Boost_${UPPERCOMPONENT}_FOUND ) + if(NOT Boost_FIND_QUIETLY) + message (STATUS " ${COMPONENT}") + endif() + list(APPEND Boost_LIBRARIES ${Boost_${UPPERCOMPONENT}_LIBRARY}) + endif() + endforeach() +else() + if(Boost_FIND_REQUIRED) + message(SEND_ERROR "Unable to find the requested Boost libraries.\n${Boost_ERROR_REASON}") + else() + if(NOT Boost_FIND_QUIETLY) + # we opt not to automatically output Boost_ERROR_REASON here as + # it could be quite lengthy and somewhat imposing in its requests + # Since Boost is not always a required dependency we'll leave this + # up to the end-user. + if(Boost_DEBUG OR Boost_DETAILED_FAILURE_MSG) + message(STATUS "Could NOT find Boost\n${Boost_ERROR_REASON}") + else() + message(STATUS "Could NOT find Boost") + endif() + endif() + endif() +endif() + +# Configure display of cache entries in GUI. +foreach(v BOOSTROOT BOOST_ROOT ${_Boost_VARS_INC} ${_Boost_VARS_LIB}) + get_property(_type CACHE ${v} PROPERTY TYPE) + if(_type) + set_property(CACHE ${v} PROPERTY ADVANCED 1) + if("x${_type}" STREQUAL "xUNINITIALIZED") + if("x${v}" STREQUAL "xBoost_ADDITIONAL_VERSIONS") + set_property(CACHE ${v} PROPERTY TYPE STRING) + else() + set_property(CACHE ${v} PROPERTY TYPE PATH) + endif() + endif() + endif() +endforeach() + +# Record last used values of input variables so we can +# detect on the next run if the user changed them. +foreach(v + ${_Boost_VARS_INC} ${_Boost_VARS_LIB} + ${_Boost_VARS_DIR} ${_Boost_VARS_NAME} + ) + if(DEFINED ${v}) + set(_${v}_LAST "${${v}}" CACHE INTERNAL "Last used ${v} value.") + else() + unset(_${v}_LAST CACHE) + endif() +endforeach() + +# Maintain a persistent list of components requested anywhere since +# the last flush. +set(_Boost_COMPONENTS_SEARCHED "${_Boost_COMPONENTS_SEARCHED}") +list(APPEND _Boost_COMPONENTS_SEARCHED ${Boost_FIND_COMPONENTS}) +list(REMOVE_DUPLICATES _Boost_COMPONENTS_SEARCHED) +list(SORT _Boost_COMPONENTS_SEARCHED) +set(_Boost_COMPONENTS_SEARCHED "${_Boost_COMPONENTS_SEARCHED}" + CACHE INTERNAL "Components requested for this build tree.") + +# Restore project's policies +cmake_policy(POP) From 78d12342ba7bdea25b9c4da41679f668788044da Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Fri, 28 Feb 2020 09:31:45 +0000 Subject: [PATCH 03/43] Lock before stopping when it is necessary to notify other threads (#2608) This prevents having all threads waiting and the system freezing. Mostly important for tests, especially with sanitizers, but could happen on stopping the node normally. Portmapping requires an atomic bool, and the database queue doesn't after this change. Other small related bootstrap changes included in this PR per Serg's suggestion. --- nano/node/bootstrap/bootstrap_attempt.cpp | 6 ++-- nano/node/bootstrap/bootstrap_connections.cpp | 28 +++++++++++-------- nano/node/bootstrap/bootstrap_lazy.cpp | 2 +- nano/node/confirmation_height_processor.cpp | 5 +++- nano/node/portmapping.hpp | 2 +- nano/node/write_database_queue.cpp | 5 +++- nano/node/write_database_queue.hpp | 3 +- 7 files changed, 32 insertions(+), 19 deletions(-) diff --git a/nano/node/bootstrap/bootstrap_attempt.cpp b/nano/node/bootstrap/bootstrap_attempt.cpp index d0914c25c3..32c9b0b6db 100644 --- a/nano/node/bootstrap/bootstrap_attempt.cpp +++ b/nano/node/bootstrap/bootstrap_attempt.cpp @@ -202,9 +202,11 @@ bool nano::bootstrap_attempt_legacy::consume_future (std::future & future_ void nano::bootstrap_attempt_legacy::stop () { + nano::unique_lock lock (mutex); stopped = true; + lock.unlock (); condition.notify_all (); - nano::unique_lock lock (mutex); + lock.lock (); if (auto i = frontiers.lock ()) { try @@ -478,7 +480,7 @@ bool nano::bootstrap_attempt_legacy::request_frontier (nano::unique_lockbootstrap_initiator.connections->connection (shared_from_this (), first_attempt)); lock_a.lock (); - if (connection_l) + if (connection_l && !stopped) { endpoint_frontier_request = connection_l->channel->get_tcp_endpoint (); std::future future; diff --git a/nano/node/bootstrap/bootstrap_connections.cpp b/nano/node/bootstrap/bootstrap_connections.cpp index 93e66ac550..f1ab2200f2 100644 --- a/nano/node/bootstrap/bootstrap_connections.cpp +++ b/nano/node/bootstrap/bootstrap_connections.cpp @@ -76,7 +76,8 @@ std::shared_ptr nano::bootstrap_connections::connection if (result == nullptr && connections_count == 0 && new_connections_empty && attempt_a != nullptr) { node.logger.try_log (boost::str (boost::format ("Bootstrap attempt stopped because there are no peers"))); - attempt_a->stopped = true; + lock.unlock (); + attempt_a->stop (); } return result; } @@ -440,19 +441,22 @@ void nano::bootstrap_connections::requeue_pull (nano::pull_info const & pull_a, void nano::bootstrap_connections::clear_pulls (uint64_t bootstrap_id_a) { - nano::lock_guard lock (mutex); - auto i (pulls.begin ()); - while (i != pulls.end ()) { - if (i->bootstrap_id == bootstrap_id_a) - { - i = pulls.erase (i); - } - else + nano::lock_guard lock (mutex); + auto i (pulls.begin ()); + while (i != pulls.end ()) { - ++i; + if (i->bootstrap_id == bootstrap_id_a) + { + i = pulls.erase (i); + } + else + { + ++i; + } } } + condition.notify_all (); } void nano::bootstrap_connections::run () @@ -477,9 +481,11 @@ void nano::bootstrap_connections::run () void nano::bootstrap_connections::stop () { + nano::unique_lock lock (mutex); stopped = true; + lock.unlock (); condition.notify_all (); - nano::lock_guard lock (mutex); + lock.lock (); for (auto i : clients) { if (auto client = i.lock ()) diff --git a/nano/node/bootstrap/bootstrap_lazy.cpp b/nano/node/bootstrap/bootstrap_lazy.cpp index b48f3021a2..ef1dc82e56 100644 --- a/nano/node/bootstrap/bootstrap_lazy.cpp +++ b/nano/node/bootstrap/bootstrap_lazy.cpp @@ -519,7 +519,7 @@ void nano::bootstrap_attempt_wallet::request_pending (nano::unique_lockbootstrap_initiator.connections->connection (shared_from_this ())); lock_a.lock (); - if (connection_l) + if (connection_l && !stopped) { auto account (wallet_accounts.front ()); wallet_accounts.pop_front (); diff --git a/nano/node/confirmation_height_processor.cpp b/nano/node/confirmation_height_processor.cpp index 42d6b85ec7..f7ede3759d 100644 --- a/nano/node/confirmation_height_processor.cpp +++ b/nano/node/confirmation_height_processor.cpp @@ -34,7 +34,10 @@ nano::confirmation_height_processor::~confirmation_height_processor () void nano::confirmation_height_processor::stop () { - stopped = true; + { + nano::lock_guard guard (mutex); + stopped = true; + } condition.notify_one (); if (thread.joinable ()) { diff --git a/nano/node/portmapping.hpp b/nano/node/portmapping.hpp index ccf754d45f..3828785bf5 100644 --- a/nano/node/portmapping.hpp +++ b/nano/node/portmapping.hpp @@ -59,7 +59,7 @@ class port_mapping boost::asio::ip::address_v4 address; std::array protocols; uint64_t check_count{ 0 }; - bool on{ false }; + std::atomic on{ false }; std::mutex mutex; }; } diff --git a/nano/node/write_database_queue.cpp b/nano/node/write_database_queue.cpp index 3378f0a787..c350f049a7 100644 --- a/nano/node/write_database_queue.cpp +++ b/nano/node/write_database_queue.cpp @@ -77,6 +77,9 @@ nano::write_guard nano::write_database_queue::pop () void nano::write_database_queue::stop () { - stopped = true; + { + nano::lock_guard guard (mutex); + stopped = true; + } cv.notify_all (); } diff --git a/nano/node/write_database_queue.hpp b/nano/node/write_database_queue.hpp index b2657c00bc..e18803482c 100644 --- a/nano/node/write_database_queue.hpp +++ b/nano/node/write_database_queue.hpp @@ -2,7 +2,6 @@ #include -#include #include #include #include @@ -53,6 +52,6 @@ class write_database_queue final std::mutex mutex; nano::condition_variable cv; std::function guard_finish_callback; - std::atomic stopped{ false }; + bool stopped{ false }; }; } From 79fceac1afcc60b33c260d2d06d26030ed50bf8d Mon Sep 17 00:00:00 2001 From: Wesley Shillingford Date: Fri, 28 Feb 2020 12:50:53 +0000 Subject: [PATCH 04/43] Simplify telemetry data processing (#2598) * Simplify telemetry data processing * Stein review comments * Fix network.replace_port --- nano/core_test/network.cpp | 1 + nano/core_test/node_telemetry.cpp | 421 ++++------------------- nano/core_test/testutil.hpp | 7 +- nano/node/json_handler.cpp | 61 ++-- nano/node/network.cpp | 5 +- nano/node/node.cpp | 15 +- nano/node/node.hpp | 2 +- nano/node/telemetry.cpp | 540 +++++++++++------------------- nano/node/telemetry.hpp | 187 ++++------- nano/node/transport/tcp.hpp | 2 +- nano/rpc_test/rpc.cpp | 19 +- nano/slow_test/node.cpp | 142 ++------ 12 files changed, 434 insertions(+), 968 deletions(-) diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index 901d4ada2a..47f3c170ad 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -879,6 +879,7 @@ TEST (network, replace_port) nano::system system; nano::node_flags node_flags; node_flags.disable_udp = false; + node_flags.disable_ongoing_telemetry_requests = true; auto node0 = system.add_node (node_flags); ASSERT_EQ (0, node0->network.size ()); auto node1 (std::make_shared (system.io_ctx, nano::get_available_port (), nano::unique_path (), system.alarm, system.logging, system.work, node_flags)); diff --git a/nano/core_test/node_telemetry.cpp b/nano/core_test/node_telemetry.cpp index c42b396cce..2996950271 100644 --- a/nano/core_test/node_telemetry.cpp +++ b/nano/core_test/node_telemetry.cpp @@ -263,38 +263,28 @@ TEST (node_telemetry, no_peers) nano::system system (1); std::atomic done{ false }; - system.nodes[0]->telemetry.get_metrics_peers_async ([&done](nano::telemetry_data_responses const & responses_a) { - ASSERT_TRUE (responses_a.telemetry_datas.empty ()); - ASSERT_FALSE (responses_a.all_received); - done = true; - }); - - system.deadline_set (10s); - while (!done) - { - ASSERT_NO_ERROR (system.poll ()); - } + auto responses = system.nodes[0]->telemetry->get_metrics (); + ASSERT_TRUE (responses.empty ()); } -namespace nano -{ TEST (node_telemetry, basic) { nano::system system; - nano::node_flags node_flags; - node_flags.disable_ongoing_telemetry_requests = true; - auto node_client = system.add_node (node_flags); - auto node_server = system.add_node (node_flags); + auto node_client = system.add_node (); + auto node_server = system.add_node (); wait_peer_connections (system); // Request telemetry metrics - std::unordered_map all_telemetry_datas; + nano::telemetry_data telemetry_data; + auto server_endpoint = node_server->network.endpoint (); + auto channel = node_client->network.find_channel (node_server->network.endpoint ()); { std::atomic done{ false }; - node_client->telemetry.get_metrics_peers_async ([&done, &all_telemetry_datas](nano::telemetry_data_responses const & responses_a) { - ASSERT_TRUE (responses_a.all_received); - all_telemetry_datas = responses_a.telemetry_datas; + node_client->telemetry->get_metrics_single_peer_async (channel, [&done, &server_endpoint, &telemetry_data](nano::telemetry_data_response const & response_a) { + ASSERT_FALSE (response_a.error); + ASSERT_EQ (server_endpoint, response_a.endpoint); + telemetry_data = response_a.telemetry_data; done = true; }); @@ -306,15 +296,14 @@ TEST (node_telemetry, basic) } // Check the metrics are correct - ASSERT_EQ (all_telemetry_datas.size (), 1); - compare_default_test_result_data (all_telemetry_datas.begin ()->second, *node_server); + compare_default_test_result_data (telemetry_data, *node_server); // Call again straight away. It should use the cache { std::atomic done{ false }; - node_client->telemetry.get_metrics_peers_async ([&done, &all_telemetry_datas](nano::telemetry_data_responses const & responses_a) { - ASSERT_EQ (all_telemetry_datas, responses_a.telemetry_datas); - ASSERT_TRUE (responses_a.all_received); + node_client->telemetry->get_metrics_single_peer_async (channel, [&done, &telemetry_data](nano::telemetry_data_response const & response_a) { + ASSERT_EQ (telemetry_data, response_a.telemetry_data); + ASSERT_FALSE (response_a.error); done = true; }); @@ -329,9 +318,9 @@ TEST (node_telemetry, basic) std::this_thread::sleep_for (nano::telemetry_cache_cutoffs::test); std::atomic done{ false }; - node_client->telemetry.get_metrics_peers_async ([&done, &all_telemetry_datas](nano::telemetry_data_responses const & responses_a) { - ASSERT_NE (all_telemetry_datas, responses_a.telemetry_datas); - ASSERT_TRUE (responses_a.all_received); + node_client->telemetry->get_metrics_single_peer_async (channel, [&done, &telemetry_data](nano::telemetry_data_response const & response_a) { + ASSERT_NE (telemetry_data, response_a.telemetry_data); + ASSERT_FALSE (response_a.error); done = true; }); @@ -341,19 +330,20 @@ TEST (node_telemetry, basic) ASSERT_NO_ERROR (system.poll ()); } } -} TEST (node_telemetry, many_nodes) { nano::system system; // The telemetry responses can timeout if using a large number of nodes under sanitizers, so lower the number. - const auto num_nodes = (is_sanitizer_build || nano::running_within_valgrind ()) ? 4 : 10; + const auto num_nodes = (is_sanitizer_build || nano::running_within_valgrind ()) ? 4 : 10; //3; // 10; + nano::node_flags node_flags; + node_flags.disable_ongoing_telemetry_requests = true; for (auto i = 0; i < num_nodes; ++i) { nano::node_config node_config (nano::get_available_port (), system.logging); // Make a metric completely different for each node so we can check afterwards that there are no duplicates node_config.bandwidth_limit = 100000 + i; - system.add_node (node_config); + system.add_node (node_config, node_flags); } wait_peer_connections (system); @@ -371,25 +361,32 @@ TEST (node_telemetry, many_nodes) // This is the node which will request metrics from all other nodes auto node_client = system.nodes.front (); - std::atomic done{ false }; - std::unordered_map all_telemetry_datas; - node_client->telemetry.get_metrics_peers_async ([&done, &all_telemetry_datas](nano::telemetry_data_responses const & responses_a) { - ASSERT_TRUE (responses_a.all_received); - all_telemetry_datas = responses_a.telemetry_datas; - done = true; - }); + std::mutex mutex; + std::vector telemetry_datas; + auto peers = node_client->network.list (num_nodes - 1); + ASSERT_EQ (peers.size (), num_nodes - 1); + for (auto const & peer : peers) + { + node_client->telemetry->get_metrics_single_peer_async (peer, [&telemetry_datas, &mutex](nano::telemetry_data_response const & response_a) { + ASSERT_FALSE (response_a.error); + nano::lock_guard guard (mutex); + telemetry_datas.push_back (response_a.telemetry_data); + }); + } system.deadline_set (20s); - while (!done) + nano::unique_lock lk (mutex); + while (telemetry_datas.size () != num_nodes - 1) { + lk.unlock (); ASSERT_NO_ERROR (system.poll ()); + lk.lock (); } // Check the metrics nano::network_params params; - for (auto & telemetry_data : all_telemetry_datas) + for (auto & data : telemetry_datas) { - auto & data = telemetry_data.second; ASSERT_EQ (data.unchecked_count, 0); ASSERT_EQ (data.cemented_count, 1); ASSERT_LE (data.peer_count, 9); @@ -408,10 +405,10 @@ TEST (node_telemetry, many_nodes) } // We gave some nodes different bandwidth caps, confirm they are not all the same - auto bandwidth_cap = all_telemetry_datas.begin ()->second.bandwidth_cap; - all_telemetry_datas.erase (all_telemetry_datas.begin ()); - auto all_bandwidth_limits_same = std::all_of (all_telemetry_datas.begin (), all_telemetry_datas.end (), [bandwidth_cap](auto & telemetry_data) { - return telemetry_data.second.bandwidth_cap == bandwidth_cap; + auto bandwidth_cap = telemetry_datas.front ().bandwidth_cap; + telemetry_datas.erase (telemetry_datas.begin ()); + auto all_bandwidth_limits_same = std::all_of (telemetry_datas.begin (), telemetry_datas.end (), [bandwidth_cap](auto & telemetry_data) { + return telemetry_data.bandwidth_cap == bandwidth_cap; }); ASSERT_FALSE (all_bandwidth_limits_same); } @@ -423,7 +420,7 @@ TEST (node_telemetry, receive_from_non_listening_channel) nano::telemetry_ack message (nano::telemetry_data{}); node->network.process_message (message, node->network.udp_channels.create (node->network.endpoint ())); // We have not sent a telemetry_req message to this endpoint, so shouldn't count telemetry_ack received from it. - ASSERT_EQ (node->telemetry.telemetry_data_size (), 0); + ASSERT_EQ (node->telemetry->telemetry_data_size (), 0); } TEST (node_telemetry, over_udp) @@ -438,10 +435,10 @@ TEST (node_telemetry, over_udp) wait_peer_connections (system); std::atomic done{ false }; - std::unordered_map all_telemetry_datas; - node_client->telemetry.get_metrics_peers_async ([&done, &all_telemetry_datas](nano::telemetry_data_responses const & responses_a) { - ASSERT_TRUE (responses_a.all_received); - all_telemetry_datas = responses_a.telemetry_datas; + auto channel = node_client->network.find_channel (node_server->network.endpoint ()); + node_client->telemetry->get_metrics_single_peer_async (channel, [&done, &node_server](nano::telemetry_data_response const & response_a) { + ASSERT_FALSE (response_a.error); + compare_default_test_result_data (response_a.telemetry_data, *node_server); done = true; }); @@ -451,9 +448,6 @@ TEST (node_telemetry, over_udp) ASSERT_NO_ERROR (system.poll ()); } - ASSERT_EQ (all_telemetry_datas.size (), 1); - compare_default_test_result_data (all_telemetry_datas.begin ()->second, *node_server); - // Check channels are indeed udp ASSERT_EQ (1, node_client->network.size ()); auto list1 (node_client->network.list (2)); @@ -465,77 +459,7 @@ TEST (node_telemetry, over_udp) ASSERT_EQ (nano::transport::transport_type::udp, list2[0]->get_type ()); } -namespace nano -{ -TEST (node_telemetry, single_request) -{ - nano::system system; - nano::node_flags node_flags; - node_flags.disable_ongoing_telemetry_requests = true; - - auto node_client = system.add_node (node_flags); - auto node_server = system.add_node (node_flags); - - wait_peer_connections (system); - - // Request telemetry metrics - auto channel = node_client->network.find_channel (node_server->network.endpoint ()); - nano::telemetry_data telemetry_data; - { - std::atomic done{ false }; - - node_client->telemetry.get_metrics_single_peer_async (channel, [&done, &telemetry_data, &channel](nano::telemetry_data_response const & response_a) { - ASSERT_FALSE (response_a.error); - ASSERT_EQ (channel->get_endpoint (), response_a.endpoint); - telemetry_data = response_a.telemetry_data; - done = true; - }); - - system.deadline_set (10s); - while (!done) - { - ASSERT_NO_ERROR (system.poll ()); - } - } - - // Check the metrics are correct - compare_default_test_result_data (telemetry_data, *node_server); - - // Call again straight away. It should use the cache - { - std::atomic done{ false }; - node_client->telemetry.get_metrics_single_peer_async (channel, [&done, &telemetry_data](nano::telemetry_data_response const & response_a) { - ASSERT_EQ (telemetry_data, response_a.telemetry_data); - ASSERT_FALSE (response_a.error); - done = true; - }); - - system.deadline_set (10s); - while (!done) - { - ASSERT_NO_ERROR (system.poll ()); - } - } - - // Wait the cache period and check cache is not used - std::this_thread::sleep_for (nano::telemetry_cache_cutoffs::test); - - std::atomic done{ false }; - node_client->telemetry.get_metrics_single_peer_async (channel, [&done, &telemetry_data](nano::telemetry_data_response const & response_a) { - ASSERT_NE (telemetry_data, response_a.telemetry_data); - ASSERT_FALSE (response_a.error); - done = true; - }); - - system.deadline_set (10s); - while (!done) - { - ASSERT_NO_ERROR (system.poll ()); - } -} -} - -TEST (node_telemetry, single_request_invalid_channel) +TEST (node_telemetry, invalid_channel) { nano::system system (2); @@ -543,7 +467,7 @@ TEST (node_telemetry, single_request_invalid_channel) auto node_server = system.nodes.back (); std::atomic done{ false }; - node_client->telemetry.get_metrics_single_peer_async (nullptr, [&done](nano::telemetry_data_response const & response_a) { + node_client->telemetry->get_metrics_single_peer_async (nullptr, [&done](nano::telemetry_data_response const & response_a) { ASSERT_TRUE (response_a.error); done = true; }); @@ -555,7 +479,7 @@ TEST (node_telemetry, single_request_invalid_channel) } } -TEST (node_telemetry, blocking_single_and_random) +TEST (node_telemetry, blocking_request) { nano::system system (2); @@ -584,104 +508,15 @@ TEST (node_telemetry, blocking_single_and_random) system.deadline_set (10s); node_client->worker.push_task (call_system_poll); - // Blocking version of get_random_metrics_async - auto telemetry_data_responses = node_client->telemetry.get_metrics_peers (); - ASSERT_TRUE (telemetry_data_responses.all_received); - compare_default_test_result_data (telemetry_data_responses.telemetry_datas.begin ()->second, *node_server); - // Now try single request metric - auto telemetry_data_response = node_client->telemetry.get_metrics_single_peer (node_client->network.find_channel (node_server->network.endpoint ())); + auto telemetry_data_response = node_client->telemetry->get_metrics_single_peer (node_client->network.find_channel (node_server->network.endpoint ())); ASSERT_FALSE (telemetry_data_response.error); compare_default_test_result_data (telemetry_data_response.telemetry_data, *node_server); - ASSERT_EQ (*telemetry_data_response.telemetry_data.timestamp, *telemetry_data_responses.telemetry_datas.begin ()->second.timestamp); done = true; promise.get_future ().wait (); } -namespace nano -{ -TEST (node_telemetry, multiple_single_request_clearing) -{ - nano::system system (2); - - auto node_client = system.nodes.front (); - auto node_server = system.nodes.back (); - - nano::node_config node_config (nano::get_available_port (), system.logging); - node_config.bandwidth_limit = 100000; - auto node_server1 = system.add_node (node_config); - - wait_peer_connections (system); - - // Request telemetry metrics - auto channel = node_client->network.find_channel (node_server->network.endpoint ()); - - std::atomic done{ false }; - std::chrono::system_clock::time_point last_updated; - node_client->telemetry.get_metrics_single_peer_async (channel, [&done, &last_updated](nano::telemetry_data_response const & response_a) { - ASSERT_FALSE (response_a.error); - last_updated = *response_a.telemetry_data.timestamp; - done = true; - }); - - ASSERT_EQ (1, node_client->telemetry.single_requests.size ()); - system.deadline_set (10s); - while (!done) - { - ASSERT_NO_ERROR (system.poll ()); - } - - done = false; - // Make another request to keep the time updated - system.deadline_set (10s); - node_client->telemetry.get_metrics_single_peer_async (channel, [&done, last_updated](nano::telemetry_data_response const & response_a) { - ASSERT_FALSE (response_a.error); - ASSERT_EQ (last_updated, *response_a.telemetry_data.timestamp); - done = true; - }); - - system.deadline_set (10s); - while (!done) - { - ASSERT_NO_ERROR (system.poll ()); - } - - done = false; - auto channel1 = node_client->network.find_channel (node_server1->network.endpoint ()); - node_client->telemetry.get_metrics_single_peer_async (channel1, [&done, &last_updated](nano::telemetry_data_response const & response_a) { - ASSERT_FALSE (response_a.error); - ASSERT_NE (last_updated, *response_a.telemetry_data.timestamp); - last_updated = *response_a.telemetry_data.timestamp; - done = true; - }); - - system.deadline_set (10s); - - while (!done) - { - ASSERT_NO_ERROR (system.poll ()); - } - - done = false; - node_client->telemetry.get_metrics_single_peer_async (channel1, [&done, last_updated](nano::telemetry_data_response const & response_a) { - ASSERT_FALSE (response_a.error); - ASSERT_EQ (last_updated, *response_a.telemetry_data.timestamp); - done = true; - }); - - // single_requests should be removed as no more calls are being back - system.deadline_set (10s); - nano::unique_lock lk (node_client->telemetry.mutex); - while (!node_client->telemetry.single_requests.empty () || !done) - { - lk.unlock (); - ASSERT_NO_ERROR (system.poll ()); - lk.lock (); - } -} -} - TEST (node_telemetry, disconnects) { nano::system system (2); @@ -697,19 +532,7 @@ TEST (node_telemetry, disconnects) ASSERT_TRUE (channel); std::atomic done{ false }; - node_client->telemetry.get_metrics_peers_async ([&done](nano::telemetry_data_responses const & responses_a) { - ASSERT_FALSE (responses_a.all_received); - done = true; - }); - - system.deadline_set (10s); - while (!done) - { - ASSERT_NO_ERROR (system.poll ()); - } - - done = false; - node_client->telemetry.get_metrics_single_peer_async (channel, [&done](nano::telemetry_data_response const & response_a) { + node_client->telemetry->get_metrics_single_peer_async (channel, [&done](nano::telemetry_data_response const & response_a) { ASSERT_TRUE (response_a.error); done = true; }); @@ -721,7 +544,7 @@ TEST (node_telemetry, disconnects) } } -TEST (node_telemetry, batch_use_single_request_cache) +TEST (node_telemetry, all_peers_use_single_request_cache) { nano::system system; nano::node_flags node_flags; @@ -736,7 +559,7 @@ TEST (node_telemetry, batch_use_single_request_cache) { std::atomic done{ false }; auto channel = node_client->network.find_channel (node_server->network.endpoint ()); - node_client->telemetry.get_metrics_single_peer_async (channel, [&done, &telemetry_data](nano::telemetry_data_response const & response_a) { + node_client->telemetry->get_metrics_single_peer_async (channel, [&done, &telemetry_data](nano::telemetry_data_response const & response_a) { telemetry_data = response_a.telemetry_data; done = true; }); @@ -748,20 +571,8 @@ TEST (node_telemetry, batch_use_single_request_cache) } } - { - std::atomic done{ false }; - node_client->telemetry.get_metrics_peers_async ([&done, &telemetry_data](nano::telemetry_data_responses const & responses_a) { - ASSERT_TRUE (responses_a.all_received); - ASSERT_EQ (telemetry_data, responses_a.telemetry_datas.begin ()->second); - done = true; - }); - - system.deadline_set (10s); - while (!done) - { - ASSERT_NO_ERROR (system.poll ()); - } - } + auto responses = node_client->telemetry->get_metrics (); + ASSERT_EQ (telemetry_data, responses.begin ()->second); // Confirm only 1 request was made ASSERT_EQ (1, node_client->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_ack, nano::stat::dir::in)); @@ -771,54 +582,17 @@ TEST (node_telemetry, batch_use_single_request_cache) ASSERT_EQ (1, node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::in)); ASSERT_EQ (0, node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::out)); - // Wait until there is something pending - system.deadline_set (10s); - while (node_client->telemetry.finished_single_requests_size () == 0) - { - ASSERT_NO_ERROR (system.poll ()); - } - std::this_thread::sleep_for (nano::telemetry_cache_cutoffs::test); - system.deadline_set (10s); - std::atomic done{ false }; - node_client->telemetry.get_metrics_peers_async ([&done](nano::telemetry_data_responses const & responses_a) { - ASSERT_EQ (1, responses_a.telemetry_datas.size ()); - done = true; - }); - - system.deadline_set (10s); - while (!done) - { - ASSERT_NO_ERROR (system.poll ()); - } - - ASSERT_EQ (0, node_client->telemetry.finished_single_requests_size ()); - ASSERT_EQ (2, node_client->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_ack, nano::stat::dir::in)); - ASSERT_EQ (0, node_client->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::in)); - ASSERT_EQ (2, node_client->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::out)); - ASSERT_EQ (0, node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_ack, nano::stat::dir::in)); - ASSERT_EQ (2, node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::in)); - ASSERT_EQ (0, node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::out)); -} - -TEST (node_telemetry, single_request_use_batch_cache) -{ - nano::system system (2); + // Should be empty + responses = node_client->telemetry->get_metrics (); + ASSERT_TRUE (responses.empty ()); - auto node_client = system.nodes.front (); - auto node_server = system.nodes.back (); - - wait_peer_connections (system); - - // Request batched metric first - std::unordered_map all_telemetry_datas; { std::atomic done{ false }; - node_client->telemetry.get_metrics_peers_async ([&done, &all_telemetry_datas](nano::telemetry_data_responses const & responses_a) { - ASSERT_TRUE (responses_a.all_received); - ASSERT_EQ (1, responses_a.telemetry_datas.size ()); - all_telemetry_datas = responses_a.telemetry_datas; + auto channel = node_client->network.find_channel (node_server->network.endpoint ()); + node_client->telemetry->get_metrics_single_peer_async (channel, [&done, &telemetry_data](nano::telemetry_data_response const & response_a) { + telemetry_data = response_a.telemetry_data; done = true; }); @@ -829,26 +603,14 @@ TEST (node_telemetry, single_request_use_batch_cache) } } - std::atomic done{ false }; - auto channel = node_client->network.find_channel (node_server->network.endpoint ()); - node_client->telemetry.get_metrics_single_peer_async (channel, [&done, &all_telemetry_datas](nano::telemetry_data_response const & response_a) { - ASSERT_EQ (all_telemetry_datas.begin ()->second, response_a.telemetry_data); - ASSERT_FALSE (response_a.error); - done = true; - }); - - system.deadline_set (10s); - while (!done) - { - ASSERT_NO_ERROR (system.poll ()); - } + responses = node_client->telemetry->get_metrics (); + ASSERT_EQ (telemetry_data, responses.begin ()->second); - // Confirm only 1 request was made - ASSERT_EQ (1, node_client->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_ack, nano::stat::dir::in)); + ASSERT_EQ (2, node_client->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_ack, nano::stat::dir::in)); ASSERT_EQ (0, node_client->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::in)); - ASSERT_EQ (1, node_client->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::out)); + ASSERT_EQ (2, node_client->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::out)); ASSERT_EQ (0, node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_ack, nano::stat::dir::in)); - ASSERT_EQ (1, node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::in)); + ASSERT_EQ (2, node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::in)); ASSERT_EQ (0, node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::out)); } @@ -947,7 +709,7 @@ TEST (node_telemetry, dos_udp) } } -TEST (node_telemetry, disable_metrics_single) +TEST (node_telemetry, disable_metrics) { nano::system system (1); auto node_client = system.nodes.front (); @@ -962,7 +724,7 @@ TEST (node_telemetry, disable_metrics_single) ASSERT_TRUE (channel); std::atomic done{ false }; - node_client->telemetry.get_metrics_single_peer_async (channel, [&done](nano::telemetry_data_response const & response_a) { + node_client->telemetry->get_metrics_single_peer_async (channel, [&done](nano::telemetry_data_response const & response_a) { ASSERT_TRUE (response_a.error); done = true; }); @@ -976,7 +738,7 @@ TEST (node_telemetry, disable_metrics_single) // It should still be able to receive metrics though done = false; auto channel1 = node_server->network.find_channel (node_client->network.endpoint ()); - node_server->telemetry.get_metrics_single_peer_async (channel1, [&done, node_server](nano::telemetry_data_response const & response_a) { + node_server->telemetry->get_metrics_single_peer_async (channel1, [&done, node_server](nano::telemetry_data_response const & response_a) { ASSERT_FALSE (response_a.error); compare_default_test_result_data (response_a.telemetry_data, *node_server); done = true; @@ -989,47 +751,6 @@ TEST (node_telemetry, disable_metrics_single) } } -TEST (node_telemetry, disable_metrics_batch) -{ - nano::system system (1); - auto node_client = system.nodes.front (); - nano::node_flags node_flags; - node_flags.disable_providing_telemetry_metrics = true; - auto node_server = system.add_node (node_flags); - - wait_peer_connections (system); - - // Try and request metrics from a node which is turned off but a channel is not closed yet - auto channel = node_client->network.find_channel (node_server->network.endpoint ()); - ASSERT_TRUE (channel); - - std::atomic done{ false }; - node_client->telemetry.get_metrics_peers_async ([&done](nano::telemetry_data_responses const & responses_a) { - ASSERT_FALSE (responses_a.all_received); - done = true; - }); - - system.deadline_set (10s); - while (!done) - { - ASSERT_NO_ERROR (system.poll ()); - } - - // It should still be able to receive metrics though - done = false; - node_server->telemetry.get_metrics_peers_async ([&done, node_server](nano::telemetry_data_responses const & responses_a) { - ASSERT_TRUE (responses_a.all_received); - compare_default_test_result_data (responses_a.telemetry_datas.begin ()->second, *node_server); - done = true; - }); - - system.deadline_set (10s); - while (!done) - { - ASSERT_NO_ERROR (system.poll ()); - } -} - namespace { void wait_peer_connections (nano::system & system_a) diff --git a/nano/core_test/testutil.hpp b/nano/core_test/testutil.hpp index 24202a4ccc..348e88cc70 100644 --- a/nano/core_test/testutil.hpp +++ b/nano/core_test/testutil.hpp @@ -187,9 +187,14 @@ namespace util return val; } + void increment_required_count () + { + ++required_count; + } + private: std::atomic count{ 0 }; - unsigned required_count; + std::atomic required_count; }; } diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 80a5bc5b28..a163925a77 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -3977,29 +3977,36 @@ void nano::json_handler::telemetry () if (!ec) { debug_assert (channel); - node.telemetry.get_metrics_single_peer_async (channel, [rpc_l](auto const & telemetry_response_a) { - if (!telemetry_response_a.error) - { - nano::jsonconfig config_l; - auto err = telemetry_response_a.telemetry_data.serialize_json (config_l); - auto const & ptree = config_l.get_tree (); - - if (!err) + if (node.telemetry) + { + node.telemetry->get_metrics_single_peer_async (channel, [rpc_l](auto const & telemetry_response_a) { + if (!telemetry_response_a.error) { - rpc_l->response_l.insert (rpc_l->response_l.begin (), ptree.begin (), ptree.end ()); + nano::jsonconfig config_l; + auto err = telemetry_response_a.telemetry_data.serialize_json (config_l); + auto const & ptree = config_l.get_tree (); + + if (!err) + { + rpc_l->response_l.insert (rpc_l->response_l.begin (), ptree.begin (), ptree.end ()); + } + else + { + rpc_l->ec = nano::error_rpc::generic; + } } else { rpc_l->ec = nano::error_rpc::generic; } - } - else - { - rpc_l->ec = nano::error_rpc::generic; - } - rpc_l->response_errors (); - }); + rpc_l->response_errors (); + }); + } + else + { + response_errors (); + } } else { @@ -4012,11 +4019,13 @@ void nano::json_handler::telemetry () // setting "raw" to true returns metrics from all nodes requested. auto raw = request.get_optional ("raw"); auto output_raw = raw.value_or (false); - node.telemetry.get_metrics_peers_async ([rpc_l, output_raw](telemetry_data_responses const & telemetry_responses_a) { + if (node.telemetry) + { + auto telemetry_responses = node.telemetry->get_metrics (); if (output_raw) { boost::property_tree::ptree metrics; - for (auto & telemetry_metrics : telemetry_responses_a.telemetry_datas) + for (auto & telemetry_metrics : telemetry_responses) { nano::jsonconfig config_l; auto err = telemetry_metrics.second.serialize_json (config_l); @@ -4028,18 +4037,18 @@ void nano::json_handler::telemetry () } else { - rpc_l->ec = nano::error_rpc::generic; + ec = nano::error_rpc::generic; } } - rpc_l->response_l.put_child ("metrics", metrics); + response_l.put_child ("metrics", metrics); } else { nano::jsonconfig config_l; std::vector telemetry_datas; - telemetry_datas.reserve (telemetry_responses_a.telemetry_datas.size ()); - std::transform (telemetry_responses_a.telemetry_datas.begin (), telemetry_responses_a.telemetry_datas.end (), std::back_inserter (telemetry_datas), [](auto const & endpoint_telemetry_data) { + telemetry_datas.reserve (telemetry_responses.size ()); + std::transform (telemetry_responses.begin (), telemetry_responses.end (), std::back_inserter (telemetry_datas), [](auto const & endpoint_telemetry_data) { return endpoint_telemetry_data.second; }); @@ -4049,16 +4058,16 @@ void nano::json_handler::telemetry () if (!err) { - rpc_l->response_l.insert (rpc_l->response_l.begin (), ptree.begin (), ptree.end ()); + response_l.insert (response_l.begin (), ptree.begin (), ptree.end ()); } else { - rpc_l->ec = nano::error_rpc::generic; + ec = nano::error_rpc::generic; } } + } - rpc_l->response_errors (); - }); + response_errors (); } } diff --git a/nano/node/network.cpp b/nano/node/network.cpp index 26a4c98b77..aba02256d9 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -489,7 +489,10 @@ class network_message_visitor : public nano::message_visitor node.logger.try_log (boost::str (boost::format ("Received telemetry_ack message from %1%") % channel->to_string ())); } node.stats.inc (nano::stat::type::message, nano::stat::detail::telemetry_ack, nano::stat::dir::in); - node.telemetry.add (message_a.data, channel->get_endpoint (), message_a.is_empty_payload ()); + if (node.telemetry) + { + node.telemetry->set (message_a.data, channel->get_endpoint (), message_a.is_empty_payload ()); + } } nano::node & node; std::shared_ptr channel; diff --git a/nano/node/node.cpp b/nano/node/node.cpp index c921d78fd8..f6381fd4db 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -126,7 +126,7 @@ gap_cache (*this), ledger (store, stats, flags_a.generate_cache), checker (config.signature_checker_threads), network (*this, config.peering_port), -telemetry (network, alarm, worker, flags.disable_ongoing_telemetry_requests), +telemetry (std::make_shared (network, alarm, worker, flags.disable_ongoing_telemetry_requests)), bootstrap_initiator (*this), bootstrap (config.peering_port, *this), application_path (application_path_a), @@ -153,6 +153,8 @@ startup_time (std::chrono::steady_clock::now ()) { if (!init_error ()) { + telemetry->start (); + if (config.websocket_config.enabled) { auto endpoint_l (nano::tcp_endpoint (boost::asio::ip::make_address_v6 (config.websocket_config.address), config.websocket_config.port)); @@ -586,7 +588,10 @@ std::unique_ptr nano::collect_container_info (no composite->add_component (collect_container_info (node.bootstrap_initiator, "bootstrap_initiator")); composite->add_component (collect_container_info (node.bootstrap, "bootstrap")); composite->add_component (collect_container_info (node.network, "network")); - composite->add_component (collect_container_info (node.telemetry, "telemetry")); + if (node.telemetry) + { + composite->add_component (collect_container_info (*node.telemetry, "telemetry")); + } composite->add_component (collect_container_info (node.observers, "observers")); composite->add_component (collect_container_info (node.wallets, "wallets")); composite->add_component (collect_container_info (node.vote_processor, "vote_processor")); @@ -699,7 +704,11 @@ void nano::node::stop () active.stop (); confirmation_height_processor.stop (); network.stop (); - telemetry.stop (); + if (telemetry) + { + telemetry->stop (); + telemetry = nullptr; + } if (websocket_server) { websocket_server->stop (); diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 7431619fef..c45c90fa1a 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -167,7 +167,7 @@ class node final : public std::enable_shared_from_this nano::ledger ledger; nano::signature_checker checker; nano::network network; - nano::telemetry telemetry; + std::shared_ptr telemetry; nano::bootstrap_initiator bootstrap_initiator; nano::bootstrap_listener bootstrap; boost::filesystem::path application_path; diff --git a/nano/node/telemetry.cpp b/nano/node/telemetry.cpp index a9e506610a..efbd39a413 100644 --- a/nano/node/telemetry.cpp +++ b/nano/node/telemetry.cpp @@ -17,172 +17,131 @@ nano::telemetry::telemetry (nano::network & network_a, nano::alarm & alarm_a, na network (network_a), alarm (alarm_a), worker (worker_a), -batch_request (std::make_shared (network, alarm, worker)) +disable_ongoing_requests (disable_ongoing_requests_a) { - // Before callbacks are called with the batch request, check if any of the single request data can be appended to give - batch_request->pre_callback_callback = [this](std::unordered_map & datas_a, std::mutex & mutex_a) { - nano::lock_guard guard (this->mutex); - for (auto & single_request : single_requests) - { - nano::lock_guard guard (single_request.second.impl->mutex); - if (!single_request.second.impl->cached_telemetry_data.empty ()) - { - nano::lock_guard batch_request_guard (mutex_a); - auto it = this->batch_request->cached_telemetry_data.find (single_request.first); - if (it != this->batch_request->cached_telemetry_data.cend () && single_request.second.last_updated > it->second.last_updated) - { - it->second = single_request.second.impl->cached_telemetry_data.begin ()->second; - } - else - { - datas_a.emplace (single_request.first, single_request.second.impl->cached_telemetry_data.begin ()->second.data); - } - } - } - - for (auto & pending : finished_single_requests) - { - nano::lock_guard batch_request_guard (mutex_a); - auto it = this->batch_request->cached_telemetry_data.find (pending.first); - if (it != this->batch_request->cached_telemetry_data.cend () && pending.second.last_updated > it->second.last_updated) - { - it->second = pending.second; - } - else - { - datas_a.emplace (pending.first, pending.second.data); - } - } - finished_single_requests.clear (); - }; +} - if (!disable_ongoing_requests_a) +void nano::telemetry::start () +{ + // Cannot be done in the constructor as a shared_from_this () call is made in ongoing_req_all_peers + if (!disable_ongoing_requests) { - ongoing_req_all_peers (); + ongoing_req_all_peers (std::chrono::milliseconds (0)); } } void nano::telemetry::stop () { - nano::lock_guard guard (mutex); - batch_request = nullptr; - single_requests.clear (); stopped = true; } -void nano::telemetry::add (nano::telemetry_data const & telemetry_data_a, nano::endpoint const & endpoint_a, bool is_empty_a) +void nano::telemetry::set (nano::telemetry_data const & telemetry_data_a, nano::endpoint const & endpoint_a, bool is_empty_a) { - nano::lock_guard guard (mutex); if (!stopped) { - batch_request->add (telemetry_data_a, endpoint_a, is_empty_a); - - for (auto & request : single_requests) + nano::lock_guard guard (mutex); + auto it = recent_or_initial_request_telemetry_data.find (endpoint_a); + if (it == recent_or_initial_request_telemetry_data.cend ()) { - request.second.impl->add (telemetry_data_a, endpoint_a, is_empty_a); + // Not requesting telemetry data from this peer so ignore it + return; } + + recent_or_initial_request_telemetry_data.modify (it, [&telemetry_data_a](nano::telemetry_info & telemetry_info_a) { + telemetry_info_a.data = telemetry_data_a; + telemetry_info_a.undergoing_request = false; + }); + + channel_processed (endpoint_a, is_empty_a); } } -void nano::telemetry::ongoing_req_all_peers () +bool nano::telemetry::within_cache_cutoff (telemetry_info const & telemetry_info) const +{ + auto is_within = (telemetry_info.last_request + nano::telemetry_cache_cutoffs::network_to_time (network_params.network)) >= std::chrono::steady_clock::now (); + return !telemetry_info.awaiting_first_response () && is_within; +} + +void nano::telemetry::ongoing_req_all_peers (std::chrono::milliseconds next_request_interval) { - alarm.add (std::chrono::steady_clock::now () + batch_request->cache_cutoff + batch_request->alarm_cutoff, [this, telemetry_impl_w = std::weak_ptr (batch_request)]() { - if (auto batch_telemetry_impl = telemetry_impl_w.lock ()) + // Check if any peers actually need requesting + alarm.add (std::chrono::steady_clock::now () + next_request_interval, [this_w = std::weak_ptr (shared_from_this ())]() { + if (auto this_l = this_w.lock ()) { - nano::lock_guard guard (this->mutex); - if (!this->stopped) + // Check if there are any peers which are in the peers list which haven't been request, or any which are below or equal to the cache cutoff time + if (!this_l->stopped) { - auto peers = this->network.list (std::numeric_limits::max (), network_params.protocol.telemetry_protocol_version_min, false); - // If exists in single_requests don't request because they will just be rejected by other peers until the next round - auto const & single_requests = this->single_requests; - peers.erase (std::remove_if (peers.begin (), peers.end (), [&single_requests](auto const & channel_a) { - return single_requests.count (channel_a->get_endpoint ()) > 0; - }), - peers.cend ()); - if (!peers.empty ()) + auto peers = this_l->network.list (std::numeric_limits::max (), this_l->network_params.protocol.telemetry_protocol_version_min, false); + { - batch_telemetry_impl->get_metrics_async (peers, [](nano::telemetry_data_responses const &) { - // Intentionally empty, just using to refresh the cache + std::unordered_set temp_peers; + std::transform (peers.begin (), peers.end (), std::inserter (temp_peers, temp_peers.end ()), [](auto const & channel_a) { + return channel_a->get_endpoint (); }); - } - this->ongoing_req_all_peers (); - } - } - }); -} - -void nano::telemetry::get_metrics_peers_async (std::function const & callback_a) -{ - auto peers = network.list (std::numeric_limits::max (), network_params.protocol.telemetry_protocol_version_min, false); - nano::lock_guard guard (mutex); - if (!stopped && !peers.empty ()) - { - // If exists in single_requests, don't request because they will just be rejected by other nodes, instead all it as additional values - peers.erase (std::remove_if (peers.begin (), peers.end (), [& single_requests = this->single_requests](auto const & channel_a) { - return single_requests.count (channel_a->get_endpoint ()) > 0; - }), - peers.cend ()); - - batch_request->get_metrics_async (peers, [callback_a](nano::telemetry_data_responses const & telemetry_data_responses) { - callback_a (telemetry_data_responses); - }); - } - else - { - const auto all_received = false; - callback_a (nano::telemetry_data_responses{ {}, all_received }); - } -} + // Cleanup any stale saved telemetry data for non-existent peers + nano::lock_guard guard (this_l->mutex); + for (auto it = this_l->recent_or_initial_request_telemetry_data.begin (); it != this_l->recent_or_initial_request_telemetry_data.end ();) + { + if (!it->undergoing_request && !this_l->within_cache_cutoff (*it) && temp_peers.count (it->endpoint) == 0) + { + it = this_l->recent_or_initial_request_telemetry_data.erase (it); + } + else + { + ++it; + } + } -nano::telemetry_data_responses nano::telemetry::get_metrics_peers () -{ - std::promise promise; - get_metrics_peers_async ([&promise](telemetry_data_responses const & telemetry_data_responses_a) { - promise.set_value (telemetry_data_responses_a); - }); + peers.erase (std::remove_if (peers.begin (), peers.end (), [&this_l](auto const & channel_a) { + // Remove from peers list if it exists and is within the cache cutoff + auto it = this_l->recent_or_initial_request_telemetry_data.find (channel_a->get_endpoint ()); + return it != this_l->recent_or_initial_request_telemetry_data.end () && this_l->within_cache_cutoff (*it); + }), + peers.end ()); + } - return promise.get_future ().get (); -} + // Request data from new peers, or ones which are out of date + for (auto const & peer : peers) + { + this_l->get_metrics_single_peer_async (peer, [](auto const &) { + // Intentionally empty, just using to refresh the cache + }); + } -// After a request is made to a single peer we want to remove it from the container after the peer has not been requested for a while (cache_cutoff). -void nano::telemetry::ongoing_single_request_cleanup (nano::endpoint const & endpoint_a, nano::telemetry::single_request_data const & single_request_data_a) -{ - alarm.add (std::chrono::steady_clock::now () + single_request_data_a.impl->cache_cutoff, [this, telemetry_impl_w = std::weak_ptr (single_request_data_a.impl), &single_request_data_a, &endpoint_a]() { - if (auto telemetry_impl = telemetry_impl_w.lock ()) - { - nano::lock_guard guard (this->mutex); - nano::lock_guard guard_telemetry_impl (telemetry_impl->mutex); - if (std::chrono::steady_clock::now () - telemetry_impl->cache_cutoff > single_request_data_a.last_updated && telemetry_impl->callbacks.empty ()) - { - // This will be picked up by the batch request next round - if (!telemetry_impl->cached_telemetry_data.empty ()) + nano::lock_guard guard (this_l->mutex); + long long next_round = std::chrono::duration_cast (nano::telemetry_cache_cutoffs::network_to_time (this_l->network_params.network)).count (); + if (!this_l->recent_or_initial_request_telemetry_data.empty ()) { - this->finished_single_requests[endpoint_a] = telemetry_impl->cached_telemetry_data.begin ()->second; + // Use the default request time unless a telemetry request cache expires sooner + auto const cache_cutoff = nano::telemetry_cache_cutoffs::network_to_time (this_l->network_params.network); + auto const last_request = this_l->recent_or_initial_request_telemetry_data.get ().begin ()->last_request; + if (std::chrono::steady_clock::now () > last_request + cache_cutoff) + { + next_round = std::min (next_round, std::chrono::duration_cast (std::chrono::steady_clock::now () - (last_request + cache_cutoff)).count ()); + } } - this->single_requests.erase (endpoint_a); - } - else - { - // Request is still active, so call again - this->ongoing_single_request_cleanup (endpoint_a, single_request_data_a); + + this_l->ongoing_req_all_peers (std::chrono::milliseconds (next_round)); } } }); } -void nano::telemetry::update_cleanup_data (nano::endpoint const & endpoint_a, nano::telemetry::single_request_data & single_request_data_a, bool is_new_a) +std::unordered_map nano::telemetry::get_metrics () { - if (is_new_a) - { - // Clean this request up when it isn't being used anymore - ongoing_single_request_cleanup (endpoint_a, single_request_data_a); - } - else - { - // Ensure that refreshed flag is reset so we don't delete it before processing - single_request_data_a.last_updated = std::chrono::steady_clock::now (); - } + std::unordered_map telemetry_data; + + nano::lock_guard guard (mutex); + auto range = boost::make_iterator_range (recent_or_initial_request_telemetry_data); + + // clang-format off + nano::transform_if (range.begin (), range.end (), std::inserter (telemetry_data, telemetry_data.end ()), + [this](auto const & telemetry_info) { return this->within_cache_cutoff (telemetry_info); }, + [](auto const & telemetry_info) { return std::pair{ telemetry_info.endpoint, telemetry_info.data }; }); + // clang-format on + + return telemetry_data; } void nano::telemetry::get_metrics_single_peer_async (std::shared_ptr const & channel_a, std::function const & callback_a) @@ -199,7 +158,6 @@ void nano::telemetry::get_metrics_single_peer_async (std::shared_ptr guard (mutex); if (!stopped) { if (channel_a && (channel_a->get_network_version () >= network_params.protocol.telemetry_protocol_version_min)) @@ -211,42 +169,39 @@ void nano::telemetry::get_metrics_single_peer_async (std::shared_ptr guard (mutex); + auto it = recent_or_initial_request_telemetry_data.find (channel_a->get_endpoint ()); + if (it != recent_or_initial_request_telemetry_data.cend () && within_cache_cutoff (*it)) { - nano::lock_guard guard (batch_request->mutex); - auto it = batch_request->cached_telemetry_data.find (channel_a->get_endpoint ()); - if (it != batch_request->cached_telemetry_data.cend ()) - { - add_callback_async (it->second.data, it->first); - return; - } + add_callback_async (it->data, it->endpoint); } - // Next check single requests which finished and are awaiting batched requests - auto it = finished_single_requests.find (channel_a->get_endpoint ()); - if (it != finished_single_requests.cend ()) + else { - add_callback_async (it->second.data, it->first); - return; - } - - auto pair = single_requests.emplace (channel_a->get_endpoint (), single_request_data{ std::make_shared (network, alarm, worker), std::chrono::steady_clock::now () }); - auto & single_request_data_it = pair.first; - update_cleanup_data (single_request_data_it->first, single_request_data_it->second, pair.second); - - single_request_data_it->second.impl->get_metrics_async ({ channel_a }, [callback_a, channel_a](telemetry_data_responses const & telemetry_data_responses_a) { - // There should only be 1 response, so if this hasn't been received then conclude it is an error. - auto const error = !telemetry_data_responses_a.all_received; - if (!error) + if (it != recent_or_initial_request_telemetry_data.cend () && it->undergoing_request) { - debug_assert (telemetry_data_responses_a.telemetry_datas.size () == 1); - auto it = telemetry_data_responses_a.telemetry_datas.begin (); - callback_a ({ it->second, it->first, error }); + // A request is currently undergoing, add the callback + debug_assert (callbacks.count (it->endpoint) > 0); + callbacks[it->endpoint].push_back (callback_a); } else { - callback_a ({ nano::telemetry_data{}, channel_a->get_endpoint (), error }); + if (it == recent_or_initial_request_telemetry_data.cend ()) + { + recent_or_initial_request_telemetry_data.emplace (channel_a->get_endpoint (), nano::telemetry_data (), std::chrono::steady_clock::now (), true); + it = recent_or_initial_request_telemetry_data.find (channel_a->get_endpoint ()); + } + else + { + recent_or_initial_request_telemetry_data.modify (it, [](nano::telemetry_info & telemetry_info_a) { + telemetry_info_a.undergoing_request = true; + telemetry_info_a.last_request = std::chrono::steady_clock::now (); + }); + } + callbacks[it->endpoint].push_back (callback_a); + fire_request_message (channel_a); } - }); + } } else { @@ -269,252 +224,143 @@ nano::telemetry_data_response nano::telemetry::get_metrics_single_peer (std::sha return promise.get_future ().get (); } -size_t nano::telemetry::telemetry_data_size () +void nano::telemetry::fire_request_message (std::shared_ptr const & channel) { - nano::lock_guard guard (mutex); - auto total = std::accumulate (single_requests.begin (), single_requests.end (), static_cast (0), [](size_t total, auto & single_request) { - return total += single_request.second.impl->telemetry_data_size (); - }); + // Fire off a telemetry request to all passed in channels + debug_assert (channel->get_network_version () >= network_params.protocol.telemetry_protocol_version_min); - if (batch_request) + uint64_t round_l; { - total += batch_request->telemetry_data_size (); + auto it = recent_or_initial_request_telemetry_data.find (channel->get_endpoint ()); + recent_or_initial_request_telemetry_data.modify (it, [](nano::telemetry_info & telemetry_info_a) { + ++telemetry_info_a.round; + }); + round_l = it->round; } - return total; -} - -size_t nano::telemetry::finished_single_requests_size () -{ - nano::lock_guard guard (mutex); - return finished_single_requests.size (); -} -nano::telemetry_impl::telemetry_impl (nano::network & network_a, nano::alarm & alarm_a, nano::worker & worker_a) : -alarm_cutoff (is_sanitizer_build || nano::running_within_valgrind () ? 6 : 3), -network (network_a), -alarm (alarm_a), -worker (worker_a) -{ -} + std::weak_ptr this_w (shared_from_this ()); + nano::telemetry_req message; + // clang-format off + channel->send (message, [this_w, endpoint = channel->get_endpoint ()](boost::system::error_code const & ec, size_t size_a) { + if (auto this_l = this_w.lock ()) + { + if (ec) + { + // Error sending the telemetry_req message + nano::lock_guard guard (this_l->mutex); + this_l->channel_processed (endpoint, true); + } + } + }, + nano::buffer_drop_policy::no_socket_drop); + // clang-format on -void nano::telemetry_impl::flush_callbacks_async () -{ - // Post to worker so that it's truly async and not on the calling thread (same problem as std::async otherwise) - worker.push_task ([this_w = std::weak_ptr (shared_from_this ())]() { + // If no response is seen after a certain period of time remove it + alarm.add (std::chrono::steady_clock::now () + response_time_cutoff, [round_l, this_w, endpoint = channel->get_endpoint ()]() { if (auto this_l = this_w.lock ()) { - nano::unique_lock lk (this_l->mutex); - // Invoke all callbacks, it's possible that during the mutex unlock other callbacks were added, - // so check again and invoke those too - this_l->invoking = true; - while (!this_l->callbacks.empty ()) + nano::lock_guard guard (this_l->mutex); + auto it = this_l->recent_or_initial_request_telemetry_data.find (endpoint); + if (it != this_l->recent_or_initial_request_telemetry_data.cend () && it->undergoing_request && round_l == it->round) { - lk.unlock (); - this_l->invoke_callbacks (); - lk.lock (); + this_l->channel_processed (endpoint, true); } - this_l->invoking = false; } }); } -void nano::telemetry_impl::get_metrics_async (std::deque> const & channels_a, std::function const & callback_a) +void nano::telemetry::channel_processed (nano::endpoint const & endpoint_a, bool error_a) { + if (recent_or_initial_request_telemetry_data.count (endpoint_a) > 0) { - nano::unique_lock lk (mutex); - callbacks.push_back (callback_a); - if (callbacks.size () > 1 || invoking) + if (error_a) { - // This means we already have at least one pending result already, so it will handle calls this callback when it completes - return; + recent_or_initial_request_telemetry_data.erase (endpoint_a); } - - // Check if we can just return cached results - if (channels_a.empty () || std::chrono::steady_clock::now () <= (last_time + cache_cutoff)) - { - flush_callbacks_async (); - return; - } - - failed.clear (); - debug_assert (required_responses.empty ()); - std::transform (channels_a.begin (), channels_a.end (), std::inserter (required_responses, required_responses.end ()), [](auto const & channel) { - return channel->get_endpoint (); - }); + flush_callbacks_async (endpoint_a, error_a); } - - fire_request_messages (channels_a); } -void nano::telemetry_impl::add (nano::telemetry_data const & telemetry_data_a, nano::endpoint const & endpoint_a, bool is_empty_a) +void nano::telemetry::flush_callbacks_async (nano::endpoint const & endpoint_a, bool error_a) { - nano::unique_lock lk (mutex); - if (required_responses.find (endpoint_a) == required_responses.cend ()) - { - // Not requesting telemetry data from this channel so ignore it - return; - } - - if (!is_empty_a) - { - current_telemetry_data_responses[endpoint_a] = { telemetry_data_a, std::chrono::steady_clock::now () }; - } - channel_processed (lk, endpoint_a); + // Post to worker so that it's truly async and not on the calling thread (same problem as std::async otherwise) + worker.push_task ([endpoint_a, error_a, this_w = std::weak_ptr (shared_from_this ())]() { + if (auto this_l = this_w.lock ()) + { + nano::unique_lock lk (this_l->mutex); + while (!this_l->callbacks[endpoint_a].empty ()) + { + lk.unlock (); + this_l->invoke_callbacks (endpoint_a, error_a); + lk.lock (); + } + } + }); } -void nano::telemetry_impl::invoke_callbacks () +void nano::telemetry::invoke_callbacks (nano::endpoint const & endpoint_a, bool error_a) { - decltype (callbacks) callbacks_l; - bool all_received; - std::unordered_map cached_responses_l; + std::vector> callbacks_l; + telemetry_data_response response_data{ nano::telemetry_data (), endpoint_a, error_a }; { - // Copy callbacks so that they can be called outside of holding the lock + // Copy data so that it can be used outside of holding the lock nano::lock_guard guard (mutex); - callbacks_l = callbacks; - cached_responses_l.reserve (cached_telemetry_data.size ()); - std::transform (cached_telemetry_data.begin (), cached_telemetry_data.end (), std::inserter (cached_responses_l, cached_responses_l.end ()), [](auto const & endpoint_telemetry_data) { - return std::pair{ endpoint_telemetry_data.first, endpoint_telemetry_data.second.data }; - }); - current_telemetry_data_responses.clear (); - callbacks.clear (); - all_received = failed.empty (); + callbacks_l = callbacks[endpoint_a]; + auto it = recent_or_initial_request_telemetry_data.find (endpoint_a); + if (it != recent_or_initial_request_telemetry_data.end ()) + { + response_data.telemetry_data = it->data; + } + callbacks.erase (endpoint_a); } - if (pre_callback_callback) - { - pre_callback_callback (cached_responses_l, mutex); - } // Need to account for nodes which disable telemetry data in responses - bool all_received_l = !cached_responses_l.empty () && all_received; for (auto & callback : callbacks_l) { - callback ({ cached_responses_l, all_received_l }); - } -} - -void nano::telemetry_impl::channel_processed (nano::unique_lock & lk_a, nano::endpoint const & endpoint_a) -{ - debug_assert (lk_a.owns_lock ()); - auto num_removed = required_responses.erase (endpoint_a); - if (num_removed > 0 && required_responses.empty ()) - { - debug_assert (lk_a.owns_lock ()); - cached_telemetry_data = current_telemetry_data_responses; - - last_time = std::chrono::steady_clock::now (); - flush_callbacks_async (); + callback (response_data); } } -void nano::telemetry_impl::fire_request_messages (std::deque> const & channels) -{ - uint64_t round_l; - { - nano::lock_guard guard (mutex); - ++round; - round_l = round; - } - - // Fire off a telemetry request to all passed in channels - nano::telemetry_req message; - for (auto & channel : channels) - { - debug_assert (channel->get_network_version () >= network_params.protocol.telemetry_protocol_version_min); - - std::weak_ptr this_w (shared_from_this ()); - // clang-format off - channel->send (message, [this_w, endpoint = channel->get_endpoint ()](boost::system::error_code const & ec, size_t size_a) { - if (auto this_l = this_w.lock ()) - { - if (ec) - { - // Error sending the telemetry_req message - nano::unique_lock lk (this_l->mutex); - this_l->failed.push_back (endpoint); - this_l->channel_processed (lk, endpoint); - } - } - }, - nano::buffer_drop_policy::no_socket_drop); - // clang-format on - - // If no response is seen after a certain period of time, remove it from the list of expected responses. However, only if it is part of the same round. - alarm.add (std::chrono::steady_clock::now () + alarm_cutoff, [this_w, endpoint = channel->get_endpoint (), round_l]() { - if (auto this_l = this_w.lock ()) - { - nano::unique_lock lk (this_l->mutex); - if (this_l->round == round_l && this_l->required_responses.find (endpoint) != this_l->required_responses.cend ()) - { - this_l->failed.push_back (endpoint); - this_l->channel_processed (lk, endpoint); - } - } - }); - } -} - -size_t nano::telemetry_impl::telemetry_data_size () +size_t nano::telemetry::telemetry_data_size () { nano::lock_guard guard (mutex); - return current_telemetry_data_responses.size (); + return recent_or_initial_request_telemetry_data.size (); } -bool nano::telemetry_data_time_pair::operator== (telemetry_data_time_pair const & telemetry_data_time_pair_a) const +nano::telemetry_info::telemetry_info (nano::endpoint const & endpoint_a, nano::telemetry_data const & data_a, std::chrono::steady_clock::time_point last_request_a, bool undergoing_request_a) : +endpoint (endpoint_a), +data (data_a), +last_request (last_request_a), +undergoing_request (undergoing_request_a) { - return data == telemetry_data_time_pair_a.data && last_updated == telemetry_data_time_pair_a.last_updated; } -bool nano::telemetry_data_time_pair::operator!= (telemetry_data_time_pair const & telemetry_data_time_pair_a) const +bool nano::telemetry_info::awaiting_first_response () const { - return !(*this == telemetry_data_time_pair_a); + return data == nano::telemetry_data (); } std::unique_ptr nano::collect_container_info (telemetry & telemetry, const std::string & name) { - size_t single_requests_count; - { - nano::lock_guard guard (telemetry.mutex); - single_requests_count = telemetry.single_requests.size (); - } - auto composite = std::make_unique (name); - if (telemetry.batch_request) + size_t callbacks_count; { - composite->add_component (collect_container_info (*telemetry.batch_request, "batch_request")); + nano::lock_guard guard (telemetry.mutex); + std::unordered_map>> callbacks; + callbacks_count = std::accumulate (callbacks.begin (), callbacks.end (), static_cast (0), [](auto total, auto const & callback_a) { + return total += callback_a.second.size (); + }); } - composite->add_component (std::make_unique (container_info{ "single_requests", single_requests_count, sizeof (decltype (telemetry.single_requests)::value_type) })); - composite->add_component (std::make_unique (container_info{ "finished_single_requests", telemetry.finished_single_requests_size (), sizeof (decltype (telemetry.finished_single_requests)::value_type) })); - return composite; -} -std::unique_ptr nano::collect_container_info (telemetry_impl & telemetry_impl, const std::string & name) -{ - size_t callback_count; - size_t all_telemetry_data_count; - size_t cached_telemetry_data_count; - size_t required_responses_count; - { - nano::lock_guard guard (telemetry_impl.mutex); - callback_count = telemetry_impl.callbacks.size (); - all_telemetry_data_count = telemetry_impl.current_telemetry_data_responses.size (); - cached_telemetry_data_count = telemetry_impl.cached_telemetry_data.size (); - required_responses_count = telemetry_impl.required_responses.size (); - } + composite->add_component (std::make_unique (container_info{ "recent_or_initial_request_telemetry_data", telemetry.telemetry_data_size (), sizeof (decltype (telemetry.recent_or_initial_request_telemetry_data)::value_type) })); + composite->add_component (std::make_unique (container_info{ "callbacks", callbacks_count, sizeof (decltype (telemetry.callbacks)::value_type::second_type) })); - auto composite = std::make_unique (name); - composite->add_component (std::make_unique (container_info{ "callbacks", callback_count, sizeof (decltype (telemetry_impl.callbacks)::value_type) })); - composite->add_component (std::make_unique (container_info{ "current_telemetry_data_responses", all_telemetry_data_count, sizeof (decltype (telemetry_impl.current_telemetry_data_responses)::value_type) })); - composite->add_component (std::make_unique (container_info{ "cached_telemetry_data", cached_telemetry_data_count, sizeof (decltype (telemetry_impl.cached_telemetry_data)::value_type) })); - composite->add_component (std::make_unique (container_info{ "required_responses", required_responses_count, sizeof (decltype (telemetry_impl.required_responses)::value_type) })); return composite; } nano::telemetry_data nano::consolidate_telemetry_data (std::vector const & telemetry_datas) { - std::vector telemetry_data_time_pairs; - telemetry_data_time_pairs.reserve (telemetry_datas.size ()); - if (telemetry_datas.empty ()) { return {}; diff --git a/nano/node/telemetry.hpp b/nano/node/telemetry.hpp index 4406e7cd24..1f704af9b3 100644 --- a/nano/node/telemetry.hpp +++ b/nano/node/telemetry.hpp @@ -3,30 +3,26 @@ #include #include +#include +#include +#include +#include + #include #include -#include + +namespace mi = boost::multi_index; namespace nano { class network; class alarm; class worker; -class telemetry; namespace transport { class channel; } -class telemetry_data_time_pair -{ -public: - nano::telemetry_data data; - std::chrono::steady_clock::time_point last_updated; - bool operator== (telemetry_data_time_pair const &) const; - bool operator!= (telemetry_data_time_pair const &) const; -}; - /* * Holds a response from a telemetry request */ @@ -38,106 +34,48 @@ class telemetry_data_response bool error{ true }; }; -/* - * Holds many responses from telemetry requests - */ -class telemetry_data_responses -{ -public: - std::unordered_map telemetry_datas; - bool all_received{ false }; -}; - -/* - * This class requests node telemetry metrics and invokes any callbacks - * which have been aggregated. Further calls to get_metrics_async may return cached telemetry metrics - * if they are within cache_cutoff time from the latest request. - */ -class telemetry_impl : public std::enable_shared_from_this +class telemetry_info final { public: - telemetry_impl (nano::network & network_a, nano::alarm & alarm_a, nano::worker & worker_a); + telemetry_info () = default; + telemetry_info (nano::endpoint const & endpoint, nano::telemetry_data const & data, std::chrono::steady_clock::time_point last_request, bool undergoing_request); + bool awaiting_first_response () const; -private: - // Class only available to the telemetry class - void get_metrics_async (std::deque> const & channels_a, std::function const & callback_a); - void add (nano::telemetry_data const & telemetry_data_a, nano::endpoint const & endpoint_a, bool is_empty_a); - size_t telemetry_data_size (); - - nano::network_params network_params; - // Anything older than this requires requesting metrics from other nodes. - std::chrono::seconds const cache_cutoff{ nano::telemetry_cache_cutoffs::network_to_time (network_params.network) }; - std::chrono::seconds const alarm_cutoff; - - // All data in this chunk is protected by this mutex - std::mutex mutex; - std::vector> callbacks; - std::chrono::steady_clock::time_point last_time = std::chrono::steady_clock::now () - cache_cutoff; - /* The responses received during this request round */ - std::unordered_map current_telemetry_data_responses; - /* The metrics for the last request round */ - std::unordered_map cached_telemetry_data; - std::unordered_set required_responses; + nano::endpoint endpoint; + nano::telemetry_data data; + std::chrono::steady_clock::time_point last_request; + bool undergoing_request{ false }; uint64_t round{ 0 }; - /* Currently executing callbacks */ - bool invoking{ false }; - std::vector failed; - - nano::network & network; - nano::alarm & alarm; - nano::worker & worker; - - std::function & data_a, std::mutex &)> pre_callback_callback; - - void invoke_callbacks (); - void channel_processed (nano::unique_lock & lk_a, nano::endpoint const & endpoint_a); - void flush_callbacks_async (); - void fire_request_messages (std::deque> const & channels); - - friend std::unique_ptr collect_container_info (telemetry_impl &, const std::string &); - friend nano::telemetry; - friend class node_telemetry_single_request_Test; - friend class node_telemetry_basic_Test; - friend class node_telemetry_ongoing_requests_Test; }; -std::unique_ptr collect_container_info (telemetry_impl & telemetry_impl, const std::string & name); - /* - * This class has 2 main operations: - * Request metrics from specific single peers (single_requests) - * - If this peer is in the batched request, it will use the value from that, otherwise send a telemetry_req message (non-droppable) - * Request metrics from all peers (batched_request) - * - This is polled every minute. - * - If a single request is currently underway, do not request because other peers will just reject if within a hotzone time - * - This will be proactively added when callbacks are called inside pre_callback_callback + * This class requests node telemetry metrics from peers and invokes any callbacks which have been aggregated. + * All calls to get_metrics return cached data, it does not do any requests, these are periodically done in + * ongoing_req_all_peers. This can be disabled with the disable_ongoing_telemetry_requests node flag. + * Calls to get_metrics_single_peer_async will wait until a response is made if it is not within the cache + * cut off. */ -class telemetry +class telemetry : public std::enable_shared_from_this { public: - telemetry (nano::network & network_a, nano::alarm & alarm_a, nano::worker & worker_a, bool disable_ongoing_requests_a); - - /* - * Add telemetry metrics received from this endpoint. - * Should this be unsolicited, it will not be added. - * Some peers may have disabled responding with telemetry data, need to account for this - */ - void add (nano::telemetry_data const & telemetry_data_a, nano::endpoint const & endpoint_a, bool is_empty_a); + telemetry (nano::network &, nano::alarm &, nano::worker &, bool); + void start (); + void stop (); /* - * Collects metrics from all known peers and invokes the callback when complete. + * Set the telemetry data associated with this peer */ - void get_metrics_peers_async (std::function const & callback_a); + void set (nano::telemetry_data const &, nano::endpoint const &, bool); /* - * A blocking version of get_metrics_peers_async (). + * This returns what ever is in the cache */ - telemetry_data_responses get_metrics_peers (); + std::unordered_map get_metrics (); /* * This makes a telemetry request to the specific channel */ - void get_metrics_single_peer_async (std::shared_ptr const &, std::function const & callback_a); + void get_metrics_single_peer_async (std::shared_ptr const &, std::function const &); /* * A blocking version of get_metrics_single_peer_async @@ -149,51 +87,52 @@ class telemetry */ size_t telemetry_data_size (); - /* - * Return the number of finished_single_requests elements - */ - size_t finished_single_requests_size (); - - /* - * Stop the telemetry processor - */ - void stop (); - private: + class tag_endpoint + { + }; + class tag_last_updated + { + }; + nano::network & network; nano::alarm & alarm; nano::worker & worker; + std::atomic stopped{ false }; nano::network_params network_params; - - class single_request_data - { - public: - std::shared_ptr impl; - std::chrono::steady_clock::time_point last_updated{ std::chrono::steady_clock::now () }; - }; + bool disable_ongoing_requests; std::mutex mutex; - /* Requests telemetry data from a random selection of peers */ - std::shared_ptr batch_request; - /* Any requests to specific individual peers is maintained here */ - std::unordered_map single_requests; - /* This holds data from single_requests after the cache is removed */ - std::unordered_map finished_single_requests; - bool stopped{ false }; - - void update_cleanup_data (nano::endpoint const & endpoint_a, nano::telemetry::single_request_data & single_request_data_a, bool is_new_a); - void ongoing_single_request_cleanup (nano::endpoint const & endpoint_a, nano::telemetry::single_request_data const & single_request_data_a); - void ongoing_req_all_peers (); - - friend class node_telemetry_multiple_single_request_clearing_Test; - friend class node_telemetry_ongoing_requests_Test; - friend std::unique_ptr collect_container_info (telemetry &, const std::string &); + // clang-format off + // This holds the last telemetry data received from peers or can be a placeholder awaiting the first response (check with awaiting_first_response ()) + boost::multi_index_container, + mi::member>, + mi::ordered_non_unique, + mi::member>>> recent_or_initial_request_telemetry_data; + // clang-format on + + // Anything older than this requires requesting metrics from other nodes. + std::chrono::seconds const cache_cutoff{ nano::telemetry_cache_cutoffs::network_to_time (network_params.network) }; + std::chrono::seconds const response_time_cutoff{ is_sanitizer_build || nano::running_within_valgrind () ? 6 : 3 }; + + std::unordered_map>> callbacks; + + void ongoing_req_all_peers (std::chrono::milliseconds); + + void fire_request_message (std::shared_ptr const & channel); + void channel_processed (nano::endpoint const &, bool); + void flush_callbacks_async (nano::endpoint const &, bool); + void invoke_callbacks (nano::endpoint const &, bool); + + bool within_cache_cutoff (nano::telemetry_info const &) const; + friend std::unique_ptr collect_container_info (telemetry & telemetry, const std::string & name); }; std::unique_ptr collect_container_info (telemetry & telemetry, const std::string & name); nano::telemetry_data consolidate_telemetry_data (std::vector const & telemetry_data); -nano::telemetry_data_time_pair consolidate_telemetry_data_time_pairs (std::vector const & telemetry_data_time_pairs); nano::telemetry_data local_telemetry_data (nano::ledger_cache const &, nano::network &, uint64_t, nano::network_params const &, std::chrono::steady_clock::time_point); } \ No newline at end of file diff --git a/nano/node/transport/tcp.hpp b/nano/node/transport/tcp.hpp index 29525bb72b..b71dda5a0d 100644 --- a/nano/node/transport/tcp.hpp +++ b/nano/node/transport/tcp.hpp @@ -78,7 +78,7 @@ namespace transport class tcp_channels final { friend class nano::transport::channel_tcp; - friend class node_telemetry_simultaneous_single_and_all_requests_Test; + friend class node_telemetry_simultaneous_requests_Test; public: tcp_channels (nano::node &); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 9c8919733a..4431bae883 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -7990,12 +7990,25 @@ TEST (rpc, node_telemetry_all) { ASSERT_NO_ERROR (system.poll ()); - auto transaction = system.nodes.back ()->store.tx_begin_read (); - peers_stored = system.nodes.back ()->store.peer_count (transaction) != 0; + auto transaction = node1.store.tx_begin_read (); + peers_stored = node1.store.peer_count (transaction) != 0; } - boost::property_tree::ptree request; + // First need to set up the cached data + std::atomic done{ false }; auto node = system.nodes.front (); + node1.telemetry->get_metrics_single_peer_async (node1.network.find_channel (node->network.endpoint ()), [&done](nano::telemetry_data_response const & telemetry_data_response_a) { + ASSERT_FALSE (telemetry_data_response_a.error); + done = true; + }); + + system.deadline_set (10s); + while (!done) + { + ASSERT_NO_ERROR (system.poll ()); + } + + boost::property_tree::ptree request; request.put ("action", "node_telemetry"); { test_response response (request, rpc.config.port, system.io_ctx); diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index 08209b527f..253cdd0ea6 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -857,10 +857,8 @@ class data class shared_data { public: + nano::util::counted_completion write_completion{ 0 }; std::atomic done{ false }; - std::atomic count{ 0 }; - std::promise promise; - std::shared_future shared_future{ promise.get_future () }; }; template @@ -880,16 +878,10 @@ void callback_process (shared_data & shared_data_a, data & data, T & all_node_da data.awaiting_cache = true; data.orig_time = last_updated; } - if (--shared_data_a.count == 0 && std::all_of (all_node_data_a.begin (), all_node_data_a.end (), [](auto const & data) { return !data.keep_requesting_metrics; })) - { - shared_data_a.done = true; - shared_data_a.promise.set_value (); - } + shared_data_a.write_completion.increment (); }; } -namespace nano -{ TEST (node_telemetry, ongoing_requests) { nano::system system (2); @@ -899,20 +891,20 @@ TEST (node_telemetry, ongoing_requests) wait_peer_connections (system); - ASSERT_EQ (0, node_client->telemetry.telemetry_data_size ()); - ASSERT_EQ (0, node_server->telemetry.telemetry_data_size ()); + ASSERT_EQ (0, node_client->telemetry->telemetry_data_size ()); + ASSERT_EQ (0, node_server->telemetry->telemetry_data_size ()); ASSERT_EQ (0, node_client->stats.count (nano::stat::type::bootstrap, nano::stat::detail::telemetry_ack, nano::stat::dir::in)); ASSERT_EQ (0, node_client->stats.count (nano::stat::type::bootstrap, nano::stat::detail::telemetry_req, nano::stat::dir::out)); system.deadline_set (20s); - while (node_client->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_ack, nano::stat::dir::in) != 1 || node_client->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::out) != 1) + while (node_client->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_ack, nano::stat::dir::in) != 1 || node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_ack, nano::stat::dir::in) != 1) { ASSERT_NO_ERROR (system.poll ()); } // Wait till the next ongoing will be called, and add a 1s buffer for the actual processing auto time = std::chrono::steady_clock::now (); - while (std::chrono::steady_clock::now () < (time + nano::telemetry_cache_cutoffs::test + node_client->telemetry.batch_request->alarm_cutoff + 1s)) + while (std::chrono::steady_clock::now () < (time + nano::telemetry_cache_cutoffs::test + 1s)) { ASSERT_NO_ERROR (system.poll ()); } @@ -924,70 +916,12 @@ TEST (node_telemetry, ongoing_requests) ASSERT_EQ (2, node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::in)); ASSERT_EQ (2, node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::out)); } -} - -TEST (node_telemetry, simultaneous_all_requests) -{ - const auto num_nodes = 4; - nano::system system (num_nodes); - - // Wait until peers are stored as they are done in the background - wait_peer_connections (system); - - std::vector threads; - const auto num_threads = 4; - - std::array all_data{}; - for (auto i = 0; i < num_nodes; ++i) - { - all_data[i].node = system.nodes[i]; - } - - shared_data shared_data; - - // Create a few threads where each node sends out telemetry request messages to all other nodes continuously, until the cache it reached and subsequently expired. - // The test waits until all telemetry_ack messages have been received. - for (int i = 0; i < num_threads; ++i) - { - threads.emplace_back ([&all_data, &shared_data]() { - while (std::any_of (all_data.cbegin (), all_data.cend (), [](auto const & data) { return data.keep_requesting_metrics.load (); })) - { - for (auto & data : all_data) - { - // Keep calling requesting telemetry metrics until the cache has been saved and then become outdated (after a certain period of time) for each node - if (data.keep_requesting_metrics) - { - ++shared_data.count; - data.node->telemetry.get_metrics_peers_async ([&shared_data, &data, &all_data](nano::telemetry_data_responses const & responses_a) { - callback_process (shared_data, data, all_data, *responses_a.telemetry_datas.begin ()->second.timestamp); - }); - } - std::this_thread::sleep_for (1ms); - } - } - - shared_data.shared_future.wait (); - ASSERT_EQ (shared_data.count, 0); - }); - } - - system.deadline_set (20s); - while (!shared_data.done) - { - ASSERT_NO_ERROR (system.poll ()); - } - - for (auto & thread : threads) - { - thread.join (); - } -} namespace nano { namespace transport { - TEST (node_telemetry, simultaneous_single_and_all_requests) + TEST (node_telemetry, simultaneous_requests) { const auto num_nodes = 4; nano::system system (num_nodes); @@ -997,66 +931,52 @@ namespace transport std::vector threads; const auto num_threads = 4; - std::array node_data_single{}; - std::array node_data_all{}; + std::array node_data{}; for (auto i = 0; i < num_nodes; ++i) { - node_data_single[i].node = system.nodes[i]; - node_data_all[i].node = system.nodes[i]; + node_data[i].node = system.nodes[i]; } - shared_data shared_data_single; - shared_data shared_data_all; + shared_data shared_data; // Create a few threads where each node sends out telemetry request messages to all other nodes continuously, until the cache it reached and subsequently expired. // The test waits until all telemetry_ack messages have been received. for (int i = 0; i < num_threads; ++i) { - threads.emplace_back ([&node_data_single, &node_data_all, &shared_data_single, &shared_data_all]() { - auto func = [](auto & all_node_data_a, shared_data & shared_data_a, bool single_a) { - while (std::any_of (all_node_data_a.cbegin (), all_node_data_a.cend (), [](auto const & data) { return data.keep_requesting_metrics.load (); })) + threads.emplace_back ([&node_data, &shared_data]() { + while (std::any_of (node_data.cbegin (), node_data.cend (), [](auto const & data) { return data.keep_requesting_metrics.load (); })) + { + for (auto & data : node_data) { - for (auto & data : all_node_data_a) + // Keep calling get_metrics_async until the cache has been saved and then become outdated (after a certain period of time) for each node + if (data.keep_requesting_metrics) { - // Keep calling get_metrics_async until the cache has been saved and then become outdated (after a certain period of time) for each node - if (data.keep_requesting_metrics) - { - ++shared_data_a.count; - - if (single_a) - { - // Pick first peer to be consistent - auto peer = data.node->network.tcp_channels.channels[0].channel; - data.node->telemetry.get_metrics_single_peer_async (peer, [&shared_data_a, &data, &all_node_data_a](nano::telemetry_data_response const & telemetry_data_response_a) { - callback_process (shared_data_a, data, all_node_data_a, *telemetry_data_response_a.telemetry_data.timestamp); - }); - } - else - { - data.node->telemetry.get_metrics_peers_async ([&shared_data_a, &data, &all_node_data_a](nano::telemetry_data_responses const & telemetry_data_responses_a) { - callback_process (shared_data_a, data, all_node_data_a, *telemetry_data_responses_a.telemetry_datas.begin ()->second.timestamp); - }); - } - } - std::this_thread::sleep_for (1ms); + shared_data.write_completion.increment_required_count (); + + // Pick first peer to be consistent + auto peer = data.node->network.tcp_channels.channels[0].channel; + data.node->telemetry->get_metrics_single_peer_async (peer, [&shared_data, &data, &node_data](nano::telemetry_data_response const & telemetry_data_response_a) { + ASSERT_FALSE (telemetry_data_response_a.error); + callback_process (shared_data, data, node_data, *telemetry_data_response_a.telemetry_data.timestamp); + }); } + std::this_thread::sleep_for (1ms); } + } - shared_data_a.shared_future.wait (); - ASSERT_EQ (shared_data_a.count, 0); - }; - - func (node_data_single, shared_data_single, true); - func (node_data_all, shared_data_all, false); + shared_data.write_completion.await_count_for (20s); + shared_data.done = true; }); } system.deadline_set (30s); - while (!shared_data_all.done || !shared_data_single.done) + while (!shared_data.done) { ASSERT_NO_ERROR (system.poll ()); } + ASSERT_TRUE (std::all_of (node_data.begin (), node_data.end (), [](auto const & data) { return !data.keep_requesting_metrics; })); + for (auto & thread : threads) { thread.join (); From 90ea27ef68f019574b5a78444a62aceca8afe887 Mon Sep 17 00:00:00 2001 From: Sergey Kroshnin Date: Fri, 28 Feb 2020 19:35:58 +0300 Subject: [PATCH 05/43] Launch flag --allow_bootstrap_peers_duplicates (#2606) Allowing multiple connections to same bootstrap server (useful in tests or for fastest beta nodes sync) --- nano/node/bootstrap/bootstrap_connections.cpp | 5 ++--- nano/node/cli.cpp | 2 ++ nano/node/nodeconfig.hpp | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/nano/node/bootstrap/bootstrap_connections.cpp b/nano/node/bootstrap/bootstrap_connections.cpp index f1ab2200f2..25a1129f30 100644 --- a/nano/node/bootstrap/bootstrap_connections.cpp +++ b/nano/node/bootstrap/bootstrap_connections.cpp @@ -273,7 +273,6 @@ void nano::bootstrap_connections::populate_connections (bool repeat) if (node.config.logging.bulk_pull_logging ()) { - nano::unique_lock lock (mutex); node.logger.try_log (boost::str (boost::format ("Bulk pull connections: %1%, rate: %2% blocks/sec, bootstrap attempts %3%, remaining pulls: %4%") % connections_count.load () % (int)rate_sum % attempts_count % num_pulls)); } @@ -285,11 +284,11 @@ void nano::bootstrap_connections::populate_connections (bool repeat) for (auto i = 0u; i < delta; i++) { auto endpoint (node.network.bootstrap_peer (true)); - if (endpoint != nano::tcp_endpoint (boost::asio::ip::address_v6::any (), 0) && endpoints.find (endpoint) == endpoints.end () && !node.bootstrap_initiator.excluded_peers.check (endpoint)) + if (endpoint != nano::tcp_endpoint (boost::asio::ip::address_v6::any (), 0) && (node.flags.allow_bootstrap_peers_duplicates || endpoints.find (endpoint) == endpoints.end ()) && !node.bootstrap_initiator.excluded_peers.check (endpoint)) { connect_client (endpoint); - nano::lock_guard lock (mutex); endpoints.insert (endpoint); + nano::lock_guard lock (mutex); new_connections_empty = false; } else if (connections_count == 0) diff --git a/nano/node/cli.cpp b/nano/node/cli.cpp index 35080e6aa7..45f4823af6 100644 --- a/nano/node/cli.cpp +++ b/nano/node/cli.cpp @@ -96,6 +96,7 @@ void nano::add_node_flag_options (boost::program_options::options_description & ("disable_unchecked_drop", "Disables drop of unchecked table at startup") ("disable_providing_telemetry_metrics", "Disable using any node information in the telemetry_ack messages.") ("disable_block_processor_unchecked_deletion", "Disable deletion of unchecked blocks after processing") + ("allow_bootstrap_peers_duplicates", "Allow multiple connections to same peer in bootstrap attempts") ("fast_bootstrap", "Increase bootstrap speed for high end nodes with higher limits") ("batch_size", boost::program_options::value(), "Increase sideband batch size, default 512") ("block_processor_batch_size", boost::program_options::value(), "Increase block processor transaction batch write size, default 0 (limited by config block_processor_batch_max_time), 256k for fast_bootstrap") @@ -134,6 +135,7 @@ std::error_code nano::update_flags (nano::node_flags & flags_a, boost::program_o flags_a.disable_unchecked_cleanup = (vm.count ("disable_unchecked_cleanup") > 0); flags_a.disable_unchecked_drop = (vm.count ("disable_unchecked_drop") > 0); flags_a.disable_block_processor_unchecked_deletion = (vm.count ("disable_block_processor_unchecked_deletion") > 0); + flags_a.allow_bootstrap_peers_duplicates = (vm.count ("allow_bootstrap_peers_duplicates") > 0); flags_a.fast_bootstrap = (vm.count ("fast_bootstrap") > 0); if (flags_a.fast_bootstrap) { diff --git a/nano/node/nodeconfig.hpp b/nano/node/nodeconfig.hpp index 74a2fcab29..b982e67b0b 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -129,6 +129,7 @@ class node_flags final bool disable_block_processor_unchecked_deletion{ false }; bool disable_block_processor_republishing{ false }; bool disable_ongoing_telemetry_requests{ false }; + bool allow_bootstrap_peers_duplicates{ false }; bool fast_bootstrap{ false }; bool read_only{ false }; nano::confirmation_height_mode confirmation_height_processor_mode{ nano::confirmation_height_mode::automatic }; From ac5f398de06b7d23054e1e8b57850a73e1c68941 Mon Sep 17 00:00:00 2001 From: cryptocode Date: Fri, 28 Feb 2020 20:18:31 +0100 Subject: [PATCH 06/43] Fix intermittent send_node_id_handshake unit test failures (#2612) --- nano/core_test/network.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index 47f3c170ad..5af1537761 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -92,15 +92,21 @@ TEST (network, send_node_id_handshake) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_EQ (0, node0->network.size ()); - ASSERT_EQ (1, node1->network.size ()); + system.deadline_set (10s); + while (node0->network.size () != 0 && node1->network.size () != 1) + { + ASSERT_NO_ERROR (system.poll ()); + } system.deadline_set (10s); while (node0->stats.count (nano::stat::type::message, nano::stat::detail::node_id_handshake, nano::stat::dir::in) < initial + 2) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_EQ (1, node0->network.size ()); - ASSERT_EQ (1, node1->network.size ()); + system.deadline_set (10s); + while (node0->network.size () != 1 && node1->network.size () != 1) + { + ASSERT_NO_ERROR (system.poll ()); + } auto list1 (node0->network.list (1)); ASSERT_EQ (node1->network.endpoint (), list1[0]->get_endpoint ()); auto list2 (node1->network.list (1)); From aaf1dbe7aa83d86b3d66a549bdb6a5e334942c93 Mon Sep 17 00:00:00 2001 From: cryptocode Date: Sun, 1 Mar 2020 12:35:32 +0100 Subject: [PATCH 07/43] Update preconfigured_peers comment regarding port (#2616) --- nano/node/nodeconfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nano/node/nodeconfig.cpp b/nano/node/nodeconfig.cpp index 134c63e4a3..d0f1a08fe3 100644 --- a/nano/node/nodeconfig.cpp +++ b/nano/node/nodeconfig.cpp @@ -110,7 +110,7 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const work_peers_l->push_back (boost::str (boost::format ("%1%:%2%") % i->first % i->second)); } - auto preconfigured_peers_l (toml.create_array ("preconfigured_peers", "A list of \"address:port\" entries to identify preconfigured peers.")); + auto preconfigured_peers_l (toml.create_array ("preconfigured_peers", "A list of \"address\" (hostname or ip address) entries to identify preconfigured peers.")); for (auto i (preconfigured_peers.begin ()), n (preconfigured_peers.end ()); i != n; ++i) { preconfigured_peers_l->push_back (*i); From 0fa01519c0106f267bba3be75b1e797c1e0ba81f Mon Sep 17 00:00:00 2001 From: Russel Waters Date: Mon, 2 Mar 2020 04:31:59 -0500 Subject: [PATCH 08/43] update url for sourceforge (#2617) --- util/build_prep/bootstrap_boost.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/build_prep/bootstrap_boost.sh b/util/build_prep/bootstrap_boost.sh index 47033144bf..acec3e3010 100755 --- a/util/build_prep/bootstrap_boost.sh +++ b/util/build_prep/bootstrap_boost.sh @@ -75,17 +75,17 @@ fi case "${boostVersion}" in 1.67) BOOST_BASENAME=boost_1_67_0 - BOOST_URL=https://dl.bintray.com/boostorg/release/1.67.0/source/${BOOST_BASENAME}.tar.bz2 + BOOST_URL=https://sourceforge.net/projects/boost/files/boost/1.67.0/${BOOST_BASENAME}.tar.bz2/download BOOST_ARCHIVE_SHA256='2684c972994ee57fc5632e03bf044746f6eb45d4920c343937a465fd67a5adba' ;; 1.69) BOOST_BASENAME=boost_1_69_0 - BOOST_URL=https://dl.bintray.com/boostorg/release/1.69.0/source/${BOOST_BASENAME}.tar.bz2 + BOOST_URL=https://sourceforge.net/projects/boost/files/boost/1.69.0/${BOOST_BASENAME}.tar.bz2/download BOOST_ARCHIVE_SHA256='8f32d4617390d1c2d16f26a27ab60d97807b35440d45891fa340fc2648b04406' ;; 1.70) BOOST_BASENAME=boost_1_70_0 - BOOST_URL=https://dl.bintray.com/boostorg/release/1.70.0/source/${BOOST_BASENAME}.tar.bz2 + BOOST_URL=https://sourceforge.net/projects/boost/files/boost/1.70.0/${BOOST_BASENAME}.tar.bz2/download BOOST_ARCHIVE_SHA256='430ae8354789de4fd19ee52f3b1f739e1fba576f0aded0897c3c2bc00fb38778' ;; *) From 5dac531c911b0e19e2c615397fa38e94a9b34ecd Mon Sep 17 00:00:00 2001 From: clemahieu Date: Mon, 2 Mar 2020 10:36:13 +0100 Subject: [PATCH 09/43] Election state refactor (#2535) Converting the election class into a state machine instead of working on rebroadcast iterations. --- nano/core_test/CMakeLists.txt | 1 + nano/core_test/active_transactions.cpp | 152 ++++++++++- nano/core_test/confirmation_height.cpp | 18 +- nano/core_test/confirmation_solicitor.cpp | 4 +- nano/core_test/election.cpp | 18 ++ nano/core_test/ledger.cpp | 2 +- nano/core_test/node.cpp | 2 - nano/node/active_transactions.cpp | 162 +++--------- nano/node/active_transactions.hpp | 21 +- nano/node/blockprocessor.cpp | 6 +- nano/node/election.cpp | 305 +++++++++++++++++++--- nano/node/election.hpp | 59 ++++- nano/node/json_handler.cpp | 2 +- nano/node/node.cpp | 49 ++-- nano/node/repcrawler.cpp | 6 - nano/node/repcrawler.hpp | 3 - 16 files changed, 549 insertions(+), 261 deletions(-) create mode 100644 nano/core_test/election.cpp diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index 947d5dff0d..06aac192f2 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable (core_test conflicts.cpp difficulty.cpp distributed_work.cpp + election.cpp entry.cpp epochs.cpp gap_cache.cpp diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 15b74bc141..b9b6379f4f 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -7,7 +7,7 @@ using namespace std::chrono_literals; -TEST (active_transactions, confirm_one) +TEST (active_transactions, confirm_active) { nano::system system (1); auto & node1 = *system.nodes[0]; @@ -15,7 +15,7 @@ TEST (active_transactions, confirm_one) system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); auto send (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); system.deadline_set (5s); - while (!node1.active.empty () && !node1.block_confirmed_or_being_confirmed (node1.store.tx_begin_read (), send->hash ())) + while (!node1.active.empty () || !node1.block_confirmed_or_being_confirmed (node1.store.tx_begin_read (), send->hash ())) { ASSERT_NO_ERROR (system.poll ()); } @@ -27,12 +27,58 @@ TEST (active_transactions, confirm_one) node1.network.flood_block (send, nano::buffer_drop_policy::no_limiter_drop); ASSERT_NO_ERROR (system.poll ()); } - while (node2.ledger.cache.cemented_count < 2) + while (node2.ledger.cache.cemented_count < 2 || !node2.active.empty ()) { ASSERT_NO_ERROR (system.poll ()); } } +TEST (active_transactions, confirm_frontier) +{ + nano::system system (1); + auto & node1 = *system.nodes[0]; + // Send and vote for a block before peering with node2 + system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); + auto send (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); + system.deadline_set (5s); + while (!node1.active.empty () || !node1.block_confirmed_or_being_confirmed (node1.store.tx_begin_read (), send->hash ())) + { + ASSERT_NO_ERROR (system.poll ()); + } + auto & node2 = *system.add_node (nano::node_config (nano::get_available_port (), system.logging)); + ASSERT_EQ (nano::process_result::progress, node2.process (*send).code); + system.deadline_set (5s); + while (node2.ledger.cache.cemented_count < 2 || !node2.active.empty ()) + { + ASSERT_NO_ERROR (system.poll ()); + } +} + +TEST (active_transactions, confirm_dependent) +{ + nano::system system; + nano::node_flags node_flags; + node_flags.disable_request_loop = true; + auto & node1 = *system.add_node (node_flags); + system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); + auto send1 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); + auto send2 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); + auto send3 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); + nano::node_config node_config; + node_config.peering_port = nano::get_available_port (); + node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + auto & node2 = *system.add_node (node_config); + node2.process_local (send1); + node2.process_local (send2); + node2.process_active (send3); + system.deadline_set (5s); + while (!node2.active.empty ()) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_EQ (4, node2.ledger.cache.cemented_count); +} + TEST (active_transactions, adjusted_difficulty_priority) { nano::system system; @@ -76,7 +122,7 @@ TEST (active_transactions, adjusted_difficulty_priority) } } system.deadline_set (10s); - while (node1.ledger.cache.cemented_count < 5) + while (node1.ledger.cache.cemented_count < 5 || !node1.active.empty ()) { ASSERT_NO_ERROR (system.poll ()); } @@ -400,7 +446,7 @@ TEST (active_transactions, inactive_votes_cache_fork) while (!confirmed) { auto transaction (node.store.tx_begin_read ()); - confirmed = node.block (send1->hash ()) != nullptr && node.ledger.block_confirmed (transaction, send1->hash ()); + confirmed = node.block (send1->hash ()) != nullptr && node.ledger.block_confirmed (transaction, send1->hash ()) && node.active.empty (); ASSERT_NO_ERROR (system.poll ()); } ASSERT_EQ (1, node.stats.count (nano::stat::type::election, nano::stat::detail::vote_cached)); @@ -557,9 +603,11 @@ TEST (active_transactions, update_difficulty) ASSERT_NE (existing3, node2.active.roots.end ()); auto const existing4 (node2.active.roots.find (send2->qualified_root ())); ASSERT_NE (existing4, node2.active.roots.end ()); - auto updated = (existing1->difficulty > difficulty1) && (existing2->difficulty > difficulty2); - auto propogated = (existing3->difficulty > difficulty1) && (existing4->difficulty > difficulty2); - done = updated && propogated; + auto updated1 = existing1->difficulty > difficulty1; + auto updated2 = existing2->difficulty > difficulty2; + auto propogated1 = existing3->difficulty > difficulty1; + auto propogated2 = existing4->difficulty > difficulty2; + done = updated1 && updated2 && propogated1 && propogated2; } ASSERT_NO_ERROR (system.poll ()); } @@ -583,15 +631,28 @@ TEST (active_transactions, vote_replays) node.process_active (open1); node.block_processor.flush (); ASSERT_EQ (2, node.active.size ()); - // First vote is not a replay and confirms the election, second vote should be indeterminate since the election no longer exists + // First vote is not a replay and confirms the election, second vote should be a replay since the election has confirmed but not yet removed auto vote_send1 (std::make_shared (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 0, send1)); ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_send1)); - ASSERT_EQ (1, node.active.size ()); + ASSERT_EQ (2, node.active.size ()); + ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1)); + // Wait until the election is removed, at which point the vote should be indeterminate + system.deadline_set (3s); + while (node.active.size () != 1) + { + ASSERT_NO_ERROR (system.poll ()); + } ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_send1)); // Open new account auto vote_open1 (std::make_shared (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 0, open1)); ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_open1)); - ASSERT_TRUE (node.active.empty ()); + ASSERT_EQ (1, node.active.size ()); + ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1)); + system.deadline_set (3s); + while (!node.active.empty ()) + { + ASSERT_NO_ERROR (system.poll ()); + } ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_open1)); ASSERT_EQ (nano::Gxrb_ratio, node.ledger.weight (key.pub)); @@ -607,7 +668,76 @@ TEST (active_transactions, vote_replays) ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2)); ASSERT_EQ (1, node.active.size ()); ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote1_send2)); + ASSERT_EQ (1, node.active.size ()); + ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2)); + while (!node.active.empty ()) + { + ASSERT_NO_ERROR (system.poll ()); + } ASSERT_EQ (0, node.active.size ()); ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote1_send2)); ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote2_send2)); } + +TEST (active_transactions, activate_dependencies) +{ + // Ensure that we attempt to backtrack if an election isn't getting confirmed and there are more uncemented blocks to start elections for + nano::system system; + nano::node_config config (nano::get_available_port (), system.logging); + config.enable_voting = true; + nano::node_flags flags; + flags.disable_bootstrap_listener = true; + config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + auto node1 (system.add_node (config, flags)); + config.peering_port = nano::get_available_port (); + auto node2 (system.add_node (config, flags)); + system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); + nano::genesis genesis; + nano::block_builder builder; + system.deadline_set (std::chrono::seconds (15)); + std::shared_ptr block0 = builder.state () + .account (nano::test_genesis_key.pub) + .previous (genesis.hash ()) + .representative (nano::test_genesis_key.pub) + .balance (nano::genesis_amount - nano::Gxrb_ratio) + .link (0) + .sign (nano::test_genesis_key.prv, nano::test_genesis_key.pub) + .work (node1->work_generate_blocking (genesis.hash ()).value ()) + .build (); + // Establish a representative + node2->process_active (block0); + node2->block_processor.flush (); + while (node1->block (block0->hash ()) == nullptr) + { + ASSERT_NO_ERROR (system.poll ()); + } + auto block1 = builder.state () + .account (nano::test_genesis_key.pub) + .previous (block0->hash ()) + .representative (nano::test_genesis_key.pub) + .balance (nano::genesis_amount - nano::Gxrb_ratio) + .link (0) + .sign (nano::test_genesis_key.prv, nano::test_genesis_key.pub) + .work (node1->work_generate_blocking (block0->hash ()).value ()) + .build (); + { + auto transaction = node2->store.tx_begin_write (); + ASSERT_EQ (nano::process_result::progress, node2->ledger.process (transaction, *block1).code); + } + std::shared_ptr block2 = builder.state () + .account (nano::test_genesis_key.pub) + .previous (block1->hash ()) + .representative (nano::test_genesis_key.pub) + .balance (nano::genesis_amount - 2 * nano::Gxrb_ratio) + .link (0) + .sign (nano::test_genesis_key.prv, nano::test_genesis_key.pub) + .work (node1->work_generate_blocking (block1->hash ()).value ()) + .build (); + node2->process_active (block2); + node2->block_processor.flush (); + while (node1->block (block2->hash ()) == nullptr) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_NE (nullptr, node1->block (block2->hash ())); +} diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index 714694648a..33830efffa 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -1187,13 +1187,8 @@ TEST (confirmation_height, dependent_election) add_callback_stats (*node); - // Wait until it has been processed + // Start an election and vote, should confirm the block node->block_confirm (send2); - system.deadline_set (10s); - while (node->active.size () > 0) - { - ASSERT_NO_ERROR (system.poll ()); - } { // The write guard prevents the confirmation height processor doing any writes. @@ -1426,17 +1421,6 @@ TEST (confirmation_height, election_winner_details_clearing) add_callback_stats (*node); node->block_confirm (send1); - system.deadline_set (10s); - while (node->active.size () > 0) - { - ASSERT_NO_ERROR (system.poll ()); - } - - ASSERT_EQ (0, node->active.list_confirmed ().size ()); - { - nano::lock_guard guard (node->active.mutex); - ASSERT_EQ (0, node->active.blocks.size ()); - } system.deadline_set (10s); while (node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) != 2) diff --git a/nano/core_test/confirmation_solicitor.cpp b/nano/core_test/confirmation_solicitor.cpp index 8be720b051..d2595da088 100644 --- a/nano/core_test/confirmation_solicitor.cpp +++ b/nano/core_test/confirmation_solicitor.cpp @@ -34,12 +34,12 @@ TEST (confirmation_solicitor, batches) auto send (std::make_shared (nano::genesis_hash, nano::keypair ().pub, nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (nano::genesis_hash))); for (size_t i (0); i < nano::network::confirm_req_hashes_max; ++i) { - auto election (std::make_shared (node2, send, false, nullptr)); + auto election (std::make_shared (node2, send, nullptr)); ASSERT_FALSE (node2.active.solicitor.add (*election)); } ASSERT_EQ (1, node2.active.solicitor.max_confirm_req_batches); // Reached the maximum amount of requests for the channel - auto election (std::make_shared (node2, send, false, nullptr)); + auto election (std::make_shared (node2, send, nullptr)); ASSERT_TRUE (node2.active.solicitor.add (*election)); // Broadcasting should be immediate ASSERT_EQ (0, node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::out)); diff --git a/nano/core_test/election.cpp b/nano/core_test/election.cpp new file mode 100644 index 0000000000..8ccc8af4d2 --- /dev/null +++ b/nano/core_test/election.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + +#include + +TEST (election, construction) +{ + nano::system system (1); + nano::genesis genesis; + auto & node = *system.nodes[0]; + auto election = node.active.insert (genesis.open).first; + ASSERT_TRUE (election->idle ()); + election->transition_active (); + ASSERT_FALSE (election->idle ()); + election->transition_passive (); + ASSERT_FALSE (election->idle ()); +} diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index ed438fa646..b71139c6a0 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -768,7 +768,7 @@ TEST (votes, add_one) auto vote1 (std::make_shared (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 1, send1)); ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1)); auto vote2 (std::make_shared (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 2, send1)); - ASSERT_EQ (nano::vote_code::indeterminate, node1.active.vote (vote2)); + ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2)); lock.lock (); ASSERT_EQ (2, election1.first->last_votes.size ()); auto existing1 (election1.first->last_votes.find (nano::test_genesis_key.pub)); diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index b13f0d812d..eab6c6ef7f 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -2134,7 +2134,6 @@ TEST (node, rep_weight) auto vote0 = std::make_shared (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 0, genesis.open); auto vote1 = std::make_shared (keypair1.pub, keypair1.prv, 0, genesis.open); auto vote2 = std::make_shared (keypair2.pub, keypair2.prv, 0, genesis.open); - node.rep_crawler.add (genesis.open->hash ()); node.rep_crawler.response (channel0, vote0); node.rep_crawler.response (channel1, vote1); node.rep_crawler.response (channel2, vote2); @@ -2217,7 +2216,6 @@ TEST (node, rep_remove) nano::amount amount100 (100); node.network.udp_channels.insert (endpoint0, node.network_params.protocol.protocol_version); auto vote1 = std::make_shared (keypair1.pub, keypair1.prv, 0, genesis.open); - node.rep_crawler.add (genesis.hash ()); node.rep_crawler.response (channel0, vote1); system.deadline_set (5s); while (node.rep_crawler.representative_count () != 1) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 7e9923d589..fd80025924 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -17,12 +17,7 @@ node (node_a), multipliers_cb (20, 1.), trended_active_difficulty (node_a.network_params.network.publish_threshold), solicitor (node_a.network, node_a.network_params.network), -long_election_threshold (node_a.network_params.network.is_test_network () ? 2s : 24s), -election_request_delay (node_a.network_params.network.is_test_network () ? 0s : 1s), -election_time_to_live (node_a.network_params.network.is_test_network () ? 0s : 10s), -min_time_between_requests (node_a.network_params.network.is_test_network () ? 25ms : 3s), -min_time_between_floods (node_a.network_params.network.is_test_network () ? 50ms : 6s), -min_request_count_flood (node_a.network_params.network.is_test_network () ? 0 : 2), +election_time_to_live (node_a.network_params.network.is_test_network () ? 0s : 2s), thread ([this]() { nano::thread_role::set (nano::thread_role::name::request_loop); request_loop (); @@ -38,8 +33,6 @@ thread ([this]() { this->block_already_cemented_callback (hash_a); }); - debug_assert (min_time_between_requests > std::chrono::milliseconds (node.network_params.network.request_interval_ms)); - debug_assert (min_time_between_floods > std::chrono::milliseconds (node.network_params.network.request_interval_ms)); nano::unique_lock lock (mutex); condition.wait (lock, [& started = started] { return started; }); } @@ -104,8 +97,10 @@ void nano::active_transactions::search_frontiers (nano::transaction const & tran if (info.block_count > confirmation_height_info.height && !this->confirmation_height_processor.is_processing_block (info.head)) { auto block (this->node.store.block_get (transaction_a, info.head)); - if (this->insert (block, true).first) + auto election = this->insert (block); + if (election.second) { + election.first->transition_active (); ++elections_count; // Calculate votes for local representatives if (representative) @@ -163,7 +158,7 @@ void nano::active_transactions::block_cemented_callback (std::shared_ptr lk (mutex); - if (election->confirmed () && !election->stopped && election->status.winner->hash () == hash) + if (election->confirmed () && election->status.winner->hash () == hash) { add_confirmed (election->status, block_a->qualified_root ()); lk.unlock (); @@ -206,60 +201,10 @@ void nano::active_transactions::block_already_cemented_callback (nano::block_has election_winner_details.erase (hash_a); } -void nano::active_transactions::election_escalate (std::shared_ptr & election_l, nano::transaction const & transaction_l, size_t const & roots_size_l) -{ - constexpr unsigned high_confirmation_request_count{ 128 }; - // Log votes for very long unconfirmed elections - if (election_l->confirmation_request_count % (4 * high_confirmation_request_count) == 1) - { - auto tally_l (election_l->tally ()); - election_l->log_votes (tally_l); - } - /* - * Escalation for long unconfirmed elections - * Start new elections for previous block & source if there are less than 100 active elections - */ - if (election_l->confirmation_request_count % high_confirmation_request_count == 1 && roots_size_l < 100 && !node.network_params.network.is_test_network ()) - { - bool escalated_l (false); - std::shared_ptr previous_l; - auto previous_hash_l (election_l->status.winner->previous ()); - if (!previous_hash_l.is_zero ()) - { - previous_l = node.store.block_get (transaction_l, previous_hash_l); - if (previous_l != nullptr && blocks.find (previous_hash_l) == blocks.end () && !node.block_confirmed_or_being_confirmed (transaction_l, previous_hash_l)) - { - insert_impl (std::move (previous_l), true); - escalated_l = true; - } - } - /* If previous block not existing/not commited yet, block_source can cause segfault for state blocks - So source check can be done only if previous != nullptr or previous is 0 (open account) */ - if (previous_hash_l.is_zero () || previous_l != nullptr) - { - auto source_hash_l (node.ledger.block_source (transaction_l, *election_l->status.winner)); - if (!source_hash_l.is_zero () && source_hash_l != previous_hash_l && blocks.find (source_hash_l) == blocks.end ()) - { - auto source_l (node.store.block_get (transaction_l, source_hash_l)); - if (source_l != nullptr && !node.block_confirmed_or_being_confirmed (transaction_l, source_hash_l)) - { - insert_impl (std::move (source_l), true); - escalated_l = true; - } - } - } - if (escalated_l) - { - election_l->update_dependent (); - } - } -} - void nano::active_transactions::request_confirm (nano::unique_lock & lock_a) { debug_assert (!mutex.try_lock ()); auto transaction_l (node.store.tx_begin_read ()); - std::unordered_set inactive_l; /* * Confirm frontiers when there aren't many confirmations already pending and node finished initial bootstrap * In auto mode start confirm only if node contains almost principal representative (half of required for principal weight) @@ -283,19 +228,9 @@ void nano::active_transactions::request_confirm (nano::unique_lock & solicitor.prepare (node.rep_crawler.representatives (node.network_params.protocol.tcp_realtime_protocol_version_min)); lock_a.lock (); - auto const now (std::chrono::steady_clock::now ()); - // Any new election started from process_live only gets requests after at least 1 second - auto cutoff_l (now - election_request_delay); - // Elections taking too long get escalated - auto long_election_cutoff_l (now - long_election_threshold); - // The lowest PoW difficulty elections have a maximum time to live if they are beyond the soft threshold size for the container - auto election_ttl_cutoff_l (now - election_time_to_live); - // Rate-limitting floods - auto const flood_cutoff (now - min_time_between_floods); - // Rate-limitting confirmation requests - auto const request_cutoff (now - min_time_between_requests); - + auto election_ttl_cutoff_l (std::chrono::steady_clock::now () - election_time_to_live); auto roots_size_l (roots.size ()); + bool saturated_l (roots_size_l > node.config.active_elections_size / 2); auto & sorted_roots_l = roots.get (); size_t count_l{ 0 }; @@ -306,60 +241,22 @@ void nano::active_transactions::request_confirm (nano::unique_lock & * Elections extending the soft config.active_elections_size limit are flushed after a certain time-to-live cutoff * Flushed elections are later re-activated via frontier confirmation */ - for (auto i = sorted_roots_l.begin (), n = sorted_roots_l.end (); i != n; ++i, ++count_l) + for (auto i = sorted_roots_l.begin (), n = sorted_roots_l.end (); i != n; ++count_l) { - auto election_l (i->election); - auto root_l (i->root); - if (election_l->confirmed () || (election_l->confirmation_request_count != 0 && !node.ledger.could_fit (transaction_l, *election_l->status.winner))) + auto & election_l (i->election); + if ((count_l >= node.config.active_elections_size && election_l->election_start < election_ttl_cutoff_l && !node.wallets.watcher->is_watched (i->root)) || election_l->transition_time (saturated_l)) { - election_l->stop (); + election_l->clear_blocks (); + i = sorted_roots_l.erase (i); } - // Erase finished elections - if ((election_l->stopped)) - { - inactive_l.insert (root_l); - } - // Drop elections - else if (count_l >= node.config.active_elections_size && election_l->election_start < election_ttl_cutoff_l && !node.wallets.watcher->is_watched (root_l)) + else { - election_l->stop (); - inactive_l.insert (root_l); - } - // Attempt obtaining votes - else if (election_l->skip_delay || election_l->election_start < cutoff_l) - { - // Broadcast the winner when elections are taking longer to confirm - if (election_l->confirmation_request_count >= min_request_count_flood && election_l->last_broadcast < flood_cutoff && !solicitor.broadcast (*election_l)) - { - election_l->last_broadcast = now; - } - // Rate-limited requests for confirmation - else if (election_l->last_request < request_cutoff && !solicitor.add (*election_l)) - { - ++election_l->confirmation_request_count; - election_l->last_request = now; - } - // Escalate long election after a certain time and number of requests performed - if (election_l->confirmation_request_count > 4 && election_l->election_start < long_election_cutoff_l) - { - election_escalate (election_l, transaction_l, roots_size_l); - } + ++i; } } lock_a.unlock (); solicitor.flush (); lock_a.lock (); - // Erase inactive elections - for (auto i (inactive_l.begin ()), n (inactive_l.end ()); i != n; ++i) - { - auto root_it (roots.get ().find (*i)); - if (root_it != roots.get ().end ()) - { - root_it->election->clear_blocks (); - root_it->election->clear_dependent (); - roots.get ().erase (root_it); - } - } } void nano::active_transactions::request_loop () @@ -566,7 +463,7 @@ void nano::active_transactions::stop () roots.clear (); } -std::pair, bool> nano::active_transactions::insert_impl (std::shared_ptr block_a, bool const skip_delay_a, std::function)> const & confirmation_action_a) +std::pair, bool> nano::active_transactions::insert_impl (std::shared_ptr block_a, std::function)> const & confirmation_action_a) { std::pair, bool> result = { nullptr, false }; if (!stopped) @@ -579,7 +476,7 @@ std::pair, bool> nano::active_transactions::inse { result.second = true; auto hash (block_a->hash ()); - result.first = nano::make_shared (node, block_a, skip_delay_a, confirmation_action_a); + result.first = nano::make_shared (node, block_a, confirmation_action_a); auto difficulty (block_a->difficulty ()); roots.get ().emplace (nano::conflict_info{ root, difficulty, difficulty, result.first }); blocks.emplace (hash, result.first); @@ -595,10 +492,10 @@ std::pair, bool> nano::active_transactions::inse return result; } -std::pair, bool> nano::active_transactions::insert (std::shared_ptr block_a, bool const skip_delay_a, std::function)> const & confirmation_action_a) +std::pair, bool> nano::active_transactions::insert (std::shared_ptr block_a, std::function)> const & confirmation_action_a) { nano::lock_guard lock (mutex); - return insert_impl (block_a, skip_delay_a, confirmation_action_a); + return insert_impl (block_a, confirmation_action_a); } // Validate a vote and apply it to the current election if one exists @@ -671,6 +568,18 @@ bool nano::active_transactions::active (nano::block const & block_a) return active (block_a.qualified_root ()); } +std::shared_ptr nano::active_transactions::election (nano::qualified_root const & root_a) const +{ + std::shared_ptr result; + nano::lock_guard lock (mutex); + auto existing = roots.get ().find (root_a); + if (existing != roots.get ().end ()) + { + result = existing->election; + } + return result; +} + void nano::active_transactions::update_difficulty (std::shared_ptr block_a) { nano::unique_lock lock (mutex); @@ -711,7 +620,7 @@ void nano::active_transactions::adjust_difficulty (nano::block_hash const & hash if (processed_blocks.find (hash) == processed_blocks.end ()) { auto existing (blocks.find (hash)); - if (existing != blocks.end () && !existing->second->confirmed () && !existing->second->stopped && existing->second->status.winner->hash () == hash) + if (existing != blocks.end () && !existing->second->confirmed () && existing->second->status.winner->hash () == hash) { auto previous (existing->second->status.winner->previous ()); if (!previous.is_zero ()) @@ -793,10 +702,9 @@ void nano::active_transactions::update_active_difficulty (nano::unique_lock active_root_difficulties; active_root_difficulties.reserve (std::min (sorted_roots.size (), node.config.active_elections_size)); size_t count (0); - auto cutoff (std::chrono::steady_clock::now () - election_request_delay - 1s); for (auto it (sorted_roots.begin ()), end (sorted_roots.end ()); it != end && count++ < node.config.active_elections_size; ++it) { - if (!it->election->confirmed () && !it->election->stopped && it->election->election_start < cutoff) + if (!it->election->confirmed () && !it->election->idle ()) { active_root_difficulties.push_back (it->adjusted_difficulty); } @@ -865,9 +773,7 @@ void nano::active_transactions::erase (nano::block const & block_a) auto root_it (roots.get ().find (block_a.qualified_root ())); if (root_it != roots.get ().end ()) { - root_it->election->stop (); root_it->election->clear_blocks (); - root_it->election->clear_dependent (); roots.get ().erase (root_it); node.logger.try_log (boost::str (boost::format ("Election erased for block block %1% root %2%") % block_a.hash ().to_string () % block_a.root ().to_string ())); } @@ -894,7 +800,7 @@ bool nano::active_transactions::publish (std::shared_ptr block_a) { auto election (existing->election); result = election->publish (block_a); - if (!result && !election->confirmed ()) + if (!result) { blocks.emplace (block_a->hash (), election); } @@ -910,7 +816,7 @@ boost::optional nano::active_transactions::confirm_b auto existing (blocks.find (hash)); if (existing != blocks.end ()) { - if (!existing->second->confirmed () && !existing->second->stopped && existing->second->status.winner->hash () == hash) + if (!existing->second->confirmed () && existing->second->status.winner->hash () == hash) { existing->second->confirm_once (nano::election_status_type::active_confirmation_height); return nano::election_status_type::active_confirmation_height; diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index d1c68f45db..22c2af6d86 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -74,6 +74,8 @@ class inactive_cache_information final // Holds all active blocks i.e. recently added blocks that need confirmation class active_transactions final { + friend class nano::election; + // clang-format off class tag_account {}; class tag_difficulty {}; @@ -90,13 +92,14 @@ class active_transactions final // Start an election for a block // Call action with confirmed block, may be different than what we started with // clang-format off - std::pair, bool> insert (std::shared_ptr, bool const = false, std::function)> const & = [](std::shared_ptr) {}); + std::pair, bool> insert (std::shared_ptr, std::function)> const & = [](std::shared_ptr) {}); // clang-format on // Distinguishes replay votes, cannot be determined if the block is not in any election nano::vote_code vote (std::shared_ptr); // Is the root of this block in the roots container bool active (nano::block const &); bool active (nano::qualified_root const &); + std::shared_ptr election (nano::qualified_root const &) const; void update_difficulty (std::shared_ptr); void adjust_difficulty (nano::block_hash const &); void update_active_difficulty (nano::unique_lock &); @@ -130,7 +133,7 @@ class active_transactions final void erase_inactive_votes_cache (nano::block_hash const &); nano::confirmation_height_processor & confirmation_height_processor; nano::node & node; - std::mutex mutex; + mutable std::mutex mutex; boost::circular_buffer multipliers_cb; uint64_t trended_active_difficulty; size_t priority_cementable_frontiers_size (); @@ -147,11 +150,10 @@ class active_transactions final // Call action with confirmed block, may be different than what we started with // clang-format off - std::pair, bool> insert_impl (std::shared_ptr, bool const = false, std::function)> const & = [](std::shared_ptr) {}); + std::pair, bool> insert_impl (std::shared_ptr, std::function)> const & = [](std::shared_ptr) {}); // clang-format on void request_loop (); void search_frontiers (nano::transaction const &); - void election_escalate (std::shared_ptr &, nano::transaction const &, size_t const &); void request_confirm (nano::unique_lock &); nano::account next_frontier_account{ 0 }; std::chrono::steady_clock::time_point next_frontier_check{ std::chrono::steady_clock::now () }; @@ -159,18 +161,8 @@ class active_transactions final bool started{ false }; std::atomic stopped{ false }; - // Minimum time an election must be active before escalation - std::chrono::seconds const long_election_threshold; - // Delay until requesting confirmation for an election - std::chrono::milliseconds const election_request_delay; // Maximum time an election can be kept active if it is extending the container std::chrono::seconds const election_time_to_live; - // Minimum time between confirmation requests for an election - std::chrono::milliseconds const min_time_between_requests; - // Minimum time between broadcasts of the current winner of an election, as a backup to requesting confirmations - std::chrono::milliseconds const min_time_between_floods; - // Minimum election request count to start broadcasting blocks, as a backup to requesting confirmations - size_t const min_request_count_flood; // clang-format off boost::multi_index_container const &, nano::block_hash const &, bool &); - static size_t constexpr dropped_elections_cache_max{ 32 * 1024 }; boost::thread thread; friend class confirmation_height_prioritize_frontiers_Test; diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index dbdb3f6bd5..c7f0d9e9dc 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -372,7 +372,11 @@ void nano::block_processor::process_live (nano::block_hash const & hash_a, std:: } // Start collecting quorum on block - node.active.insert (block_a, false); + auto election = node.active.insert (block_a); + if (election.second) + { + election.first->transition_passive (); + } // Announce block contents to the network if (initial_publish_a) diff --git a/nano/node/election.cpp b/nano/node/election.cpp index bda1bdb33b..5e52ed6d5b 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -3,20 +3,29 @@ #include +using namespace std::chrono; + +int constexpr nano::election::passive_duration_factor; +int constexpr nano::election::active_duration_factor; +int constexpr nano::election::confirmed_duration_factor; +int constexpr nano::election::confirmed_duration_factor_saturated; + +std::chrono::milliseconds nano::election::base_latency () const +{ + return node.network_params.network.is_test_network () ? 25ms : 1000ms; +} + nano::election_vote_result::election_vote_result (bool replay_a, bool processed_a) { replay = replay_a; processed = processed_a; } -nano::election::election (nano::node & node_a, std::shared_ptr block_a, bool const skip_delay_a, std::function)> const & confirmation_action_a) : +nano::election::election (nano::node & node_a, std::shared_ptr block_a, std::function)> const & confirmation_action_a) : confirmation_action (confirmation_action_a), -confirmed_m (false), +state_start (std::chrono::steady_clock::now ()), node (node_a), -election_start (std::chrono::steady_clock::now ()), -status ({ block_a, 0, std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values::zero (), 0, 1, 0, nano::election_status_type::ongoing }), -skip_delay (skip_delay_a), -stopped (false) +status ({ block_a, 0, std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values::zero (), 0, 1, 0, nano::election_status_type::ongoing }) { last_votes.emplace (node.network_params.random.not_an_account, nano::vote_info{ std::chrono::steady_clock::now (), 0, block_a->hash () }); blocks.emplace (block_a->hash (), block_a); @@ -26,7 +35,7 @@ stopped (false) void nano::election::confirm_once (nano::election_status_type type_a) { debug_assert (!node.active.mutex.try_lock ()); - if (!confirmed_m.exchange (true)) + if (state_m.exchange (nano::election::state_t::confirmed) != nano::election::state_t::confirmed) { status.election_end = std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()); status.election_duration = std::chrono::duration_cast (std::chrono::steady_clock::now () - election_start); @@ -48,30 +57,254 @@ void nano::election::confirm_once (nano::election_status_type type_a) node_l->process_confirmed (status_l, this_l); confirmation_action_l (status_l.winner); }); - clear_blocks (); - clear_dependent (); - node.active.roots.erase (status.winner->qualified_root ()); } } -void nano::election::stop () +bool nano::election::valid_change (nano::election::state_t expected_a, nano::election::state_t desired_a) const { - debug_assert (!node.active.mutex.try_lock ()); - if (!stopped && !confirmed ()) + bool result = false; + switch (expected_a) { - stopped = true; - status.election_end = std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()); - status.election_duration = std::chrono::duration_cast (std::chrono::steady_clock::now () - election_start); - status.confirmation_request_count = confirmation_request_count; - status.block_count = nano::narrow_cast (blocks.size ()); - status.voter_count = nano::narrow_cast (last_votes.size ()); - status.type = nano::election_status_type::stopped; + case nano::election::state_t::idle: + switch (desired_a) + { + case nano::election::state_t::passive: + case nano::election::state_t::active: + result = true; + break; + default: + break; + } + break; + case nano::election::state_t::passive: + switch (desired_a) + { + case nano::election::state_t::idle: + case nano::election::state_t::active: + case nano::election::state_t::confirmed: + case nano::election::state_t::expired_unconfirmed: + result = true; + break; + default: + break; + } + break; + case nano::election::state_t::active: + switch (desired_a) + { + case nano::election::state_t::idle: + case nano::election::state_t::backtracking: + case nano::election::state_t::confirmed: + case nano::election::state_t::expired_unconfirmed: + result = true; + break; + default: + break; + } + case nano::election::state_t::backtracking: + switch (desired_a) + { + case nano::election::state_t::idle: + case nano::election::state_t::confirmed: + case nano::election::state_t::expired_unconfirmed: + result = true; + break; + default: + break; + } + case nano::election::state_t::confirmed: + switch (desired_a) + { + case nano::election::state_t::expired_confirmed: + result = true; + break; + default: + break; + } + case nano::election::state_t::expired_unconfirmed: + break; + case nano::election::state_t::expired_confirmed: + break; + } + return result; +} + +bool nano::election::state_change (nano::election::state_t expected_a, nano::election::state_t desired_a) +{ + debug_assert (!timepoints_mutex.try_lock ()); + bool result = true; + if (valid_change (expected_a, desired_a)) + { + if (state_m.compare_exchange_strong (expected_a, desired_a)) + { + state_start = std::chrono::steady_clock::now (); + result = false; + } + } + else + { + debug_assert (false); + } + return result; +} + +void nano::election::send_confirm_req () +{ + if (last_req + std::chrono::seconds (15) < std::chrono::steady_clock::now ()) + { + if (!node.active.solicitor.add (*this)) + { + last_req = std::chrono::steady_clock::now (); + } } } -bool nano::election::confirmed () +void nano::election::transition_passive () +{ + nano::lock_guard guard (timepoints_mutex); + transition_passive_impl (); +} + +void nano::election::transition_passive_impl () +{ + state_change (nano::election::state_t::idle, nano::election::state_t::passive); +} + +void nano::election::transition_active () +{ + nano::lock_guard guard (timepoints_mutex); + transition_active_impl (); +} + +void nano::election::transition_active_impl () { - return confirmed_m; + if (!state_change (nano::election::state_t::idle, nano::election::state_t::active)) + { + if (base_latency () * 5 < std::chrono::steady_clock::now () - last_block) + { + last_block = std::chrono::steady_clock::now (); + node.network.flood_block (status.winner); + } + } +} + +bool nano::election::idle () const +{ + return state_m == nano::election::state_t::idle; +} + +bool nano::election::confirmed () const +{ + return state_m == nano::election::state_t::confirmed || state_m == nano::election::state_t::expired_confirmed; +} + +void nano::election::activate_dependencies () +{ + auto transaction = node.store.tx_begin_read (); + bool escalated_l (false); + std::shared_ptr previous_l; + auto previous_hash_l (status.winner->previous ()); + if (!previous_hash_l.is_zero () && node.active.blocks.find (previous_hash_l) == node.active.blocks.end ()) + { + previous_l = node.store.block_get (transaction, previous_hash_l); + if (previous_l != nullptr && !node.block_confirmed_or_being_confirmed (transaction, previous_hash_l)) + { + auto election = node.active.insert_impl (previous_l); + if (election.second) + { + election.first->transition_active (); + escalated_l = true; + } + } + } + /* If previous block not existing/not commited yet, block_source can cause segfault for state blocks + So source check can be done only if previous != nullptr or previous is 0 (open account) */ + if (previous_hash_l.is_zero () || previous_l != nullptr) + { + auto source_hash_l (node.ledger.block_source (transaction, *status.winner)); + if (!source_hash_l.is_zero () && source_hash_l != previous_hash_l && node.active.blocks.find (source_hash_l) == node.active.blocks.end ()) + { + auto source_l (node.store.block_get (transaction, source_hash_l)); + if (source_l != nullptr && !node.block_confirmed_or_being_confirmed (transaction, source_hash_l)) + { + auto election = node.active.insert_impl (source_l); + if (election.second) + { + election.first->transition_active (); + escalated_l = true; + } + } + } + } + if (escalated_l) + { + update_dependent (); + } +} + +void nano::election::broadcast_block () +{ + if (base_latency () * 5 < std::chrono::steady_clock::now () - last_block) + { + if (!node.active.solicitor.broadcast (*this)) + { + last_block = std::chrono::steady_clock::now (); + } + } +} + +bool nano::election::transition_time (bool const saturated_a) +{ + debug_assert (!node.active.mutex.try_lock ()); + nano::unique_lock lock (timepoints_mutex); + bool result = false; + switch (state_m) + { + case nano::election::state_t::idle: + break; + case nano::election::state_t::passive: + { + if (base_latency () * passive_duration_factor < std::chrono::steady_clock::now () - state_start) + { + state_change (nano::election::state_t::passive, nano::election::state_t::active); + } + break; + } + case nano::election::state_t::active: + broadcast_block (); + send_confirm_req (); + if (base_latency () * active_duration_factor < std::chrono::steady_clock::now () - state_start) + { + state_change (nano::election::state_t::active, nano::election::state_t::backtracking); + lock.unlock (); + activate_dependencies (); + } + break; + case nano::election::state_t::backtracking: + broadcast_block (); + send_confirm_req (); + break; + case nano::election::state_t::confirmed: + if (base_latency () * (saturated_a ? confirmed_duration_factor_saturated : confirmed_duration_factor) < std::chrono::steady_clock::now () - state_start) + { + result = true; + state_change (nano::election::state_t::confirmed, nano::election::state_t::expired_confirmed); + } + break; + case nano::election::state_t::expired_unconfirmed: + case nano::election::state_t::expired_confirmed: + debug_assert (false); + break; + } + // Note: lock (timepoints_mutex) is at an unknown state here - possibly unlocked before activate_dependencies + if (!confirmed () && std::chrono::minutes (5) < std::chrono::steady_clock::now () - election_start) + { + result = true; + state_change (state_m.load (), nano::election::state_t::expired_unconfirmed); + status.type = nano::election_status_type::stopped; + log_votes (tally ()); + } + return result; } bool nano::election::have_quorum (nano::tally_t const & tally_a, nano::uint128_t tally_sum) const @@ -189,10 +422,10 @@ nano::election_vote_result nano::election::vote (nano::account rep, uint64_t seq } else { - auto last_vote (last_vote_it->second); - if (last_vote.sequence < sequence || (last_vote.sequence == sequence && last_vote.hash < block_hash)) + auto last_vote_l (last_vote_it->second); + if (last_vote_l.sequence < sequence || (last_vote_l.sequence == sequence && last_vote_l.hash < block_hash)) { - if (last_vote.time <= std::chrono::steady_clock::now () - std::chrono::seconds (cooldown)) + if (last_vote_l.time <= std::chrono::steady_clock::now () - std::chrono::seconds (cooldown)) { should_process = true; } @@ -217,8 +450,9 @@ nano::election_vote_result nano::election::vote (nano::account rep, uint64_t seq bool nano::election::publish (std::shared_ptr block_a) { - auto result (false); - if (blocks.size () >= 10) + // Do not insert new blocks if already confirmed + auto result (confirmed ()); + if (!result && blocks.size () >= 10) { if (last_tally[block_a->hash ()] < node.online_reps.online_stake () / 10) { @@ -276,7 +510,7 @@ void nano::election::update_dependent () for (auto & block_search : blocks_search) { auto existing (node.active.blocks.find (block_search)); - if (existing != node.active.blocks.end () && !existing->second->confirmed () && !existing->second->stopped) + if (existing != node.active.blocks.end () && !existing->second->confirmed ()) { if (existing->second->dependent_blocks.find (hash) == existing->second->dependent_blocks.end ()) { @@ -286,27 +520,18 @@ void nano::election::update_dependent () } } -void nano::election::clear_dependent () -{ - for (auto & dependent_block : dependent_blocks) - { - node.active.adjust_difficulty (dependent_block); - } -} - void nano::election::clear_blocks () { auto winner_hash (status.winner->hash ()); - for (auto & block : blocks) + for (auto const & block : blocks) { auto & hash (block.first); auto erased (node.active.blocks.erase (hash)); (void)erased; - // clear_blocks () can be called in active_transactions::publish () before blocks insertion if election was confirmed - debug_assert (erased == 1 || confirmed ()); + debug_assert (erased == 1); node.active.erase_inactive_votes_cache (hash); // Notify observers about dropped elections & blocks lost confirmed elections - if (stopped || hash != winner_hash) + if (!confirmed () || hash != winner_hash) { node.observers.active_stopped.notify (hash); } diff --git a/nano/node/election.hpp b/nano/node/election.hpp index cfa76733a6..3205345f67 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -50,11 +50,42 @@ class election_vote_result final }; class election final : public std::enable_shared_from_this { + // Minimum time between broadcasts of the current winner of an election, as a backup to requesting confirmations + std::chrono::milliseconds base_latency () const; std::function)> confirmation_action; - std::atomic confirmed_m; + +private: // State management + enum class state_t + { + idle, + passive, + active, + backtracking, + confirmed, + expired_confirmed, + expired_unconfirmed + }; + static int constexpr passive_duration_factor = 5; + static int constexpr active_duration_factor = 20; + static int constexpr confirmed_duration_factor = 10; + static int constexpr confirmed_duration_factor_saturated = 1; + std::atomic state_m = { state_t::idle }; + + // Protects state_start, last_vote and last_block + std::mutex timepoints_mutex; + std::chrono::steady_clock::time_point state_start = { std::chrono::steady_clock::now () }; + std::chrono::steady_clock::time_point last_vote = { std::chrono::steady_clock::time_point () }; + std::chrono::steady_clock::time_point last_block = { std::chrono::steady_clock::time_point () }; + std::chrono::steady_clock::time_point last_req = { std::chrono::steady_clock::time_point () }; + + bool valid_change (nano::election::state_t, nano::election::state_t) const; + bool state_change (nano::election::state_t, nano::election::state_t); + void broadcast_block (); + void send_confirm_req (); + void activate_dependencies (); public: - election (nano::node &, std::shared_ptr, bool const, std::function)> const &); + election (nano::node &, std::shared_ptr, std::function)> const &); nano::election_vote_result vote (nano::account, uint64_t, nano::block_hash); nano::tally_t tally (); // Check if we have vote quorum @@ -66,22 +97,28 @@ class election final : public std::enable_shared_from_this bool publish (std::shared_ptr block_a); size_t last_votes_size (); void update_dependent (); - void clear_dependent (); void clear_blocks (); void insert_inactive_votes_cache (nano::block_hash const &); - void stop (); - bool confirmed (); + +public: // State transitions + bool transition_time (bool const saturated); + void transition_passive (); + void transition_active (); + +private: + void transition_passive_impl (); + void transition_active_impl (); + +public: + bool idle () const; + bool confirmed () const; nano::node & node; std::unordered_map last_votes; std::unordered_map> blocks; - std::chrono::steady_clock::time_point election_start; + std::chrono::steady_clock::time_point election_start = { std::chrono::steady_clock::now () }; nano::election_status status; - bool skip_delay; - bool stopped; - std::unordered_map last_tally; unsigned confirmation_request_count{ 0 }; - std::chrono::steady_clock::time_point last_broadcast; - std::chrono::steady_clock::time_point last_request; + std::unordered_map last_tally; std::unordered_set dependent_blocks; std::chrono::seconds late_blocks_delay{ 5 }; }; diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index a163925a77..34a3231ed2 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1766,7 +1766,7 @@ void nano::json_handler::confirmation_active () nano::lock_guard lock (node.active.mutex); for (auto i (node.active.roots.begin ()), n (node.active.roots.end ()); i != n; ++i) { - if (i->election->confirmation_request_count >= announcements && !i->election->confirmed () && !i->election->stopped) + if (i->election->confirmation_request_count >= announcements && !i->election->confirmed ()) { boost::property_tree::ptree entry; entry.put ("", i->root.to_string ()); diff --git a/nano/node/node.cpp b/nano/node/node.cpp index f6381fd4db..84811903cf 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -549,29 +549,29 @@ void nano::node::process_fork (nano::transaction const & transaction_a, std::sha if (ledger_block && !block_confirmed_or_being_confirmed (transaction_a, ledger_block->hash ())) { std::weak_ptr this_w (shared_from_this ()); - if (active.insert (ledger_block, false, [this_w, root](std::shared_ptr) { - if (auto this_l = this_w.lock ()) - { - auto attempt (this_l->bootstrap_initiator.current_attempt ()); - if (attempt && attempt->mode == nano::bootstrap_mode::legacy) - { - auto transaction (this_l->store.tx_begin_read ()); - auto account (this_l->ledger.store.frontier_get (transaction, root)); - if (!account.is_zero ()) - { - this_l->bootstrap_initiator.connections->requeue_pull (nano::pull_info (account, root, root, attempt->incremental_id)); - } - else if (this_l->ledger.store.account_exists (transaction, root)) - { - this_l->bootstrap_initiator.connections->requeue_pull (nano::pull_info (root, nano::block_hash (0), nano::block_hash (0), attempt->incremental_id)); - } - } - } - }) - .first) + auto election = active.insert (ledger_block, [this_w, root](std::shared_ptr) { + if (auto this_l = this_w.lock ()) + { + auto attempt (this_l->bootstrap_initiator.current_attempt ()); + if (attempt && attempt->mode == nano::bootstrap_mode::legacy) + { + auto transaction (this_l->store.tx_begin_read ()); + auto account (this_l->ledger.store.frontier_get (transaction, root)); + if (!account.is_zero ()) + { + this_l->bootstrap_initiator.connections->requeue_pull (nano::pull_info (account, root, root, attempt->incremental_id)); + } + else if (this_l->ledger.store.account_exists (transaction, root)) + { + this_l->bootstrap_initiator.connections->requeue_pull (nano::pull_info (root, nano::block_hash (0), nano::block_hash (0), attempt->incremental_id)); + } + } + } + }); + if (election.second) { logger.always_log (boost::str (boost::format ("Resolving fork between our block: %1% and block %2% both with root %3%") % ledger_block->hash ().to_string () % block_a->hash ().to_string () % block_a->root ().to_string ())); - network.broadcast_confirm_req (ledger_block); + election.first->transition_active (); } } } @@ -1101,8 +1101,11 @@ void nano::node::add_initial_peers () void nano::node::block_confirm (std::shared_ptr block_a) { - active.insert (block_a, false); - network.broadcast_confirm_req (block_a); + auto election = active.insert (block_a); + if (election.second) + { + election.first->transition_active (); + } // Calculate votes for local representatives if (config.enable_voting && wallets.rep_counts ().voting > 0 && active.active (*block_a)) { diff --git a/nano/node/repcrawler.cpp b/nano/node/repcrawler.cpp index 5e458d0df0..d1af4a7663 100644 --- a/nano/node/repcrawler.cpp +++ b/nano/node/repcrawler.cpp @@ -14,12 +14,6 @@ node (node_a) } } -void nano::rep_crawler::add (nano::block_hash const & hash_a) -{ - nano::lock_guard lock (active_mutex); - active.insert (hash_a); -} - void nano::rep_crawler::remove (nano::block_hash const & hash_a) { nano::lock_guard lock (active_mutex); diff --git a/nano/node/repcrawler.hpp b/nano/node/repcrawler.hpp index 3421d28faf..0071584f53 100644 --- a/nano/node/repcrawler.hpp +++ b/nano/node/repcrawler.hpp @@ -80,9 +80,6 @@ class rep_crawler final /** Start crawling */ void start (); - /** Add block hash to list of active rep queries */ - void add (nano::block_hash const &); - /** Remove block hash from list of active rep queries */ void remove (nano::block_hash const &); From 90f1c862a87b583bf611414570755094fc1aa833 Mon Sep 17 00:00:00 2001 From: Sergey Kroshnin Date: Mon, 2 Mar 2020 21:52:23 +0300 Subject: [PATCH 10/43] Allow CLI --config values for inactive node tests (#2594) --- nano/nano_node/entry.cpp | 5 ----- nano/nano_wallet/entry.cpp | 11 +++-------- nano/node/cli.cpp | 6 ++++++ nano/node/node.cpp | 19 ++++++++++++++++++- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index eeb5af6d03..425ac08de1 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -155,11 +155,6 @@ int main (int argc, char * const * argv) std::cerr << flags_ec.message () << std::endl; std::exit (1); } - auto config (vm.find ("config")); - if (config != vm.end ()) - { - flags.config_overrides = config->second.as> (); - } daemon.run (data_path, flags); } else if (vm.count ("debug_block_count")) diff --git a/nano/nano_wallet/entry.cpp b/nano/nano_wallet/entry.cpp index 570d3904cb..290808e3d2 100644 --- a/nano/nano_wallet/entry.cpp +++ b/nano/nano_wallet/entry.cpp @@ -64,7 +64,7 @@ nano::error read_wallet_config (nano::wallet_config & config_a, boost::filesyste } } -int run_wallet (QApplication & application, int argc, char * const * argv, boost::filesystem::path const & data_path, std::vector const & config_overrides, nano::node_flags const & flags) +int run_wallet (QApplication & application, int argc, char * const * argv, boost::filesystem::path const & data_path, nano::node_flags const & flags) { int result (0); nano_qt::eventloop_processor processor; @@ -81,7 +81,7 @@ int run_wallet (QApplication & application, int argc, char * const * argv, boost nano::daemon_config config (data_path); nano::wallet_config wallet_config; - auto error = nano::read_node_config_toml (data_path, config, config_overrides); + auto error = nano::read_node_config_toml (data_path, config, flags.config_overrides); if (!error) { error = read_wallet_config (wallet_config, data_path); @@ -331,12 +331,7 @@ int main (int argc, char * const * argv) { throw std::runtime_error (flags_ec.message ()); } - auto config (vm.find ("config")); - if (config != vm.end ()) - { - flags.config_overrides = config->second.as> (); - } - result = run_wallet (application, argc, argv, data_path, config_overrides, flags); + result = run_wallet (application, argc, argv, data_path, flags); } catch (std::exception const & e) { diff --git a/nano/node/cli.cpp b/nano/node/cli.cpp index 45f4823af6..edb1af4ad4 100644 --- a/nano/node/cli.cpp +++ b/nano/node/cli.cpp @@ -169,6 +169,12 @@ std::error_code nano::update_flags (nano::node_flags & flags_a, boost::program_o { flags_a.vote_processor_capacity = vote_processor_capacity_it->second.as (); } + // Config overriding + auto config (vm.find ("config")); + if (config != vm.end ()) + { + flags_a.config_overrides = config->second.as> (); + } return ec; } diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 84811903cf..e6597f6061 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -1355,7 +1356,23 @@ peering_port (peering_port_a) nano::set_secure_perm_directory (path, error_chmod); logging.max_size = std::numeric_limits::max (); logging.init (path); - node = std::make_shared (*io_context, peering_port, path, alarm, logging, work, node_flags); + // Config overriding + nano::node_config config (peering_port, logging); + std::stringstream config_overrides_stream; + for (auto const & entry : node_flags.config_overrides) + { + config_overrides_stream << entry << std::endl; + } + config_overrides_stream << std::endl; + nano::tomlconfig toml; + toml.read (config_overrides_stream); + auto error = config.deserialize_toml (toml); + if (error) + { + std::cerr << "Error deserializing --config option" << std::endl; + std::exit (1); + } + node = std::make_shared (*io_context, path, alarm, config, work, node_flags); node->active.stop (); } From 62fc659143825179914376ee0584e8599ebc9443 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Mon, 2 Mar 2020 22:10:54 +0000 Subject: [PATCH 11/43] Attach sideband to block (#2596) * Move epoch from secure to nano_lib * Attach sideband to block and always (de)serialize along with it Using the sideband is only valid for blocks that were processed with code `progress`, otherwise they may not be set (important examples: old, fork). * Make sideband optional to ensure usage correctness * Interim * Use new nano::optional_ptr to hold the sideband * Adjust some tests to ensure two nodes don't simultaneously process the same block object * Update comment --- nano/core_test/block_store.cpp | 358 +++++++++----------- nano/core_test/epochs.cpp | 2 +- nano/core_test/ledger.cpp | 159 ++++----- nano/core_test/node.cpp | 21 +- nano/core_test/versioning.cpp | 12 +- nano/lib/CMakeLists.txt | 2 + nano/lib/blocks.cpp | 189 +++++++++++ nano/lib/blocks.hpp | 64 ++++ nano/{secure => lib}/epoch.cpp | 2 +- nano/{secure => lib}/epoch.hpp | 0 nano/nano_node/entry.cpp | 6 +- nano/node/active_transactions.cpp | 10 +- nano/node/active_transactions.hpp | 2 +- nano/node/confirmation_height_bounded.cpp | 46 ++- nano/node/confirmation_height_bounded.hpp | 4 +- nano/node/confirmation_height_processor.cpp | 4 +- nano/node/confirmation_height_processor.hpp | 6 +- nano/node/confirmation_height_unbounded.cpp | 32 +- nano/node/confirmation_height_unbounded.hpp | 8 +- nano/node/json_handler.cpp | 32 +- nano/node/lmdb/lmdb.cpp | 22 +- nano/node/node.cpp | 8 +- nano/node/node.hpp | 4 +- nano/secure/CMakeLists.txt | 2 - nano/secure/blockstore.cpp | 158 +-------- nano/secure/blockstore.hpp | 87 +---- nano/secure/blockstore_partial.hpp | 87 ++--- nano/secure/common.hpp | 2 +- nano/secure/ledger.cpp | 57 ++-- nano/secure/ledger.hpp | 2 +- 30 files changed, 703 insertions(+), 685 deletions(-) rename nano/{secure => lib}/epoch.cpp (98%) rename nano/{secure => lib}/epoch.hpp (100%) diff --git a/nano/core_test/block_store.cpp b/nano/core_test/block_store.cpp index 289e4adc99..ddaf19dd5d 100644 --- a/nano/core_test/block_store.cpp +++ b/nano/core_test/block_store.cpp @@ -90,7 +90,6 @@ TEST (block_store, block_details_serialization) TEST (block_store, sideband_serialization) { nano::block_sideband sideband1; - sideband1.type = nano::block_type::receive; sideband1.account = 1; sideband1.balance = 2; sideband1.height = 3; @@ -99,12 +98,11 @@ TEST (block_store, sideband_serialization) std::vector vector; { nano::vectorstream stream1 (vector); - sideband1.serialize (stream1); + sideband1.serialize (stream1, nano::block_type::receive); } nano::bufferstream stream2 (vector.data (), vector.size ()); nano::block_sideband sideband2; - sideband2.type = nano::block_type::receive; - ASSERT_FALSE (sideband2.deserialize (stream2)); + ASSERT_FALSE (sideband2.deserialize (stream2, nano::block_type::receive)); ASSERT_EQ (sideband1.account, sideband2.account); ASSERT_EQ (sideband1.balance, sideband2.balance); ASSERT_EQ (sideband1.height, sideband2.height); @@ -118,13 +116,13 @@ TEST (block_store, add_item) auto store = nano::make_store (logger, nano::unique_path ()); ASSERT_TRUE (!store->init_error ()); nano::open_block block (0, 1, 0, nano::keypair ().prv, 0, 0); + block.sideband_set ({}); auto hash1 (block.hash ()); auto transaction (store->tx_begin_write ()); auto latest1 (store->block_get (transaction, hash1)); ASSERT_EQ (nullptr, latest1); ASSERT_FALSE (store->block_exists (transaction, hash1)); - nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, hash1, block, sideband); + store->block_put (transaction, hash1, block); auto latest2 (store->block_get (transaction, hash1)); ASSERT_NE (nullptr, latest2); ASSERT_EQ (block, *latest2); @@ -141,20 +139,30 @@ TEST (block_store, clear_successor) auto store = nano::make_store (logger, nano::unique_path ()); ASSERT_TRUE (!store->init_error ()); nano::open_block block1 (0, 1, 0, nano::keypair ().prv, 0, 0); + block1.sideband_set ({}); auto transaction (store->tx_begin_write ()); - nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, block1.hash (), block1, sideband); + store->block_put (transaction, block1.hash (), block1); nano::open_block block2 (0, 2, 0, nano::keypair ().prv, 0, 0); - store->block_put (transaction, block2.hash (), block2, sideband); - ASSERT_NE (nullptr, store->block_get (transaction, block1.hash (), &sideband)); - ASSERT_EQ (0, sideband.successor.number ()); - sideband.successor = block2.hash (); - store->block_put (transaction, block1.hash (), block1, sideband); - ASSERT_NE (nullptr, store->block_get (transaction, block1.hash (), &sideband)); - ASSERT_EQ (block2.hash (), sideband.successor); + block2.sideband_set ({}); + store->block_put (transaction, block2.hash (), block2); + auto block2_store (store->block_get (transaction, block1.hash ())); + ASSERT_NE (nullptr, block2_store); + ASSERT_EQ (0, block2_store->sideband ().successor.number ()); + auto modified_sideband = block2_store->sideband (); + modified_sideband.successor = block2.hash (); + block1.sideband_set (modified_sideband); + store->block_put (transaction, block1.hash (), block1); + { + auto block1_store (store->block_get (transaction, block1.hash ())); + ASSERT_NE (nullptr, block1_store); + ASSERT_EQ (block2.hash (), block1_store->sideband ().successor); + } store->block_successor_clear (transaction, block1.hash ()); - ASSERT_NE (nullptr, store->block_get (transaction, block1.hash (), &sideband)); - ASSERT_EQ (0, sideband.successor.number ()); + { + auto block1_store (store->block_get (transaction, block1.hash ())); + ASSERT_NE (nullptr, block1_store); + ASSERT_EQ (0, block1_store->sideband ().successor.number ()); + } } TEST (block_store, add_nonempty_block) @@ -164,13 +172,13 @@ TEST (block_store, add_nonempty_block) ASSERT_TRUE (!store->init_error ()); nano::keypair key1; nano::open_block block (0, 1, 0, nano::keypair ().prv, 0, 0); + block.sideband_set ({}); auto hash1 (block.hash ()); block.signature = nano::sign_message (key1.prv, key1.pub, hash1); auto transaction (store->tx_begin_write ()); auto latest1 (store->block_get (transaction, hash1)); ASSERT_EQ (nullptr, latest1); - nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, hash1, block, sideband); + store->block_put (transaction, hash1, block); auto latest2 (store->block_get (transaction, hash1)); ASSERT_NE (nullptr, latest2); ASSERT_EQ (block, *latest2); @@ -183,21 +191,21 @@ TEST (block_store, add_two_items) ASSERT_TRUE (!store->init_error ()); nano::keypair key1; nano::open_block block (0, 1, 1, nano::keypair ().prv, 0, 0); + block.sideband_set ({}); auto hash1 (block.hash ()); block.signature = nano::sign_message (key1.prv, key1.pub, hash1); auto transaction (store->tx_begin_write ()); auto latest1 (store->block_get (transaction, hash1)); ASSERT_EQ (nullptr, latest1); nano::open_block block2 (0, 1, 3, nano::keypair ().prv, 0, 0); + block2.sideband_set ({}); block2.hashables.account = 3; auto hash2 (block2.hash ()); block2.signature = nano::sign_message (key1.prv, key1.pub, hash2); auto latest2 (store->block_get (transaction, hash2)); ASSERT_EQ (nullptr, latest2); - nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, hash1, block, sideband); - nano::block_sideband sideband2 (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, hash2, block2, sideband2); + store->block_put (transaction, hash1, block); + store->block_put (transaction, hash2, block2); auto latest3 (store->block_get (transaction, hash1)); ASSERT_NE (nullptr, latest3); ASSERT_EQ (block, *latest3); @@ -215,15 +223,15 @@ TEST (block_store, add_receive) nano::keypair key1; nano::keypair key2; nano::open_block block1 (0, 1, 0, nano::keypair ().prv, 0, 0); + block1.sideband_set ({}); auto transaction (store->tx_begin_write ()); - nano::block_sideband sideband1 (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, block1.hash (), block1, sideband1); + store->block_put (transaction, block1.hash (), block1); nano::receive_block block (block1.hash (), 1, nano::keypair ().prv, 2, 3); + block.sideband_set ({}); nano::block_hash hash1 (block.hash ()); auto latest1 (store->block_get (transaction, hash1)); ASSERT_EQ (nullptr, latest1); - nano::block_sideband sideband (nano::block_type::receive, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, hash1, block, sideband); + store->block_put (transaction, hash1, block); auto latest2 (store->block_get (transaction, hash1)); ASSERT_NE (nullptr, latest2); ASSERT_EQ (block, *latest2); @@ -463,9 +471,9 @@ TEST (block_store, one_block) auto store = nano::make_store (logger, nano::unique_path ()); ASSERT_TRUE (!store->init_error ()); nano::open_block block1 (0, 1, 0, nano::keypair ().prv, 0, 0); + block1.sideband_set ({}); auto transaction (store->tx_begin_write ()); - nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, block1.hash (), block1, sideband); + store->block_put (transaction, block1.hash (), block1); ASSERT_TRUE (store->block_exists (transaction, block1.hash ())); } @@ -560,19 +568,19 @@ TEST (block_store, two_block) auto store = nano::make_store (logger, nano::unique_path ()); ASSERT_TRUE (!store->init_error ()); nano::open_block block1 (0, 1, 1, nano::keypair ().prv, 0, 0); + block1.sideband_set ({}); block1.hashables.account = 1; std::vector hashes; std::vector blocks; hashes.push_back (block1.hash ()); blocks.push_back (block1); auto transaction (store->tx_begin_write ()); - nano::block_sideband sideband1 (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, hashes[0], block1, sideband1); + store->block_put (transaction, hashes[0], block1); nano::open_block block2 (0, 1, 2, nano::keypair ().prv, 0, 0); + block2.sideband_set ({}); hashes.push_back (block2.hash ()); blocks.push_back (block2); - nano::block_sideband sideband2 (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, hashes[1], block2, sideband2); + store->block_put (transaction, hashes[1], block2); ASSERT_TRUE (store->block_exists (transaction, block1.hash ())); ASSERT_TRUE (store->block_exists (transaction, block2.hash ())); } @@ -755,12 +763,12 @@ TEST (block_store, block_replace) auto store = nano::make_store (logger, nano::unique_path ()); ASSERT_TRUE (!store->init_error ()); nano::send_block send1 (0, 0, 0, nano::keypair ().prv, 0, 1); + send1.sideband_set ({}); nano::send_block send2 (0, 0, 0, nano::keypair ().prv, 0, 2); + send2.sideband_set ({}); auto transaction (store->tx_begin_write ()); - nano::block_sideband sideband1 (nano::block_type::send, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, 0, send1, sideband1); - nano::block_sideband sideband2 (nano::block_type::send, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, 0, send2, sideband2); + store->block_put (transaction, 0, send1); + store->block_put (transaction, 0, send2); auto block3 (store->block_get (transaction, 0)); ASSERT_NE (nullptr, block3); ASSERT_EQ (2, block3->block_work ()); @@ -775,9 +783,9 @@ TEST (block_store, block_count) auto transaction (store->tx_begin_write ()); ASSERT_EQ (0, store->block_count (transaction).sum ()); nano::open_block block (0, 1, 0, nano::keypair ().prv, 0, 0); + block.sideband_set ({}); auto hash1 (block.hash ()); - nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, hash1, block, sideband); + store->block_put (transaction, hash1, block); } auto transaction (store->tx_begin_read ()); ASSERT_EQ (1, store->block_count (transaction).sum ()); @@ -1172,13 +1180,13 @@ TEST (block_store, state_block) nano::genesis genesis; nano::keypair key1; nano::state_block block1 (1, genesis.hash (), 3, 4, 6, key1.prv, key1.pub, 7); + block1.sideband_set ({}); { nano::ledger_cache ledger_cache; auto transaction (store->tx_begin_write ()); store->initialize (transaction, genesis, ledger_cache); ASSERT_EQ (nano::block_type::state, block1.type ()); - nano::block_sideband sideband1 (nano::block_type::state, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (transaction, block1.hash (), block1, sideband1); + store->block_put (transaction, block1.hash (), block1); ASSERT_TRUE (store->block_exists (transaction, block1.hash ())); auto block2 (store->block_get (transaction, block1.hash ())); ASSERT_NE (nullptr, block2); @@ -1209,10 +1217,9 @@ TEST (mdb_block_store, upgrade_sideband_genesis) nano::ledger_cache ledger_cache; store.initialize (transaction, genesis, ledger_cache); modify_account_info_to_v13 (store, transaction, nano::genesis_account, nano::genesis_hash); - nano::block_sideband sideband; - auto genesis_block (store.block_get (transaction, genesis.hash (), &sideband)); + auto genesis_block (store.block_get (transaction, genesis.hash ())); ASSERT_NE (nullptr, genesis_block); - ASSERT_EQ (1, sideband.height); + ASSERT_EQ (1, genesis_block->sideband ().height); ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "state_v1", MDB_CREATE, &store.state_blocks_v1)); write_sideband_v12 (store, transaction, *genesis_block, 0, store.open_blocks); nano::block_sideband_v14 sideband1; @@ -1226,10 +1233,9 @@ TEST (mdb_block_store, upgrade_sideband_genesis) ASSERT_FALSE (store.init_error ()); auto transaction (store.tx_begin_read ()); ASSERT_TRUE (store.full_sideband (transaction)); - nano::block_sideband sideband; - auto genesis_block (store.block_get (transaction, genesis.hash (), &sideband)); + auto genesis_block (store.block_get (transaction, genesis.hash ())); ASSERT_NE (nullptr, genesis_block); - ASSERT_EQ (1, sideband.height); + ASSERT_EQ (1, genesis_block->sideband ().height); } TEST (mdb_block_store, upgrade_sideband_two_blocks) @@ -1265,14 +1271,12 @@ TEST (mdb_block_store, upgrade_sideband_two_blocks) ASSERT_FALSE (store.init_error ()); auto transaction (store.tx_begin_read ()); ASSERT_TRUE (store.full_sideband (transaction)); - nano::block_sideband sideband; - auto genesis_block (store.block_get (transaction, genesis.hash (), &sideband)); + auto genesis_block (store.block_get (transaction, genesis.hash ())); ASSERT_NE (nullptr, genesis_block); - ASSERT_EQ (1, sideband.height); - nano::block_sideband sideband2; - auto block2 (store.block_get (transaction, hash2, &sideband2)); + ASSERT_EQ (1, genesis_block->sideband ().height); + auto block2 (store.block_get (transaction, hash2)); ASSERT_NE (nullptr, block2); - ASSERT_EQ (2, sideband2.height); + ASSERT_EQ (2, block2->sideband ().height); } TEST (mdb_block_store, upgrade_sideband_two_accounts) @@ -1314,18 +1318,15 @@ TEST (mdb_block_store, upgrade_sideband_two_accounts) ASSERT_FALSE (store.init_error ()); auto transaction (store.tx_begin_read ()); ASSERT_TRUE (store.full_sideband (transaction)); - nano::block_sideband sideband; - auto genesis_block (store.block_get (transaction, genesis.hash (), &sideband)); + auto genesis_block (store.block_get (transaction, genesis.hash ())); ASSERT_NE (nullptr, genesis_block); - ASSERT_EQ (1, sideband.height); - nano::block_sideband sideband2; - auto block2 (store.block_get (transaction, hash2, &sideband2)); + ASSERT_EQ (1, genesis_block->sideband ().height); + auto block2 (store.block_get (transaction, hash2)); ASSERT_NE (nullptr, block2); - ASSERT_EQ (2, sideband2.height); - nano::block_sideband sideband3; - auto block3 (store.block_get (transaction, hash3, &sideband3)); + ASSERT_EQ (2, block2->sideband ().height); + auto block3 (store.block_get (transaction, hash3)); ASSERT_NE (nullptr, block3); - ASSERT_EQ (1, sideband3.height); + ASSERT_EQ (1, block3->sideband ().height); } TEST (mdb_block_store, insert_after_legacy) @@ -1406,9 +1407,8 @@ TEST (mdb_block_store, upgrade_sideband_epoch) auto transaction (store.tx_begin_write ()); ASSERT_TRUE (store.full_sideband (transaction)); ASSERT_EQ (nano::epoch::epoch_1, store.block_version (transaction, hash2)); - nano::block_sideband sideband; - auto block1 (store.block_get (transaction, hash2, &sideband)); - ASSERT_NE (0, sideband.height); + auto block1 (store.block_get (transaction, hash2)); + ASSERT_NE (0, block1->sideband ().height); nano::state_block block2 (nano::test_genesis_key.pub, hash2, nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::test_genesis_key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (hash2)); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, block2).code); ASSERT_EQ (nano::epoch::epoch_1, store.block_version (transaction, block2.hash ())); @@ -1452,42 +1452,30 @@ TEST (mdb_block_store, sideband_height) ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, state_receive).code); nano::open_block open (state_send3.hash (), nano::test_genesis_key.pub, key3.pub, key3.prv, key3.pub, *pool.generate (key3.pub)); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, open).code); - nano::block_sideband sideband1; - auto block1 (store.block_get (transaction, genesis.hash (), &sideband1)); - ASSERT_EQ (sideband1.height, 1); - nano::block_sideband sideband2; - auto block2 (store.block_get (transaction, send.hash (), &sideband2)); - ASSERT_EQ (sideband2.height, 2); - nano::block_sideband sideband3; - auto block3 (store.block_get (transaction, receive.hash (), &sideband3)); - ASSERT_EQ (sideband3.height, 3); - nano::block_sideband sideband4; - auto block4 (store.block_get (transaction, change.hash (), &sideband4)); - ASSERT_EQ (sideband4.height, 4); - nano::block_sideband sideband5; - auto block5 (store.block_get (transaction, state_send1.hash (), &sideband5)); - ASSERT_EQ (sideband5.height, 5); - nano::block_sideband sideband6; - auto block6 (store.block_get (transaction, state_send2.hash (), &sideband6)); - ASSERT_EQ (sideband6.height, 6); - nano::block_sideband sideband7; - auto block7 (store.block_get (transaction, state_send3.hash (), &sideband7)); - ASSERT_EQ (sideband7.height, 7); - nano::block_sideband sideband8; - auto block8 (store.block_get (transaction, state_open.hash (), &sideband8)); - ASSERT_EQ (sideband8.height, 1); - nano::block_sideband sideband9; - auto block9 (store.block_get (transaction, epoch.hash (), &sideband9)); - ASSERT_EQ (sideband9.height, 2); - nano::block_sideband sideband10; - auto block10 (store.block_get (transaction, epoch_open.hash (), &sideband10)); - ASSERT_EQ (sideband10.height, 1); - nano::block_sideband sideband11; - auto block11 (store.block_get (transaction, state_receive.hash (), &sideband11)); - ASSERT_EQ (sideband11.height, 2); - nano::block_sideband sideband12; - auto block12 (store.block_get (transaction, open.hash (), &sideband12)); - ASSERT_EQ (sideband12.height, 1); + auto block1 (store.block_get (transaction, genesis.hash ())); + ASSERT_EQ (block1->sideband ().height, 1); + auto block2 (store.block_get (transaction, send.hash ())); + ASSERT_EQ (block2->sideband ().height, 2); + auto block3 (store.block_get (transaction, receive.hash ())); + ASSERT_EQ (block3->sideband ().height, 3); + auto block4 (store.block_get (transaction, change.hash ())); + ASSERT_EQ (block4->sideband ().height, 4); + auto block5 (store.block_get (transaction, state_send1.hash ())); + ASSERT_EQ (block5->sideband ().height, 5); + auto block6 (store.block_get (transaction, state_send2.hash ())); + ASSERT_EQ (block6->sideband ().height, 6); + auto block7 (store.block_get (transaction, state_send3.hash ())); + ASSERT_EQ (block7->sideband ().height, 7); + auto block8 (store.block_get (transaction, state_open.hash ())); + ASSERT_EQ (block8->sideband ().height, 1); + auto block9 (store.block_get (transaction, epoch.hash ())); + ASSERT_EQ (block9->sideband ().height, 2); + auto block10 (store.block_get (transaction, epoch_open.hash ())); + ASSERT_EQ (block10->sideband ().height, 1); + auto block11 (store.block_get (transaction, state_receive.hash ())); + ASSERT_EQ (block11->sideband ().height, 2); + auto block12 (store.block_get (transaction, open.hash ())); + ASSERT_EQ (block12->sideband ().height, 1); } TEST (block_store, peers) @@ -1751,14 +1739,12 @@ TEST (mdb_block_store, upgrade_v14_v15) ASSERT_NE (error_get_state_v1, MDB_SUCCESS); // Check that the epochs are set correctly for the sideband, accounts and pending entries - nano::block_sideband sideband; - auto block = store.block_get (transaction, state_send.hash (), &sideband); + auto block = store.block_get (transaction, state_send.hash ()); ASSERT_NE (block, nullptr); - ASSERT_EQ (sideband.details.epoch, nano::epoch::epoch_1); - block = store.block_get (transaction, send.hash (), &sideband); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1); + block = store.block_get (transaction, send.hash ()); ASSERT_NE (block, nullptr); - nano::block_sideband sideband1; - ASSERT_EQ (sideband1.details.epoch, nano::epoch::epoch_0); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_0); ASSERT_EQ (info.epoch (), nano::epoch::epoch_1); nano::pending_info pending_info; store.pending_get (transaction, nano::pending_key (nano::test_genesis_key.pub, send.hash ()), pending_info); @@ -1818,7 +1804,7 @@ TEST (mdb_block_store, upgrade_v16_v17) nano::state_block block2 (nano::test_genesis_key.pub, block1.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio - 1, nano::test_genesis_key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (block1.hash ())); nano::state_block block3 (nano::test_genesis_key.pub, block2.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio - 2, nano::test_genesis_key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (block2.hash ())); - auto code = [block1, block2, block3](auto confirmation_height, nano::block_hash const & expected_cemented_frontier) { + auto code = [&block1, &block2, &block3](auto confirmation_height, nano::block_hash const & expected_cemented_frontier) { auto path (nano::unique_path ()); nano::mdb_val value; { @@ -1937,124 +1923,112 @@ TEST (mdb_block_store, upgrade_v17_v18) // Check that sidebands are correctly populated { // Non-state unaffected - nano::block_sideband sideband; - auto block = store.block_get (transaction, send_zero.hash (), &sideband); + auto block = store.block_get (transaction, send_zero.hash ()); ASSERT_NE (block, nullptr); // All defaults - ASSERT_EQ (sideband.details.epoch, nano::epoch::epoch_0); - ASSERT_FALSE (sideband.details.is_epoch); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_0); + ASSERT_FALSE (block->sideband ().details.is_epoch); + ASSERT_FALSE (block->sideband ().details.is_send); + ASSERT_FALSE (block->sideband ().details.is_receive); } { // State receive from old zero send - nano::block_sideband sideband; - auto block = store.block_get (transaction, state_receive_zero.hash (), &sideband); + auto block = store.block_get (transaction, state_receive_zero.hash ()); ASSERT_NE (block, nullptr); - ASSERT_EQ (sideband.details.epoch, nano::epoch::epoch_0); - ASSERT_FALSE (sideband.details.is_epoch); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_TRUE (sideband.details.is_receive); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_0); + ASSERT_FALSE (block->sideband ().details.is_epoch); + ASSERT_FALSE (block->sideband ().details.is_send); + ASSERT_TRUE (block->sideband ().details.is_receive); } { // Epoch - nano::block_sideband sideband; - auto block = store.block_get (transaction, epoch.hash (), &sideband); + auto block = store.block_get (transaction, epoch.hash ()); ASSERT_NE (block, nullptr); - ASSERT_EQ (sideband.details.epoch, nano::epoch::epoch_1); - ASSERT_TRUE (sideband.details.is_epoch); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1); + ASSERT_TRUE (block->sideband ().details.is_epoch); + ASSERT_FALSE (block->sideband ().details.is_send); + ASSERT_FALSE (block->sideband ().details.is_receive); } { // State send - nano::block_sideband sideband; - auto block = store.block_get (transaction, state_send.hash (), &sideband); + auto block = store.block_get (transaction, state_send.hash ()); ASSERT_NE (block, nullptr); - ASSERT_EQ (sideband.details.epoch, nano::epoch::epoch_1); - ASSERT_FALSE (sideband.details.is_epoch); - ASSERT_TRUE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1); + ASSERT_FALSE (block->sideband ().details.is_epoch); + ASSERT_TRUE (block->sideband ().details.is_send); + ASSERT_FALSE (block->sideband ().details.is_receive); } { // State receive - nano::block_sideband sideband; - auto block = store.block_get (transaction, state_receive.hash (), &sideband); + auto block = store.block_get (transaction, state_receive.hash ()); ASSERT_NE (block, nullptr); - ASSERT_EQ (sideband.details.epoch, nano::epoch::epoch_1); - ASSERT_FALSE (sideband.details.is_epoch); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_TRUE (sideband.details.is_receive); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1); + ASSERT_FALSE (block->sideband ().details.is_epoch); + ASSERT_FALSE (block->sideband ().details.is_send); + ASSERT_TRUE (block->sideband ().details.is_receive); } { // State change - nano::block_sideband sideband; - auto block = store.block_get (transaction, state_change.hash (), &sideband); + auto block = store.block_get (transaction, state_change.hash ()); ASSERT_NE (block, nullptr); - ASSERT_EQ (sideband.details.epoch, nano::epoch::epoch_1); - ASSERT_FALSE (sideband.details.is_epoch); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1); + ASSERT_FALSE (block->sideband ().details.is_epoch); + ASSERT_FALSE (block->sideband ().details.is_send); + ASSERT_FALSE (block->sideband ().details.is_receive); } { // State send + change - nano::block_sideband sideband; - auto block = store.block_get (transaction, state_send_change.hash (), &sideband); + auto block = store.block_get (transaction, state_send_change.hash ()); ASSERT_NE (block, nullptr); - ASSERT_EQ (sideband.details.epoch, nano::epoch::epoch_1); - ASSERT_FALSE (sideband.details.is_epoch); - ASSERT_TRUE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1); + ASSERT_FALSE (block->sideband ().details.is_epoch); + ASSERT_TRUE (block->sideband ().details.is_send); + ASSERT_FALSE (block->sideband ().details.is_receive); } { // Epoch on unopened account - nano::block_sideband sideband; - auto block = store.block_get (transaction, epoch_first.hash (), &sideband); + auto block = store.block_get (transaction, epoch_first.hash ()); ASSERT_NE (block, nullptr); - ASSERT_EQ (sideband.details.epoch, nano::epoch::epoch_2); - ASSERT_TRUE (sideband.details.is_epoch); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_2); + ASSERT_TRUE (block->sideband ().details.is_epoch); + ASSERT_FALSE (block->sideband ().details.is_send); + ASSERT_FALSE (block->sideband ().details.is_receive); } { // State open following epoch - nano::block_sideband sideband; - auto block = store.block_get (transaction, state_receive2.hash (), &sideband); + auto block = store.block_get (transaction, state_receive2.hash ()); ASSERT_NE (block, nullptr); - ASSERT_EQ (sideband.details.epoch, nano::epoch::epoch_2); - ASSERT_FALSE (sideband.details.is_epoch); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_TRUE (sideband.details.is_receive); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_2); + ASSERT_FALSE (block->sideband ().details.is_epoch); + ASSERT_FALSE (block->sideband ().details.is_send); + ASSERT_TRUE (block->sideband ().details.is_receive); } { // Another state send - nano::block_sideband sideband; - auto block = store.block_get (transaction, state_send2.hash (), &sideband); + auto block = store.block_get (transaction, state_send2.hash ()); ASSERT_NE (block, nullptr); - ASSERT_EQ (sideband.details.epoch, nano::epoch::epoch_1); - ASSERT_FALSE (sideband.details.is_epoch); - ASSERT_TRUE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1); + ASSERT_FALSE (block->sideband ().details.is_epoch); + ASSERT_TRUE (block->sideband ().details.is_send); + ASSERT_FALSE (block->sideband ().details.is_receive); } { // State open - nano::block_sideband sideband; - auto block = store.block_get (transaction, state_open.hash (), &sideband); + auto block = store.block_get (transaction, state_open.hash ()); ASSERT_NE (block, nullptr); - ASSERT_EQ (sideband.details.epoch, nano::epoch::epoch_1); - ASSERT_FALSE (sideband.details.is_epoch); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_TRUE (sideband.details.is_receive); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1); + ASSERT_FALSE (block->sideband ().details.is_epoch); + ASSERT_FALSE (block->sideband ().details.is_send); + ASSERT_TRUE (block->sideband ().details.is_receive); } { // State send to an epoch link - nano::block_sideband sideband; - auto block = store.block_get (transaction, state_send_epoch_link.hash (), &sideband); + auto block = store.block_get (transaction, state_send_epoch_link.hash ()); ASSERT_NE (block, nullptr); - ASSERT_EQ (sideband.details.epoch, nano::epoch::epoch_1); - ASSERT_FALSE (sideband.details.is_epoch); - ASSERT_TRUE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); + ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1); + ASSERT_FALSE (block->sideband ().details.is_epoch); + ASSERT_TRUE (block->sideband ().details.is_send); + ASSERT_FALSE (block->sideband ().details.is_receive); } // Version should be correct ASSERT_LT (17, store.version_get (transaction)); @@ -2166,8 +2140,8 @@ TEST (mdb_block_store, upgrade_confirmation_height_many) { nano::account account (i); nano::open_block open (1, nano::genesis_account, 3, nullptr); - nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store.block_put (transaction, open.hash (), open, sideband); + open.sideband_set ({}); + store.block_put (transaction, open.hash (), open); nano::account_info_v13 account_info_v13 (open.hash (), open.hash (), open.hash (), 3, 4, 1, nano::epoch::epoch_0); auto status (mdb_put (store.env.tx (transaction), store.accounts_v0, nano::mdb_val (account), nano::mdb_val (account_info_v13), 0)); ASSERT_EQ (status, 0); @@ -2224,6 +2198,7 @@ TEST (block_store, reset_renew_existing_transaction) nano::keypair key1; nano::open_block block (0, 1, 1, nano::keypair ().prv, 0, 0); + block.sideband_set ({}); auto hash1 (block.hash ()); auto read_transaction = store->tx_begin_read (); @@ -2237,8 +2212,7 @@ TEST (block_store, reset_renew_existing_transaction) // Write the block { auto write_transaction (store->tx_begin_write ()); - nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store->block_put (write_transaction, hash1, block, sideband); + store->block_put (write_transaction, hash1, block); } read_transaction.renew (); @@ -2297,11 +2271,10 @@ void write_sideband_v12 (nano::mdb_store & store_a, nano::transaction & transact void write_sideband_v14 (nano::mdb_store & store_a, nano::transaction & transaction_a, nano::block const & block_a, MDB_dbi db_a) { - nano::block_sideband sideband; - auto block = store_a.block_get (transaction_a, block_a.hash (), &sideband); + auto block = store_a.block_get (transaction_a, block_a.hash ()); ASSERT_NE (block, nullptr); - nano::block_sideband_v14 sideband_v14 (sideband.type, sideband.account, sideband.successor, sideband.balance, sideband.timestamp, sideband.height); + nano::block_sideband_v14 sideband_v14 (block->type (), block->sideband ().account, block->sideband ().successor, block->sideband ().balance, block->sideband ().timestamp, block->sideband ().height); std::vector data; { nano::vectorstream stream (data); @@ -2310,23 +2283,22 @@ void write_sideband_v14 (nano::mdb_store & store_a, nano::transaction & transact } MDB_val val{ data.size (), data.data () }; - ASSERT_FALSE (mdb_put (store_a.env.tx (transaction_a), sideband.details.epoch == nano::epoch::epoch_0 ? store_a.state_blocks_v0 : store_a.state_blocks_v1, nano::mdb_val (block_a.hash ()), &val, 0)); + ASSERT_FALSE (mdb_put (store_a.env.tx (transaction_a), block->sideband ().details.epoch == nano::epoch::epoch_0 ? store_a.state_blocks_v0 : store_a.state_blocks_v1, nano::mdb_val (block_a.hash ()), &val, 0)); } void write_sideband_v15 (nano::mdb_store & store_a, nano::transaction & transaction_a, nano::block const & block_a) { - nano::block_sideband sideband; - auto block = store_a.block_get (transaction_a, block_a.hash (), &sideband); + auto block = store_a.block_get (transaction_a, block_a.hash ()); ASSERT_NE (block, nullptr); - ASSERT_LE (sideband.details.epoch, nano::epoch::max); + ASSERT_LE (block->sideband ().details.epoch, nano::epoch::max); // Simulated by writing 0 on every of the most significant bits, leaving out epoch only, as if pre-upgrade - nano::block_sideband sideband_v15 (sideband.type, sideband.account, sideband.successor, sideband.balance, sideband.timestamp, sideband.height, sideband.details.epoch, false, false, false); + nano::block_sideband sideband_v15 (block->sideband ().account, block->sideband ().successor, block->sideband ().balance, block->sideband ().timestamp, block->sideband ().height, block->sideband ().details.epoch, false, false, false); std::vector data; { nano::vectorstream stream (data); block_a.serialize (stream); - sideband_v15.serialize (stream); + sideband_v15.serialize (stream, block_a.type ()); } MDB_val val{ data.size (), data.data () }; diff --git a/nano/core_test/epochs.cpp b/nano/core_test/epochs.cpp index 96a9c3292d..4355885ab4 100644 --- a/nano/core_test/epochs.cpp +++ b/nano/core_test/epochs.cpp @@ -1,5 +1,5 @@ +#include #include -#include #include diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index b71139c6a0..08e0b6adc4 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -71,6 +71,21 @@ TEST (system, system_genesis) } } +TEST (ledger, process_modifies_sideband) +{ + nano::logger_mt logger; + auto store = nano::make_store (logger, nano::unique_path ()); + ASSERT_TRUE (!store->init_error ()); + nano::stat stats; + nano::ledger ledger (*store, stats); + nano::genesis genesis; + store->initialize (store->tx_begin_write (), genesis, ledger.cache); + nano::work_pool pool (std::numeric_limits::max ()); + nano::state_block send1 (nano::genesis_account, genesis.hash (), nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, nano::genesis_account, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (genesis.hash ())); + ASSERT_EQ (nano::process_result::progress, ledger.process (store->tx_begin_write (), send1).code); + ASSERT_EQ (send1.sideband ().timestamp, store->block_get (store->tx_begin_read (), send1.hash ())->sideband ().timestamp); +} + // Create a send block and publish it. TEST (ledger, process_send) { @@ -92,6 +107,8 @@ TEST (ledger, process_send) ASSERT_EQ (1, info1.block_count); // This was a valid block, it should progress. auto return1 (ledger.process (transaction, send)); + ASSERT_EQ (nano::test_genesis_key.pub, send.sideband ().account); + ASSERT_EQ (2, send.sideband ().height); ASSERT_EQ (nano::genesis_amount - 50, ledger.amount (transaction, hash1)); ASSERT_TRUE (store->frontier_get (transaction, info1.head).is_zero ()); ASSERT_EQ (nano::test_genesis_key.pub, store->frontier_get (transaction, hash1)); @@ -113,6 +130,10 @@ TEST (ledger, process_send) nano::block_hash hash2 (open.hash ()); // This was a valid block, it should progress. auto return2 (ledger.process (transaction, open)); + ASSERT_EQ (nano::process_result::progress, return2.code); + ASSERT_EQ (key2.pub, open.sideband ().account); + ASSERT_EQ (nano::genesis_amount - 50, open.sideband ().balance.number ()); + ASSERT_EQ (1, open.sideband ().height); ASSERT_EQ (nano::genesis_amount - 50, ledger.amount (transaction, hash2)); ASSERT_EQ (nano::process_result::progress, return2.code); ASSERT_EQ (key2.pub, return2.account); @@ -190,6 +211,9 @@ TEST (ledger, process_receive) auto return1 (ledger.process (transaction, open)); ASSERT_EQ (nano::process_result::progress, return1.code); ASSERT_EQ (key2.pub, return1.account); + ASSERT_EQ (key2.pub, open.sideband ().account); + ASSERT_EQ (nano::genesis_amount - 50, open.sideband ().balance.number ()); + ASSERT_EQ (1, open.sideband ().height); ASSERT_EQ (nano::genesis_amount - 50, return1.amount.number ()); ASSERT_EQ (nano::genesis_amount - 50, ledger.weight (key3.pub)); nano::send_block send2 (hash1, key2.pub, 25, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (hash1)); @@ -199,6 +223,9 @@ TEST (ledger, process_receive) auto hash4 (receive.hash ()); ASSERT_EQ (key2.pub, store->frontier_get (transaction, hash2)); auto return2 (ledger.process (transaction, receive)); + ASSERT_EQ (key2.pub, receive.sideband ().account); + ASSERT_EQ (nano::genesis_amount - 25, receive.sideband ().balance.number ()); + ASSERT_EQ (2, receive.sideband ().height); ASSERT_EQ (25, ledger.amount (transaction, hash4)); ASSERT_TRUE (store->frontier_get (transaction, hash2).is_zero ()); ASSERT_EQ (key2.pub, store->frontier_get (transaction, hash4)); @@ -1680,22 +1707,21 @@ TEST (ledger, state_send_receive) nano::state_block send1 (nano::genesis_account, genesis.hash (), nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, nano::genesis_account, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (genesis.hash ())); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, send1).code); ASSERT_TRUE (store->block_exists (transaction, send1.hash ())); - nano::block_sideband sideband; - auto send2 (store->block_get (transaction, send1.hash (), &sideband)); + auto send2 (store->block_get (transaction, send1.hash ())); ASSERT_NE (nullptr, send2); ASSERT_EQ (send1, *send2); ASSERT_EQ (nano::genesis_amount - nano::Gxrb_ratio, ledger.balance (transaction, send1.hash ())); ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, send1.hash ())); ASSERT_EQ (nano::genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::genesis_account)); ASSERT_TRUE (store->pending_exists (transaction, nano::pending_key (nano::genesis_account, send1.hash ()))); - ASSERT_TRUE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); - ASSERT_FALSE (sideband.details.is_epoch); + ASSERT_EQ (2, send2->sideband ().height); + ASSERT_TRUE (send2->sideband ().details.is_send); + ASSERT_FALSE (send2->sideband ().details.is_receive); + ASSERT_FALSE (send2->sideband ().details.is_epoch); nano::state_block receive1 (nano::genesis_account, send1.hash (), nano::genesis_account, nano::genesis_amount, send1.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (send1.hash ())); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, receive1).code); ASSERT_TRUE (store->block_exists (transaction, receive1.hash ())); - nano::block_sideband sideband2; - auto receive2 (store->block_get (transaction, receive1.hash (), &sideband2)); + auto receive2 (store->block_get (transaction, receive1.hash ())); ASSERT_NE (nullptr, receive2); ASSERT_EQ (receive1, *receive2); ASSERT_EQ (nano::genesis_amount, ledger.balance (transaction, receive1.hash ())); @@ -1703,9 +1729,10 @@ TEST (ledger, state_send_receive) ASSERT_EQ (nano::genesis_amount, ledger.weight (nano::genesis_account)); ASSERT_FALSE (store->pending_exists (transaction, nano::pending_key (nano::genesis_account, send1.hash ()))); ASSERT_EQ (store->account_count (transaction), ledger.cache.account_count); - ASSERT_FALSE (sideband2.details.is_send); - ASSERT_TRUE (sideband2.details.is_receive); - ASSERT_FALSE (sideband2.details.is_epoch); + ASSERT_EQ (3, receive2->sideband ().height); + ASSERT_FALSE (receive2->sideband ().details.is_send); + ASSERT_TRUE (receive2->sideband ().details.is_receive); + ASSERT_FALSE (receive2->sideband ().details.is_epoch); } TEST (ledger, state_receive) @@ -1731,16 +1758,16 @@ TEST (ledger, state_receive) nano::state_block receive1 (nano::genesis_account, send1.hash (), nano::genesis_account, nano::genesis_amount, send1.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (send1.hash ())); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, receive1).code); ASSERT_TRUE (store->block_exists (transaction, receive1.hash ())); - nano::block_sideband sideband; - auto receive2 (store->block_get (transaction, receive1.hash (), &sideband)); + auto receive2 (store->block_get (transaction, receive1.hash ())); ASSERT_NE (nullptr, receive2); ASSERT_EQ (receive1, *receive2); ASSERT_EQ (nano::genesis_amount, ledger.balance (transaction, receive1.hash ())); ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, receive1.hash ())); ASSERT_EQ (nano::genesis_amount, ledger.weight (nano::genesis_account)); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_TRUE (sideband.details.is_receive); - ASSERT_FALSE (sideband.details.is_epoch); + ASSERT_EQ (3, receive2->sideband ().height); + ASSERT_FALSE (receive2->sideband ().details.is_send); + ASSERT_TRUE (receive2->sideband ().details.is_receive); + ASSERT_FALSE (receive2->sideband ().details.is_epoch); } TEST (ledger, state_rep_change) @@ -1758,17 +1785,17 @@ TEST (ledger, state_rep_change) nano::state_block change1 (nano::genesis_account, genesis.hash (), rep.pub, nano::genesis_amount, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (genesis.hash ())); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, change1).code); ASSERT_TRUE (store->block_exists (transaction, change1.hash ())); - nano::block_sideband sideband; - auto change2 (store->block_get (transaction, change1.hash (), &sideband)); + auto change2 (store->block_get (transaction, change1.hash ())); ASSERT_NE (nullptr, change2); ASSERT_EQ (change1, *change2); ASSERT_EQ (nano::genesis_amount, ledger.balance (transaction, change1.hash ())); ASSERT_EQ (0, ledger.amount (transaction, change1.hash ())); ASSERT_EQ (0, ledger.weight (nano::genesis_account)); ASSERT_EQ (nano::genesis_amount, ledger.weight (rep.pub)); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); - ASSERT_FALSE (sideband.details.is_epoch); + ASSERT_EQ (2, change2->sideband ().height); + ASSERT_FALSE (change2->sideband ().details.is_send); + ASSERT_FALSE (change2->sideband ().details.is_receive); + ASSERT_FALSE (change2->sideband ().details.is_epoch); } TEST (ledger, state_open) @@ -1797,17 +1824,17 @@ TEST (ledger, state_open) ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, open1).code); ASSERT_FALSE (store->pending_exists (transaction, nano::pending_key (destination.pub, send1.hash ()))); ASSERT_TRUE (store->block_exists (transaction, open1.hash ())); - nano::block_sideband sideband; - auto open2 (store->block_get (transaction, open1.hash (), &sideband)); + auto open2 (store->block_get (transaction, open1.hash ())); ASSERT_NE (nullptr, open2); ASSERT_EQ (open1, *open2); ASSERT_EQ (nano::Gxrb_ratio, ledger.balance (transaction, open1.hash ())); ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, open1.hash ())); ASSERT_EQ (nano::genesis_amount, ledger.weight (nano::genesis_account)); ASSERT_EQ (ledger.cache.account_count, store->account_count (transaction)); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_TRUE (sideband.details.is_receive); - ASSERT_FALSE (sideband.details.is_epoch); + ASSERT_EQ (1, open2->sideband ().height); + ASSERT_FALSE (open2->sideband ().details.is_send); + ASSERT_TRUE (open2->sideband ().details.is_receive); + ASSERT_FALSE (open2->sideband ().details.is_epoch); } // Make sure old block types can't be inserted after a state block. @@ -2050,17 +2077,17 @@ TEST (ledger, state_send_change) nano::state_block send1 (nano::genesis_account, genesis.hash (), rep.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::genesis_account, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (genesis.hash ())); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, send1).code); ASSERT_TRUE (store->block_exists (transaction, send1.hash ())); - nano::block_sideband sideband; - auto send2 (store->block_get (transaction, send1.hash (), &sideband)); + auto send2 (store->block_get (transaction, send1.hash ())); ASSERT_NE (nullptr, send2); ASSERT_EQ (send1, *send2); ASSERT_EQ (nano::genesis_amount - nano::Gxrb_ratio, ledger.balance (transaction, send1.hash ())); ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, send1.hash ())); ASSERT_EQ (0, ledger.weight (nano::genesis_account)); ASSERT_EQ (nano::genesis_amount - nano::Gxrb_ratio, ledger.weight (rep.pub)); - ASSERT_TRUE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); - ASSERT_FALSE (sideband.details.is_epoch); + ASSERT_EQ (2, send2->sideband ().height); + ASSERT_TRUE (send2->sideband ().details.is_send); + ASSERT_FALSE (send2->sideband ().details.is_receive); + ASSERT_FALSE (send2->sideband ().details.is_epoch); } TEST (ledger, state_receive_change) @@ -2087,17 +2114,17 @@ TEST (ledger, state_receive_change) nano::state_block receive1 (nano::genesis_account, send1.hash (), rep.pub, nano::genesis_amount, send1.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (send1.hash ())); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, receive1).code); ASSERT_TRUE (store->block_exists (transaction, receive1.hash ())); - nano::block_sideband sideband2; - auto receive2 (store->block_get (transaction, receive1.hash (), &sideband2)); + auto receive2 (store->block_get (transaction, receive1.hash ())); ASSERT_NE (nullptr, receive2); ASSERT_EQ (receive1, *receive2); ASSERT_EQ (nano::genesis_amount, ledger.balance (transaction, receive1.hash ())); ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, receive1.hash ())); ASSERT_EQ (0, ledger.weight (nano::genesis_account)); ASSERT_EQ (nano::genesis_amount, ledger.weight (rep.pub)); - ASSERT_FALSE (sideband2.details.is_send); - ASSERT_TRUE (sideband2.details.is_receive); - ASSERT_FALSE (sideband2.details.is_epoch); + ASSERT_EQ (3, receive2->sideband ().height); + ASSERT_FALSE (receive2->sideband ().details.is_send); + ASSERT_TRUE (receive2->sideband ().details.is_receive); + ASSERT_FALSE (receive2->sideband ().details.is_epoch); } TEST (ledger, state_open_old) @@ -2341,13 +2368,9 @@ TEST (ledger, epoch_blocks_v1_general) nano::keypair destination; nano::state_block epoch1 (nano::genesis_account, genesis.hash (), nano::genesis_account, nano::genesis_amount, ledger.epoch_link (nano::epoch::epoch_1), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (genesis.hash ())); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, epoch1).code); - { - nano::block_sideband sideband; - (void)ledger.store.block_get (transaction, epoch1.hash (), &sideband); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); - ASSERT_TRUE (sideband.details.is_epoch); - } + ASSERT_FALSE (epoch1.sideband ().details.is_send); + ASSERT_FALSE (epoch1.sideband ().details.is_receive); + ASSERT_TRUE (epoch1.sideband ().details.is_epoch); nano::state_block epoch2 (nano::genesis_account, epoch1.hash (), nano::genesis_account, nano::genesis_amount, ledger.epoch_link (nano::epoch::epoch_1), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (epoch1.hash ())); ASSERT_EQ (nano::process_result::block_position, ledger.process (transaction, epoch2).code); nano::account_info genesis_info; @@ -2359,37 +2382,25 @@ TEST (ledger, epoch_blocks_v1_general) ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, epoch1).code); ASSERT_FALSE (ledger.store.account_get (transaction, nano::genesis_account, genesis_info)); ASSERT_EQ (genesis_info.epoch (), nano::epoch::epoch_1); - { - nano::block_sideband sideband; - (void)ledger.store.block_get (transaction, epoch1.hash (), &sideband); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); - ASSERT_TRUE (sideband.details.is_epoch); - } + ASSERT_FALSE (epoch1.sideband ().details.is_send); + ASSERT_FALSE (epoch1.sideband ().details.is_receive); + ASSERT_TRUE (epoch1.sideband ().details.is_epoch); nano::change_block change1 (epoch1.hash (), nano::genesis_account, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (epoch1.hash ())); ASSERT_EQ (nano::process_result::block_position, ledger.process (transaction, change1).code); nano::state_block send1 (nano::genesis_account, epoch1.hash (), nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, destination.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (epoch1.hash ())); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, send1).code); - { - nano::block_sideband sideband; - (void)ledger.store.block_get (transaction, send1.hash (), &sideband); - ASSERT_TRUE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); - ASSERT_FALSE (sideband.details.is_epoch); - } + ASSERT_TRUE (send1.sideband ().details.is_send); + ASSERT_FALSE (send1.sideband ().details.is_receive); + ASSERT_FALSE (send1.sideband ().details.is_epoch); nano::open_block open1 (send1.hash (), nano::genesis_account, destination.pub, destination.prv, destination.pub, *pool.generate (destination.pub)); ASSERT_EQ (nano::process_result::unreceivable, ledger.process (transaction, open1).code); nano::state_block epoch3 (destination.pub, 0, nano::genesis_account, 0, ledger.epoch_link (nano::epoch::epoch_1), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (destination.pub)); ASSERT_EQ (nano::process_result::representative_mismatch, ledger.process (transaction, epoch3).code); nano::state_block epoch4 (destination.pub, 0, 0, 0, ledger.epoch_link (nano::epoch::epoch_1), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (destination.pub)); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, epoch4).code); - { - nano::block_sideband sideband; - (void)ledger.store.block_get (transaction, epoch4.hash (), &sideband); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_receive); - ASSERT_TRUE (sideband.details.is_epoch); - } + ASSERT_FALSE (epoch4.sideband ().details.is_send); + ASSERT_FALSE (epoch4.sideband ().details.is_receive); + ASSERT_TRUE (epoch4.sideband ().details.is_epoch); nano::receive_block receive1 (epoch4.hash (), send1.hash (), destination.prv, destination.pub, *pool.generate (epoch4.hash ())); ASSERT_EQ (nano::process_result::block_position, ledger.process (transaction, receive1).code); nano::state_block receive2 (destination.pub, epoch4.hash (), destination.pub, nano::Gxrb_ratio, send1.hash (), destination.prv, destination.pub, *pool.generate (epoch4.hash ())); @@ -2399,13 +2410,9 @@ TEST (ledger, epoch_blocks_v1_general) ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, receive2.hash ())); ASSERT_EQ (nano::genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::genesis_account)); ASSERT_EQ (nano::Gxrb_ratio, ledger.weight (destination.pub)); - { - nano::block_sideband sideband; - (void)ledger.store.block_get (transaction, receive2.hash (), &sideband); - ASSERT_FALSE (sideband.details.is_send); - ASSERT_TRUE (sideband.details.is_receive); - ASSERT_FALSE (sideband.details.is_epoch); - } + ASSERT_FALSE (receive2.sideband ().details.is_send); + ASSERT_TRUE (receive2.sideband ().details.is_receive); + ASSERT_FALSE (receive2.sideband ().details.is_epoch); } TEST (ledger, epoch_blocks_v2_general) @@ -2804,12 +2811,12 @@ TEST (ledger, unchecked_epoch_invalid) nano::account_info info; ASSERT_FALSE (node1.store.account_get (transaction, destination.pub, info)); ASSERT_NE (info.epoch (), nano::epoch::epoch_1); - nano::block_sideband sideband; - ASSERT_NE (nullptr, node1.store.block_get (transaction, epoch2->hash (), &sideband)); - ASSERT_EQ (nano::epoch::epoch_0, sideband.details.epoch); - ASSERT_TRUE (sideband.details.is_send); - ASSERT_FALSE (sideband.details.is_epoch); - ASSERT_FALSE (sideband.details.is_receive); + auto epoch2_store (node1.store.block_get (transaction, epoch2->hash ())); + ASSERT_NE (nullptr, epoch2_store); + ASSERT_EQ (nano::epoch::epoch_0, epoch2_store->sideband ().details.epoch); + ASSERT_TRUE (epoch2_store->sideband ().details.is_send); + ASSERT_FALSE (epoch2_store->sideband ().details.is_epoch); + ASSERT_FALSE (epoch2_store->sideband ().details.is_receive); } } diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index eab6c6ef7f..2d5a8db1ac 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -1506,8 +1506,10 @@ TEST (node, fork_open_flip) nano::keypair rep1; nano::keypair rep2; auto send1 (std::make_shared (genesis.hash (), key1.pub, nano::genesis_amount - 1, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ()))); + // A copy is necessary to avoid data races during ledger processing, which sets the sideband + auto send1_copy (std::make_shared (*send1)); node1.process_active (send1); - node2.process_active (send1); + node2.process_active (send1_copy); // We should be keeping this block auto open1 (std::make_shared (send1->hash (), rep1.pub, key1.pub, key1.prv, key1.pub, *system.work.generate (key1.pub))); // This block should be evicted @@ -1741,9 +1743,6 @@ TEST (node, broadcast_elected) nano::keypair rep_big; nano::keypair rep_small; nano::keypair rep_other; - //std::cerr << "Big: " << rep_big.pub.to_account () << std::endl; - //std::cerr << "Small: " << rep_small.pub.to_account () << std::endl; - //std::cerr << "Other: " << rep_other.pub.to_account () << std::endl; { auto transaction0 (node0->store.tx_begin_write ()); auto transaction1 (node1->store.tx_begin_write ()); @@ -1784,14 +1783,14 @@ TEST (node, broadcast_elected) system.wallet (2)->insert_adhoc (rep_other.prv); auto fork0 (std::make_shared (node2->latest (nano::test_genesis_key.pub), rep_small.pub, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0)); node0->work_generate_blocking (*fork0); + // A copy is necessary to avoid data races during ledger processing, which sets the sideband + auto fork0_copy (std::make_shared (*fork0)); node0->process_active (fork0); - node1->process_active (fork0); + node1->process_active (fork0_copy); auto fork1 (std::make_shared (node2->latest (nano::test_genesis_key.pub), rep_big.pub, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0)); node0->work_generate_blocking (*fork1); system.wallet (2)->insert_adhoc (rep_small.prv); node2->process_active (fork1); - //std::cerr << "fork0: " << fork_hash.to_string () << std::endl; - //std::cerr << "fork1: " << fork1.hash ().to_string () << std::endl; system.deadline_set (10s); while (!node0->ledger.block_exists (fork0->hash ()) || !node1->ledger.block_exists (fork0->hash ())) { @@ -2486,15 +2485,17 @@ TEST (node, block_confirm) nano::keypair key; system.wallet (1)->insert_adhoc (nano::test_genesis_key.prv); auto send1 (std::make_shared (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *node1.work_generate_blocking (genesis.hash ()))); + // A copy is necessary to avoid data races during ledger processing, which sets the sideband + auto send1_copy (std::make_shared (*send1)); node1.block_processor.add (send1, nano::seconds_since_epoch ()); - node2.block_processor.add (send1, nano::seconds_since_epoch ()); + node2.block_processor.add (send1_copy, nano::seconds_since_epoch ()); system.deadline_set (std::chrono::seconds (5)); - while (!node1.ledger.block_exists (send1->hash ()) || !node2.ledger.block_exists (send1->hash ())) + while (!node1.ledger.block_exists (send1->hash ()) || !node2.ledger.block_exists (send1_copy->hash ())) { ASSERT_NO_ERROR (system.poll ()); } ASSERT_TRUE (node1.ledger.block_exists (send1->hash ())); - ASSERT_TRUE (node2.ledger.block_exists (send1->hash ())); + ASSERT_TRUE (node2.ledger.block_exists (send1_copy->hash ())); auto send2 (std::make_shared (nano::test_genesis_key.pub, send1->hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio * 2, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *node1.work_generate_blocking (send1->hash ()))); { auto transaction (node1.store.tx_begin_write ()); diff --git a/nano/core_test/versioning.cpp b/nano/core_test/versioning.cpp index 7e5c8696d5..f47bec0b89 100644 --- a/nano/core_test/versioning.cpp +++ b/nano/core_test/versioning.cpp @@ -11,14 +11,14 @@ TEST (versioning, account_info_v1) auto file (nano::unique_path ()); nano::account account (1); nano::open_block open (1, 2, 3, nullptr); + open.sideband_set ({}); nano::account_info_v1 v1 (open.hash (), open.hash (), 3, 4); { nano::logger_mt logger; nano::mdb_store store (logger, file); ASSERT_FALSE (store.init_error ()); auto transaction (store.tx_begin_write ()); - nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store.block_put (transaction, open.hash (), open, sideband); + store.block_put (transaction, open.hash (), open); auto status (mdb_put (store.env.tx (transaction), store.accounts_v0, nano::mdb_val (account), nano::mdb_val (sizeof (v1), &v1), 0)); ASSERT_EQ (0, status); store.version_put (transaction, 1); @@ -47,14 +47,14 @@ TEST (versioning, account_info_v5) auto file (nano::unique_path ()); nano::account account (1); nano::open_block open (1, 2, 3, nullptr); + open.sideband_set ({}); nano::account_info_v5 v5 (open.hash (), open.hash (), open.hash (), 3, 4); { nano::logger_mt logger; nano::mdb_store store (logger, file); ASSERT_FALSE (store.init_error ()); auto transaction (store.tx_begin_write ()); - nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store.block_put (transaction, open.hash (), open, sideband); + store.block_put (transaction, open.hash (), open); auto status (mdb_put (store.env.tx (transaction), store.accounts_v0, nano::mdb_val (account), nano::mdb_val (sizeof (v5), &v5), 0)); ASSERT_EQ (0, status); store.version_put (transaction, 5); @@ -83,14 +83,14 @@ TEST (versioning, account_info_v13) auto file (nano::unique_path ()); nano::account account (1); nano::open_block open (1, 2, 3, nullptr); + open.sideband_set ({}); nano::account_info_v13 v13 (open.hash (), open.hash (), open.hash (), 3, 4, 10, nano::epoch::epoch_0); { nano::logger_mt logger; nano::mdb_store store (logger, file); ASSERT_FALSE (store.init_error ()); auto transaction (store.tx_begin_write ()); - nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0, nano::epoch::epoch_0, false, false, false); - store.block_put (transaction, open.hash (), open, sideband); + store.block_put (transaction, open.hash (), open); auto status (mdb_put (store.env.tx (transaction), store.accounts_v0, nano::mdb_val (account), nano::mdb_val (v13), 0)); ASSERT_EQ (0, status); store.version_put (transaction, 13); diff --git a/nano/lib/CMakeLists.txt b/nano/lib/CMakeLists.txt index 69e9afd269..c0996b359c 100644 --- a/nano/lib/CMakeLists.txt +++ b/nano/lib/CMakeLists.txt @@ -25,6 +25,8 @@ add_library (nano_lib configbase.hpp diagnosticsconfig.hpp diagnosticsconfig.cpp + epoch.hpp + epoch.cpp errors.hpp errors.cpp ipc.hpp diff --git a/nano/lib/blocks.cpp b/nano/lib/blocks.cpp index 0907e376cc..e8ad752488 100644 --- a/nano/lib/blocks.cpp +++ b/nano/lib/blocks.cpp @@ -9,6 +9,8 @@ #include #include +#include + /** Compare blocks, first by type, then content. This is an optimization over dynamic_cast, which is very slow on some platforms. */ namespace { @@ -136,6 +138,17 @@ nano::block_hash nano::block::full_hash () const return result; } +nano::block_sideband const & nano::block::sideband () const +{ + debug_assert (sideband_m.is_initialized ()); + return *sideband_m; +} + +void nano::block::sideband_set (nano::block_sideband const & sideband_a) +{ + sideband_m = sideband_a; +} + nano::account const & nano::block::representative () const { static nano::account rep{ 0 }; @@ -176,6 +189,11 @@ void nano::send_block::visit (nano::block_visitor & visitor_a) const visitor_a.send_block (*this); } +void nano::send_block::visit (nano::mutable_block_visitor & visitor_a) +{ + visitor_a.send_block (*this); +} + void nano::send_block::hash (blake2b_state & hash_a) const { hashables.hash (hash_a); @@ -660,6 +678,11 @@ void nano::open_block::visit (nano::block_visitor & visitor_a) const visitor_a.open_block (*this); } +void nano::open_block::visit (nano::mutable_block_visitor & visitor_a) +{ + visitor_a.open_block (*this); +} + nano::block_type nano::open_block::type () const { return nano::block_type::open; @@ -896,6 +919,11 @@ void nano::change_block::visit (nano::block_visitor & visitor_a) const visitor_a.change_block (*this); } +void nano::change_block::visit (nano::mutable_block_visitor & visitor_a) +{ + visitor_a.change_block (*this); +} + nano::block_type nano::change_block::type () const { return nano::block_type::change; @@ -1203,6 +1231,11 @@ void nano::state_block::visit (nano::block_visitor & visitor_a) const visitor_a.state_block (*this); } +void nano::state_block::visit (nano::mutable_block_visitor & visitor_a) +{ + visitor_a.state_block (*this); +} + nano::block_type nano::state_block::type () const { return nano::block_type::state; @@ -1364,6 +1397,11 @@ void nano::receive_block::visit (nano::block_visitor & visitor_a) const visitor_a.receive_block (*this); } +void nano::receive_block::visit (nano::mutable_block_visitor & visitor_a) +{ + visitor_a.receive_block (*this); +} + bool nano::receive_block::operator== (nano::receive_block const & other_a) const { auto result (hashables.previous == other_a.hashables.previous && hashables.source == other_a.hashables.source && work == other_a.work && signature == other_a.signature); @@ -1608,6 +1646,157 @@ void nano::receive_hashables::hash (blake2b_state & hash_a) const blake2b_update (&hash_a, source.bytes.data (), sizeof (source.bytes)); } +nano::block_details::block_details (nano::epoch const epoch_a, bool const is_send_a, bool const is_receive_a, bool const is_epoch_a) : +epoch (epoch_a), is_send (is_send_a), is_receive (is_receive_a), is_epoch (is_epoch_a) +{ +} + +constexpr size_t nano::block_details::size () +{ + return 1; +} + +bool nano::block_details::operator== (nano::block_details const & other_a) const +{ + return epoch == other_a.epoch && is_send == other_a.is_send && is_receive == other_a.is_receive && is_epoch == other_a.is_epoch; +} + +uint8_t nano::block_details::packed () const +{ + std::bitset<8> result (static_cast (epoch)); + result.set (7, is_send); + result.set (6, is_receive); + result.set (5, is_epoch); + return static_cast (result.to_ulong ()); +} + +void nano::block_details::unpack (uint8_t details_a) +{ + constexpr std::bitset<8> epoch_mask{ 0b00011111 }; + auto as_bitset = static_cast> (details_a); + is_send = as_bitset.test (7); + is_receive = as_bitset.test (6); + is_epoch = as_bitset.test (5); + epoch = static_cast ((as_bitset & epoch_mask).to_ulong ()); +} + +void nano::block_details::serialize (nano::stream & stream_a) const +{ + nano::write (stream_a, packed ()); +} + +bool nano::block_details::deserialize (nano::stream & stream_a) +{ + bool result (false); + try + { + uint8_t packed{ 0 }; + nano::read (stream_a, packed); + unpack (packed); + } + catch (std::runtime_error &) + { + result = true; + } + + return result; +} + +nano::block_sideband::block_sideband (nano::account const & account_a, nano::block_hash const & successor_a, nano::amount const & balance_a, uint64_t height_a, uint64_t timestamp_a, nano::epoch epoch_a, bool is_send, bool is_receive, bool is_epoch) : +successor (successor_a), +account (account_a), +balance (balance_a), +height (height_a), +timestamp (timestamp_a), +details (epoch_a, is_send, is_receive, is_epoch) +{ +} + +size_t nano::block_sideband::size (nano::block_type type_a) +{ + size_t result (0); + result += sizeof (successor); + if (type_a != nano::block_type::state && type_a != nano::block_type::open) + { + result += sizeof (account); + } + if (type_a != nano::block_type::open) + { + result += sizeof (height); + } + if (type_a == nano::block_type::receive || type_a == nano::block_type::change || type_a == nano::block_type::open) + { + result += sizeof (balance); + } + result += sizeof (timestamp); + if (type_a == nano::block_type::state) + { + static_assert (sizeof (nano::epoch) == nano::block_details::size (), "block_details is larger than the epoch enum"); + result += nano::block_details::size (); + } + return result; +} + +void nano::block_sideband::serialize (nano::stream & stream_a, nano::block_type type_a) const +{ + nano::write (stream_a, successor.bytes); + if (type_a != nano::block_type::state && type_a != nano::block_type::open) + { + nano::write (stream_a, account.bytes); + } + if (type_a != nano::block_type::open) + { + nano::write (stream_a, boost::endian::native_to_big (height)); + } + if (type_a == nano::block_type::receive || type_a == nano::block_type::change || type_a == nano::block_type::open) + { + nano::write (stream_a, balance.bytes); + } + nano::write (stream_a, boost::endian::native_to_big (timestamp)); + if (type_a == nano::block_type::state) + { + details.serialize (stream_a); + } +} + +bool nano::block_sideband::deserialize (nano::stream & stream_a, nano::block_type type_a) +{ + bool result (false); + try + { + nano::read (stream_a, successor.bytes); + if (type_a != nano::block_type::state && type_a != nano::block_type::open) + { + nano::read (stream_a, account.bytes); + } + if (type_a != nano::block_type::open) + { + nano::read (stream_a, height); + boost::endian::big_to_native_inplace (height); + } + else + { + height = 1; + } + if (type_a == nano::block_type::receive || type_a == nano::block_type::change || type_a == nano::block_type::open) + { + nano::read (stream_a, balance.bytes); + } + nano::read (stream_a, timestamp); + boost::endian::big_to_native_inplace (timestamp); + if (type_a == nano::block_type::state) + { + result = details.deserialize (stream_a); + } + } + catch (std::runtime_error &) + { + result = true; + } + + return result; +} + std::shared_ptr nano::block_uniquer::unique (std::shared_ptr block_a) { auto result (block_a); diff --git a/nano/lib/blocks.hpp b/nano/lib/blocks.hpp index 16053bea40..793af471c6 100644 --- a/nano/lib/blocks.hpp +++ b/nano/lib/blocks.hpp @@ -1,8 +1,10 @@ #pragma once #include +#include #include #include +#include #include #include #include @@ -14,6 +16,7 @@ namespace nano { class block_visitor; +class mutable_block_visitor; enum class block_type : uint8_t { invalid = 0, @@ -24,6 +27,43 @@ enum class block_type : uint8_t change = 5, state = 6 }; +class block_details +{ + static_assert (std::is_same::type, uint8_t> (), "Epoch enum is not the proper type"); + static_assert (static_cast (nano::epoch::max) < (1 << 5), "Epoch max is too large for the sideband"); + +public: + block_details () = default; + block_details (nano::epoch const epoch_a, bool const is_send_a, bool const is_receive_a, bool const is_epoch_a); + static constexpr size_t size (); + bool operator== (block_details const & other_a) const; + void serialize (nano::stream &) const; + bool deserialize (nano::stream &); + nano::epoch epoch{ nano::epoch::epoch_0 }; + bool is_send{ false }; + bool is_receive{ false }; + bool is_epoch{ false }; + +private: + uint8_t packed () const; + void unpack (uint8_t); +}; + +class block_sideband final +{ +public: + block_sideband () = default; + block_sideband (nano::account const &, nano::block_hash const &, nano::amount const &, uint64_t, uint64_t, nano::epoch, bool is_send, bool is_receive, bool is_epoch); + void serialize (nano::stream &, nano::block_type) const; + bool deserialize (nano::stream &, nano::block_type); + static size_t size (nano::block_type); + nano::block_hash successor{ 0 }; + nano::account account{ 0 }; + nano::amount balance{ 0 }; + uint64_t height{ 0 }; + uint64_t timestamp{ 0 }; + nano::block_details details; +}; class block { public: @@ -31,6 +71,8 @@ class block nano::block_hash const & hash () const; // Return a digest of hashables and non-hashables in this block. nano::block_hash full_hash () const; + nano::block_sideband const & sideband () const; + void sideband_set (nano::block_sideband const &); std::string to_json () const; virtual void hash (blake2b_state &) const = 0; virtual uint64_t block_work () const = 0; @@ -52,6 +94,7 @@ class block virtual void serialize_json (std::string &, bool = false) const = 0; virtual void serialize_json (boost::property_tree::ptree &) const = 0; virtual void visit (nano::block_visitor &) const = 0; + virtual void visit (nano::mutable_block_visitor &) = 0; virtual bool operator== (nano::block const &) const = 0; virtual nano::block_type type () const = 0; virtual nano::signature const & block_signature () const = 0; @@ -66,6 +109,12 @@ class block protected: mutable nano::block_hash cached_hash{ 0 }; + /** + * Contextual details about a block, some fields may or may not be set depending on block type. + * This field is set via sideband_set in ledger processing or deserializing blocks from the database. + * Otherwise it may be null (for example, an old block or fork). + */ + nano::optional_ptr sideband_m; private: nano::block_hash generate_hash () const; @@ -104,6 +153,7 @@ class send_block : public nano::block void serialize_json (boost::property_tree::ptree &) const override; bool deserialize_json (boost::property_tree::ptree const &); void visit (nano::block_visitor &) const override; + void visit (nano::mutable_block_visitor &) override; nano::block_type type () const override; nano::signature const & block_signature () const override; void signature_set (nano::signature const &) override; @@ -148,6 +198,7 @@ class receive_block : public nano::block void serialize_json (boost::property_tree::ptree &) const override; bool deserialize_json (boost::property_tree::ptree const &); void visit (nano::block_visitor &) const override; + void visit (nano::mutable_block_visitor &) override; nano::block_type type () const override; nano::signature const & block_signature () const override; void signature_set (nano::signature const &) override; @@ -196,6 +247,7 @@ class open_block : public nano::block void serialize_json (boost::property_tree::ptree &) const override; bool deserialize_json (boost::property_tree::ptree const &); void visit (nano::block_visitor &) const override; + void visit (nano::mutable_block_visitor &) override; nano::block_type type () const override; nano::signature const & block_signature () const override; void signature_set (nano::signature const &) override; @@ -240,6 +292,7 @@ class change_block : public nano::block void serialize_json (boost::property_tree::ptree &) const override; bool deserialize_json (boost::property_tree::ptree const &); void visit (nano::block_visitor &) const override; + void visit (nano::mutable_block_visitor &) override; nano::block_type type () const override; nano::signature const & block_signature () const override; void signature_set (nano::signature const &) override; @@ -300,6 +353,7 @@ class state_block : public nano::block void serialize_json (boost::property_tree::ptree &) const override; bool deserialize_json (boost::property_tree::ptree const &); void visit (nano::block_visitor &) const override; + void visit (nano::mutable_block_visitor &) override; nano::block_type type () const override; nano::signature const & block_signature () const override; void signature_set (nano::signature const &) override; @@ -321,6 +375,16 @@ class block_visitor virtual void state_block (nano::state_block const &) = 0; virtual ~block_visitor () = default; }; +class mutable_block_visitor +{ +public: + virtual void send_block (nano::send_block &) = 0; + virtual void receive_block (nano::receive_block &) = 0; + virtual void open_block (nano::open_block &) = 0; + virtual void change_block (nano::change_block &) = 0; + virtual void state_block (nano::state_block &) = 0; + virtual ~mutable_block_visitor () = default; +}; /** * This class serves to find and return unique variants of a block in order to minimize memory usage */ diff --git a/nano/secure/epoch.cpp b/nano/lib/epoch.cpp similarity index 98% rename from nano/secure/epoch.cpp rename to nano/lib/epoch.cpp index 352dd1f42c..226d1bb41c 100644 --- a/nano/secure/epoch.cpp +++ b/nano/lib/epoch.cpp @@ -1,5 +1,5 @@ +#include #include -#include nano::link const & nano::epochs::link (nano::epoch epoch_a) const { diff --git a/nano/secure/epoch.hpp b/nano/lib/epoch.hpp similarity index 100% rename from nano/secure/epoch.hpp rename to nano/lib/epoch.hpp diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index 425ac08de1..ca4b0054db 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -995,14 +995,14 @@ int main (int argc, char * const * argv) auto hash (info.open_block); nano::block_hash calculated_hash (0); - nano::block_sideband sideband; - auto block (node.node->store.block_get (transaction, hash, &sideband)); // Block data + auto block (node.node->store.block_get (transaction, hash)); // Block data uint64_t height (0); uint64_t previous_timestamp (0); nano::account calculated_representative (0); while (!hash.is_zero () && block != nullptr) { ++block_count; + auto const & sideband (block->sideband ()); // Check for state & open blocks if account field is correct if (block->type () == nano::block_type::open || block->type () == nano::block_type::state) { @@ -1125,7 +1125,7 @@ int main (int argc, char * const * argv) // Retrieving block data if (!hash.is_zero ()) { - block = node.node->store.block_get (transaction, hash, &sideband); + block = node.node->store.block_get (transaction, hash); } } // Check if required block exists diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index fd80025924..3447955552 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -24,8 +24,8 @@ thread ([this]() { }) { // Register a callback which will get called after a block is cemented - confirmation_height_processor.add_cemented_observer ([this](nano::block_w_sideband const & callback_data_a) { - this->block_cemented_callback (callback_data_a.block, callback_data_a.sideband); + confirmation_height_processor.add_cemented_observer ([this](std::shared_ptr callback_block_a) { + this->block_cemented_callback (callback_block_a); }); // Register a callback which will get called after a batch of blocks is written and observer calls finished @@ -119,7 +119,7 @@ void nano::active_transactions::search_frontiers (nano::transaction const & tran } } -void nano::active_transactions::block_cemented_callback (std::shared_ptr const & block_a, nano::block_sideband const & sideband_a) +void nano::active_transactions::block_cemented_callback (std::shared_ptr const & block_a) { auto transaction = node.store.tx_begin_read (); @@ -142,7 +142,7 @@ void nano::active_transactions::block_cemented_callback (std::shared_ptrhash (), sideband_a, account, amount, is_state_send, pending_account); + node.process_confirmed_data (transaction, block_a, block_a->hash (), account, amount, is_state_send, pending_account); node.observers.blocks.notify (nano::election_status{ block_a, 0, std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values::zero (), 0, 1, 0, nano::election_status_type::inactive_confirmation_height }, account, amount, is_state_send); } else @@ -167,7 +167,7 @@ void nano::active_transactions::block_cemented_callback (std::shared_ptrstatus.type = *election_status_type; election->status.confirmation_request_count = election->confirmation_request_count; node.observers.blocks.notify (election->status, account, amount, is_state_send); diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 22c2af6d86..c6166ab2ca 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -112,7 +112,7 @@ class active_transactions final void stop (); bool publish (std::shared_ptr block_a); boost::optional confirm_block (nano::transaction const &, std::shared_ptr); - void block_cemented_callback (std::shared_ptr const & block_a, nano::block_sideband const & sideband_a); + void block_cemented_callback (std::shared_ptr const & block_a); void block_already_cemented_callback (nano::block_hash const &); // clang-format off boost::multi_index_container -nano::confirmation_height_bounded::confirmation_height_bounded (nano::ledger & ledger_a, nano::write_database_queue & write_database_queue_a, std::chrono::milliseconds batch_separate_pending_min_time_a, nano::logger_mt & logger_a, std::atomic & stopped_a, nano::block_hash const & original_hash_a, std::function const &)> const & notify_observers_callback_a, std::function const & notify_block_already_cemented_observers_callback_a, std::function const & awaiting_processing_size_callback_a) : +nano::confirmation_height_bounded::confirmation_height_bounded (nano::ledger & ledger_a, nano::write_database_queue & write_database_queue_a, std::chrono::milliseconds batch_separate_pending_min_time_a, nano::logger_mt & logger_a, std::atomic & stopped_a, nano::block_hash const & original_hash_a, std::function> const &)> const & notify_observers_callback_a, std::function const & notify_block_already_cemented_observers_callback_a, std::function const & awaiting_processing_size_callback_a) : ledger (ledger_a), write_database_queue (write_database_queue_a), batch_separate_pending_min_time (batch_separate_pending_min_time_a), @@ -67,13 +67,12 @@ void nano::confirmation_height_bounded::process () current = hash_to_process.top; auto top_level_hash = current; - nano::block_sideband sideband; - auto block = ledger.store.block_get (transaction, current, &sideband); + auto block = ledger.store.block_get (transaction, current); debug_assert (block != nullptr); nano::account account (block->account ()); if (account.is_zero ()) { - account = sideband.account; + account = block->sideband ().account; } // Checks if we have encountered this account before but not commited changes yet, if so then update the cached confirmation height @@ -90,13 +89,13 @@ void nano::confirmation_height_bounded::process () (void)error; debug_assert (!error); // This block was added to the confirmation height processor but is already confirmed - if (first_iter && confirmation_height_info.height >= sideband.height && current == original_hash) + if (first_iter && confirmation_height_info.height >= block->sideband ().height && current == original_hash) { notify_block_already_cemented_observers_callback (original_hash); } } - auto block_height = sideband.height; + auto block_height = block->sideband ().height; bool already_cemented = confirmation_height_info.height >= block_height; // If we are not already at the bottom of the account chain (1 above cemented frontier) then find it @@ -199,14 +198,14 @@ void nano::confirmation_height_bounded::process () nano::block_hash nano::confirmation_height_bounded::get_least_unconfirmed_hash_from_top_level (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::account const & account_a, nano::confirmation_height_info const & confirmation_height_info_a, uint64_t & block_height_a) { nano::block_hash least_unconfirmed_hash = hash_a; - nano::block_sideband sideband; if (confirmation_height_info_a.height != 0) { if (block_height_a > confirmation_height_info_a.height) { - release_assert (ledger.store.block_get (transaction_a, confirmation_height_info_a.frontier, &sideband) != nullptr); - least_unconfirmed_hash = sideband.successor; - block_height_a = sideband.height + 1; + auto block (ledger.store.block_get (transaction_a, confirmation_height_info_a.frontier)); + release_assert (block != nullptr); + least_unconfirmed_hash = block->sideband ().successor; + block_height_a = block->sideband ().height + 1; } } else @@ -225,14 +224,13 @@ bool nano::confirmation_height_bounded::iterate (nano::read_transaction const & bool reached_target = false; bool hit_receive = false; auto hash = bottom_hash_a; - nano::block_sideband sideband; uint64_t num_blocks = 0; while (!hash.is_zero () && !reached_target && !stopped) { // Keep iterating upwards until we either reach the desired block or the second receive. // Once a receive is cemented, we can cement all blocks above it until the next receive, so store those details for later. ++num_blocks; - auto block = ledger.store.block_get (transaction_a, hash, &sideband); + auto block = ledger.store.block_get (transaction_a, hash); auto source (block->source ()); if (source.is_zero ()) { @@ -243,6 +241,7 @@ bool nano::confirmation_height_bounded::iterate (nano::read_transaction const & { hit_receive = true; reached_target = true; + auto const & sideband (block->sideband ()); auto next = !sideband.successor.is_zero () && sideband.successor != top_level_hash_a ? boost::optional (sideband.successor) : boost::none; receive_source_pairs_a.push_back ({ receive_chain_details{ account_a, sideband.height, hash, top_level_hash_a, next, bottom_height_a, bottom_hash_a }, source }); // Store a checkpoint every max_items so that we can always traverse a long number of accounts to genesis @@ -261,7 +260,7 @@ bool nano::confirmation_height_bounded::iterate (nano::read_transaction const & } else { - hash = sideband.successor; + hash = block->sideband ().successor; } } @@ -337,7 +336,7 @@ bool nano::confirmation_height_bounded::cement_blocks () { // Will contain all blocks that have been cemented (bounded by batch_write_size) // and will get run through the cemented observer callback - std::vector cemented_blocks; + std::vector> cemented_blocks; { // This only writes to the confirmation_height table and is the only place to do so in a single process auto transaction (ledger.store.tx_begin_write ({}, { nano::tables::confirmation_height })); @@ -354,9 +353,9 @@ bool nano::confirmation_height_bounded::cement_blocks () // Extra debug checks nano::confirmation_height_info confirmation_height_info; debug_assert (!ledger.store.confirmation_height_get (transaction, account, confirmation_height_info)); - nano::block_sideband sideband; - debug_assert (ledger.store.block_get (transaction, confirmed_frontier, &sideband)); - debug_assert (sideband.height == confirmation_height_info.height + num_blocks_cemented); + auto block (ledger.store.block_get (transaction, confirmed_frontier)); + debug_assert (block != nullptr); + debug_assert (block->sideband ().height == confirmation_height_info.height + num_blocks_cemented); #endif ledger.store.confirmation_height_put (transaction, account, nano::confirmation_height_info{ confirmation_height, confirmed_frontier }); ledger.cache.cemented_count += num_blocks_cemented; @@ -370,7 +369,6 @@ bool nano::confirmation_height_bounded::cement_blocks () // Some blocks need to be cemented at least if (pending.top_height > confirmation_height_info.height) { - nano::block_sideband sideband; // The highest hash which will be cemented nano::block_hash new_cemented_frontier; uint64_t num_blocks_confirmed = 0; @@ -385,8 +383,8 @@ bool nano::confirmation_height_bounded::cement_blocks () } else { - auto block = ledger.store.block_get (transaction, confirmation_height_info.frontier, &sideband); - new_cemented_frontier = sideband.successor; + auto block = ledger.store.block_get (transaction, confirmation_height_info.frontier); + new_cemented_frontier = block->sideband ().successor; num_blocks_confirmed = pending.top_height - confirmation_height_info.height; start_height = confirmation_height_info.height + 1; } @@ -394,7 +392,7 @@ bool nano::confirmation_height_bounded::cement_blocks () auto total_blocks_cemented = 0; auto num_blocks_iterated = 0; - auto block = ledger.store.block_get (transaction, new_cemented_frontier, &sideband); + auto block = ledger.store.block_get (transaction, new_cemented_frontier); // Cementing starts from the bottom of the chain and works upwards. This is because chains can have effectively // an infinite number of send/change blocks in a row. We don't want to hold the write transaction open for too long. @@ -409,7 +407,7 @@ bool nano::confirmation_height_bounded::cement_blocks () return true; } - cemented_blocks.emplace_back (block, sideband); + cemented_blocks.emplace_back (block); // We have likely hit a long chain, flush these callbacks and continue if (cemented_blocks.size () == confirmation_height::batch_write_size) @@ -427,8 +425,8 @@ bool nano::confirmation_height_bounded::cement_blocks () auto last_iteration = (num_blocks_confirmed - num_blocks_iterated) == 1; if (!last_iteration) { - new_cemented_frontier = sideband.successor; - block = ledger.store.block_get (transaction, new_cemented_frontier, &sideband); + new_cemented_frontier = block->sideband ().successor; + block = ledger.store.block_get (transaction, new_cemented_frontier); } else { diff --git a/nano/node/confirmation_height_bounded.hpp b/nano/node/confirmation_height_bounded.hpp index 8a444245b5..4d32ad36dd 100644 --- a/nano/node/confirmation_height_bounded.hpp +++ b/nano/node/confirmation_height_bounded.hpp @@ -15,7 +15,7 @@ class write_database_queue; class confirmation_height_bounded final { public: - confirmation_height_bounded (nano::ledger &, nano::write_database_queue &, std::chrono::milliseconds, nano::logger_mt &, std::atomic &, nano::block_hash const &, std::function const &)> const &, std::function const &, std::function const &); + confirmation_height_bounded (nano::ledger &, nano::write_database_queue &, std::chrono::milliseconds, nano::logger_mt &, std::atomic &, nano::block_hash const &, std::function> const &)> const &, std::function const &, std::function const &); bool pending_empty () const; void prepare_new (); void process (); @@ -114,7 +114,7 @@ class confirmation_height_bounded final nano::logger_mt & logger; std::atomic & stopped; nano::block_hash const & original_hash; - std::function const &)> notify_observers_callback; + std::function> const &)> notify_observers_callback; std::function notify_block_already_cemented_observers_callback; std::function awaiting_processing_size_callback; diff --git a/nano/node/confirmation_height_processor.cpp b/nano/node/confirmation_height_processor.cpp index f7ede3759d..cad2739572 100644 --- a/nano/node/confirmation_height_processor.cpp +++ b/nano/node/confirmation_height_processor.cpp @@ -165,7 +165,7 @@ void nano::confirmation_height_processor::set_next_hash () } // Not thread-safe, only call before this processor has begun cementing -void nano::confirmation_height_processor::add_cemented_observer (std::function const & callback_a) +void nano::confirmation_height_processor::add_cemented_observer (std::function)> const & callback_a) { cemented_observers.push_back (callback_a); } @@ -176,7 +176,7 @@ void nano::confirmation_height_processor::add_block_already_cemented_observer (s block_already_cemented_observers.push_back (callback_a); } -void nano::confirmation_height_processor::notify_observers (std::vector const & cemented_blocks) +void nano::confirmation_height_processor::notify_observers (std::vector> const & cemented_blocks) { for (auto const & block_callback_data : cemented_blocks) { diff --git a/nano/node/confirmation_height_processor.hpp b/nano/node/confirmation_height_processor.hpp index facc3e53e5..ea56f97511 100644 --- a/nano/node/confirmation_height_processor.hpp +++ b/nano/node/confirmation_height_processor.hpp @@ -35,7 +35,7 @@ class confirmation_height_processor final bool is_processing_block (nano::block_hash const &); nano::block_hash current (); - void add_cemented_observer (std::function const &); + void add_cemented_observer (std::function)> const &); void add_block_already_cemented_observer (std::function const &); private: @@ -51,7 +51,7 @@ class confirmation_height_processor final nano::condition_variable condition; std::atomic stopped{ false }; - std::vector> cemented_observers; + std::vector)>> cemented_observers; std::vector> block_already_cemented_observers; nano::ledger & ledger; @@ -61,7 +61,7 @@ class confirmation_height_processor final std::thread thread; void set_next_hash (); - void notify_observers (std::vector const &); + void notify_observers (std::vector> const &); void notify_observers (nano::block_hash const &); friend std::unique_ptr collect_container_info (confirmation_height_processor &, const std::string &); diff --git a/nano/node/confirmation_height_unbounded.cpp b/nano/node/confirmation_height_unbounded.cpp index bdca0bb28c..b2399839d3 100644 --- a/nano/node/confirmation_height_unbounded.cpp +++ b/nano/node/confirmation_height_unbounded.cpp @@ -5,7 +5,7 @@ #include -nano::confirmation_height_unbounded::confirmation_height_unbounded (nano::ledger & ledger_a, nano::write_database_queue & write_database_queue_a, std::chrono::milliseconds batch_separate_pending_min_time_a, nano::logger_mt & logger_a, std::atomic & stopped_a, nano::block_hash const & original_hash_a, std::function const &)> const & notify_observers_callback_a, std::function const & notify_block_already_cemented_observers_callback_a, std::function const & awaiting_processing_size_callback_a) : +nano::confirmation_height_unbounded::confirmation_height_unbounded (nano::ledger & ledger_a, nano::write_database_queue & write_database_queue_a, std::chrono::milliseconds batch_separate_pending_min_time_a, nano::logger_mt & logger_a, std::atomic & stopped_a, nano::block_hash const & original_hash_a, std::function> const &)> const & notify_observers_callback_a, std::function const & notify_block_already_cemented_observers_callback_a, std::function const & awaiting_processing_size_callback_a) : ledger (ledger_a), write_database_queue (write_database_queue_a), batch_separate_pending_min_time (batch_separate_pending_min_time_a), @@ -49,16 +49,14 @@ void nano::confirmation_height_unbounded::process () } } - std::shared_ptr block; - nano::block_sideband sideband; - get_block_and_sideband (current, read_transaction, block, sideband); + auto block (get_block_and_sideband (current, read_transaction)); nano::account account (block->account ()); if (account.is_zero ()) { - account = sideband.account; + account = block->sideband ().account; } - auto block_height = sideband.height; + auto block_height = block->sideband ().height; uint64_t confirmation_height = 0; auto account_it = confirmed_iterated_pairs.find (account); if (account_it != confirmed_iterated_pairs.cend ()) @@ -164,10 +162,7 @@ void nano::confirmation_height_unbounded::collect_unconfirmed_receive_and_source bool hit_receive = false; while ((num_to_confirm > 0) && !hash.is_zero () && !stopped) { - std::shared_ptr block; - nano::block_sideband sideband; - get_block_and_sideband (hash, transaction_a, block, sideband); - + auto block (get_block_and_sideband (hash, transaction_a)); if (block) { auto source (block->source ()); @@ -330,11 +325,10 @@ bool nano::confirmation_height_unbounded::cement_blocks () { #ifndef NDEBUG // Do more thorough checking in Debug mode, indicates programming error. - nano::block_sideband sideband; - auto block = ledger.store.block_get (transaction, pending.hash, &sideband); + auto block = ledger.store.block_get (transaction, pending.hash); static nano::network_constants network_constants; debug_assert (network_constants.is_test_network () || block != nullptr); - debug_assert (network_constants.is_test_network () || sideband.height == pending.height); + debug_assert (network_constants.is_test_network () || block->sideband ().height == pending.height); if (!block) { @@ -355,7 +349,7 @@ bool nano::confirmation_height_unbounded::cement_blocks () // Reverse it so that the callbacks start from the lowest newly cemented block and move upwards std::reverse (pending.block_callback_data.begin (), pending.block_callback_data.end ()); - std::vector callback_data; + std::vector> callback_data; callback_data.reserve (pending.block_callback_data.size ()); std::transform (pending.block_callback_data.begin (), pending.block_callback_data.end (), std::back_inserter (callback_data), [& block_cache = block_cache](auto const & hash_a) { debug_assert (block_cache.find (hash_a) != block_cache.end ()); @@ -373,19 +367,19 @@ bool nano::confirmation_height_unbounded::cement_blocks () return false; } -void nano::confirmation_height_unbounded::get_block_and_sideband (nano::block_hash const & hash_a, nano::transaction const & transaction_a, std::shared_ptr & block_a, nano::block_sideband & sideband_a) +std::shared_ptr nano::confirmation_height_unbounded::get_block_and_sideband (nano::block_hash const & hash_a, nano::transaction const & transaction_a) { auto block_cache_it = block_cache.find (hash_a); if (block_cache_it != block_cache.cend ()) { - block_a = block_cache_it->second.block; - sideband_a = block_cache_it->second.sideband; + return block_cache_it->second; } else { - block_a = ledger.store.block_get (transaction_a, hash_a, &sideband_a); - block_cache.emplace (std::piecewise_construct, std::forward_as_tuple (hash_a), std::forward_as_tuple (block_a, sideband_a)); + auto block (ledger.store.block_get (transaction_a, hash_a)); + block_cache.emplace (hash_a, block); ++block_cache_size; + return block; } } diff --git a/nano/node/confirmation_height_unbounded.hpp b/nano/node/confirmation_height_unbounded.hpp index 6ce3eefcbf..194599a5e1 100644 --- a/nano/node/confirmation_height_unbounded.hpp +++ b/nano/node/confirmation_height_unbounded.hpp @@ -16,7 +16,7 @@ class write_database_queue; class confirmation_height_unbounded final { public: - confirmation_height_unbounded (nano::ledger &, nano::write_database_queue &, std::chrono::milliseconds, nano::logger_mt &, std::atomic &, nano::block_hash const &, std::function const &)> const &, std::function const &, std::function const &); + confirmation_height_unbounded (nano::ledger &, nano::write_database_queue &, std::chrono::milliseconds, nano::logger_mt &, std::atomic &, nano::block_hash const &, std::function> const &)> const &, std::function const &, std::function const &); bool pending_empty () const; void prepare_new (); void process (); @@ -55,9 +55,9 @@ class confirmation_height_unbounded final std::unordered_map confirmed_iterated_pairs; std::atomic confirmed_iterated_pairs_size{ 0 }; - std::unordered_map block_cache; + std::unordered_map> block_cache; std::atomic block_cache_size{ 0 }; - void get_block_and_sideband (nano::block_hash const &, nano::transaction const &, std::shared_ptr &, nano::block_sideband &); + std::shared_ptr get_block_and_sideband (nano::block_hash const &, nano::transaction const &); std::deque pending_writes; std::atomic pending_writes_size{ 0 }; std::vector orig_block_callback_data; @@ -90,7 +90,7 @@ class confirmation_height_unbounded final nano::logger_mt & logger; std::atomic & stopped; nano::block_hash const & original_hash; - std::function const &)> notify_observers_callback; + std::function> const &)> notify_observers_callback; std::function notify_block_already_cemented_observers_callback; std::function awaiting_processing_size_callback; diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 34a3231ed2..a43e23f2a1 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -970,19 +970,18 @@ void nano::json_handler::block_info () auto hash (hash_impl ()); if (!ec) { - nano::block_sideband sideband; auto transaction (node.store.tx_begin_read ()); - auto block (node.store.block_get (transaction, hash, &sideband)); + auto block (node.store.block_get (transaction, hash)); if (block != nullptr) { - nano::account account (block->account ().is_zero () ? sideband.account : block->account ()); + nano::account account (block->account ().is_zero () ? block->sideband ().account : block->account ()); response_l.put ("block_account", account.to_account ()); auto amount (node.ledger.amount (transaction, hash)); response_l.put ("amount", amount.convert_to ()); auto balance (node.ledger.balance (transaction, hash)); response_l.put ("balance", balance.convert_to ()); - response_l.put ("height", std::to_string (sideband.height)); - response_l.put ("local_timestamp", std::to_string (sideband.timestamp)); + response_l.put ("height", std::to_string (block->sideband ().height)); + response_l.put ("local_timestamp", std::to_string (block->sideband ().timestamp)); auto confirmed (node.ledger.block_confirmed (transaction, hash)); response_l.put ("confirmed", confirmed); @@ -1121,19 +1120,18 @@ void nano::json_handler::blocks_info () nano::block_hash hash; if (!hash.decode_hex (hash_text)) { - nano::block_sideband sideband; - auto block (node.store.block_get (transaction, hash, &sideband)); + auto block (node.store.block_get (transaction, hash)); if (block != nullptr) { boost::property_tree::ptree entry; - nano::account account (block->account ().is_zero () ? sideband.account : block->account ()); + nano::account account (block->account ().is_zero () ? block->sideband ().account : block->account ()); entry.put ("block_account", account.to_account ()); auto amount (node.ledger.amount (transaction, hash)); entry.put ("amount", amount.convert_to ()); auto balance (node.ledger.balance (transaction, hash)); entry.put ("balance", balance.convert_to ()); - entry.put ("height", std::to_string (sideband.height)); - entry.put ("local_timestamp", std::to_string (sideband.timestamp)); + entry.put ("height", std::to_string (block->sideband ().height)); + entry.put ("local_timestamp", std::to_string (block->sideband ().timestamp)); auto confirmed (node.ledger.block_confirmed (transaction, hash)); entry.put ("confirmed", confirmed); @@ -2557,8 +2555,7 @@ void nano::json_handler::account_history () boost::property_tree::ptree history; bool output_raw (request.get_optional ("raw") == true); response_l.put ("account", account.to_account ()); - nano::block_sideband sideband; - auto block (node.store.block_get (transaction, hash, &sideband)); + auto block (node.store.block_get (transaction, hash)); while (block != nullptr && count > 0) { if (offset > 0) @@ -2572,8 +2569,8 @@ void nano::json_handler::account_history () block->visit (visitor); if (!entry.empty ()) { - entry.put ("local_timestamp", std::to_string (sideband.timestamp)); - entry.put ("height", std::to_string (sideband.height)); + entry.put ("local_timestamp", std::to_string (block->sideband ().timestamp)); + entry.put ("height", std::to_string (block->sideband ().height)); entry.put ("hash", hash.to_string ()); if (output_raw) { @@ -2585,7 +2582,7 @@ void nano::json_handler::account_history () } } hash = reverse ? node.store.block_successor (transaction, hash) : block->previous (); - block = node.store.block_get (transaction, hash, &sideband); + block = node.store.block_get (transaction, hash); } response_l.add_child ("history", history); if (!hash.is_zero ()) @@ -4589,9 +4586,8 @@ void nano::json_handler::wallet_history () auto hash (info.head); while (timestamp >= modified_since && !hash.is_zero ()) { - nano::block_sideband sideband; - auto block (node.store.block_get (block_transaction, hash, &sideband)); - timestamp = sideband.timestamp; + auto block (node.store.block_get (block_transaction, hash)); + timestamp = block->sideband ().timestamp; if (block != nullptr && timestamp >= modified_since) { boost::property_tree::ptree entry; diff --git a/nano/node/lmdb/lmdb.cpp b/nano/node/lmdb/lmdb.cpp index f013e06fb7..3cf7fb2df0 100644 --- a/nano/node/lmdb/lmdb.cpp +++ b/nano/node/lmdb/lmdb.cpp @@ -507,7 +507,7 @@ void nano::mdb_store::upgrade_v12_to_v13 (nano::write_transaction & transaction_ } nano::mdb_val value{ vector.size (), (void *)vector.data () }; - MDB_dbi database = is_state_block_v1 ? state_blocks_v1 : table_to_dbi (block_database (sideband.type)); + MDB_dbi database = is_state_block_v1 ? state_blocks_v1 : table_to_dbi (block_database (block->type ())); auto status = mdb_put (env.tx (transaction_a), database, nano::mdb_val (hash), value, 0); release_assert (success (status)); @@ -628,14 +628,14 @@ void nano::mdb_store::upgrade_v14_to_v15 (nano::write_transaction & transaction_ nano::state_block_w_sideband_v14 state_block_w_sideband_v14 (i_state->second); auto & sideband_v14 = state_block_w_sideband_v14.sideband; - nano::block_sideband sideband (sideband_v14.type, sideband_v14.account, sideband_v14.successor, sideband_v14.balance, sideband_v14.height, sideband_v14.timestamp, i_state.from_first_database ? nano::epoch::epoch_0 : nano::epoch::epoch_1, false, false, false); + nano::block_sideband sideband (sideband_v14.account, sideband_v14.successor, sideband_v14.balance, sideband_v14.height, sideband_v14.timestamp, i_state.from_first_database ? nano::epoch::epoch_0 : nano::epoch::epoch_1, false, false, false); // Write these out std::vector data; { nano::vectorstream stream (data); state_block_w_sideband_v14.state_block->serialize (stream); - sideband.serialize (stream); + sideband.serialize (stream, sideband_v14.type); } nano::mdb_val value{ data.size (), (void *)data.data () }; @@ -732,27 +732,25 @@ void nano::mdb_store::upgrade_v16_to_v17 (nano::write_transaction const & transa if (account_info_i->second.block_count / 2 >= confirmation_height) { // The confirmation height of the account is closer to the bottom of the chain, so start there and work up - nano::block_sideband sideband; - auto block = block_get (transaction_a, account_info.open_block, &sideband); + auto block = block_get (transaction_a, account_info.open_block); debug_assert (block); auto height = 1; while (height != confirmation_height) { - block = block_get (transaction_a, sideband.successor, &sideband); + block = block_get (transaction_a, block->sideband ().successor); debug_assert (block); ++height; } - debug_assert (sideband.height == confirmation_height); + debug_assert (block->sideband ().height == confirmation_height); confirmation_height_infos.emplace_back (account, confirmation_height_info{ confirmation_height, block->hash () }); } else { // The confirmation height of the account is closer to the top of the chain so start there and work down - nano::block_sideband sideband; - auto block = block_get (transaction_a, account_info.head, &sideband); - auto height = sideband.height; + auto block = block_get (transaction_a, account_info.head); + auto height = block->sideband ().height; while (height != confirmation_height) { block = block_get (transaction_a, block->previous ()); @@ -820,13 +818,13 @@ void nano::mdb_store::upgrade_v17_to_v18 (nano::write_transaction const & transa is_receive = true; } - nano::block_sideband new_sideband (sideband.type, sideband.account, sideband.successor, sideband.balance, sideband.height, sideband.timestamp, sideband.details.epoch, is_send, is_receive, is_epoch); + nano::block_sideband new_sideband (sideband.account, sideband.successor, sideband.balance, sideband.height, sideband.timestamp, sideband.details.epoch, is_send, is_receive, is_epoch); // Write these out std::vector data; { nano::vectorstream stream (data); block->serialize (stream); - new_sideband.serialize (stream); + new_sideband.serialize (stream, block->type ()); } nano::mdb_val value{ data.size (), (void *)data.data () }; auto s = mdb_cursor_put (state_i.cursor, state_i->first, value, MDB_CURRENT); diff --git a/nano/node/node.cpp b/nano/node/node.cpp index e6597f6061..a4b863c3cd 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -616,7 +616,7 @@ void nano::node::process_active (std::shared_ptr incoming) block_processor.add (incoming, nano::seconds_since_epoch ()); } -nano::process_return nano::node::process (nano::block const & block_a) +nano::process_return nano::node::process (nano::block & block_a) { auto transaction (store.tx_begin_write ({ tables::accounts, tables::cached_counts, tables::change_blocks, tables::frontiers, tables::open_blocks, tables::pending, tables::receive_blocks, tables::representation, tables::send_blocks, tables::state_blocks }, { tables::confirmation_height })); auto result (ledger.process (transaction, block_a)); @@ -1225,18 +1225,18 @@ void nano::node::receive_confirmed (nano::transaction const & transaction_a, std block_a->visit (visitor); } -void nano::node::process_confirmed_data (nano::transaction const & transaction_a, std::shared_ptr block_a, nano::block_hash const & hash_a, nano::block_sideband const & sideband_a, nano::account & account_a, nano::uint128_t & amount_a, bool & is_state_send_a, nano::account & pending_account_a) +void nano::node::process_confirmed_data (nano::transaction const & transaction_a, std::shared_ptr block_a, nano::block_hash const & hash_a, nano::account & account_a, nano::uint128_t & amount_a, bool & is_state_send_a, nano::account & pending_account_a) { // Faster account calculation account_a = block_a->account (); if (account_a.is_zero ()) { - account_a = sideband_a.account; + account_a = block_a->sideband ().account; } // Faster amount calculation auto previous (block_a->previous ()); auto previous_balance (ledger.balance (transaction_a, previous)); - auto block_balance (store.block_balance_calculated (block_a, sideband_a)); + auto block_balance (store.block_balance_calculated (block_a)); if (hash_a != ledger.network_params.ledger.genesis_account) { amount_a = block_balance > previous_balance ? block_balance - previous_balance : previous_balance - block_balance; diff --git a/nano/node/node.hpp b/nano/node/node.hpp index c45c90fa1a..6dd5860c4b 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -103,10 +103,10 @@ class node final : public std::enable_shared_from_this std::shared_ptr shared (); int store_version (); void receive_confirmed (nano::transaction const &, std::shared_ptr, nano::block_hash const &); - void process_confirmed_data (nano::transaction const &, std::shared_ptr, nano::block_hash const &, nano::block_sideband const &, nano::account &, nano::uint128_t &, bool &, nano::account &); + void process_confirmed_data (nano::transaction const &, std::shared_ptr, nano::block_hash const &, nano::account &, nano::uint128_t &, bool &, nano::account &); void process_confirmed (nano::election_status const &, std::shared_ptr const &, uint8_t = 0); void process_active (std::shared_ptr); - nano::process_return process (nano::block const &); + nano::process_return process (nano::block &); nano::process_return process_local (std::shared_ptr, bool const = false); void keepalive_preconfigured (std::vector const &); nano::block_hash latest (nano::account const &); diff --git a/nano/secure/CMakeLists.txt b/nano/secure/CMakeLists.txt index cfe874160b..2098cceac3 100644 --- a/nano/secure/CMakeLists.txt +++ b/nano/secure/CMakeLists.txt @@ -41,8 +41,6 @@ add_library (secure buffer.hpp common.hpp common.cpp - epoch.hpp - epoch.cpp ledger.hpp ledger.cpp network_filter.hpp diff --git a/nano/secure/blockstore.cpp b/nano/secure/blockstore.cpp index 909dd2313e..1babdb2f54 100644 --- a/nano/secure/blockstore.cpp +++ b/nano/secure/blockstore.cpp @@ -1,162 +1,6 @@ #include #include -#include - -#include - -nano::block_details::block_details (nano::epoch const epoch_a, bool const is_send_a, bool const is_receive_a, bool const is_epoch_a) : -epoch (epoch_a), is_send (is_send_a), is_receive (is_receive_a), is_epoch (is_epoch_a) -{ -} - -constexpr size_t nano::block_details::size () -{ - return 1; -} - -bool nano::block_details::operator== (nano::block_details const & other_a) const -{ - return epoch == other_a.epoch && is_send == other_a.is_send && is_receive == other_a.is_receive && is_epoch == other_a.is_epoch; -} - -uint8_t nano::block_details::packed () const -{ - std::bitset<8> result (static_cast (epoch)); - result.set (7, is_send); - result.set (6, is_receive); - result.set (5, is_epoch); - return static_cast (result.to_ulong ()); -} - -void nano::block_details::unpack (uint8_t details_a) -{ - constexpr std::bitset<8> epoch_mask{ 0b00011111 }; - auto as_bitset = static_cast> (details_a); - is_send = as_bitset.test (7); - is_receive = as_bitset.test (6); - is_epoch = as_bitset.test (5); - epoch = static_cast ((as_bitset & epoch_mask).to_ulong ()); -} - -void nano::block_details::serialize (nano::stream & stream_a) const -{ - nano::write (stream_a, packed ()); -} - -bool nano::block_details::deserialize (nano::stream & stream_a) -{ - bool result (false); - try - { - uint8_t packed{ 0 }; - nano::read (stream_a, packed); - unpack (packed); - } - catch (std::runtime_error &) - { - result = true; - } - - return result; -} - -nano::block_sideband::block_sideband (nano::block_type type_a, nano::account const & account_a, nano::block_hash const & successor_a, nano::amount const & balance_a, uint64_t height_a, uint64_t timestamp_a, nano::epoch epoch_a, bool is_send, bool is_receive, bool is_epoch) : -type (type_a), -successor (successor_a), -account (account_a), -balance (balance_a), -height (height_a), -timestamp (timestamp_a), -details (epoch_a, is_send, is_receive, is_epoch) -{ -} - -size_t nano::block_sideband::size (nano::block_type type_a) -{ - size_t result (0); - result += sizeof (successor); - if (type_a != nano::block_type::state && type_a != nano::block_type::open) - { - result += sizeof (account); - } - if (type_a != nano::block_type::open) - { - result += sizeof (height); - } - if (type_a == nano::block_type::receive || type_a == nano::block_type::change || type_a == nano::block_type::open) - { - result += sizeof (balance); - } - result += sizeof (timestamp); - if (type_a == nano::block_type::state) - { - static_assert (sizeof (nano::epoch) == nano::block_details::size (), "block_details is larger than the epoch enum"); - result += nano::block_details::size (); - } - return result; -} - -void nano::block_sideband::serialize (nano::stream & stream_a) const -{ - nano::write (stream_a, successor.bytes); - if (type != nano::block_type::state && type != nano::block_type::open) - { - nano::write (stream_a, account.bytes); - } - if (type != nano::block_type::open) - { - nano::write (stream_a, boost::endian::native_to_big (height)); - } - if (type == nano::block_type::receive || type == nano::block_type::change || type == nano::block_type::open) - { - nano::write (stream_a, balance.bytes); - } - nano::write (stream_a, boost::endian::native_to_big (timestamp)); - if (type == nano::block_type::state) - { - details.serialize (stream_a); - } -} - -bool nano::block_sideband::deserialize (nano::stream & stream_a) -{ - bool result (false); - try - { - nano::read (stream_a, successor.bytes); - if (type != nano::block_type::state && type != nano::block_type::open) - { - nano::read (stream_a, account.bytes); - } - if (type != nano::block_type::open) - { - nano::read (stream_a, height); - boost::endian::big_to_native_inplace (height); - } - else - { - height = 1; - } - if (type == nano::block_type::receive || type == nano::block_type::change || type == nano::block_type::open) - { - nano::read (stream_a, balance.bytes); - } - nano::read (stream_a, timestamp); - boost::endian::big_to_native_inplace (timestamp); - if (type == nano::block_type::state) - { - result = details.deserialize (stream_a); - } - } - catch (std::runtime_error &) - { - result = true; - } - - return result; -} - nano::summation_visitor::summation_visitor (nano::transaction const & transaction_a, nano::block_store const & store_a, bool is_v14_upgrade_a) : transaction (transaction_a), store (store_a), @@ -397,7 +241,7 @@ nano::uint128_t nano::summation_visitor::compute_balance (nano::block_hash const std::shared_ptr nano::summation_visitor::block_get (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const { - return is_v14_upgrade ? store.block_get_v14 (transaction, hash_a) : store.block_get (transaction, hash_a); + return is_v14_upgrade ? store.block_get_v14 (transaction, hash_a) : store.block_get_no_sideband (transaction, hash_a); } nano::representative_visitor::representative_visitor (nano::transaction const & transaction_a, nano::block_store & store_a) : diff --git a/nano/secure/blockstore.hpp b/nano/secure/blockstore.hpp index 66ac7e8d52..310347a862 100644 --- a/nano/secure/blockstore.hpp +++ b/nano/secure/blockstore.hpp @@ -16,45 +16,6 @@ namespace nano { -class block_details -{ - static_assert (std::is_same::type, uint8_t> (), "Epoch enum is not the proper type"); - static_assert (static_cast (nano::epoch::max) < (1 << 5), "Epoch max is too large for the sideband"); - -public: - block_details () = default; - block_details (nano::epoch const epoch_a, bool const is_send_a, bool const is_receive_a, bool const is_epoch_a); - static constexpr size_t size (); - bool operator== (block_details const & other_a) const; - void serialize (nano::stream &) const; - bool deserialize (nano::stream &); - nano::epoch epoch{ nano::epoch::epoch_0 }; - bool is_send{ false }; - bool is_receive{ false }; - bool is_epoch{ false }; - -private: - uint8_t packed () const; - void unpack (uint8_t); -}; - -class block_sideband final -{ -public: - block_sideband () = default; - block_sideband (nano::block_type, nano::account const &, nano::block_hash const &, nano::amount const &, uint64_t, uint64_t, nano::epoch, bool is_send, bool is_receive, bool is_epoch); - void serialize (nano::stream &) const; - bool deserialize (nano::stream &); - static size_t size (nano::block_type); - nano::block_type type{ nano::block_type::invalid }; - nano::block_hash successor{ 0 }; - nano::account account{ 0 }; - nano::amount balance{ 0 }; - uint64_t height{ 0 }; - uint64_t timestamp{ 0 }; - nano::block_details details; -}; - // Move to versioning with a specific version if required for a future upgrade class state_block_w_sideband { @@ -311,33 +272,33 @@ class db_val return result; } -private: - // Common usage for versioning - template ::value || std::is_same::value>> - T as () const + explicit operator state_block_w_sideband () const { nano::bufferstream stream (reinterpret_cast (data ()), size ()); auto error (false); - T block_w_sideband; + nano::state_block_w_sideband block_w_sideband; block_w_sideband.state_block = std::make_shared (error, stream); debug_assert (!error); - block_w_sideband.sideband.type = nano::block_type::state; - error = block_w_sideband.sideband.deserialize (stream); + error = block_w_sideband.sideband.deserialize (stream, nano::block_type::state); debug_assert (!error); return block_w_sideband; } -public: - explicit operator state_block_w_sideband () const - { - return as (); - } - explicit operator state_block_w_sideband_v14 () const { - return as (); + nano::bufferstream stream (reinterpret_cast (data ()), size ()); + auto error (false); + nano::state_block_w_sideband_v14 block_w_sideband; + block_w_sideband.state_block = std::make_shared (error, stream); + debug_assert (!error); + + block_w_sideband.sideband.type = nano::block_type::state; + error = block_w_sideband.sideband.deserialize (stream); + debug_assert (!error); + + return block_w_sideband; } explicit operator nano::no_value () const @@ -441,19 +402,6 @@ class db_val class transaction; class block_store; -class block_w_sideband final -{ -public: - block_w_sideband (std::shared_ptr const & block_a, nano::block_sideband const & sideband_a) : - block (block_a), - sideband (sideband_a) - { - } - - std::shared_ptr block; - nano::block_sideband sideband; -}; - /** * Summation visitor for blocks, supporting amount and balance computations. These * computations are mutually dependant. The natural solution is to use mutual recursion @@ -714,10 +662,11 @@ class block_store public: virtual ~block_store () = default; virtual void initialize (nano::write_transaction const &, nano::genesis const &, nano::ledger_cache &) = 0; - virtual void block_put (nano::write_transaction const &, nano::block_hash const &, nano::block const &, nano::block_sideband const &) = 0; + virtual void block_put (nano::write_transaction const &, nano::block_hash const &, nano::block const &) = 0; virtual nano::block_hash block_successor (nano::transaction const &, nano::block_hash const &) const = 0; virtual void block_successor_clear (nano::write_transaction const &, nano::block_hash const &) = 0; - virtual std::shared_ptr block_get (nano::transaction const &, nano::block_hash const &, nano::block_sideband * = nullptr) const = 0; + virtual std::shared_ptr block_get (nano::transaction const &, nano::block_hash const &) const = 0; + virtual std::shared_ptr block_get_no_sideband (nano::transaction const &, nano::block_hash const &) const = 0; virtual std::shared_ptr block_get_v14 (nano::transaction const &, nano::block_hash const &, nano::block_sideband_v14 * = nullptr, bool * = nullptr) const = 0; virtual std::shared_ptr block_random (nano::transaction const &) = 0; virtual void block_del (nano::write_transaction const &, nano::block_hash const &, nano::block_type) = 0; @@ -753,7 +702,7 @@ class block_store virtual bool block_info_get (nano::transaction const &, nano::block_hash const &, nano::block_info &) const = 0; virtual nano::uint128_t block_balance (nano::transaction const &, nano::block_hash const &) = 0; - virtual nano::uint128_t block_balance_calculated (std::shared_ptr, nano::block_sideband const &) const = 0; + virtual nano::uint128_t block_balance_calculated (std::shared_ptr const &) const = 0; virtual nano::epoch block_version (nano::transaction const &, nano::block_hash const &) = 0; virtual void unchecked_clear (nano::write_transaction const &) = 0; diff --git a/nano/secure/blockstore_partial.hpp b/nano/secure/blockstore_partial.hpp index 6e837aacab..d58543d981 100644 --- a/nano/secure/blockstore_partial.hpp +++ b/nano/secure/blockstore_partial.hpp @@ -31,8 +31,8 @@ class block_store_partial : public block_store { auto hash_l (genesis_a.hash ()); debug_assert (latest_begin (transaction_a) == latest_end ()); - nano::block_sideband sideband (nano::block_type::open, network_params.ledger.genesis_account, 0, network_params.ledger.genesis_amount, 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false, false, false); - block_put (transaction_a, hash_l, *genesis_a.open, sideband); + genesis_a.open->sideband_set (nano::block_sideband (network_params.ledger.genesis_account, 0, network_params.ledger.genesis_amount, 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false, false, false)); + block_put (transaction_a, hash_l, *genesis_a.open); ++ledger_cache_a.block_count; confirmation_height_put (transaction_a, network_params.ledger.genesis_account, nano::confirmation_height_info{ 1, genesis_a.hash () }); ++ledger_cache_a.cemented_count; @@ -44,9 +44,8 @@ class block_store_partial : public block_store nano::uint128_t block_balance (nano::transaction const & transaction_a, nano::block_hash const & hash_a) override { - nano::block_sideband sideband; - auto block (block_get (transaction_a, hash_a, &sideband)); - nano::uint128_t result (block_balance_calculated (block, sideband)); + auto block (block_get (transaction_a, hash_a)); + nano::uint128_t result (block_balance_calculated (block)); return result; } @@ -97,15 +96,14 @@ class block_store_partial : public block_store return result; } - void block_put (nano::write_transaction const & transaction_a, nano::block_hash const & hash_a, nano::block const & block_a, nano::block_sideband const & sideband_a) override + void block_put (nano::write_transaction const & transaction_a, nano::block_hash const & hash_a, nano::block const & block_a) override { - debug_assert (block_a.type () == sideband_a.type); - debug_assert (sideband_a.successor.is_zero () || block_exists (transaction_a, sideband_a.successor)); + debug_assert (block_a.sideband ().successor.is_zero () || block_exists (transaction_a, block_a.sideband ().successor)); std::vector vector; { nano::vectorstream stream (vector); block_a.serialize (stream); - sideband_a.serialize (stream); + block_a.sideband ().serialize (stream, block_a.type ()); } block_raw_put (transaction_a, vector, block_a.type (), hash_a); nano::block_predecessor_set predecessor (transaction_a, *this); @@ -116,13 +114,12 @@ class block_store_partial : public block_store // Converts a block hash to a block height uint64_t block_account_height (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const override { - nano::block_sideband sideband; - auto block = block_get (transaction_a, hash_a, &sideband); + auto block = block_get (transaction_a, hash_a); debug_assert (block != nullptr); - return sideband.height; + return block->sideband ().height; } - std::shared_ptr block_get (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_sideband * sideband_a = nullptr) const override + std::shared_ptr block_get (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const override { nano::block_type type; auto value (block_raw_get (transaction_a, hash_a, type)); @@ -132,25 +129,37 @@ class block_store_partial : public block_store nano::bufferstream stream (reinterpret_cast (value.data ()), value.size ()); result = nano::deserialize_block (stream, type); debug_assert (result != nullptr); - if (sideband_a) + nano::block_sideband sideband; + if (full_sideband (transaction_a) || entry_has_sideband (value.size (), type)) { - sideband_a->type = type; - if (full_sideband (transaction_a) || entry_has_sideband (value.size (), type)) - { - auto error (sideband_a->deserialize (stream)); - (void)error; - debug_assert (!error); - } - else - { - // Reconstruct sideband data for block. - sideband_a->account = block_account_computed (transaction_a, hash_a); - sideband_a->balance = block_balance_computed (transaction_a, hash_a); - sideband_a->successor = block_successor (transaction_a, hash_a); - sideband_a->height = 0; - sideband_a->timestamp = 0; - } + auto error (sideband.deserialize (stream, type)); + (void)error; + debug_assert (!error); } + else + { + // Reconstruct sideband data for block. + sideband.account = block_account_computed (transaction_a, hash_a); + sideband.balance = block_balance_computed (transaction_a, hash_a); + sideband.successor = block_successor (transaction_a, hash_a); + sideband.height = 0; + sideband.timestamp = 0; + } + result->sideband_set (sideband); + } + return result; + } + + std::shared_ptr block_get_no_sideband (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const override + { + nano::block_type type; + auto value (block_raw_get (transaction_a, hash_a, type)); + std::shared_ptr result; + if (value.size () != 0) + { + nano::bufferstream stream (reinterpret_cast (value.data ()), value.size ()); + result = nano::deserialize_block (stream, type); + debug_assert (result != nullptr); } return result; } @@ -186,18 +195,17 @@ class block_store_partial : public block_store nano::account block_account (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const override { - nano::block_sideband sideband; - auto block (block_get (transaction_a, hash_a, &sideband)); + auto block (block_get (transaction_a, hash_a)); nano::account result (block->account ()); if (result.is_zero ()) { - result = sideband.account; + result = block->sideband ().account; } debug_assert (!result.is_zero ()); return result; } - nano::uint128_t block_balance_calculated (std::shared_ptr block_a, nano::block_sideband const & sideband_a) const override + nano::uint128_t block_balance_calculated (std::shared_ptr const & block_a) const override { nano::uint128_t result; switch (block_a->type ()) @@ -205,7 +213,7 @@ class block_store_partial : public block_store case nano::block_type::open: case nano::block_type::receive: case nano::block_type::change: - result = sideband_a.balance.number (); + result = block_a->sideband ().balance.number (); break; case nano::block_type::send: result = boost::polymorphic_downcast (block_a.get ())->hashables.balance.number (); @@ -407,11 +415,10 @@ class block_store_partial : public block_store nano::epoch block_version (nano::transaction const & transaction_a, nano::block_hash const & hash_a) override { nano::db_val value; - nano::block_sideband sideband; - auto block = block_get (transaction_a, hash_a, &sideband); - if (sideband.type == nano::block_type::state) + auto block = block_get (transaction_a, hash_a); + if (block && block->type () == nano::block_type::state) { - return sideband.details.epoch; + return block->sideband ().details.epoch; } return nano::epoch::epoch_0; @@ -842,7 +849,7 @@ class block_store_partial : public block_store auto hash (hash_a); while (result.is_zero ()) { - auto block (block_get (transaction_a, hash)); + auto block (block_get_no_sideband (transaction_a, hash)); debug_assert (block); result = block->account (); if (result.is_zero ()) diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index 715ab3e19f..ea5a43ee6e 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -4,10 +4,10 @@ #include #include #include +#include #include #include #include -#include #include #include diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 5f49b16095..3b74b2552a 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -172,18 +172,18 @@ class rollback_visitor : public nano::block_visitor bool error{ false }; }; -class ledger_processor : public nano::block_visitor +class ledger_processor : public nano::mutable_block_visitor { public: ledger_processor (nano::ledger &, nano::write_transaction const &, nano::signature_verification = nano::signature_verification::unknown); virtual ~ledger_processor () = default; - void send_block (nano::send_block const &) override; - void receive_block (nano::receive_block const &) override; - void open_block (nano::open_block const &) override; - void change_block (nano::change_block const &) override; - void state_block (nano::state_block const &) override; - void state_block_impl (nano::state_block const &); - void epoch_block_impl (nano::state_block const &); + void send_block (nano::send_block &) override; + void receive_block (nano::receive_block &) override; + void open_block (nano::open_block &) override; + void change_block (nano::change_block &) override; + void state_block (nano::state_block &) override; + void state_block_impl (nano::state_block &); + void epoch_block_impl (nano::state_block &); nano::ledger & ledger; nano::write_transaction const & transaction; nano::signature_verification verification; @@ -230,7 +230,7 @@ bool ledger_processor::validate_epoch_block (nano::state_block const & block_a) return (block_a.hashables.balance == prev_balance); } -void ledger_processor::state_block (nano::state_block const & block_a) +void ledger_processor::state_block (nano::state_block & block_a) { result.code = nano::process_result::progress; auto is_epoch_block = false; @@ -253,7 +253,7 @@ void ledger_processor::state_block (nano::state_block const & block_a) } } -void ledger_processor::state_block_impl (nano::state_block const & block_a) +void ledger_processor::state_block_impl (nano::state_block & block_a) { auto hash (block_a.hash ()); auto existing (ledger.store.block_exists (transaction, block_a.type (), hash)); @@ -334,9 +334,8 @@ void ledger_processor::state_block_impl (nano::state_block const & block_a) if (result.code == nano::process_result::progress) { ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::state_block); - result.state_is_send = is_send; - nano::block_sideband sideband (nano::block_type::state, block_a.hashables.account /* unused */, 0, 0 /* unused */, info.block_count + 1, nano::seconds_since_epoch (), epoch, is_send, is_receive, false); - ledger.store.block_put (transaction, hash, block_a, sideband); + block_a.sideband_set (nano::block_sideband (block_a.hashables.account /* unused */, 0, 0 /* unused */, info.block_count + 1, nano::seconds_since_epoch (), epoch, is_send, is_receive, false)); + ledger.store.block_put (transaction, hash, block_a); if (!info.head.is_zero ()) { @@ -371,7 +370,7 @@ void ledger_processor::state_block_impl (nano::state_block const & block_a) } } -void ledger_processor::epoch_block_impl (nano::state_block const & block_a) +void ledger_processor::epoch_block_impl (nano::state_block & block_a) { auto hash (block_a.hash ()); auto existing (ledger.store.block_exists (transaction, block_a.type (), hash)); @@ -423,8 +422,8 @@ void ledger_processor::epoch_block_impl (nano::state_block const & block_a) ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::epoch_block); result.account = block_a.hashables.account; result.amount = 0; - nano::block_sideband sideband (nano::block_type::state, block_a.hashables.account /* unused */, 0, 0 /* unused */, info.block_count + 1, nano::seconds_since_epoch (), epoch, false, false, true); - ledger.store.block_put (transaction, hash, block_a, sideband); + block_a.sideband_set (nano::block_sideband (block_a.hashables.account /* unused */, 0, 0 /* unused */, info.block_count + 1, nano::seconds_since_epoch (), epoch, false, false, true)); + ledger.store.block_put (transaction, hash, block_a); nano::account_info new_info (hash, block_a.representative (), info.open_block.is_zero () ? hash : info.open_block, info.balance, nano::seconds_since_epoch (), info.block_count + 1, epoch); ledger.change_latest (transaction, block_a.hashables.account, info, new_info); if (!ledger.store.frontier_get (transaction, info.head).is_zero ()) @@ -439,7 +438,7 @@ void ledger_processor::epoch_block_impl (nano::state_block const & block_a) } } -void ledger_processor::change_block (nano::change_block const & block_a) +void ledger_processor::change_block (nano::change_block & block_a) { auto hash (block_a.hash ()); auto existing (ledger.store.block_exists (transaction, block_a.type (), hash)); @@ -471,8 +470,8 @@ void ledger_processor::change_block (nano::change_block const & block_a) { debug_assert (!validate_message (account, hash, block_a.signature)); result.verified = nano::signature_verification::valid; - nano::block_sideband sideband (nano::block_type::change, account, 0, info.balance, info.block_count + 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false, false, false); - ledger.store.block_put (transaction, hash, block_a, sideband); + block_a.sideband_set (nano::block_sideband (account, 0, info.balance, info.block_count + 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */)); + ledger.store.block_put (transaction, hash, block_a); auto balance (ledger.balance (transaction, block_a.hashables.previous)); ledger.cache.rep_weights.representation_add (block_a.representative (), balance); ledger.cache.rep_weights.representation_add (info.representative, 0 - balance); @@ -490,7 +489,7 @@ void ledger_processor::change_block (nano::change_block const & block_a) } } -void ledger_processor::send_block (nano::send_block const & block_a) +void ledger_processor::send_block (nano::send_block & block_a) { auto hash (block_a.hash ()); auto existing (ledger.store.block_exists (transaction, block_a.type (), hash)); @@ -527,8 +526,8 @@ void ledger_processor::send_block (nano::send_block const & block_a) { auto amount (info.balance.number () - block_a.hashables.balance.number ()); ledger.cache.rep_weights.representation_add (info.representative, 0 - amount); - nano::block_sideband sideband (nano::block_type::send, account, 0, block_a.hashables.balance /* unused */, info.block_count + 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */); - ledger.store.block_put (transaction, hash, block_a, sideband); + block_a.sideband_set (nano::block_sideband (account, 0, block_a.hashables.balance /* unused */, info.block_count + 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */)); + ledger.store.block_put (transaction, hash, block_a); nano::account_info new_info (hash, info.representative, info.open_block, block_a.hashables.balance, nano::seconds_since_epoch (), info.block_count + 1, nano::epoch::epoch_0); ledger.change_latest (transaction, account, info, new_info); ledger.store.pending_put (transaction, nano::pending_key (block_a.hashables.destination, hash), { account, amount, nano::epoch::epoch_0 }); @@ -546,7 +545,7 @@ void ledger_processor::send_block (nano::send_block const & block_a) } } -void ledger_processor::receive_block (nano::receive_block const & block_a) +void ledger_processor::receive_block (nano::receive_block & block_a) { auto hash (block_a.hash ()); auto existing (ledger.store.block_exists (transaction, block_a.type (), hash)); @@ -595,8 +594,8 @@ void ledger_processor::receive_block (nano::receive_block const & block_a) (void)error; debug_assert (!error); ledger.store.pending_del (transaction, key); - nano::block_sideband sideband (nano::block_type::receive, account, 0, new_balance, info.block_count + 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */); - ledger.store.block_put (transaction, hash, block_a, sideband); + block_a.sideband_set (nano::block_sideband (account, 0, new_balance, info.block_count + 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */)); + ledger.store.block_put (transaction, hash, block_a); nano::account_info new_info (hash, info.representative, info.open_block, new_balance, nano::seconds_since_epoch (), info.block_count + 1, nano::epoch::epoch_0); ledger.change_latest (transaction, account, info, new_info); ledger.cache.rep_weights.representation_add (info.representative, pending.amount.number ()); @@ -620,7 +619,7 @@ void ledger_processor::receive_block (nano::receive_block const & block_a) } } -void ledger_processor::open_block (nano::open_block const & block_a) +void ledger_processor::open_block (nano::open_block & block_a) { auto hash (block_a.hash ()); auto existing (ledger.store.block_exists (transaction, block_a.type (), hash)); @@ -659,8 +658,8 @@ void ledger_processor::open_block (nano::open_block const & block_a) (void)error; debug_assert (!error); ledger.store.pending_del (transaction, key); - nano::block_sideband sideband (nano::block_type::open, block_a.hashables.account, 0, pending.amount, 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */); - ledger.store.block_put (transaction, hash, block_a, sideband); + block_a.sideband_set (nano::block_sideband (block_a.hashables.account, 0, pending.amount, 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */)); + ledger.store.block_put (transaction, hash, block_a); nano::account_info new_info (hash, block_a.representative (), hash, pending.amount.number (), nano::seconds_since_epoch (), 1, nano::epoch::epoch_0); ledger.change_latest (transaction, block_a.hashables.account, info, new_info); ledger.cache.rep_weights.representation_add (block_a.representative (), pending.amount.number ()); @@ -752,7 +751,7 @@ nano::uint128_t nano::ledger::account_pending (nano::transaction const & transac return result; } -nano::process_return nano::ledger::process (nano::write_transaction const & transaction_a, nano::block const & block_a, nano::signature_verification verification) +nano::process_return nano::ledger::process (nano::write_transaction const & transaction_a, nano::block & block_a, nano::signature_verification verification) { debug_assert (!nano::work_validate (block_a)); ledger_processor processor (*this, transaction_a, verification); diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 60e7106069..00a460c58b 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -38,7 +38,7 @@ class ledger final bool is_send (nano::transaction const &, nano::state_block const &) const; nano::account const & block_destination (nano::transaction const &, nano::block const &); nano::block_hash block_source (nano::transaction const &, nano::block const &); - nano::process_return process (nano::write_transaction const &, nano::block const &, nano::signature_verification = nano::signature_verification::unknown); + nano::process_return process (nano::write_transaction const &, nano::block &, nano::signature_verification = nano::signature_verification::unknown); bool rollback (nano::write_transaction const &, nano::block_hash const &, std::vector> &); bool rollback (nano::write_transaction const &, nano::block_hash const &); void change_latest (nano::write_transaction const &, nano::account const &, nano::account_info const &, nano::account_info const &); From d78de58cc631995f3c6d33571a0d0126b0fc1ba7 Mon Sep 17 00:00:00 2001 From: Wesley Shillingford Date: Tue, 3 Mar 2020 10:51:57 +0000 Subject: [PATCH 12/43] Unchecked deletion (#2609) --- nano/node/blockprocessor.cpp | 8 +++----- nano/node/node.cpp | 3 ++- nano/node/rocksdb/rocksdb.cpp | 2 +- nano/secure/blockstore.hpp | 3 +-- nano/secure/blockstore_partial.hpp | 13 ++++++------- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index c7f0d9e9dc..77f84a63c7 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -558,11 +558,9 @@ void nano::block_processor::queue_unchecked (nano::write_transaction const & tra { if (!node.flags.disable_block_processor_unchecked_deletion) { - if (!node.store.unchecked_del (transaction_a, nano::unchecked_key (hash_a, info.block->hash ()))) - { - debug_assert (node.ledger.cache.unchecked_count > 0); - --node.ledger.cache.unchecked_count; - } + node.store.unchecked_del (transaction_a, nano::unchecked_key (hash_a, info.block->hash ())); + debug_assert (node.ledger.cache.unchecked_count > 0); + --node.ledger.cache.unchecked_count; } add (info); } diff --git a/nano/node/node.cpp b/nano/node/node.cpp index a4b863c3cd..bf4da967b4 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -968,8 +968,9 @@ void nano::node::unchecked_cleanup () { auto key (cleaning_list.front ()); cleaning_list.pop_front (); - if (!store.unchecked_del (transaction, key)) + if (store.unchecked_exists (transaction, key)) { + store.unchecked_del (transaction, key); debug_assert (ledger.cache.unchecked_count > 0); --ledger.cache.unchecked_count; } diff --git a/nano/node/rocksdb/rocksdb.cpp b/nano/node/rocksdb/rocksdb.cpp index ce9ff1f4fd..0b63521808 100644 --- a/nano/node/rocksdb/rocksdb.cpp +++ b/nano/node/rocksdb/rocksdb.cpp @@ -215,7 +215,7 @@ bool nano::rocksdb_store::exists (nano::transaction const & transaction_a, table int nano::rocksdb_store::del (nano::write_transaction const & transaction_a, tables table_a, nano::rocksdb_val const & key_a) { debug_assert (transaction_a.contains (table_a)); - // RocksDB errors when trying to delete an entry which doesn't exist. It is a pre-condition that the key exists + // RocksDB does not report not_found status, it is a pre-condition that the key exists debug_assert (exists (transaction_a, table_a, key_a)); // Removing an entry so counts may need adjusting diff --git a/nano/secure/blockstore.hpp b/nano/secure/blockstore.hpp index 310347a862..e4a8faef7c 100644 --- a/nano/secure/blockstore.hpp +++ b/nano/secure/blockstore.hpp @@ -710,8 +710,7 @@ class block_store virtual void unchecked_put (nano::write_transaction const &, nano::block_hash const &, std::shared_ptr const &) = 0; virtual std::vector unchecked_get (nano::transaction const &, nano::block_hash const &) = 0; virtual bool unchecked_exists (nano::transaction const & transaction_a, nano::unchecked_key const & unchecked_key_a) = 0; - /* Returns true if nothing was deleted because it was not found, false otherwise */ - virtual bool unchecked_del (nano::write_transaction const &, nano::unchecked_key const &) = 0; + virtual void unchecked_del (nano::write_transaction const &, nano::unchecked_key const &) = 0; virtual nano::store_iterator unchecked_begin (nano::transaction const &) const = 0; virtual nano::store_iterator unchecked_begin (nano::transaction const &, nano::unchecked_key const &) const = 0; virtual nano::store_iterator unchecked_end () const = 0; diff --git a/nano/secure/blockstore_partial.hpp b/nano/secure/blockstore_partial.hpp index d58543d981..00e69c8484 100644 --- a/nano/secure/blockstore_partial.hpp +++ b/nano/secure/blockstore_partial.hpp @@ -441,8 +441,8 @@ class block_store_partial : public block_store void pending_del (nano::write_transaction const & transaction_a, nano::pending_key const & key_a) override { - auto status1 = del (transaction_a, tables::pending, key_a); - release_assert (success (status1)); + auto status = del (transaction_a, tables::pending, key_a); + release_assert (success (status)); } bool pending_get (nano::transaction const & transaction_a, nano::pending_key const & key_a, nano::pending_info & pending_a) override @@ -493,11 +493,10 @@ class block_store_partial : public block_store release_assert (success (status)); } - bool unchecked_del (nano::write_transaction const & transaction_a, nano::unchecked_key const & key_a) override + void unchecked_del (nano::write_transaction const & transaction_a, nano::unchecked_key const & key_a) override { auto status (del (transaction_a, tables::unchecked, key_a)); - release_assert (success (status) || not_found (status)); - return not_found (status); + release_assert (success (status)); } std::shared_ptr vote_get (nano::transaction const & transaction_a, nano::account const & account_a) override @@ -558,8 +557,8 @@ class block_store_partial : public block_store void account_del (nano::write_transaction const & transaction_a, nano::account const & account_a) override { - auto status1 = del (transaction_a, tables::accounts, account_a); - release_assert (success (status1)); + auto status = del (transaction_a, tables::accounts, account_a); + release_assert (success (status)); } bool account_get (nano::transaction const & transaction_a, nano::account const & account_a, nano::account_info & info_a) override From 95de70d090f1ee0496c8965041b2341bf6be6ad4 Mon Sep 17 00:00:00 2001 From: Russel Waters Date: Tue, 3 Mar 2020 09:39:16 -0500 Subject: [PATCH 13/43] Host qt assets for windows on s3 (#2622) --- ci/actions/windows/install_deps.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/actions/windows/install_deps.ps1 b/ci/actions/windows/install_deps.ps1 index 29909727b7..4edbe17fb0 100644 --- a/ci/actions/windows/install_deps.ps1 +++ b/ci/actions/windows/install_deps.ps1 @@ -51,8 +51,8 @@ function Get-RedirectedUri { $qt5_root = "c:\qt" $rocksdb_url = Get-RedirectedUri "https://repo.nano.org/artifacts/rocksdb-msvc14.1-latest.7z" -$qt5base_url = Get-RedirectedUri "https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/qt5_5131/qt.qt5.5131.win64_msvc2017_64/5.13.1-0-201909031231qtbase-Windows-Windows_10-MSVC2017-Windows-Windows_10-X86_64.7z" -$qt5winextra_url = Get-RedirectedUri "https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/qt5_5131/qt.qt5.5131.win64_msvc2017_64/5.13.1-0-201909031231qtwinextras-Windows-Windows_10-MSVC2017-Windows-Windows_10-X86_64.7z" +$qt5base_url = Get-RedirectedUri "https://repo.nano.org/artifacts/5.13.1-0-201909031231qtbase-Windows-Windows_10-MSVC2017-Windows-Windows_10-X86_64.7z" +$qt5winextra_url = Get-RedirectedUri "https://repo.nano.org/artifacts/5.13.1-0-201909031231qtwinextras-Windows-Windows_10-MSVC2017-Windows-Windows_10-X86_64.7z" $rocksdb_artifact = "${env:TMP}\rocksdb.7z" $qt5base_artifact = "${env:TMP}\qt5base.7z" $qt5winextra_artifact = "${env:TMP}\qt5winextra.7z" From a1ad2fa3c53b8c00bcd7812c88a0746ceb5d2c20 Mon Sep 17 00:00:00 2001 From: Wesley Shillingford Date: Tue, 3 Mar 2020 17:44:18 +0000 Subject: [PATCH 14/43] [RocksDB] Tests not reading account count from store correctly (#2623) --- nano/core_test/node_telemetry.cpp | 55 +++++++++++++++++++++++------- nano/nano_node/entry.cpp | 3 +- nano/node/rocksdb/rocksdb.cpp | 13 ++++++- nano/secure/blockstore.hpp | 6 ++-- nano/secure/blockstore_partial.hpp | 6 ++-- 5 files changed, 61 insertions(+), 22 deletions(-) diff --git a/nano/core_test/node_telemetry.cpp b/nano/core_test/node_telemetry.cpp index 2996950271..7e4152eb2c 100644 --- a/nano/core_test/node_telemetry.cpp +++ b/nano/core_test/node_telemetry.cpp @@ -335,7 +335,7 @@ TEST (node_telemetry, many_nodes) { nano::system system; // The telemetry responses can timeout if using a large number of nodes under sanitizers, so lower the number. - const auto num_nodes = (is_sanitizer_build || nano::running_within_valgrind ()) ? 4 : 10; //3; // 10; + const auto num_nodes = (is_sanitizer_build || nano::running_within_valgrind ()) ? 4 : 10; nano::node_flags node_flags; node_flags.disable_ongoing_telemetry_requests = true; for (auto i = 0; i < num_nodes; ++i) @@ -343,7 +343,23 @@ TEST (node_telemetry, many_nodes) nano::node_config node_config (nano::get_available_port (), system.logging); // Make a metric completely different for each node so we can check afterwards that there are no duplicates node_config.bandwidth_limit = 100000 + i; - system.add_node (node_config, node_flags); + + auto node = std::make_shared (system.io_ctx, nano::unique_path (), system.alarm, node_config, system.work, node_flags); + node->start (); + system.nodes.push_back (node); + } + + // Merge peers after creating nodes as some backends (RocksDB) can take a while to initialize nodes (Windows/Debug for instance) + // and timeouts can occur between nodes while starting up many nodes synchronously. + for (auto const & node : system.nodes) + { + for (auto const & other_node : system.nodes) + { + if (node != other_node) + { + node->network.merge_peer (other_node->network.endpoint ()); + } + } } wait_peer_connections (system); @@ -755,17 +771,30 @@ namespace { void wait_peer_connections (nano::system & system_a) { - system_a.deadline_set (10s); - auto peer_count = 0; - auto num_nodes = system_a.nodes.size (); - while (peer_count != num_nodes * (num_nodes - 1)) - { - ASSERT_NO_ERROR (system_a.poll ()); - peer_count = std::accumulate (system_a.nodes.cbegin (), system_a.nodes.cend (), 0, [](auto total, auto const & node) { - auto transaction = node->store.tx_begin_read (); - return total += node->store.peer_count (transaction); - }); - } + auto wait_peer_count = [&system_a](bool in_memory) { + auto num_nodes = system_a.nodes.size (); + system_a.deadline_set (20s); + auto peer_count = 0; + while (peer_count != num_nodes * (num_nodes - 1)) + { + ASSERT_NO_ERROR (system_a.poll ()); + peer_count = std::accumulate (system_a.nodes.cbegin (), system_a.nodes.cend (), 0, [in_memory](auto total, auto const & node) { + if (in_memory) + { + return total += node->network.size (); + } + else + { + auto transaction = node->store.tx_begin_read (); + return total += node->store.peer_count (transaction); + } + }); + } + }; + + // Do a pre-pass with in-memory containers to reduce IO if still in the process of connecting to peers + wait_peer_count (true); + wait_peer_count (false); } void compare_default_test_result_data (nano::telemetry_data const & telemetry_data_a, nano::node const & node_server_a) diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index ca4b0054db..643cd52b9f 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -276,8 +276,7 @@ int main (int argc, char * const * argv) else if (vm.count ("debug_account_count")) { nano::inactive_node node (data_path); - auto transaction (node.node->store.tx_begin_read ()); - std::cout << boost::str (boost::format ("Frontier count: %1%\n") % node.node->store.account_count (transaction)); + std::cout << boost::str (boost::format ("Frontier count: %1%\n") % node.node->ledger.cache.account_count); } else if (vm.count ("debug_mass_activity")) { diff --git a/nano/node/rocksdb/rocksdb.cpp b/nano/node/rocksdb/rocksdb.cpp index 0b63521808..6eb2b5d22b 100644 --- a/nano/node/rocksdb/rocksdb.cpp +++ b/nano/node/rocksdb/rocksdb.cpp @@ -366,7 +366,7 @@ uint64_t nano::rocksdb_store::count (nano::transaction const & transaction_a, ro size_t nano::rocksdb_store::count (nano::transaction const & transaction_a, tables table_a) const { size_t sum = 0; - // Some column families are small enough that they can just be iterated, rather than doing extra io caching counts + // Some column families are small enough (except unchecked) that they can just be iterated, rather than doing extra io caching counts if (table_a == tables::peers) { for (auto i (peers_begin (transaction_a)), n (peers_end ()); i != n; ++i) @@ -381,6 +381,7 @@ size_t nano::rocksdb_store::count (nano::transaction const & transaction_a, tabl ++sum; } } + // This should only be used during initialization as can be expensive during bootstrapping else if (table_a == tables::unchecked) { for (auto i (unchecked_begin (transaction_a)), n (unchecked_end ()); i != n; ++i) @@ -388,8 +389,18 @@ size_t nano::rocksdb_store::count (nano::transaction const & transaction_a, tabl ++sum; } } + // This should only be used in tests + else if (table_a == tables::accounts) + { + debug_assert (network_constants ().is_test_network ()); + for (auto i (latest_begin (transaction_a)), n (latest_end ()); i != n; ++i) + { + ++sum; + } + } else { + debug_assert (is_caching_counts (table_a)); return count (transaction_a, table_to_column_family (table_a)); } diff --git a/nano/secure/blockstore.hpp b/nano/secure/blockstore.hpp index e4a8faef7c..30b3cef5ac 100644 --- a/nano/secure/blockstore.hpp +++ b/nano/secure/blockstore.hpp @@ -688,9 +688,9 @@ class block_store virtual size_t account_count (nano::transaction const &) = 0; virtual void confirmation_height_clear (nano::write_transaction const &, nano::account const &, uint64_t) = 0; virtual void confirmation_height_clear (nano::write_transaction const &) = 0; - virtual nano::store_iterator latest_begin (nano::transaction const &, nano::account const &) = 0; - virtual nano::store_iterator latest_begin (nano::transaction const &) = 0; - virtual nano::store_iterator latest_end () = 0; + virtual nano::store_iterator latest_begin (nano::transaction const &, nano::account const &) const = 0; + virtual nano::store_iterator latest_begin (nano::transaction const &) const = 0; + virtual nano::store_iterator latest_end () const = 0; virtual void pending_put (nano::write_transaction const &, nano::pending_key const &, nano::pending_info const &) = 0; virtual void pending_del (nano::write_transaction const &, nano::pending_key const &) = 0; diff --git a/nano/secure/blockstore_partial.hpp b/nano/secure/blockstore_partial.hpp index 00e69c8484..0e9bb13c3c 100644 --- a/nano/secure/blockstore_partial.hpp +++ b/nano/secure/blockstore_partial.hpp @@ -354,7 +354,7 @@ class block_store_partial : public block_store return nano::store_iterator (nullptr); } - nano::store_iterator latest_end () override + nano::store_iterator latest_end () const override { return nano::store_iterator (nullptr); } @@ -723,12 +723,12 @@ class block_store_partial : public block_store return exists (transaction_a, tables::confirmation_height, nano::db_val (account_a)); } - nano::store_iterator latest_begin (nano::transaction const & transaction_a, nano::account const & account_a) override + nano::store_iterator latest_begin (nano::transaction const & transaction_a, nano::account const & account_a) const override { return make_iterator (transaction_a, tables::accounts, nano::db_val (account_a)); } - nano::store_iterator latest_begin (nano::transaction const & transaction_a) override + nano::store_iterator latest_begin (nano::transaction const & transaction_a) const override { return make_iterator (transaction_a, tables::accounts); } From 3ce9fe867b713021758091ffaf6002b454bac9d2 Mon Sep 17 00:00:00 2001 From: Wesley Shillingford Date: Tue, 3 Mar 2020 19:43:17 +0000 Subject: [PATCH 15/43] Fix confirmation_height.gap_live intermittent test failure (#2621) --- nano/core_test/confirmation_height.cpp | 99 +++++++++++++------------- 1 file changed, 48 insertions(+), 51 deletions(-) diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index 33830efffa..5100ca6291 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -308,7 +308,7 @@ TEST (confirmation_height, gap_live) node_flags.confirmation_height_processor_mode = mode_a; nano::node_config node_config (nano::get_available_port (), system.logging); node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto node1 = system.add_node (node_config, node_flags); + auto node = system.add_node (node_config, node_flags); node_config.peering_port = nano::get_available_port (); system.add_node (node_config, node_flags); nano::keypair destination; @@ -317,72 +317,69 @@ TEST (confirmation_height, gap_live) nano::genesis genesis; auto send1 (std::make_shared (nano::genesis_account, genesis.hash (), nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, destination.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0)); - node1->work_generate_blocking (*send1); + node->work_generate_blocking (*send1); auto send2 (std::make_shared (nano::genesis_account, send1->hash (), nano::genesis_account, nano::genesis_amount - 2 * nano::Gxrb_ratio, destination.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0)); - node1->work_generate_blocking (*send2); + node->work_generate_blocking (*send2); auto send3 (std::make_shared (nano::genesis_account, send2->hash (), nano::genesis_account, nano::genesis_amount - 3 * nano::Gxrb_ratio, destination.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0)); - node1->work_generate_blocking (*send3); + node->work_generate_blocking (*send3); auto open1 (std::make_shared (send1->hash (), destination.pub, destination.pub, destination.prv, destination.pub, 0)); - node1->work_generate_blocking (*open1); + node->work_generate_blocking (*open1); auto receive1 (std::make_shared (open1->hash (), send2->hash (), destination.prv, destination.pub, 0)); - node1->work_generate_blocking (*receive1); + node->work_generate_blocking (*receive1); auto receive2 (std::make_shared (receive1->hash (), send3->hash (), destination.prv, destination.pub, 0)); - node1->work_generate_blocking (*receive2); + node->work_generate_blocking (*receive2); - for (auto & node : system.nodes) - { - node->block_processor.add (send1); - node->block_processor.add (send2); - node->block_processor.add (send3); - node->block_processor.add (receive1); - node->block_processor.flush (); + node->block_processor.add (send1); + node->block_processor.add (send2); + node->block_processor.add (send3); + node->block_processor.add (receive1); + node->block_processor.flush (); - add_callback_stats (*node); + add_callback_stats (*node); - // Receive 2 comes in on the live network, however the chain has not been finished so it gets added to unchecked - node->process_active (receive2); - node->block_processor.flush (); + // Receive 2 comes in on the live network, however the chain has not been finished so it gets added to unchecked + node->process_active (receive2); + node->block_processor.flush (); - // Confirmation heights should not be updated - { - auto transaction = node->store.tx_begin_read (); - nano::confirmation_height_info confirmation_height_info; - ASSERT_FALSE (node->store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height_info)); - ASSERT_EQ (1, confirmation_height_info.height); - ASSERT_EQ (nano::genesis_hash, confirmation_height_info.frontier); - } + // Confirmation heights should not be updated + { + auto transaction = node->store.tx_begin_read (); + nano::confirmation_height_info confirmation_height_info; + ASSERT_FALSE (node->store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height_info)); + ASSERT_EQ (1, confirmation_height_info.height); + ASSERT_EQ (nano::genesis_hash, confirmation_height_info.frontier); + } - // Now complete the chain where the block comes in on the live network - node->process_active (open1); - node->block_processor.flush (); + // Now complete the chain where the block comes in on the live network + node->process_active (open1); + node->block_processor.flush (); - system.deadline_set (10s); - while (node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) != 6) - { - ASSERT_NO_ERROR (system.poll ()); - } + system.deadline_set (10s); + while (node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) != 6) + { + ASSERT_NO_ERROR (system.poll ()); + } - // This should confirm the open block and the source of the receive blocks - auto transaction (node->store.tx_begin_read ()); - auto unchecked_count (node->store.unchecked_count (transaction)); - ASSERT_EQ (unchecked_count, 0); + // This should confirm the open block and the source of the receive blocks + auto transaction (node->store.tx_begin_read ()); + auto unchecked_count (node->store.unchecked_count (transaction)); + ASSERT_EQ (unchecked_count, 0); - nano::confirmation_height_info confirmation_height_info; - ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive2->hash ())); - ASSERT_FALSE (node->store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height_info)); - ASSERT_EQ (4, confirmation_height_info.height); - ASSERT_EQ (send3->hash (), confirmation_height_info.frontier); - ASSERT_FALSE (node->store.confirmation_height_get (transaction, destination.pub, confirmation_height_info)); - ASSERT_EQ (3, confirmation_height_info.height); - ASSERT_EQ (receive2->hash (), confirmation_height_info.frontier); + nano::confirmation_height_info confirmation_height_info; + ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive2->hash ())); + ASSERT_FALSE (node->store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height_info)); + ASSERT_EQ (4, confirmation_height_info.height); + ASSERT_EQ (send3->hash (), confirmation_height_info.frontier); + ASSERT_FALSE (node->store.confirmation_height_get (transaction, destination.pub, confirmation_height_info)); + ASSERT_EQ (3, confirmation_height_info.height); + ASSERT_EQ (receive2->hash (), confirmation_height_info.frontier); - ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (6, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out)); + ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); + ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); + ASSERT_EQ (6, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out)); - ASSERT_EQ (0, node->active.election_winner_details_size ()); - } + ASSERT_EQ (0, node->active.election_winner_details_size ()); }; test_mode (nano::confirmation_height_mode::bounded); From 7c6f436f6d8e6c45babfe93e1b5ef5f656d47595 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Wed, 4 Mar 2020 08:57:07 +0000 Subject: [PATCH 16/43] Simplify request aggregator mutex lock behavior (#2614) --- nano/node/request_aggregator.cpp | 32 +++++++++++--------------------- nano/node/request_aggregator.hpp | 4 +--- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/nano/node/request_aggregator.cpp b/nano/node/request_aggregator.cpp index ac54f4d89c..9bbd3cdb10 100644 --- a/nano/node/request_aggregator.cpp +++ b/nano/node/request_aggregator.cpp @@ -13,7 +13,6 @@ nano::request_aggregator::request_aggregator (nano::network_constants const & ne max_delay (network_constants_a.is_test_network () ? 50 : 300), small_delay (network_constants_a.is_test_network () ? 10 : 50), max_channel_requests (config_a.max_queued_requests), -max_consecutive_requests (network_constants_a.is_test_network () ? 1 : 10), stats (stats_a), votes_cache (cache_a), store (store_a), @@ -68,7 +67,6 @@ void nano::request_aggregator::run () lock.unlock (); condition.notify_all (); lock.lock (); - unsigned consecutive_requests = 0; while (!stopped) { if (!requests.empty ()) @@ -85,31 +83,28 @@ void nano::request_aggregator::run () auto channel = pool.channel; // Safely erase this pool requests_by_deadline.erase (front); - if (!remaining.empty ()) + lock.unlock (); + // Send cached votes + for (auto const & vote : remaining.first) { - lock.unlock (); - // Generate votes for the remaining hashes - generate (transaction, std::move (remaining), channel); - consecutive_requests = 0; - lock.lock (); + nano::confirm_ack confirm (vote); + channel->send (confirm); } - else if (++consecutive_requests == max_consecutive_requests) + if (!remaining.second.empty ()) { - lock.unlock (); - consecutive_requests = 0; - lock.lock (); + // Generate votes for the remaining hashes + generate (transaction, std::move (remaining.second), channel); } + lock.lock (); } else { - consecutive_requests = 0; auto deadline = front->deadline; condition.wait_until (lock, deadline, [this, &deadline]() { return this->stopped || deadline < std::chrono::steady_clock::now (); }); } } else { - consecutive_requests = 0; condition.wait_for (lock, small_delay, [this]() { return this->stopped || !this->requests.empty (); }); } } @@ -139,7 +134,7 @@ bool nano::request_aggregator::empty () return size () == 0; } -std::vector nano::request_aggregator::aggregate (nano::transaction const & transaction_a, channel_pool & pool_a) const +std::pair>, std::vector> nano::request_aggregator::aggregate (nano::transaction const & transaction_a, channel_pool & pool_a) const { // Unique hashes using pair = decltype (pool_a.hashes_roots)::value_type; @@ -205,14 +200,9 @@ std::vector nano::request_aggregator::aggregate (nano::transac // Unique votes std::sort (cached_votes.begin (), cached_votes.end ()); cached_votes.erase (std::unique (cached_votes.begin (), cached_votes.end ()), cached_votes.end ()); - for (auto const & vote : cached_votes) - { - nano::confirm_ack confirm (vote); - pool_a.channel->send (confirm); - } stats.add (nano::stat::type::requests, nano::stat::detail::requests_cached_hashes, stat::dir::in, cached_hashes); stats.add (nano::stat::type::requests, nano::stat::detail::requests_cached_votes, stat::dir::in, cached_votes.size ()); - return to_generate; + return { cached_votes, to_generate }; } void nano::request_aggregator::generate (nano::transaction const & transaction_a, std::vector hashes_a, std::shared_ptr & channel_a) const diff --git a/nano/node/request_aggregator.hpp b/nano/node/request_aggregator.hpp index f1b5dee8d1..a94cd87515 100644 --- a/nano/node/request_aggregator.hpp +++ b/nano/node/request_aggregator.hpp @@ -74,12 +74,10 @@ class request_aggregator final private: void run (); /** Aggregate and send cached votes for \p pool_a, returning the leftovers that were not found in cached votes **/ - std::vector aggregate (nano::transaction const &, channel_pool & pool_a) const; + std::pair>, std::vector> aggregate (nano::transaction const &, channel_pool & pool_a) const; /** Generate and send votes from \p hashes_a to \p channel_a, does not need a lock on the mutex **/ void generate (nano::transaction const &, std::vector hashes_a, std::shared_ptr & channel_a) const; - unsigned const max_consecutive_requests; - nano::stat & stats; nano::votes_cache & votes_cache; nano::block_store & store; From 5600dc6bbc88ada9053a23521a5fb2657d18d9a9 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Wed, 4 Mar 2020 09:02:06 +0000 Subject: [PATCH 17/43] Use the sideband when available in ledger.is_send (#2620) --- nano/lib/blocks.cpp | 5 +++++ nano/lib/blocks.hpp | 1 + nano/secure/ledger.cpp | 15 +++++++++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/nano/lib/blocks.cpp b/nano/lib/blocks.cpp index e8ad752488..7c0f23616a 100644 --- a/nano/lib/blocks.cpp +++ b/nano/lib/blocks.cpp @@ -149,6 +149,11 @@ void nano::block::sideband_set (nano::block_sideband const & sideband_a) sideband_m = sideband_a; } +bool nano::block::has_sideband () const +{ + return sideband_m.is_initialized (); +} + nano::account const & nano::block::representative () const { static nano::account rep{ 0 }; diff --git a/nano/lib/blocks.hpp b/nano/lib/blocks.hpp index 793af471c6..d0ee95c94f 100644 --- a/nano/lib/blocks.hpp +++ b/nano/lib/blocks.hpp @@ -73,6 +73,7 @@ class block nano::block_hash full_hash () const; nano::block_sideband const & sideband () const; void sideband_set (nano::block_sideband const &); + bool has_sideband () const; std::string to_json () const; virtual void hash (blake2b_state &) const = 0; virtual uint64_t block_work () const = 0; diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 3b74b2552a..1e552b5aa9 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -807,12 +807,19 @@ std::string nano::ledger::block_text (nano::block_hash const & hash_a) bool nano::ledger::is_send (nano::transaction const & transaction_a, nano::state_block const & block_a) const { bool result (false); - nano::block_hash previous (block_a.hashables.previous); - if (!previous.is_zero ()) + if (block_a.has_sideband ()) { - if (block_a.hashables.balance < balance (transaction_a, previous)) + result = block_a.sideband ().details.is_send; + } + else + { + nano::block_hash previous (block_a.hashables.previous); + if (!previous.is_zero ()) { - result = true; + if (block_a.hashables.balance < balance (transaction_a, previous)) + { + result = true; + } } } return result; From ec2649a95ec6cfbb4b1c271924d58f18d140ac88 Mon Sep 17 00:00:00 2001 From: Wesley Shillingford Date: Wed, 4 Mar 2020 09:11:41 +0000 Subject: [PATCH 18/43] confirmation_height.modified_chain test fails on a non-debug build (#2624) --- nano/core_test/confirmation_height.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index 5100ca6291..13ba95d6c2 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -802,7 +802,30 @@ TEST (confirmation_height, modified_chain) ASSERT_NO_ERROR (system.poll ()); } - ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::invalid_block, nano::stat::dir::in)); + auto check_for_modified_chains = true; + if (mode_a == nano::confirmation_height_mode::unbounded) + { +#ifdef NDEBUG + // Unbounded processor in release config does not check that a chain has been modified prior to setting the confirmation height (as an optimization) + check_for_modified_chains = false; +#endif + } + + nano::confirmation_height_info confirmation_height_info; + ASSERT_FALSE (node->store.confirmation_height_get (node->store.tx_begin_read (), nano::test_genesis_key.pub, confirmation_height_info)); + if (check_for_modified_chains) + { + ASSERT_EQ (1, confirmation_height_info.height); + ASSERT_EQ (nano::genesis_hash, confirmation_height_info.frontier); + ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::invalid_block, nano::stat::dir::in)); + } + else + { + // A non-existent block is cemented, expected given these conditions but is of course incorrect. + ASSERT_EQ (2, confirmation_height_info.height); + ASSERT_EQ (send->hash (), confirmation_height_info.frontier); + } + ASSERT_EQ (0, node->active.election_winner_details_size ()); }; From 6c011e96561d6d59243ef153d246c8a76086fb73 Mon Sep 17 00:00:00 2001 From: cryptocode Date: Wed, 4 Mar 2020 12:11:25 +0100 Subject: [PATCH 19/43] tsan fix: rpc.wallet_destroy (#2615) --- nano/node/wallet.cpp | 4 +++- nano/node/wallet.hpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index 0a705dd2a0..e5bac374c8 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -380,7 +380,9 @@ void nano::wallet_store::initialize (nano::transaction const & transaction_a, bo { debug_assert (strlen (path_a.c_str ()) == path_a.size ()); auto error (0); - error |= mdb_dbi_open (tx (transaction_a), path_a.c_str (), MDB_CREATE, &handle); + MDB_dbi handle_l; + error |= mdb_dbi_open (tx (transaction_a), path_a.c_str (), MDB_CREATE, &handle_l); + handle = handle_l; init_a = error != 0; } diff --git a/nano/node/wallet.hpp b/nano/node/wallet.hpp index dd8de020ea..9595c42c87 100644 --- a/nano/node/wallet.hpp +++ b/nano/node/wallet.hpp @@ -110,7 +110,7 @@ class wallet_store final static size_t const seed_iv_index; static int const special_count; nano::kdf & kdf; - MDB_dbi handle{ 0 }; + std::atomic handle{ 0 }; std::recursive_mutex mutex; private: From d2cbf2a57e50ec1a5960e743e7d5280d6d51bd05 Mon Sep 17 00:00:00 2001 From: Sergey Kroshnin Date: Wed, 4 Mar 2020 20:55:02 +0300 Subject: [PATCH 20/43] Improve --debug_profile_bootstrap performance (#2626) block sideband is not required for some CLI tests --- nano/nano_node/entry.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index 643cd52b9f..e30c01cd00 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -1167,7 +1167,7 @@ int main (int argc, char * const * argv) nano::pending_key const & key (i->first); nano::pending_info const & info (i->second); // Check block existance - auto block (node.node->store.block_get (transaction, key.hash)); + auto block (node.node->store.block_get_no_sideband (transaction, key.hash)); if (block == nullptr) { std::cerr << boost::str (boost::format ("Pending block does not exist %1%\n") % key.hash.to_string ()); @@ -1234,7 +1234,7 @@ int main (int argc, char * const * argv) while (!hash.is_zero ()) { // Retrieving block data - auto block (node.node->store.block_get (transaction, hash)); + auto block (node.node->store.block_get_no_sideband (transaction, hash)); if (block != nullptr) { ++count; From 3ba48bbd356134bc7684744b979a84a3b1462153 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Wed, 4 Mar 2020 18:20:45 +0000 Subject: [PATCH 21/43] Election refactor follow up (#2619) * active_transactions include cleanup * Allocate a new solicitor on every confirmation request loop * Increment election confirmation_request_count This was accidentally erased in the refactor and there was no test to ensure it. One test removed as it was not testing the intended functionality. Other tests were updated to ensure confirmation_request_count is incremented. It was necessary to disable the rep crawler to properly test the confirmation loop. * Ensuring confirmed elections are not returned in RPC confirmation_info * Adding information on confirmed blocks for RPC confirmation_active * Moving election status definitions to secure/common * Re-lock after activating dependencies (bug found by @cryptocode) Otherwise, could call state_change to `expired_unconfirmed` without owning the mutex * Enhance node.activate_dependencies test by ensuring full confirmation (Serg review) --- nano/core_test/active_transactions.cpp | 111 +++++++++++++--------- nano/core_test/confirmation_solicitor.cpp | 51 +++++----- nano/node/active_transactions.cpp | 8 +- nano/node/active_transactions.hpp | 7 -- nano/node/blockprocessor.cpp | 1 + nano/node/election.cpp | 22 +++-- nano/node/election.hpp | 27 +----- nano/node/json_handler.cpp | 24 +++-- nano/node/node.hpp | 1 + nano/node/repcrawler.hpp | 3 + nano/node/websocket.cpp | 3 +- nano/rpc_test/rpc.cpp | 93 ++++++++++++++++++ nano/secure/common.hpp | 24 +++++ 13 files changed, 255 insertions(+), 120 deletions(-) diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index b9b6379f4f..4daf82e091 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -7,19 +7,23 @@ using namespace std::chrono_literals; +namespace nano +{ TEST (active_transactions, confirm_active) { - nano::system system (1); - auto & node1 = *system.nodes[0]; - // Send and vote for a block before peering with node2 - system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); - auto send (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); - system.deadline_set (5s); - while (!node1.active.empty () || !node1.block_confirmed_or_being_confirmed (node1.store.tx_begin_read (), send->hash ())) - { - ASSERT_NO_ERROR (system.poll ()); - } - auto & node2 = *system.add_node (nano::node_config (nano::get_available_port (), system.logging)); + nano::system system; + nano::node_flags node_flags; + node_flags.disable_request_loop = true; + auto & node1 = *system.add_node (node_flags); + nano::genesis genesis; + auto send (std::make_shared (genesis.hash (), nano::public_key (), nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ()))); + ASSERT_EQ (nano::process_result::progress, node1.process (*send).code); + nano::node_config node_config2 (nano::get_available_port (), system.logging); + node_config2.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + nano::node_flags node_flags2; + // The rep crawler would otherwise request confirmations in order to find representatives + node_flags2.disable_rep_crawler = true; + auto & node2 = *system.add_node (node_config2, node_flags2); system.deadline_set (5s); // Let node2 know about the block while (node2.active.empty ()) @@ -27,56 +31,69 @@ TEST (active_transactions, confirm_active) node1.network.flood_block (send, nano::buffer_drop_policy::no_limiter_drop); ASSERT_NO_ERROR (system.poll ()); } - while (node2.ledger.cache.cemented_count < 2 || !node2.active.empty ()) - { - ASSERT_NO_ERROR (system.poll ()); - } -} - -TEST (active_transactions, confirm_frontier) -{ - nano::system system (1); - auto & node1 = *system.nodes[0]; - // Send and vote for a block before peering with node2 + // Save election to check request count afterwards + auto election = node2.active.election (send->qualified_root ()); + ASSERT_NE (nullptr, election); + // Add key to node1 system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); - auto send (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); - system.deadline_set (5s); - while (!node1.active.empty () || !node1.block_confirmed_or_being_confirmed (node1.store.tx_begin_read (), send->hash ())) + // Add representative to disabled rep crawler + auto peers (node2.network.random_set (1)); + ASSERT_FALSE (peers.empty ()); { - ASSERT_NO_ERROR (system.poll ()); + nano::lock_guard guard (node2.rep_crawler.probable_reps_mutex); + node2.rep_crawler.probable_reps.emplace (nano::test_genesis_key.pub, nano::genesis_amount, *peers.begin ()); } - auto & node2 = *system.add_node (nano::node_config (nano::get_available_port (), system.logging)); - ASSERT_EQ (nano::process_result::progress, node2.process (*send).code); - system.deadline_set (5s); while (node2.ledger.cache.cemented_count < 2 || !node2.active.empty ()) { ASSERT_NO_ERROR (system.poll ()); } + // At least one confirmation request + ASSERT_GT (election->confirmation_request_count, 0); + // Blocks were cleared (except for not_an_account) + ASSERT_EQ (1, election->blocks.size ()); +} } -TEST (active_transactions, confirm_dependent) +namespace nano +{ +TEST (active_transactions, confirm_frontier) { nano::system system; nano::node_flags node_flags; node_flags.disable_request_loop = true; auto & node1 = *system.add_node (node_flags); + nano::genesis genesis; + auto send (std::make_shared (genesis.hash (), nano::public_key (), nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ()))); + ASSERT_EQ (nano::process_result::progress, node1.process (*send).code); + nano::node_flags node_flags2; + // The rep crawler would otherwise request confirmations in order to find representatives + node_flags2.disable_rep_crawler = true; + auto & node2 = *system.add_node (node_flags2); + ASSERT_EQ (nano::process_result::progress, node2.process (*send).code); + system.deadline_set (5s); + while (node2.active.empty ()) + { + ASSERT_NO_ERROR (system.poll ()); + } + // Save election to check request count afterwards + auto election = node2.active.election (send->qualified_root ()); + ASSERT_NE (nullptr, election); + // Add key to node1 system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); - auto send1 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); - auto send2 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); - auto send3 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); - nano::node_config node_config; - node_config.peering_port = nano::get_available_port (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto & node2 = *system.add_node (node_config); - node2.process_local (send1); - node2.process_local (send2); - node2.process_active (send3); + // Add representative to disabled rep crawler + auto peers (node2.network.random_set (1)); + ASSERT_FALSE (peers.empty ()); + { + nano::lock_guard guard (node2.rep_crawler.probable_reps_mutex); + node2.rep_crawler.probable_reps.emplace (nano::test_genesis_key.pub, nano::genesis_amount, *peers.begin ()); + } system.deadline_set (5s); - while (!node2.active.empty ()) + while (node2.ledger.cache.cemented_count < 2 || !node2.active.empty ()) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_EQ (4, node2.ledger.cache.cemented_count); + ASSERT_GT (election->confirmation_request_count, 0); +} } TEST (active_transactions, adjusted_difficulty_priority) @@ -694,7 +711,6 @@ TEST (active_transactions, activate_dependencies) system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); nano::genesis genesis; nano::block_builder builder; - system.deadline_set (std::chrono::seconds (15)); std::shared_ptr block0 = builder.state () .account (nano::test_genesis_key.pub) .previous (genesis.hash ()) @@ -707,6 +723,7 @@ TEST (active_transactions, activate_dependencies) // Establish a representative node2->process_active (block0); node2->block_processor.flush (); + system.deadline_set (10s); while (node1->block (block0->hash ()) == nullptr) { ASSERT_NO_ERROR (system.poll ()); @@ -735,9 +752,17 @@ TEST (active_transactions, activate_dependencies) .build (); node2->process_active (block2); node2->block_processor.flush (); + system.deadline_set (10s); while (node1->block (block2->hash ()) == nullptr) { ASSERT_NO_ERROR (system.poll ()); } ASSERT_NE (nullptr, node1->block (block2->hash ())); + system.deadline_set (10s); + while (!node1->active.empty () || !node2->active.empty ()) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_TRUE (node1->ledger.block_confirmed (node1->store.tx_begin_read (), block2->hash ())); + ASSERT_TRUE (node2->ledger.block_confirmed (node2->store.tx_begin_read (), block2->hash ())); } diff --git a/nano/core_test/confirmation_solicitor.cpp b/nano/core_test/confirmation_solicitor.cpp index d2595da088..0e6caa3de3 100644 --- a/nano/core_test/confirmation_solicitor.cpp +++ b/nano/core_test/confirmation_solicitor.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -9,51 +10,49 @@ using namespace std::chrono_literals; TEST (confirmation_solicitor, batches) { nano::system system; - nano::node_config node_config (nano::get_available_port (), system.logging); - node_config.enable_voting = false; - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; nano::node_flags node_flags; + node_flags.disable_request_loop = true; node_flags.disable_udp = false; - auto & node1 = *system.add_node (node_config, node_flags); - node_config.peering_port = nano::get_available_port (); - // To prevent races on the solicitor + auto & node1 = *system.add_node (node_flags); + // This tests instantiates a solicitor node_flags.disable_request_loop = true; - auto & node2 = *system.add_node (node_config, node_flags); + auto & node2 = *system.add_node (node_flags); // Solicitor will only solicit from this representative auto channel1 (node2.network.udp_channels.create (node1.network.endpoint ())); nano::representative representative (nano::test_genesis_key.pub, nano::genesis_amount, channel1); - // Lock active_transactions which uses the solicitor + + std::vector representatives{ representative }; + nano::confirmation_solicitor solicitor (node2.network, node2.network_params.network); + solicitor.prepare (representatives); + // Ensure the representatives are correct + ASSERT_EQ (1, representatives.size ()); + ASSERT_EQ (channel1, representatives.front ().channel); + ASSERT_EQ (nano::test_genesis_key.pub, representatives.front ().account); + auto send (std::make_shared (nano::genesis_hash, nano::keypair ().pub, nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (nano::genesis_hash))); { - nano::lock_guard active_guard (node2.active.mutex); - std::vector representatives{ representative }; - node2.active.solicitor.prepare (representatives); - // Ensure the representatives are correct - ASSERT_EQ (1, representatives.size ()); - ASSERT_EQ (channel1, representatives.front ().channel); - ASSERT_EQ (nano::test_genesis_key.pub, representatives.front ().account); - auto send (std::make_shared (nano::genesis_hash, nano::keypair ().pub, nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (nano::genesis_hash))); + nano::lock_guard guard (node2.active.mutex); for (size_t i (0); i < nano::network::confirm_req_hashes_max; ++i) { auto election (std::make_shared (node2, send, nullptr)); - ASSERT_FALSE (node2.active.solicitor.add (*election)); + ASSERT_FALSE (solicitor.add (*election)); } - ASSERT_EQ (1, node2.active.solicitor.max_confirm_req_batches); + ASSERT_EQ (1, solicitor.max_confirm_req_batches); // Reached the maximum amount of requests for the channel auto election (std::make_shared (node2, send, nullptr)); - ASSERT_TRUE (node2.active.solicitor.add (*election)); + ASSERT_TRUE (solicitor.add (*election)); // Broadcasting should be immediate ASSERT_EQ (0, node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::out)); - ASSERT_FALSE (node2.active.solicitor.broadcast (*election)); - system.deadline_set (5s); - while (node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::out) < 1) - { - ASSERT_NO_ERROR (system.poll ()); - } + ASSERT_FALSE (solicitor.broadcast (*election)); + } + system.deadline_set (5s); + while (node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::out) < 1) + { + ASSERT_NO_ERROR (system.poll ()); } // From rep crawler ASSERT_EQ (1, node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out)); system.deadline_set (5s); - node2.active.solicitor.flush (); + solicitor.flush (); while (node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out) < 2) { ASSERT_NO_ERROR (system.poll ()); diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 3447955552..a1fc3298e7 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -1,8 +1,11 @@ #include #include #include +#include #include #include +#include +#include #include #include @@ -16,7 +19,6 @@ confirmation_height_processor (confirmation_height_processor_a), node (node_a), multipliers_cb (20, 1.), trended_active_difficulty (node_a.network_params.network.publish_threshold), -solicitor (node_a.network, node_a.network_params.network), election_time_to_live (node_a.network_params.network.is_test_network () ? 0s : 2s), thread ([this]() { nano::thread_role::set (nano::thread_role::name::request_loop); @@ -225,6 +227,7 @@ void nano::active_transactions::request_confirm (nano::unique_lock & // Only representatives ready to receive batched confirm_req lock_a.unlock (); + nano::confirmation_solicitor solicitor (node.network, node.network_params.network); solicitor.prepare (node.rep_crawler.representatives (node.network_params.protocol.tcp_realtime_protocol_version_min)); lock_a.lock (); @@ -244,7 +247,8 @@ void nano::active_transactions::request_confirm (nano::unique_lock & for (auto i = sorted_roots_l.begin (), n = sorted_roots_l.end (); i != n; ++count_l) { auto & election_l (i->election); - if ((count_l >= node.config.active_elections_size && election_l->election_start < election_ttl_cutoff_l && !node.wallets.watcher->is_watched (i->root)) || election_l->transition_time (saturated_l)) + bool const overflow_l (count_l >= node.config.active_elections_size && election_l->election_start < election_ttl_cutoff_l && !node.wallets.watcher->is_watched (i->root)); + if (overflow_l || election_l->transition_time (solicitor, saturated_l)) { election_l->clear_blocks (); i = sorted_roots_l.erase (i); diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index c6166ab2ca..6a5b58c0ce 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -1,12 +1,6 @@ #pragma once #include -#include -#include -#include -#include -#include -#include #include #include @@ -142,7 +136,6 @@ class active_transactions final size_t inactive_votes_cache_size (); size_t election_winner_details_size (); void add_election_winner_details (nano::block_hash const &, std::shared_ptr const &); - nano::confirmation_solicitor solicitor; private: std::mutex election_winner_details_mutex; diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 77f84a63c7..5447645f39 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 5e52ed6d5b..4997d7413d 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -148,13 +149,14 @@ bool nano::election::state_change (nano::election::state_t expected_a, nano::ele return result; } -void nano::election::send_confirm_req () +void nano::election::send_confirm_req (nano::confirmation_solicitor & solicitor_a) { if (last_req + std::chrono::seconds (15) < std::chrono::steady_clock::now ()) { - if (!node.active.solicitor.add (*this)) + if (!solicitor_a.add (*this)) { last_req = std::chrono::steady_clock::now (); + ++confirmation_request_count; } } } @@ -242,18 +244,18 @@ void nano::election::activate_dependencies () } } -void nano::election::broadcast_block () +void nano::election::broadcast_block (nano::confirmation_solicitor & solicitor_a) { if (base_latency () * 5 < std::chrono::steady_clock::now () - last_block) { - if (!node.active.solicitor.broadcast (*this)) + if (!solicitor_a.broadcast (*this)) { last_block = std::chrono::steady_clock::now (); } } } -bool nano::election::transition_time (bool const saturated_a) +bool nano::election::transition_time (nano::confirmation_solicitor & solicitor_a, bool const saturated_a) { debug_assert (!node.active.mutex.try_lock ()); nano::unique_lock lock (timepoints_mutex); @@ -271,18 +273,19 @@ bool nano::election::transition_time (bool const saturated_a) break; } case nano::election::state_t::active: - broadcast_block (); - send_confirm_req (); + broadcast_block (solicitor_a); + send_confirm_req (solicitor_a); if (base_latency () * active_duration_factor < std::chrono::steady_clock::now () - state_start) { state_change (nano::election::state_t::active, nano::election::state_t::backtracking); lock.unlock (); activate_dependencies (); + lock.lock (); } break; case nano::election::state_t::backtracking: - broadcast_block (); - send_confirm_req (); + broadcast_block (solicitor_a); + send_confirm_req (solicitor_a); break; case nano::election::state_t::confirmed: if (base_latency () * (saturated_a ? confirmed_duration_factor_saturated : confirmed_duration_factor) < std::chrono::steady_clock::now () - state_start) @@ -296,7 +299,6 @@ bool nano::election::transition_time (bool const saturated_a) debug_assert (false); break; } - // Note: lock (timepoints_mutex) is at an unknown state here - possibly unlocked before activate_dependencies if (!confirmed () && std::chrono::minutes (5) < std::chrono::steady_clock::now () - election_start) { result = true; diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 3205345f67..9339cc2b01 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -12,27 +12,8 @@ namespace nano { class channel; +class confirmation_solicitor; class node; -enum class election_status_type : uint8_t -{ - ongoing = 0, - active_confirmed_quorum = 1, - active_confirmation_height = 2, - inactive_confirmation_height = 3, - stopped = 5 -}; -class election_status final -{ -public: - std::shared_ptr winner; - nano::amount tally; - std::chrono::milliseconds election_end; - std::chrono::milliseconds election_duration; - unsigned confirmation_request_count; - unsigned block_count; - unsigned voter_count; - election_status_type type; -}; class vote_info final { public: @@ -80,8 +61,8 @@ class election final : public std::enable_shared_from_this bool valid_change (nano::election::state_t, nano::election::state_t) const; bool state_change (nano::election::state_t, nano::election::state_t); - void broadcast_block (); - void send_confirm_req (); + void broadcast_block (nano::confirmation_solicitor &); + void send_confirm_req (nano::confirmation_solicitor &); void activate_dependencies (); public: @@ -101,7 +82,7 @@ class election final : public std::enable_shared_from_this void insert_inactive_votes_cache (nano::block_hash const &); public: // State transitions - bool transition_time (bool const saturated); + bool transition_time (nano::confirmation_solicitor &, bool const saturated); void transition_passive (); void transition_active (); diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index a43e23f2a1..e05e32df21 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1754,6 +1754,7 @@ void nano::json_handler::chain (bool successors) void nano::json_handler::confirmation_active () { uint64_t announcements (0); + uint64_t confirmed (0); boost::optional announcements_text (request.get_optional ("announcements")); if (announcements_text.is_initialized ()) { @@ -1764,15 +1765,24 @@ void nano::json_handler::confirmation_active () nano::lock_guard lock (node.active.mutex); for (auto i (node.active.roots.begin ()), n (node.active.roots.end ()); i != n; ++i) { - if (i->election->confirmation_request_count >= announcements && !i->election->confirmed ()) + if (i->election->confirmation_request_count >= announcements) { - boost::property_tree::ptree entry; - entry.put ("", i->root.to_string ()); - elections.push_back (std::make_pair ("", entry)); + if (!i->election->confirmed ()) + { + boost::property_tree::ptree entry; + entry.put ("", i->root.to_string ()); + elections.push_back (std::make_pair ("", entry)); + } + else + { + ++confirmed; + } } } } response_l.add_child ("confirmations", elections); + response_l.put ("unconfirmed", elections.size ()); + response_l.put ("confirmed", confirmed); response_errors (); } @@ -1840,11 +1850,9 @@ void nano::json_handler::confirmation_info () nano::qualified_root root; if (!root.decode_hex (root_text)) { - nano::lock_guard lock (node.active.mutex); - auto conflict_info (node.active.roots.find (root)); - if (conflict_info != node.active.roots.end ()) + auto election (node.active.election (root)); + if (election != nullptr && !election->confirmed ()) { - auto election (conflict_info->election); response_l.put ("announcements", std::to_string (election->confirmation_request_count)); response_l.put ("voters", std::to_string (election->last_votes.size ())); response_l.put ("last_winner", election->status.winner->hash ().to_string ()); diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 6dd5860c4b..619646964e 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/node/repcrawler.hpp b/nano/node/repcrawler.hpp index 0071584f53..06f1491037 100644 --- a/nano/node/repcrawler.hpp +++ b/nano/node/repcrawler.hpp @@ -147,6 +147,9 @@ class rep_crawler final /** Probable representatives */ probably_rep_t probable_reps; + friend class active_transactions_confirm_active_Test; + friend class active_transactions_confirm_frontier_Test; + std::deque, std::shared_ptr>> responses; }; } diff --git a/nano/node/websocket.cpp b/nano/node/websocket.cpp index 4cb686e711..9e38c5726f 100644 --- a/nano/node/websocket.cpp +++ b/nano/node/websocket.cpp @@ -2,7 +2,8 @@ #include #include #include -#include +#include +#include #include #include diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 4431bae883..5323779e82 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -8151,3 +8151,96 @@ TEST (rpc, node_telemetry_self) ASSERT_EQ (std::error_code (nano::error_rpc::peer_not_found).message (), response.json.get ("error")); } } + +TEST (rpc, confirmation_active) +{ + nano::system system; + nano::node_config node_config; + node_config.ipc_config.transport_tcp.enabled = true; + node_config.ipc_config.transport_tcp.port = nano::get_available_port (); + nano::node_flags node_flags; + node_flags.disable_request_loop = true; + auto & node1 (*system.add_node (node_config, node_flags)); + scoped_io_thread_name_change scoped_thread_name_io; + nano::node_rpc_config node_rpc_config; + nano::ipc::ipc_server ipc_server (node1, node_rpc_config); + nano::rpc_config rpc_config (nano::get_available_port (), true); + rpc_config.rpc_process.ipc_port = node1.config.ipc_config.transport_tcp.port; + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); + rpc.start (); + + nano::genesis genesis; + auto send1 (std::make_shared (genesis.hash (), nano::public_key (), nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ()))); + auto send2 (std::make_shared (send1->hash (), nano::public_key (), nano::genesis_amount - 200, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ()))); + node1.process_active (send1); + node1.process_active (send2); + node1.block_processor.flush (); + ASSERT_EQ (2, node1.active.size ()); + { + nano::lock_guard guard (node1.active.mutex); + auto info (node1.active.roots.find (send1->qualified_root ())); + ASSERT_NE (node1.active.roots.end (), info); + info->election->confirm_once (); + } + + boost::property_tree::ptree request; + request.put ("action", "confirmation_active"); + { + test_response response (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); + while (response.status == 0) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_EQ (200, response.status); + auto & confirmations (response.json.get_child ("confirmations")); + ASSERT_EQ (1, confirmations.size ()); + ASSERT_EQ (send2->qualified_root ().to_string (), confirmations.front ().second.get ("")); + ASSERT_EQ (1, response.json.get ("unconfirmed")); + ASSERT_EQ (1, response.json.get ("confirmed")); + } +} + +TEST (rpc, confirmation_info) +{ + nano::system system; + auto & node1 = *add_ipc_enabled_node (system); + scoped_io_thread_name_change scoped_thread_name_io; + nano::node_rpc_config node_rpc_config; + nano::ipc::ipc_server ipc_server (node1, node_rpc_config); + nano::rpc_config rpc_config (nano::get_available_port (), true); + rpc_config.rpc_process.ipc_port = node1.config.ipc_config.transport_tcp.port; + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); + rpc.start (); + + nano::genesis genesis; + auto send (std::make_shared (genesis.hash (), nano::public_key (), nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ()))); + node1.process_active (send); + node1.block_processor.flush (); + ASSERT_FALSE (node1.active.empty ()); + + boost::property_tree::ptree request; + request.put ("action", "confirmation_info"); + request.put ("root", send->qualified_root ().to_string ()); + request.put ("representatives", "true"); + request.put ("json_block", "true"); + { + test_response response (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); + while (response.status == 0) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_EQ (200, response.status); + ASSERT_EQ (1, response.json.count ("announcements")); + ASSERT_EQ (1, response.json.get ("voters")); + ASSERT_EQ (send->hash ().to_string (), response.json.get ("last_winner")); + auto & blocks (response.json.get_child ("blocks")); + ASSERT_EQ (1, blocks.size ()); + auto & representatives (blocks.front ().second.get_child ("representatives")); + ASSERT_EQ (1, representatives.size ()); + ASSERT_EQ (0, response.json.get ("total_tally")); + } +} diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index ea5a43ee6e..be6e87497d 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -506,5 +506,29 @@ class ledger_cache std::atomic account_count{ 0 }; }; +/* Defines the possible states for an election to stop in */ +enum class election_status_type : uint8_t +{ + ongoing = 0, + active_confirmed_quorum = 1, + active_confirmation_height = 2, + inactive_confirmation_height = 3, + stopped = 5 +}; + +/* Holds a summary of an election */ +class election_status final +{ +public: + std::shared_ptr winner; + nano::amount tally; + std::chrono::milliseconds election_end; + std::chrono::milliseconds election_duration; + unsigned confirmation_request_count; + unsigned block_count; + unsigned voter_count; + election_status_type type; +}; + nano::wallet_id random_wallet_id (); } From 695dc5f5dc51e5a61f67530cab29057fb50c509f Mon Sep 17 00:00:00 2001 From: cryptocode Date: Wed, 4 Mar 2020 19:41:49 +0100 Subject: [PATCH 22/43] LMDB sync options and new config settings (#2588) * LMDB sync options and new config settings * Force sync always for wallet store --- nano/core_test/block_store.cpp | 3 +- nano/core_test/node.cpp | 6 +-- nano/core_test/toml.cpp | 18 ++++++++- nano/core_test/wallets.cpp | 2 +- nano/lib/CMakeLists.txt | 2 + nano/lib/lmdbconfig.cpp | 72 ++++++++++++++++++++++++++++++++++ nano/lib/lmdbconfig.hpp | 50 +++++++++++++++++++++++ nano/node/lmdb/lmdb.cpp | 16 +++++--- nano/node/lmdb/lmdb.hpp | 5 ++- nano/node/lmdb/lmdb_env.cpp | 29 ++++++++++---- nano/node/lmdb/lmdb_env.hpp | 47 +++++++++++++++++++++- nano/node/node.cpp | 8 ++-- nano/node/nodeconfig.cpp | 35 ++++++++++++++--- nano/node/nodeconfig.hpp | 4 +- nano/node/wallet.cpp | 4 +- nano/node/wallet.hpp | 3 +- nano/secure/blockstore.hpp | 3 +- 17 files changed, 269 insertions(+), 38 deletions(-) create mode 100644 nano/lib/lmdbconfig.cpp create mode 100644 nano/lib/lmdbconfig.hpp diff --git a/nano/core_test/block_store.cpp b/nano/core_test/block_store.cpp index ddaf19dd5d..4e88ef970b 100644 --- a/nano/core_test/block_store.cpp +++ b/nano/core_test/block_store.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -2063,7 +2064,7 @@ TEST (mdb_block_store, upgrade_backup) // Now do the upgrade and confirm that backup is saved nano::logger_mt logger; - nano::mdb_store store (logger, path, nano::txn_tracking_config{}, std::chrono::seconds (5), 128, 512, true); + nano::mdb_store store (logger, path, nano::txn_tracking_config{}, std::chrono::seconds (5), nano::lmdb_config{}, 512, true); ASSERT_FALSE (store.init_error ()); auto transaction (store.tx_begin_read ()); ASSERT_LT (14, store.version_get (transaction)); diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 2d5a8db1ac..8c76236ad9 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -555,7 +555,7 @@ TEST (node_config, serialization) config1.callback_address = "test"; config1.callback_port = 10; config1.callback_target = "test"; - config1.lmdb_max_dbs = 256; + config1.deprecated_lmdb_max_dbs = 256; nano::jsonconfig tree; config1.serialize_json (tree); nano::logging logging2; @@ -572,7 +572,7 @@ TEST (node_config, serialization) ASSERT_NE (config2.callback_address, config1.callback_address); ASSERT_NE (config2.callback_port, config1.callback_port); ASSERT_NE (config2.callback_target, config1.callback_target); - ASSERT_NE (config2.lmdb_max_dbs, config1.lmdb_max_dbs); + ASSERT_NE (config2.deprecated_lmdb_max_dbs, config1.deprecated_lmdb_max_dbs); ASSERT_FALSE (tree.get_optional ("epoch_block_link")); ASSERT_FALSE (tree.get_optional ("epoch_block_signer")); @@ -590,7 +590,7 @@ TEST (node_config, serialization) ASSERT_EQ (config2.callback_address, config1.callback_address); ASSERT_EQ (config2.callback_port, config1.callback_port); ASSERT_EQ (config2.callback_target, config1.callback_target); - ASSERT_EQ (config2.lmdb_max_dbs, config1.lmdb_max_dbs); + ASSERT_EQ (config2.deprecated_lmdb_max_dbs, config1.deprecated_lmdb_max_dbs); } TEST (node_config, v1_v2_upgrade) diff --git a/nano/core_test/toml.cpp b/nano/core_test/toml.cpp index b0bb8aafb9..0239107919 100644 --- a/nano/core_test/toml.cpp +++ b/nano/core_test/toml.cpp @@ -120,6 +120,7 @@ TEST (toml, daemon_config_deserialize_defaults) [node.statistics.log] [node.statistics.sampling] [node.websocket] + [node.lmdb] [node.rocksdb] [opencl] [rpc] @@ -158,7 +159,7 @@ TEST (toml, daemon_config_deserialize_defaults) ASSERT_EQ (conf.node.external_address, defaults.node.external_address); ASSERT_EQ (conf.node.external_port, defaults.node.external_port); ASSERT_EQ (conf.node.io_threads, defaults.node.io_threads); - ASSERT_EQ (conf.node.lmdb_max_dbs, defaults.node.lmdb_max_dbs); + ASSERT_EQ (conf.node.deprecated_lmdb_max_dbs, defaults.node.deprecated_lmdb_max_dbs); ASSERT_EQ (conf.node.max_work_generate_multiplier, defaults.node.max_work_generate_multiplier); ASSERT_EQ (conf.node.network_threads, defaults.node.network_threads); ASSERT_EQ (conf.node.secondary_work_peers, defaults.node.secondary_work_peers); @@ -245,6 +246,10 @@ TEST (toml, daemon_config_deserialize_defaults) ASSERT_EQ (conf.node.stat_config.log_counters_filename, defaults.node.stat_config.log_counters_filename); ASSERT_EQ (conf.node.stat_config.log_samples_filename, defaults.node.stat_config.log_samples_filename); + ASSERT_EQ (conf.node.lmdb_config.sync, defaults.node.lmdb_config.sync); + ASSERT_EQ (conf.node.lmdb_config.max_databases, defaults.node.lmdb_config.max_databases); + ASSERT_EQ (conf.node.lmdb_config.map_size, defaults.node.lmdb_config.map_size); + ASSERT_EQ (conf.node.rocksdb_config.enable, defaults.node.rocksdb_config.enable); ASSERT_EQ (conf.node.rocksdb_config.bloom_filter_bits, defaults.node.rocksdb_config.bloom_filter_bits); ASSERT_EQ (conf.node.rocksdb_config.block_cache, defaults.node.rocksdb_config.block_cache); @@ -495,6 +500,11 @@ TEST (toml, daemon_config_deserialize_no_defaults) enable = true port = 999 + [node.lmdb] + sync = "nosync_safe" + max_databases = 999 + map_size = 999 + [node.rocksdb] enable = true bloom_filter_bits = 10 @@ -557,7 +567,7 @@ TEST (toml, daemon_config_deserialize_no_defaults) ASSERT_NE (conf.node.external_address, defaults.node.external_address); ASSERT_NE (conf.node.external_port, defaults.node.external_port); ASSERT_NE (conf.node.io_threads, defaults.node.io_threads); - ASSERT_NE (conf.node.lmdb_max_dbs, defaults.node.lmdb_max_dbs); + ASSERT_NE (conf.node.deprecated_lmdb_max_dbs, defaults.node.deprecated_lmdb_max_dbs); ASSERT_NE (conf.node.max_work_generate_multiplier, defaults.node.max_work_generate_multiplier); ASSERT_NE (conf.node.frontiers_confirmation, defaults.node.frontiers_confirmation); ASSERT_NE (conf.node.network_threads, defaults.node.network_threads); @@ -645,6 +655,10 @@ TEST (toml, daemon_config_deserialize_no_defaults) ASSERT_NE (conf.node.stat_config.log_counters_filename, defaults.node.stat_config.log_counters_filename); ASSERT_NE (conf.node.stat_config.log_samples_filename, defaults.node.stat_config.log_samples_filename); + ASSERT_NE (conf.node.lmdb_config.sync, defaults.node.lmdb_config.sync); + ASSERT_NE (conf.node.lmdb_config.max_databases, defaults.node.lmdb_config.max_databases); + ASSERT_NE (conf.node.lmdb_config.map_size, defaults.node.lmdb_config.map_size); + ASSERT_NE (conf.node.rocksdb_config.enable, defaults.node.rocksdb_config.enable); ASSERT_NE (conf.node.rocksdb_config.bloom_filter_bits, defaults.node.rocksdb_config.bloom_filter_bits); ASSERT_NE (conf.node.rocksdb_config.block_cache, defaults.node.rocksdb_config.block_cache); diff --git a/nano/core_test/wallets.cpp b/nano/core_test/wallets.cpp index d2bfe9775a..ccb856e056 100644 --- a/nano/core_test/wallets.cpp +++ b/nano/core_test/wallets.cpp @@ -133,7 +133,7 @@ TEST (wallets, DISABLED_wallet_create_max) bool error (false); nano::wallets wallets (error, *system.nodes[0]); const int nonWalletDbs = 19; - for (int i = 0; i < system.nodes[0]->config.lmdb_max_dbs - nonWalletDbs; i++) + for (int i = 0; i < system.nodes[0]->config.deprecated_lmdb_max_dbs - nonWalletDbs; i++) { auto wallet_id = nano::random_wallet_id (); auto wallet = wallets.create (wallet_id); diff --git a/nano/lib/CMakeLists.txt b/nano/lib/CMakeLists.txt index c0996b359c..3e3bbceb80 100644 --- a/nano/lib/CMakeLists.txt +++ b/nano/lib/CMakeLists.txt @@ -36,6 +36,8 @@ add_library (nano_lib json_error_response.hpp jsonconfig.hpp jsonconfig.cpp + lmdbconfig.hpp + lmdbconfig.cpp locks.hpp locks.cpp logger_mt.hpp diff --git a/nano/lib/lmdbconfig.cpp b/nano/lib/lmdbconfig.cpp new file mode 100644 index 0000000000..664ddabfe7 --- /dev/null +++ b/nano/lib/lmdbconfig.cpp @@ -0,0 +1,72 @@ +#include +#include +#include + +#include + +nano::error nano::lmdb_config::serialize_toml (nano::tomlconfig & toml) const +{ + std::string sync_string; + switch (sync) + { + case nano::lmdb_config::sync_strategy::always: + sync_string = "always"; + break; + case nano::lmdb_config::sync_strategy::nosync_safe: + sync_string = "nosync_safe"; + break; + case nano::lmdb_config::sync_strategy::nosync_unsafe: + sync_string = "nosync_unsafe"; + break; + case nano::lmdb_config::sync_strategy::nosync_unsafe_large_memory: + sync_string = "nosync_unsafe_large_memory"; + break; + } + + toml.put ("sync", sync_string, "Sync strategy for flushing commits to the ledger database. This does not affect the wallet database.\ntype:string,{always, nosync_safe, nosync_unsafe, nosync_unsafe_large_memory}"); + toml.put ("max_databases", max_databases, "Maximum open lmdb databases. Increase default if more than 100 wallets is required.\nNote: external management is recommended when a large amounts of wallets are required (see https://docs.nano.org/integration-guides/key-management/).\ntype:uin32"); + toml.put ("map_size", map_size, "Maximum ledger database map size in bytes.\ntype:uint64"); + return toml.get_error (); +} + +nano::error nano::lmdb_config::deserialize_toml (nano::tomlconfig & toml, bool is_deprecated_lmdb_dbs_used) +{ + static nano::network_params params; + auto default_max_databases = max_databases; + toml.get_optional ("max_databases", max_databases); + toml.get_optional ("map_size", map_size); + + // For now we accept either setting, but not both + if (!params.network.is_test_network () && is_deprecated_lmdb_dbs_used && default_max_databases != max_databases) + { + toml.get_error ().set ("Both the deprecated node.lmdb_max_dbs and the new node.lmdb.max_databases setting are used. Please use max_databases only."); + } + + if (!toml.get_error ()) + { + std::string sync_string = "always"; + toml.get_optional ("sync", sync_string); + if (sync_string == "always") + { + sync = nano::lmdb_config::sync_strategy::always; + } + else if (sync_string == "nosync_safe") + { + sync = nano::lmdb_config::sync_strategy::nosync_safe; + } + else if (sync_string == "nosync_unsafe") + { + sync = nano::lmdb_config::sync_strategy::nosync_unsafe; + } + else if (sync_string == "nosync_unsafe_large_memory") + { + sync = nano::lmdb_config::sync_strategy::nosync_unsafe_large_memory; + } + else + { + toml.get_error ().set (sync_string + " is not a valid sync option"); + } + } + + return toml.get_error (); +} diff --git a/nano/lib/lmdbconfig.hpp b/nano/lib/lmdbconfig.hpp new file mode 100644 index 0000000000..342c03d126 --- /dev/null +++ b/nano/lib/lmdbconfig.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include + +namespace nano +{ +class tomlconfig; + +/** Configuration options for LMDB */ +class lmdb_config final +{ +public: + /** + * Dictates how lmdb flushes to disk on commit. + * These options only apply to the ledger database; the wallet database + * always flush. + */ + enum sync_strategy + { + /** Always flush to disk on commit. This is default. */ + always, + + /** Do not flush meta data eagerly. This may cause loss of transactions, but maintains integrity. */ + nosync_safe, + + /** + * Let the OS decide when to flush to disk. On filesystems with write ordering, this has the same + * guarantees as nosync_safe, otherwise corruption may occur on system crash. + */ + nosync_unsafe, + /** + * Use a writeable memory map. Let the OS decide when to flush to disk, and make the request asynchronous. + * This may give better performance on systems where the database fits entirely in memory, otherwise is + * may be slower. + * @warning Do not use this option if external processes uses the database concurrently. + */ + nosync_unsafe_large_memory + }; + + nano::error serialize_toml (nano::tomlconfig & toml_a) const; + nano::error deserialize_toml (nano::tomlconfig & toml_a, bool is_deprecated_lmdb_dbs_used); + + /** Sync strategy for the ledger database */ + sync_strategy sync{ always }; + uint32_t max_databases{ 128 }; + size_t map_size{ 128ULL * 1024 * 1024 * 1024 }; +}; +} diff --git a/nano/node/lmdb/lmdb.cpp b/nano/node/lmdb/lmdb.cpp index 3cf7fb2df0..97c70ded12 100644 --- a/nano/node/lmdb/lmdb.cpp +++ b/nano/node/lmdb/lmdb.cpp @@ -40,9 +40,9 @@ void mdb_val::convert_buffer_to_value () } } -nano::mdb_store::mdb_store (nano::logger_mt & logger_a, boost::filesystem::path const & path_a, nano::txn_tracking_config const & txn_tracking_config_a, std::chrono::milliseconds block_processor_batch_max_time_a, int lmdb_max_dbs, size_t const batch_size, bool backup_before_upgrade) : +nano::mdb_store::mdb_store (nano::logger_mt & logger_a, boost::filesystem::path const & path_a, nano::txn_tracking_config const & txn_tracking_config_a, std::chrono::milliseconds block_processor_batch_max_time_a, nano::lmdb_config const & lmdb_config_a, size_t const batch_size, bool backup_before_upgrade) : logger (logger_a), -env (error, path_a, lmdb_max_dbs, true), +env (error, path_a, nano::mdb_env::options::make ().set_config (lmdb_config_a).set_use_no_mem_init (true)), mdb_txn_tracker (logger_a, txn_tracking_config_a, block_processor_batch_max_time_a), txn_tracking_enabled (txn_tracking_config_a.enable) { @@ -91,7 +91,7 @@ txn_tracking_enabled (txn_tracking_config_a.enable) if (needs_vacuuming && !network_constants.is_test_network ()) { logger.always_log ("Preparing vacuum..."); - auto vacuum_success = vacuum_after_upgrade (path_a, lmdb_max_dbs); + auto vacuum_success = vacuum_after_upgrade (path_a, lmdb_config_a); logger.always_log (vacuum_success ? "Vacuum succeeded." : "Failed to vacuum. (Optional) Ensure enough disk space is available for a copy of the database and try to vacuum after shutting down the node"); } } @@ -103,7 +103,7 @@ txn_tracking_enabled (txn_tracking_config_a.enable) } } -bool nano::mdb_store::vacuum_after_upgrade (boost::filesystem::path const & path_a, int lmdb_max_dbs) +bool nano::mdb_store::vacuum_after_upgrade (boost::filesystem::path const & path_a, nano::lmdb_config const & lmdb_config_a) { // Vacuum the database. This is not a required step and may actually fail if there isn't enough storage space. auto vacuum_path = path_a.parent_path () / "vacuumed.ldb"; @@ -112,6 +112,7 @@ bool nano::mdb_store::vacuum_after_upgrade (boost::filesystem::path const & path if (vacuum_success) { // Need to close the database to release the file handle + mdb_env_sync (env.environment, true); mdb_env_close (env.environment); env.environment = nullptr; @@ -119,7 +120,10 @@ bool nano::mdb_store::vacuum_after_upgrade (boost::filesystem::path const & path boost::filesystem::rename (vacuum_path, path_a); // Set up the environment again - env.init (error, path_a, lmdb_max_dbs, true); + auto options = nano::mdb_env::options::make () + .set_config (lmdb_config_a) + .set_use_no_mem_init (true); + env.init (error, path_a, options); if (!error) { auto transaction (tx_begin_read ()); @@ -1303,4 +1307,4 @@ bool nano::mdb_store::upgrade_counters::are_equal () const } // Explicitly instantiate -template class nano::block_store_partial; \ No newline at end of file +template class nano::block_store_partial; diff --git a/nano/node/lmdb/lmdb.hpp b/nano/node/lmdb/lmdb.hpp index a0cc27f845..e46ebdf203 100644 --- a/nano/node/lmdb/lmdb.hpp +++ b/nano/node/lmdb/lmdb.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -36,7 +37,7 @@ class mdb_store : public block_store_partial using block_store_partial::block_exists; using block_store_partial::unchecked_put; - mdb_store (nano::logger_mt &, boost::filesystem::path const &, nano::txn_tracking_config const & txn_tracking_config_a = nano::txn_tracking_config{}, std::chrono::milliseconds block_processor_batch_max_time_a = std::chrono::milliseconds (5000), int lmdb_max_dbs = 128, size_t batch_size = 512, bool backup_before_upgrade = false); + mdb_store (nano::logger_mt &, boost::filesystem::path const &, nano::txn_tracking_config const & txn_tracking_config_a = nano::txn_tracking_config{}, std::chrono::milliseconds block_processor_batch_max_time_a = std::chrono::milliseconds (5000), nano::lmdb_config const & lmdb_config_a = nano::lmdb_config{}, size_t batch_size = 512, bool backup_before_upgrade = false); nano::write_transaction tx_begin_write (std::vector const & tables_requiring_lock = {}, std::vector const & tables_no_lock = {}) override; nano::read_transaction tx_begin_read () override; @@ -261,7 +262,7 @@ class mdb_store : public block_store_partial size_t count (nano::transaction const & transaction_a, tables table_a) const override; - bool vacuum_after_upgrade (boost::filesystem::path const & path_a, int lmdb_max_dbs); + bool vacuum_after_upgrade (boost::filesystem::path const & path_a, nano::lmdb_config const & lmdb_config_a); class upgrade_counters { diff --git a/nano/node/lmdb/lmdb_env.cpp b/nano/node/lmdb/lmdb_env.cpp index 61bc9f37f8..2c474ac200 100644 --- a/nano/node/lmdb/lmdb_env.cpp +++ b/nano/node/lmdb/lmdb_env.cpp @@ -2,12 +2,12 @@ #include -nano::mdb_env::mdb_env (bool & error_a, boost::filesystem::path const & path_a, int max_dbs_a, bool use_no_mem_init_a, size_t map_size_a) +nano::mdb_env::mdb_env (bool & error_a, boost::filesystem::path const & path_a, nano::mdb_env::options options_a) { - init (error_a, path_a, max_dbs_a, use_no_mem_init_a, map_size_a); + init (error_a, path_a, options_a); } -void nano::mdb_env::init (bool & error_a, boost::filesystem::path const & path_a, int max_dbs_a, bool use_no_mem_init_a, size_t map_size_a) +void nano::mdb_env::init (bool & error_a, boost::filesystem::path const & path_a, nano::mdb_env::options options_a) { boost::system::error_code error_mkdir, error_chmod; if (path_a.has_parent_path ()) @@ -18,11 +18,11 @@ void nano::mdb_env::init (bool & error_a, boost::filesystem::path const & path_a { auto status1 (mdb_env_create (&environment)); release_assert (status1 == 0); - auto status2 (mdb_env_set_maxdbs (environment, max_dbs_a)); + auto status2 (mdb_env_set_maxdbs (environment, options_a.config.max_databases)); release_assert (status2 == 0); - auto map_size = map_size_a; + auto map_size = options_a.config.map_size; auto max_valgrind_map_size = 16 * 1024 * 1024; - if (running_within_valgrind () && map_size_a > max_valgrind_map_size) + if (running_within_valgrind () && map_size > max_valgrind_map_size) { // In order to run LMDB under Valgrind, the maximum map size must be smaller than half your available RAM map_size = max_valgrind_map_size; @@ -34,7 +34,20 @@ void nano::mdb_env::init (bool & error_a, boost::filesystem::path const & path_a // MDB_NORDAHEAD will allow platforms that support it to load the DB in memory as needed. // MDB_NOMEMINIT prevents zeroing malloc'ed pages. Can provide improvement for non-sensitive data but may make memory checkers noisy (e.g valgrind). auto environment_flags = MDB_NOSUBDIR | MDB_NOTLS | MDB_NORDAHEAD; - if (!running_within_valgrind () && use_no_mem_init_a) + if (options_a.config.sync == nano::lmdb_config::sync_strategy::nosync_safe) + { + environment_flags |= MDB_NOMETASYNC; + } + else if (options_a.config.sync == nano::lmdb_config::sync_strategy::nosync_unsafe) + { + environment_flags |= MDB_NOSYNC; + } + else if (options_a.config.sync == nano::lmdb_config::sync_strategy::nosync_unsafe_large_memory) + { + environment_flags |= MDB_NOSYNC | MDB_WRITEMAP | MDB_MAPASYNC; + } + + if (!running_within_valgrind () && options_a.use_no_mem_init) { environment_flags |= MDB_NOMEMINIT; } @@ -69,6 +82,8 @@ nano::mdb_env::~mdb_env () { if (environment != nullptr) { + // Make sure the commits are flushed. This is a no-op unless MDB_NOSYNC is used. + mdb_env_sync (environment, true); mdb_env_close (environment); } } diff --git a/nano/node/lmdb/lmdb_env.hpp b/nano/node/lmdb/lmdb_env.hpp index 6bd3560c6d..139124ce09 100644 --- a/nano/node/lmdb/lmdb_env.hpp +++ b/nano/node/lmdb/lmdb_env.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -11,8 +12,50 @@ namespace nano class mdb_env final { public: - mdb_env (bool &, boost::filesystem::path const &, int max_dbs = 128, bool use_no_mem_init = false, size_t map_size = 128ULL * 1024 * 1024 * 1024); - void init (bool &, boost::filesystem::path const &, int max_dbs, bool use_no_mem_init, size_t map_size = 128ULL * 1024 * 1024 * 1024); + /** Environment options, most of which originates from the config file. */ + class options final + { + friend class mdb_env; + + public: + static options make () + { + return options (); + } + + options & set_config (nano::lmdb_config config_a) + { + config = config_a; + return *this; + } + + options & set_use_no_mem_init (int use_no_mem_init_a) + { + use_no_mem_init = use_no_mem_init_a; + return *this; + } + + /** Used by the wallet to override the config map size */ + options & override_config_map_size (size_t map_size_a) + { + config.map_size = map_size_a; + return *this; + } + + /** Used by the wallet to override the sync strategy */ + options & override_config_sync (nano::lmdb_config::sync_strategy sync_a) + { + config.sync = sync_a; + return *this; + } + + private: + bool use_no_mem_init{ false }; + nano::lmdb_config config; + }; + + mdb_env (bool &, boost::filesystem::path const &, nano::mdb_env::options options_a = nano::mdb_env::options::make ()); + void init (bool &, boost::filesystem::path const &, nano::mdb_env::options options_a = nano::mdb_env::options::make ()); ~mdb_env (); operator MDB_env * () const; nano::read_transaction tx_begin_read (mdb_txn_callbacks txn_callbacks = mdb_txn_callbacks{}) const; diff --git a/nano/node/node.cpp b/nano/node/node.cpp index bf4da967b4..77e5041e59 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -119,9 +119,9 @@ alarm (alarm_a), work (work_a), distributed_work (*this), logger (config_a.logging.min_time_between_log_output), -store_impl (nano::make_store (logger, application_path_a, flags.read_only, true, config_a.rocksdb_config, config_a.diagnostics_config.txn_tracking, config_a.block_processor_batch_max_time, config_a.lmdb_max_dbs, flags.sideband_batch_size, config_a.backup_before_upgrade, config_a.rocksdb_config.enable)), +store_impl (nano::make_store (logger, application_path_a, flags.read_only, true, config_a.rocksdb_config, config_a.diagnostics_config.txn_tracking, config_a.block_processor_batch_max_time, config_a.lmdb_config, flags.sideband_batch_size, config_a.backup_before_upgrade, config_a.rocksdb_config.enable)), store (*store_impl), -wallets_store_impl (std::make_unique (application_path_a / "wallets.ldb", config_a.lmdb_max_dbs)), +wallets_store_impl (std::make_unique (application_path_a / "wallets.ldb", config_a.lmdb_config)), wallets_store (*wallets_store_impl), gap_cache (*this), ledger (store, stats, flags_a.generate_cache), @@ -1395,7 +1395,7 @@ nano::node_flags const & nano::inactive_node_flag_defaults () return node_flags; } -std::unique_ptr nano::make_store (nano::logger_mt & logger, boost::filesystem::path const & path, bool read_only, bool add_db_postfix, nano::rocksdb_config const & rocksdb_config, nano::txn_tracking_config const & txn_tracking_config_a, std::chrono::milliseconds block_processor_batch_max_time_a, int lmdb_max_dbs, size_t batch_size, bool backup_before_upgrade, bool use_rocksdb_backend) +std::unique_ptr nano::make_store (nano::logger_mt & logger, boost::filesystem::path const & path, bool read_only, bool add_db_postfix, nano::rocksdb_config const & rocksdb_config, nano::txn_tracking_config const & txn_tracking_config_a, std::chrono::milliseconds block_processor_batch_max_time_a, nano::lmdb_config const & lmdb_config_a, size_t batch_size, bool backup_before_upgrade, bool use_rocksdb_backend) { #if NANO_ROCKSDB auto make_rocksdb = [&logger, add_db_postfix, &path, &rocksdb_config, read_only]() { @@ -1426,5 +1426,5 @@ std::unique_ptr nano::make_store (nano::logger_mt & logger, b #endif } - return std::make_unique (logger, add_db_postfix ? path / "data.ldb" : path, txn_tracking_config_a, block_processor_batch_max_time_a, lmdb_max_dbs, batch_size, backup_before_upgrade); + return std::make_unique (logger, add_db_postfix ? path / "data.ldb" : path, txn_tracking_config_a, block_processor_batch_max_time_a, lmdb_config_a, batch_size, backup_before_upgrade); } diff --git a/nano/node/nodeconfig.cpp b/nano/node/nodeconfig.cpp index d0f1a08fe3..74ff6542c3 100644 --- a/nano/node/nodeconfig.cpp +++ b/nano/node/nodeconfig.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -81,7 +80,7 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const toml.put ("bootstrap_connections", bootstrap_connections, "Number of outbound bootstrap connections. Must be a power of 2. Defaults to 4.\nWarning: a larger amount of connections may use substantially more system memory.\ntype:uint64"); toml.put ("bootstrap_connections_max", bootstrap_connections_max, "Maximum number of inbound bootstrap connections. Defaults to 64.\nWarning: a larger amount of connections may use additional system memory.\ntype:uint64"); toml.put ("bootstrap_initiator_threads", bootstrap_initiator_threads, "Number of threads dedicated to concurrent bootstrap attempts. Defaults to 2 (if the number of CPU threads is more than 1), otherwise 1.\nWarning: a larger amount of attempts may use additional system memory and disk IO.\ntype:uint64"); - toml.put ("lmdb_max_dbs", lmdb_max_dbs, "Maximum open lmdb databases. Increase default if more than 100 wallets is required.\nNote: external management is recommended when a large amounts of wallets are required (see https://docs.nano.org/integration-guides/key-management/).\ntype:uint64"); + toml.put ("lmdb_max_dbs", deprecated_lmdb_max_dbs, "DEPRECATED: use node.lmdb.max_databases instead.\nMaximum open lmdb databases. Increase default if more than 100 wallets is required.\nNote: external management is recommended when a large amounts of wallets are required (see https://docs.nano.org/integration-guides/key-management/).\ntype:uint64"); toml.put ("block_processor_batch_max_time", block_processor_batch_max_time.count (), "The maximum time the block processor can continously process blocks for.\ntype:milliseconds"); toml.put ("allow_local_peers", allow_local_peers, "Enable or disable local host peering.\ntype:bool"); toml.put ("vote_minimum", vote_minimum.to_string_dec (), "Local representatives do not vote if the delegated weight is under this threshold. Saves on system resources.\ntype:string,amount,raw"); @@ -161,6 +160,10 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const rocksdb_config.serialize_toml (rocksdb_l); toml.put_child ("rocksdb", rocksdb_l); + nano::tomlconfig lmdb_l; + lmdb_config.serialize_toml (lmdb_l); + toml.put_child ("lmdb", lmdb_l); + return toml.get_error (); } @@ -304,11 +307,33 @@ nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml) toml.get ("bootstrap_connections", bootstrap_connections); toml.get ("bootstrap_connections_max", bootstrap_connections_max); toml.get ("bootstrap_initiator_threads", bootstrap_initiator_threads); - toml.get ("lmdb_max_dbs", lmdb_max_dbs); toml.get ("enable_voting", enable_voting); toml.get ("allow_local_peers", allow_local_peers); toml.get (signature_checker_threads_key, signature_checker_threads); + auto lmdb_max_dbs_default = deprecated_lmdb_max_dbs; + toml.get ("lmdb_max_dbs", deprecated_lmdb_max_dbs); + bool is_deprecated_lmdb_dbs_used = lmdb_max_dbs_default != deprecated_lmdb_max_dbs; + + // Note: using the deprecated setting will result in a fail-fast config error in the future + if (!network_params.network.is_test_network () && is_deprecated_lmdb_dbs_used) + { + std::cerr << "WARNING: The node.lmdb_max_dbs setting is deprecated and will be removed in a future version." << std::endl; + std::cerr << "Please use the node.lmdb.max_databases setting instead." << std::endl; + } + + if (toml.has_key ("lmdb")) + { + auto lmdb_config_l (toml.get_required_child ("lmdb")); + lmdb_config.deserialize_toml (lmdb_config_l, is_deprecated_lmdb_dbs_used); + + // Note that the lmdb config fails is both the deprecated and new setting are changed. + if (is_deprecated_lmdb_dbs_used) + { + lmdb_config.max_databases = deprecated_lmdb_max_dbs; + } + } + boost::asio::ip::address_v6 external_address_l; toml.get ("external_address", external_address_l); external_address = external_address_l.to_string (); @@ -446,7 +471,7 @@ nano::error nano::node_config::serialize_json (nano::jsonconfig & json) const json.put ("callback_address", callback_address); json.put ("callback_port", callback_port); json.put ("callback_target", callback_target); - json.put ("lmdb_max_dbs", lmdb_max_dbs); + json.put ("lmdb_max_dbs", deprecated_lmdb_max_dbs); json.put ("block_processor_batch_max_time", block_processor_batch_max_time.count ()); json.put ("allow_local_peers", allow_local_peers); json.put ("vote_minimum", vote_minimum.to_string_dec ()); @@ -740,7 +765,7 @@ nano::error nano::node_config::deserialize_json (bool & upgraded_a, nano::jsonco json.get ("callback_address", callback_address); json.get ("callback_port", callback_port); json.get ("callback_target", callback_target); - json.get ("lmdb_max_dbs", lmdb_max_dbs); + json.get ("lmdb_max_dbs", deprecated_lmdb_max_dbs); json.get ("enable_voting", enable_voting); json.get ("allow_local_peers", allow_local_peers); json.get (signature_checker_threads_key, signature_checker_threads); diff --git a/nano/node/nodeconfig.hpp b/nano/node/nodeconfig.hpp index b982e67b0b..dab89a6c38 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -70,7 +71,7 @@ class node_config std::string callback_address; uint16_t callback_port{ 0 }; std::string callback_target; - int lmdb_max_dbs{ 128 }; + int deprecated_lmdb_max_dbs{ 128 }; bool allow_local_peers{ !network_params.network.is_live_network () }; // disable by default for live network nano::stat_config stat_config; nano::ipc::ipc_config ipc_config; @@ -96,6 +97,7 @@ class node_config uint64_t max_work_generate_difficulty{ nano::network_constants::publish_full_threshold }; uint32_t max_queued_requests{ 512 }; nano::rocksdb_config rocksdb_config; + nano::lmdb_config lmdb_config; nano::frontiers_confirmation_mode frontiers_confirmation{ nano::frontiers_confirmation_mode::automatic }; std::string serialize_frontiers_confirmation (nano::frontiers_confirmation_mode) const; nano::frontiers_confirmation_mode deserialize_frontiers_confirmation (std::string const &); diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index e5bac374c8..9a344ea3fa 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -1989,8 +1989,8 @@ nano::store_iterator nano::wallet_store::end { return nano::store_iterator (nullptr); } -nano::mdb_wallets_store::mdb_wallets_store (boost::filesystem::path const & path_a, int lmdb_max_dbs) : -environment (error, path_a, lmdb_max_dbs, false, 1ULL * 1024 * 1024 * 1024) +nano::mdb_wallets_store::mdb_wallets_store (boost::filesystem::path const & path_a, nano::lmdb_config const & lmdb_config_a) : +environment (error, path_a, nano::mdb_env::options::make ().set_config (lmdb_config_a).override_config_sync (nano::lmdb_config::sync_strategy::always).override_config_map_size (1ULL * 1024 * 1024 * 1024)) { } diff --git a/nano/node/wallet.hpp b/nano/node/wallet.hpp index 9595c42c87..aa5577a05a 100644 --- a/nano/node/wallet.hpp +++ b/nano/node/wallet.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -257,7 +258,7 @@ class wallets_store class mdb_wallets_store final : public wallets_store { public: - mdb_wallets_store (boost::filesystem::path const &, int lmdb_max_dbs = 128); + mdb_wallets_store (boost::filesystem::path const &, nano::lmdb_config const & lmdb_config_a = nano::lmdb_config{}); nano::mdb_env environment; bool init_error () const override; bool error{ false }; diff --git a/nano/secure/blockstore.hpp b/nano/secure/blockstore.hpp index 30b3cef5ac..2ae13f6e3c 100644 --- a/nano/secure/blockstore.hpp +++ b/nano/secure/blockstore.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -776,7 +777,7 @@ class block_store virtual std::string vendor_get () const = 0; }; -std::unique_ptr make_store (nano::logger_mt & logger, boost::filesystem::path const & path, bool open_read_only = false, bool add_db_postfix = false, nano::rocksdb_config const & rocksdb_config = nano::rocksdb_config{}, nano::txn_tracking_config const & txn_tracking_config_a = nano::txn_tracking_config{}, std::chrono::milliseconds block_processor_batch_max_time_a = std::chrono::milliseconds (5000), int lmdb_max_dbs = 128, size_t batch_size = 512, bool backup_before_upgrade = false, bool rocksdb_backend = false); +std::unique_ptr make_store (nano::logger_mt & logger, boost::filesystem::path const & path, bool open_read_only = false, bool add_db_postfix = false, nano::rocksdb_config const & rocksdb_config = nano::rocksdb_config{}, nano::txn_tracking_config const & txn_tracking_config_a = nano::txn_tracking_config{}, std::chrono::milliseconds block_processor_batch_max_time_a = std::chrono::milliseconds (5000), nano::lmdb_config const & lmdb_config_a = nano::lmdb_config{}, size_t batch_size = 512, bool backup_before_upgrade = false, bool rocksdb_backend = false); } namespace std From 1efc2e4feeb97bce9e7a997567cf2eba6ddf06db Mon Sep 17 00:00:00 2001 From: Sergey Kroshnin Date: Wed, 4 Mar 2020 23:56:07 +0300 Subject: [PATCH 23/43] Update adjusted difficulty in batches (#2604) * Update adjusted difficulty in batches each request loop because ordered roots are used only in this loop. Also prevent extra item modification if adjusted difficulty remains the same (i.e. single block election without dependencies in roots container) * Use new sorting only if mutex lock was removed for frontiers search * Adjust dependent blocks difficulty --- nano/core_test/active_transactions.cpp | 10 +- nano/core_test/conflicts.cpp | 2 + nano/node/active_transactions.cpp | 165 ++++++++++++++----------- nano/node/active_transactions.hpp | 4 +- nano/node/election.cpp | 11 +- nano/node/election.hpp | 1 + 6 files changed, 114 insertions(+), 79 deletions(-) diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 4daf82e091..5be7c22de3 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -122,6 +122,7 @@ TEST (active_transactions, adjusted_difficulty_priority) // Check adjusted difficulty { nano::lock_guard active_guard (node1.active.mutex); + node1.active.update_adjusted_difficulty (); ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ()); ASSERT_LT (node1.active.roots.find (send2->qualified_root ())->adjusted_difficulty, node1.active.roots.find (send1->qualified_root ())->adjusted_difficulty); ASSERT_LT (node1.active.roots.find (open1->qualified_root ())->adjusted_difficulty, node1.active.roots.find (send1->qualified_root ())->adjusted_difficulty); @@ -168,6 +169,7 @@ TEST (active_transactions, adjusted_difficulty_priority) // Check adjusted difficulty nano::lock_guard lock (node1.active.mutex); + node1.active.update_adjusted_difficulty (); uint64_t last_adjusted (0); for (auto i (node1.active.roots.get<1> ().begin ()), n (node1.active.roots.get<1> ().end ()); i != n; ++i) { @@ -223,7 +225,8 @@ TEST (active_transactions, adjusted_difficulty_overflow_max) modify_difficulty (send2_root); modify_difficulty (open1_root); modify_difficulty (open2_root); - node1.active.adjust_difficulty (send2->hash ()); + node1.active.add_adjust_difficulty (send2->hash ()); + node1.active.update_adjusted_difficulty (); // Test overflow ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ()); ASSERT_EQ (send1_root->adjusted_difficulty, std::numeric_limits::max ()); @@ -277,7 +280,8 @@ TEST (active_transactions, adjusted_difficulty_overflow_min) modify_difficulty (open1_root); modify_difficulty (open2_root); modify_difficulty (send3_root); - node1.active.adjust_difficulty (send1->hash ()); + node1.active.add_adjust_difficulty (send1->hash ()); + node1.active.update_adjusted_difficulty (); // Test overflow ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ()); ASSERT_EQ (send1_root->adjusted_difficulty, std::numeric_limits::min () + 3); @@ -399,6 +403,8 @@ TEST (active_transactions, prioritize_chains) } size_t seen (0); { + nano::lock_guard active_guard (node1.active.mutex); + node1.active.update_adjusted_difficulty (); auto it (node1.active.roots.get<1> ().begin ()); while (!node1.active.roots.empty () && it != node1.active.roots.get<1> ().end ()) { diff --git a/nano/core_test/conflicts.cpp b/nano/core_test/conflicts.cpp index 3309435524..1cf633a39b 100644 --- a/nano/core_test/conflicts.cpp +++ b/nano/core_test/conflicts.cpp @@ -253,6 +253,7 @@ TEST (conflicts, adjusted_difficulty) std::unordered_map adjusted_difficulties; { nano::lock_guard guard (node1.active.mutex); + node1.active.update_adjusted_difficulty (); ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ()); for (auto i (node1.active.roots.get<1> ().begin ()), n (node1.active.roots.get<1> ().end ()); i != n; ++i) { @@ -285,6 +286,7 @@ TEST (conflicts, adjusted_difficulty) } { nano::lock_guard guard (node1.active.mutex); + node1.active.update_adjusted_difficulty (); ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), open_epoch2->hash ()); } } diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index a1fc3298e7..6161501d49 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -222,14 +222,13 @@ void nano::active_transactions::request_confirm (nano::unique_lock & lock_a.unlock (); search_frontiers (transaction_l); lock_a.lock (); + update_adjusted_difficulty (); // New roots sorting } } // Only representatives ready to receive batched confirm_req - lock_a.unlock (); nano::confirmation_solicitor solicitor (node.network, node.network_params.network); solicitor.prepare (node.rep_crawler.representatives (node.network_params.protocol.tcp_realtime_protocol_version_min)); - lock_a.lock (); auto election_ttl_cutoff_l (std::chrono::steady_clock::now () - election_time_to_live); auto roots_size_l (roots.size ()); @@ -281,6 +280,7 @@ void nano::active_transactions::request_loop () // Account for the time spent in request_confirm by defining the wakeup point beforehand const auto wakeup_l (std::chrono::steady_clock::now () + std::chrono::milliseconds (node.network_params.network.request_interval_ms)); + update_adjusted_difficulty (); update_active_difficulty (lock); request_confirm (lock); @@ -484,7 +484,7 @@ std::pair, bool> nano::active_transactions::inse auto difficulty (block_a->difficulty ()); roots.get ().emplace (nano::conflict_info{ root, difficulty, difficulty, result.first }); blocks.emplace (hash, result.first); - adjust_difficulty (hash); + add_adjust_difficulty (hash); result.first->insert_inactive_votes_cache (hash); } } @@ -601,97 +601,111 @@ void nano::active_transactions::update_difficulty (std::shared_ptr info_a.difficulty = difficulty; }); existing_election->election->publish (block_a); - adjust_difficulty (block_a->hash ()); + add_adjust_difficulty (block_a->hash ()); } } } -void nano::active_transactions::adjust_difficulty (nano::block_hash const & hash_a) +void nano::active_transactions::add_adjust_difficulty (nano::block_hash const & hash_a) +{ + debug_assert (!mutex.try_lock ()); + adjust_difficulty_list.push_back (hash_a); +} + +void nano::active_transactions::update_adjusted_difficulty () { debug_assert (!mutex.try_lock ()); - std::deque> remaining_blocks; - remaining_blocks.emplace_back (hash_a, 0); std::unordered_set processed_blocks; - std::vector> elections_list; - double sum (0.); - int64_t highest_level (0); - int64_t lowest_level (0); - while (!remaining_blocks.empty ()) - { - auto const & item (remaining_blocks.front ()); - auto hash (item.first); - auto level (item.second); - if (processed_blocks.find (hash) == processed_blocks.end ()) + while (!adjust_difficulty_list.empty ()) + { + auto const & adjust_difficulty_item (adjust_difficulty_list.front ()); + std::deque> remaining_blocks; + remaining_blocks.emplace_back (adjust_difficulty_item, 0); + adjust_difficulty_list.pop_front (); + std::vector> elections_list; + double sum (0.); + int64_t highest_level (0); + int64_t lowest_level (0); + while (!remaining_blocks.empty ()) { - auto existing (blocks.find (hash)); - if (existing != blocks.end () && !existing->second->confirmed () && existing->second->status.winner->hash () == hash) + auto const & item (remaining_blocks.front ()); + auto hash (item.first); + auto level (item.second); + if (processed_blocks.find (hash) == processed_blocks.end ()) { - auto previous (existing->second->status.winner->previous ()); - if (!previous.is_zero ()) - { - remaining_blocks.emplace_back (previous, level + 1); - } - auto source (existing->second->status.winner->source ()); - if (!source.is_zero () && source != previous) - { - remaining_blocks.emplace_back (source, level + 1); - } - auto link (existing->second->status.winner->link ()); - if (!link.is_zero () && !node.ledger.is_epoch_link (link) && link != previous) + auto existing (blocks.find (hash)); + if (existing != blocks.end () && !existing->second->confirmed () && existing->second->status.winner->hash () == hash) { - remaining_blocks.emplace_back (link, level + 1); - } - for (auto & dependent_block : existing->second->dependent_blocks) - { - remaining_blocks.emplace_back (dependent_block, level - 1); - } - processed_blocks.insert (hash); - nano::qualified_root root (previous, existing->second->status.winner->root ()); - auto existing_root (roots.get ().find (root)); - if (existing_root != roots.get ().end ()) - { - sum += nano::difficulty::to_multiplier (existing_root->difficulty, node.network_params.network.publish_threshold); - elections_list.emplace_back (root, level); - if (level > highest_level) + auto previous (existing->second->status.winner->previous ()); + if (!previous.is_zero ()) + { + remaining_blocks.emplace_back (previous, level + 1); + } + auto source (existing->second->status.winner->source ()); + if (!source.is_zero () && source != previous) + { + remaining_blocks.emplace_back (source, level + 1); + } + auto link (existing->second->status.winner->link ()); + if (!link.is_zero () && !node.ledger.is_epoch_link (link) && link != previous) { - highest_level = level; + remaining_blocks.emplace_back (link, level + 1); } - else if (level < lowest_level) + for (auto & dependent_block : existing->second->dependent_blocks) { - lowest_level = level; + remaining_blocks.emplace_back (dependent_block, level - 1); + } + processed_blocks.insert (hash); + nano::qualified_root root (previous, existing->second->status.winner->root ()); + auto existing_root (roots.get ().find (root)); + if (existing_root != roots.get ().end ()) + { + sum += nano::difficulty::to_multiplier (existing_root->difficulty, node.network_params.network.publish_threshold); + elections_list.emplace_back (root, level); + if (level > highest_level) + { + highest_level = level; + } + else if (level < lowest_level) + { + lowest_level = level; + } } } } + remaining_blocks.pop_front (); } - remaining_blocks.pop_front (); - } - if (!elections_list.empty ()) - { - double multiplier = sum / elections_list.size (); - uint64_t average = nano::difficulty::from_multiplier (multiplier, node.network_params.network.publish_threshold); - // Prevent overflow - int64_t limiter (0); - if (std::numeric_limits::max () - average < static_cast (highest_level)) - { - // Highest adjusted difficulty value should be std::numeric_limits::max () - limiter = std::numeric_limits::max () - average + highest_level; - debug_assert (std::numeric_limits::max () == average + highest_level - limiter); - } - else if (average < std::numeric_limits::min () - lowest_level) + if (!elections_list.empty ()) { - // Lowest adjusted difficulty value should be std::numeric_limits::min () - limiter = std::numeric_limits::min () - average + lowest_level; - debug_assert (std::numeric_limits::min () == average + lowest_level - limiter); - } + double multiplier = sum / elections_list.size (); + uint64_t average = nano::difficulty::from_multiplier (multiplier, node.network_params.network.publish_threshold); + // Prevent overflow + int64_t limiter (0); + if (std::numeric_limits::max () - average < static_cast (highest_level)) + { + // Highest adjusted difficulty value should be std::numeric_limits::max () + limiter = std::numeric_limits::max () - average + highest_level; + debug_assert (std::numeric_limits::max () == average + highest_level - limiter); + } + else if (average < std::numeric_limits::min () - lowest_level) + { + // Lowest adjusted difficulty value should be std::numeric_limits::min () + limiter = std::numeric_limits::min () - average + lowest_level; + debug_assert (std::numeric_limits::min () == average + lowest_level - limiter); + } - // Set adjusted difficulty - for (auto & item : elections_list) - { - auto existing_root (roots.get ().find (item.first)); - uint64_t difficulty_a = average + item.second - limiter; - roots.get ().modify (existing_root, [difficulty_a](nano::conflict_info & info_a) { - info_a.adjusted_difficulty = difficulty_a; - }); + // Set adjusted difficulty + for (auto & item : elections_list) + { + auto existing_root (roots.get ().find (item.first)); + uint64_t difficulty_a = average + item.second - limiter; + if (existing_root->adjusted_difficulty != difficulty_a) + { + roots.get ().modify (existing_root, [difficulty_a](nano::conflict_info & info_a) { + info_a.adjusted_difficulty = difficulty_a; + }); + } + } } } } @@ -778,6 +792,7 @@ void nano::active_transactions::erase (nano::block const & block_a) if (root_it != roots.get ().end ()) { root_it->election->clear_blocks (); + root_it->election->adjust_dependent_difficulty (); roots.get ().erase (root_it); node.logger.try_log (boost::str (boost::format ("Election erased for block block %1% root %2%") % block_a.hash ().to_string () % block_a.root ().to_string ())); } diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 6a5b58c0ce..d1ffcb3d09 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -95,7 +95,8 @@ class active_transactions final bool active (nano::qualified_root const &); std::shared_ptr election (nano::qualified_root const &) const; void update_difficulty (std::shared_ptr); - void adjust_difficulty (nano::block_hash const &); + void add_adjust_difficulty (nano::block_hash const &); + void update_adjusted_difficulty (); void update_active_difficulty (nano::unique_lock &); uint64_t active_difficulty (); uint64_t limited_active_difficulty (); @@ -181,6 +182,7 @@ class active_transactions final void prioritize_account_for_confirmation (prioritize_num_uncemented &, size_t &, nano::account const &, nano::account_info const &, uint64_t); static size_t constexpr max_priority_cementable_frontiers{ 100000 }; static size_t constexpr confirmed_frontiers_max_pending_cut_off{ 1000 }; + std::deque adjust_difficulty_list; // clang-format off using ordered_cache = boost::multi_index_containerprocess_confirmed (status_l, this_l); confirmation_action_l (status_l.winner); }); + adjust_dependent_difficulty (); } } @@ -367,7 +368,7 @@ void nano::election::confirm_if_quorum () node.block_processor.force (block_l); status.winner = block_l; update_dependent (); - node.active.adjust_difficulty (winner_hash_l); + node.active.add_adjust_difficulty (winner_hash_l); } if (have_quorum (tally_l, sum)) { @@ -522,6 +523,14 @@ void nano::election::update_dependent () } } +void nano::election::adjust_dependent_difficulty () +{ + for (auto & dependent_block : dependent_blocks) + { + node.active.add_adjust_difficulty (dependent_block); + } +} + void nano::election::clear_blocks () { auto winner_hash (status.winner->hash ()); diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 9339cc2b01..6ea73c11ce 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -78,6 +78,7 @@ class election final : public std::enable_shared_from_this bool publish (std::shared_ptr block_a); size_t last_votes_size (); void update_dependent (); + void adjust_dependent_difficulty (); void clear_blocks (); void insert_inactive_votes_cache (nano::block_hash const &); From 31b04e24df7d5f570dca901489af4e2f1b44ce5e Mon Sep 17 00:00:00 2001 From: cryptocode Date: Thu, 5 Mar 2020 10:19:03 +0100 Subject: [PATCH 24/43] Make confirmation_solicitor.batches more robust under tsan (#2628) --- nano/core_test/confirmation_solicitor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nano/core_test/confirmation_solicitor.cpp b/nano/core_test/confirmation_solicitor.cpp index 0e6caa3de3..ef97f49d9f 100644 --- a/nano/core_test/confirmation_solicitor.cpp +++ b/nano/core_test/confirmation_solicitor.cpp @@ -50,11 +50,11 @@ TEST (confirmation_solicitor, batches) ASSERT_NO_ERROR (system.poll ()); } // From rep crawler - ASSERT_EQ (1, node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out)); system.deadline_set (5s); solicitor.flush (); - while (node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out) < 2) + while (node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out) == 1) { ASSERT_NO_ERROR (system.poll ()); } + ASSERT_LE (2, node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out)); } From 83afb8ef1d08c5b9f650aaf4142bb92b32339d3a Mon Sep 17 00:00:00 2001 From: cryptocode Date: Thu, 5 Mar 2020 12:01:33 +0100 Subject: [PATCH 25/43] Make network.replace_port more robust under tsan (#2630) --- nano/core_test/network.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index 5af1537761..99b9b9f121 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -919,7 +919,11 @@ TEST (network, replace_port) ASSERT_EQ (node0->network.endpoint (), list2[0]->get_endpoint ()); // Remove correct peer (same node ID) node0->network.udp_channels.clean_node_id (nano::endpoint (node1->network.endpoint ().address (), 23000), node1->node_id.pub); - ASSERT_EQ (node0->network.udp_channels.size (), 0); + system.deadline_set (5s); + while (node0->network.udp_channels.size () > 1) + { + ASSERT_NO_ERROR (system.poll ()); + } node1->stop (); } From 583124e368f40eb6bd727369335c9d71289ce279 Mon Sep 17 00:00:00 2001 From: cryptocode Date: Thu, 5 Mar 2020 13:40:58 +0100 Subject: [PATCH 26/43] Add ASSERT_TIMELY (#2633) * Add ASSERT_TIMELY and apply to request_aggregator.one * Add lambda version --- nano/core_test/testutil.hpp | 8 ++++++++ nano/node/testing.cpp | 11 +++++++++++ nano/node/testing.hpp | 1 + 3 files changed, 20 insertions(+) diff --git a/nano/core_test/testutil.hpp b/nano/core_test/testutil.hpp index 348e88cc70..612f207e24 100644 --- a/nano/core_test/testutil.hpp +++ b/nano/core_test/testutil.hpp @@ -32,6 +32,14 @@ GTEST_TEST_ERROR_CODE ((condition.value () > 0), #condition, "An error was expected", "", \ GTEST_FATAL_FAILURE_) +/** Asserts that the condition becomes true within the deadline */ +#define ASSERT_TIMELY(time, condition) \ + system.deadline_set (time); \ + while (!(condition)) \ + { \ + ASSERT_NO_ERROR (system.poll ()); \ + } + /* Convenience globals for core_test */ namespace nano { diff --git a/nano/node/testing.cpp b/nano/node/testing.cpp index 45c6e09290..d4c86a268c 100644 --- a/nano/node/testing.cpp +++ b/nano/node/testing.cpp @@ -178,6 +178,17 @@ std::error_code nano::system::poll (std::chrono::nanoseconds const & wait_time) return ec; } +std::error_code nano::system::poll_until_true (std::chrono::nanoseconds deadline_a, std::function predicate_a) +{ + std::error_code ec; + deadline_set (deadline_a); + while (!ec && !predicate_a ()) + { + ec = poll (); + } + return ec; +} + namespace { class traffic_generator : public std::enable_shared_from_this diff --git a/nano/node/testing.hpp b/nano/node/testing.hpp index 59d74afc3e..4b1a860d59 100644 --- a/nano/node/testing.hpp +++ b/nano/node/testing.hpp @@ -38,6 +38,7 @@ class system final * @returns 0 or nano::deadline_expired */ std::error_code poll (const std::chrono::nanoseconds & sleep_time = std::chrono::milliseconds (50)); + std::error_code poll_until_true (std::chrono::nanoseconds deadline, std::function); void stop (); void deadline_set (const std::chrono::duration & delta); std::shared_ptr add_node (nano::node_flags = nano::node_flags (), nano::transport::transport_type = nano::transport::transport_type::tcp); From f0b87995853325cdbaa57df015de2ddbb00c2d6c Mon Sep 17 00:00:00 2001 From: Russel Waters Date: Thu, 5 Mar 2020 13:46:07 -0500 Subject: [PATCH 27/43] split docker artifacts to a separate job (#2636) --- .github/workflows/beta_artifacts.yml | 14 +++++++++++++- .github/workflows/live_artifacts.yml | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/beta_artifacts.yml b/.github/workflows/beta_artifacts.yml index 8301dcd5d3..04d2c76744 100644 --- a/.github/workflows/beta_artifacts.yml +++ b/.github/workflows/beta_artifacts.yml @@ -51,10 +51,22 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-east-2 + + linux_docker_job: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@722adc6 + - uses: chrislennon/action-aws-cli@f0f8671 + - name: tag + run: echo "::set-env name=TAG::`git describe --tags $GITHUB_SHA`" + - name: Checkout Submodules + run: git submodule update --init --recursive + - name: Fetch Deps + run: ci/actions/linux/install_deps.sh - name: Deploy Docker (nanocurrency/nano-beta) run: TRAVIS_TAG=${TAG} ci/actions/linux/deploy-docker.sh env: - DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} windows_job: runs-on: windows-latest diff --git a/.github/workflows/live_artifacts.yml b/.github/workflows/live_artifacts.yml index 77aeff8b8c..0c85a7fad0 100644 --- a/.github/workflows/live_artifacts.yml +++ b/.github/workflows/live_artifacts.yml @@ -50,10 +50,22 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-east-2 + + linux_docker_job: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@722adc6 + - uses: chrislennon/action-aws-cli@f0f8671 + - name: tag + run: echo "::set-env name=TAG::`git describe --tags $GITHUB_SHA`" + - name: Checkout Submodules + run: git submodule update --init --recursive + - name: Fetch Deps + run: ci/actions/linux/install_deps.sh - name: Deploy Docker (nanocurrency/nano) run: TRAVIS_TAG=${TAG} ci/actions/linux/deploy-docker.sh env: - DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} windows_job: runs-on: windows-latest From 931eb995ea8cc9a6ea6ba68b84fc777a4ac6543f Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Fri, 6 Mar 2020 13:04:50 +0000 Subject: [PATCH 28/43] Read transaction scope in active (#2640) --- nano/node/active_transactions.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 6161501d49..3baf4e3773 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -206,7 +206,6 @@ void nano::active_transactions::block_already_cemented_callback (nano::block_has void nano::active_transactions::request_confirm (nano::unique_lock & lock_a) { debug_assert (!mutex.try_lock ()); - auto transaction_l (node.store.tx_begin_read ()); /* * Confirm frontiers when there aren't many confirmations already pending and node finished initial bootstrap * In auto mode start confirm only if node contains almost principal representative (half of required for principal weight) @@ -220,7 +219,7 @@ void nano::active_transactions::request_confirm (nano::unique_lock & if (node.config.frontiers_confirmation != nano::frontiers_confirmation_mode::disabled && bootstrap_weight_reached && probably_unconfirmed_frontiers && pending_confirmation_height_size < confirmed_frontiers_max_pending_cut_off) { lock_a.unlock (); - search_frontiers (transaction_l); + search_frontiers (node.store.tx_begin_read ()); lock_a.lock (); update_adjusted_difficulty (); // New roots sorting } From 9b7f30a0dea9e5b7f32b02d28bad021e7261ef66 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Mon, 9 Mar 2020 11:30:23 +0000 Subject: [PATCH 29/43] Fix request_aggregator unit tests (#2632) * Fix request_aggregator unit tests With #2614 cached votes are sent after removing the request and unlocking the mutex, making it necessary to wait for confirm_ack count to be updated. * Using ASSERT_TIMELY --- nano/core_test/request_aggregator.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nano/core_test/request_aggregator.cpp b/nano/core_test/request_aggregator.cpp index 819dd04354..8043c6d272 100644 --- a/nano/core_test/request_aggregator.cpp +++ b/nano/core_test/request_aggregator.cpp @@ -52,7 +52,7 @@ TEST (request_aggregator, one) ASSERT_EQ (1, node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_unknown)); ASSERT_EQ (1, node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_generated_votes)); ASSERT_EQ (1, node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_cached_votes)); - ASSERT_EQ (2, node.stats.count (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::out)); + ASSERT_TIMELY (3s, node.stats.count (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::out) == 2); } TEST (request_aggregator, one_update) @@ -91,7 +91,7 @@ TEST (request_aggregator, one_update) ASSERT_EQ (1, node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_generated_votes)); ASSERT_EQ (0, node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_cached_hashes)); ASSERT_EQ (0, node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_cached_votes)); - ASSERT_EQ (1, node.stats.count (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::out)); + ASSERT_TIMELY (3s, node.stats.count (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::out) == 1); } TEST (request_aggregator, two) @@ -136,7 +136,7 @@ TEST (request_aggregator, two) ASSERT_EQ (1, node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_generated_votes)); ASSERT_EQ (2, node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_cached_hashes)); ASSERT_EQ (1, node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_cached_votes)); - ASSERT_EQ (2, node.stats.count (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::out)); + ASSERT_TIMELY (3s, node.stats.count (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::out) == 2); // Make sure the cached vote is for both hashes auto vote1 (node.votes_cache.find (send1->hash ())); auto vote2 (node.votes_cache.find (send2->hash ())); @@ -231,7 +231,7 @@ TEST (request_aggregator, split) ASSERT_EQ (2, node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_generated_votes)); ASSERT_EQ (0, node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_unknown)); ASSERT_EQ (0, node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_cached_hashes)); - ASSERT_EQ (2, node.stats.count (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::out)); + ASSERT_TIMELY (3s, node.stats.count (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::out) == 2); } TEST (request_aggregator, channel_lifetime) From f7e50b3a8363e5a347bfcd6c66dd82028d7d2b98 Mon Sep 17 00:00:00 2001 From: Russel Waters Date: Tue, 10 Mar 2020 09:30:34 -0400 Subject: [PATCH 30/43] gather sha256 hashes of artifacts and upload to s3 with artifacts (#2647) * gather sha256 hashes of artifacts and upload to s3 with artifacts extra whitespace changes in powershell scripts * typo --- ci/actions/deploy.sh | 4 ++++ ci/actions/windows/build.ps1 | 9 ++++++--- ci/actions/windows/deploy.ps1 | 19 +++++++++++++------ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/ci/actions/deploy.sh b/ci/actions/deploy.sh index e203ed034b..7d0fc51bba 100755 --- a/ci/actions/deploy.sh +++ b/ci/actions/deploy.sh @@ -12,7 +12,11 @@ else fi if [[ "$OS" == 'Linux' ]]; then + sha256sum $GITHUB_WORKSPACE/build/nano-node-*-Linux.tar.bz2 | cut -f1 -d' ' > $GITHUB_WORKSPACE/build/nano-node-$TAG-Linux.tar.bz2.sha256 aws s3 cp $GITHUB_WORKSPACE/build/nano-node-*-Linux.tar.bz2 s3://repo.nano.org/$BUILD/binaries/nano-node-$TAG-Linux.tar.bz2 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers + aws s3 cp $GITHUB_WORKSPACE/build/nano-node-$TAG-Linux.tar.bz2.sha256 s3://repo.nano.org/$BUILD/binaries/nano-node-$TAG-Linux.tar.bz2.sha256 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers else + sha256sum $GITHUB_WORKSPACE/build/nano-node-*-Darwin.dmg | cut -f1 -d' ' > $GITHUB_WORKSPACE/build/nano-node-$TAG-Darwin.dmg.sha256 aws s3 cp $GITHUB_WORKSPACE/build/nano-node-*-Darwin.dmg s3://repo.nano.org/$BUILD/binaries/nano-node-$TAG-Darwin.dmg --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers + aws s3 cp $GITHUB_WORKSPACE/build/nano-node-$TAG-Darwin.dmg.sha256 s3://repo.nano.org/$BUILD/binaries/nano-node-$TAG-Darwin.dmg.sha256 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers fi diff --git a/ci/actions/windows/build.ps1 b/ci/actions/windows/build.ps1 index 9fe4dc7d47..5868c9b4c6 100644 --- a/ci/actions/windows/build.ps1 +++ b/ci/actions/windows/build.ps1 @@ -4,7 +4,8 @@ if (${env:artifact} -eq 1) { if ( ${env:BETA} -eq 1 ) { $env:NETWORK_CFG = "beta" $env:BUILD_TYPE = "RelWithDebInfo" - } else { + } + else { $env:NETWORK_CFG = "live" $env:BUILD_TYPE = "Release" } @@ -14,11 +15,13 @@ if (${env:artifact} -eq 1) { $env:CI = "-DCI_BUILD=ON" $env:RUN = "artifact" -} else { +} +else { if ( ${env:RELEASE} -eq 1 ) { $env:BUILD_TYPE = "RelWithDebInfo" $env:ROCKS_LIB = '-DROCKSDB_LIBRARIES="c:\vcpkg\installed\x64-windows-static\lib\rocksdb.lib"' - } else { + } + else { $env:BUILD_TYPE = "Debug" $env:ROCKS_LIB = '-DROCKSDB_LIBRARIES="c:\vcpkg\installed\x64-windows-static\debug\lib\rocksdbd.lib"' } diff --git a/ci/actions/windows/deploy.ps1 b/ci/actions/windows/deploy.ps1 index 2e766bf23d..0dbfa460a4 100644 --- a/ci/actions/windows/deploy.ps1 +++ b/ci/actions/windows/deploy.ps1 @@ -1,12 +1,19 @@ $ErrorActionPreference = "Continue" if ( ${env:BETA} -eq 1 ) { - $network_cfg="beta" -} else { - $network_cfg="live" + $network_cfg = "beta" } +else { + $network_cfg = "live" +} + +$exe = Resolve-Path -Path $env:GITHUB_WORKSPACE\build\nano-node-*-win64.exe +$zip = Resolve-Path -Path $env:GITHUB_WORKSPACE\build\nano-node-*-win64.zip + +(Get-FileHash $exe).hash | Out-file -FilePath "$exe.sh256" +(Get-FileHash $zip).hash | Out-file -FilePath "$zip.sh256" -$exe=Resolve-Path -Path $env:GITHUB_WORKSPACE\build\nano-node-*-win64.exe -$zip=Resolve-Path -Path $env:GITHUB_WORKSPACE\build\nano-node-*-win64.zip aws s3 cp $exe s3://repo.nano.org/$network_cfg/binaries/nano-node-$env:TAG-win64.exe --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers -aws s3 cp "$zip" s3://repo.nano.org/$network_cfg/binaries/nano-node-$env:TAG-win64.zip --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers \ No newline at end of file +aws s3 cp "$exe.sha256" s3://repo.nano.org/$network_cfg/binaries/nano-node-$env:TAG-win64.exe.sha256 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers +aws s3 cp "$zip" s3://repo.nano.org/$network_cfg/binaries/nano-node-$env:TAG-win64.zip --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers +aws s3 cp "$zip.sha256" s3://repo.nano.org/$network_cfg/binaries/nano-node-$env:TAG-win64.zip.sha256 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers \ No newline at end of file From b23d7c315a86bc2f99d58560c35627e530d8d695 Mon Sep 17 00:00:00 2001 From: Wesley Shillingford Date: Tue, 10 Mar 2020 15:04:24 +0000 Subject: [PATCH 31/43] Read config file for CLI commands (#2637) --- nano/core_test/node.cpp | 2 +- nano/core_test/socket.cpp | 4 +- nano/core_test/wallets.cpp | 2 +- nano/lib/utility.cpp | 12 +--- nano/nano_node/entry.cpp | 120 ++++++++++++++++++++----------------- nano/node/cli.cpp | 120 ++++++++++++++++++++++--------------- nano/node/node.cpp | 43 ++++++------- nano/node/node.hpp | 9 +-- 8 files changed, 169 insertions(+), 143 deletions(-) diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 8c76236ad9..df082522c4 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -3578,7 +3578,7 @@ TEST (node, bandwidth_limiter) nano::publish message (genesis.open); auto message_size = message.to_bytes ()->size (); auto message_limit = 4; // must be multiple of the number of channels - nano::node_config node_config (24000, system.logging); + nano::node_config node_config (nano::get_available_port (), system.logging); node_config.bandwidth_limit = message_limit * message_size; auto & node = *system.add_node (node_config); auto channel1 (node.network.udp_channels.create (node.network.endpoint ())); diff --git a/nano/core_test/socket.cpp b/nano/core_test/socket.cpp index 3ac1971cb4..cc54d11cee 100644 --- a/nano/core_test/socket.cpp +++ b/nano/core_test/socket.cpp @@ -11,7 +11,7 @@ TEST (socket, drop_policy) { auto node_flags = nano::inactive_node_flag_defaults (); node_flags.read_only = false; - nano::inactive_node inactivenode (nano::unique_path (), nano::get_available_port (), node_flags); + nano::inactive_node inactivenode (nano::unique_path (), node_flags); auto node = inactivenode.node; nano::thread_runner runner (node->io_ctx, 1); @@ -61,7 +61,7 @@ TEST (socket, concurrent_writes) { auto node_flags = nano::inactive_node_flag_defaults (); node_flags.read_only = false; - nano::inactive_node inactivenode (nano::unique_path (), nano::get_available_port (), node_flags); + nano::inactive_node inactivenode (nano::unique_path (), node_flags); auto node = inactivenode.node; // This gives more realistic execution than using system#poll, allowing writes to diff --git a/nano/core_test/wallets.cpp b/nano/core_test/wallets.cpp index ccb856e056..5d9ec86ca2 100644 --- a/nano/core_test/wallets.cpp +++ b/nano/core_test/wallets.cpp @@ -160,7 +160,7 @@ TEST (wallets, reload) ASSERT_EQ (1, node1.wallets.items.size ()); { nano::lock_guard lock_wallet (node1.wallets.mutex); - nano::inactive_node node (node1.application_path, nano::get_available_port ()); + nano::inactive_node node (node1.application_path); auto wallet (node.node->wallets.create (one)); ASSERT_NE (wallet, nullptr); } diff --git a/nano/lib/utility.cpp b/nano/lib/utility.cpp index d62865a1f2..fd71e43946 100644 --- a/nano/lib/utility.cpp +++ b/nano/lib/utility.cpp @@ -110,17 +110,11 @@ void nano::move_all_files_to_dir (boost::filesystem::path const & from, boost::f */ void assert_internal (const char * check_expr, const char * file, unsigned int line, bool is_release_assert) { - // Output stack trace + std::cerr << "Assertion (" << check_expr << ") failed " << file << ":" << line << "\n\n"; + + // Output stack trace to cerr auto backtrace_str = nano::generate_stacktrace (); - // Windows on Actions only outputs the first line of the stacktrace from standard error, use standard output -#if (defined(_WIN32) && CI) - std::cout << backtrace_str << std::endl; -#else std::cerr << backtrace_str << std::endl; -#endif - - std::cerr << "Assertion (" << check_expr << ") failed " << file << ":" << line << "\n" - << std::endl; // "abort" at the end of this function will go into any signal handlers (the daemon ones will generate a stack trace and load memory address files on non-Windows systems). // As there is no async-signal-safe way to generate stacktraces on Windows it must be done before aborting diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index e30c01cd00..ea0ef373b2 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -159,9 +159,10 @@ int main (int argc, char * const * argv) } else if (vm.count ("debug_block_count")) { - nano::inactive_node node (data_path); - auto transaction (node.node->store.tx_begin_read ()); - std::cout << boost::str (boost::format ("Block count: %1%\n") % node.node->store.block_count (transaction).sum ()); + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; + auto transaction (node->store.tx_begin_read ()); + std::cout << boost::str (boost::format ("Block count: %1%\n") % node->store.block_count (transaction).sum ()); } else if (vm.count ("debug_bootstrap_generate")) { @@ -221,11 +222,12 @@ int main (int argc, char * const * argv) } else if (vm.count ("debug_dump_online_weight")) { - nano::inactive_node node (data_path); - auto current (node.node->online_reps.online_stake ()); + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; + auto current (node->online_reps.online_stake ()); std::cout << boost::str (boost::format ("Online Weight %1%\n") % current); - auto transaction (node.node->store.tx_begin_read ()); - for (auto i (node.node->store.online_weight_begin (transaction)), n (node.node->store.online_weight_end ()); i != n; ++i) + auto transaction (node->store.tx_begin_read ()); + for (auto i (node->store.online_weight_begin (transaction)), n (node->store.online_weight_end ()); i != n; ++i) { using time_point = std::chrono::system_clock::time_point; time_point ts (std::chrono::duration_cast (std::chrono::nanoseconds (i->first))); @@ -238,11 +240,13 @@ int main (int argc, char * const * argv) else if (vm.count ("debug_dump_representatives")) { auto node_flags = nano::inactive_node_flag_defaults (); + nano::update_flags (node_flags, vm); node_flags.generate_cache.reps = true; - nano::inactive_node node (data_path, 24000, node_flags); - auto transaction (node.node->store.tx_begin_read ()); + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; + auto transaction (node->store.tx_begin_read ()); nano::uint128_t total; - auto rep_amounts = node.node->ledger.cache.rep_weights.get_rep_amounts (); + auto rep_amounts = node->ledger.cache.rep_weights.get_rep_amounts (); std::map ordered_reps (rep_amounts.begin (), rep_amounts.end ()); for (auto const & rep : ordered_reps) { @@ -252,19 +256,20 @@ int main (int argc, char * const * argv) } else if (vm.count ("debug_dump_frontier_unchecked_dependents")) { - nano::inactive_node node (data_path); + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; std::cout << "Outputting any frontier hashes which have associated key hashes in the unchecked table (may take some time)...\n"; // Cache the account heads to make searching quicker against unchecked keys. - auto transaction (node.node->store.tx_begin_read ()); + auto transaction (node->store.tx_begin_read ()); std::unordered_set frontier_hashes; - for (auto i (node.node->store.latest_begin (transaction)), n (node.node->store.latest_end ()); i != n; ++i) + for (auto i (node->store.latest_begin (transaction)), n (node->store.latest_end ()); i != n; ++i) { frontier_hashes.insert (i->second.head); } // Check all unchecked keys for matching frontier hashes. Indicates an issue with process_batch algorithm - for (auto i (node.node->store.unchecked_begin (transaction)), n (node.node->store.unchecked_end ()); i != n; ++i) + for (auto i (node->store.unchecked_begin (transaction)), n (node->store.unchecked_end ()); i != n; ++i) { auto it = frontier_hashes.find (i->first.key ()); if (it != frontier_hashes.cend ()) @@ -275,8 +280,8 @@ int main (int argc, char * const * argv) } else if (vm.count ("debug_account_count")) { - nano::inactive_node node (data_path); - std::cout << boost::str (boost::format ("Frontier count: %1%\n") % node.node->ledger.cache.account_count); + auto inactive_node = nano::default_inactive_node (data_path, vm); + std::cout << boost::str (boost::format ("Frontier count: %1%\n") % inactive_node->node->ledger.cache.account_count); } else if (vm.count ("debug_mass_activity")) { @@ -710,7 +715,7 @@ int main (int argc, char * const * argv) nano::logging logging; auto path (nano::unique_path ()); logging.init (path); - auto node_flags = nano::node_flags (); + nano::node_flags node_flags; nano::update_flags (node_flags, vm); auto node (std::make_shared (system.io_ctx, 24001, path, system.alarm, logging, work, node_flags)); nano::block_hash genesis_latest (node->latest (test_params.ledger.test_genesis_key.pub)); @@ -962,20 +967,21 @@ int main (int argc, char * const * argv) std::exit (0); }); - nano::inactive_node inactive_node_l (data_path); + auto inactive_node_l = nano::default_inactive_node (data_path, vm); nano::node_rpc_config config; - nano::ipc::ipc_server server (*inactive_node_l.node, config); - nano::json_handler handler_l (*inactive_node_l.node, config, command_l.str (), response_handler_l); + nano::ipc::ipc_server server (*inactive_node_l->node, config); + nano::json_handler handler_l (*inactive_node_l->node, config, command_l.str (), response_handler_l); handler_l.process_request (); } else if (vm.count ("debug_validate_blocks")) { - nano::inactive_node node (data_path); - auto transaction (node.node->store.tx_begin_read ()); + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; + auto transaction (node->store.tx_begin_read ()); std::cout << boost::str (boost::format ("Performing blocks hash, signature, work validation...\n")); size_t count (0); uint64_t block_count (0); - for (auto i (node.node->store.latest_begin (transaction)), n (node.node->store.latest_end ()); i != n; ++i) + for (auto i (node->store.latest_begin (transaction)), n (node->store.latest_end ()); i != n; ++i) { ++count; if ((count % 20000) == 0) @@ -985,7 +991,7 @@ int main (int argc, char * const * argv) nano::account_info const & info (i->second); nano::account const & account (i->first); nano::confirmation_height_info confirmation_height_info; - node.node->store.confirmation_height_get (transaction, account, confirmation_height_info); + node->store.confirmation_height_get (transaction, account, confirmation_height_info); if (confirmation_height_info.height > info.block_count) { @@ -994,7 +1000,7 @@ int main (int argc, char * const * argv) auto hash (info.open_block); nano::block_hash calculated_hash (0); - auto block (node.node->store.block_get (transaction, hash)); // Block data + auto block (node->store.block_get (transaction, hash)); // Block data uint64_t height (0); uint64_t previous_timestamp (0); nano::account calculated_representative (0); @@ -1046,11 +1052,11 @@ int main (int argc, char * const * argv) nano::amount prev_balance (0); if (!state_block.hashables.previous.is_zero ()) { - prev_balance = node.node->ledger.balance (transaction, state_block.hashables.previous); + prev_balance = node->ledger.balance (transaction, state_block.hashables.previous); } - if (node.node->ledger.is_epoch_link (state_block.hashables.link) && state_block.hashables.balance == prev_balance) + if (node->ledger.is_epoch_link (state_block.hashables.link) && state_block.hashables.balance == prev_balance) { - invalid = validate_message (node.node->ledger.epoch_signer (block->link ()), hash, block->block_signature ()); + invalid = validate_message (node->ledger.epoch_signer (block->link ()), hash, block->block_signature ()); } } if (invalid) @@ -1067,7 +1073,7 @@ int main (int argc, char * const * argv) } else { - auto prev_balance (node.node->ledger.balance (transaction, block->previous ())); + auto prev_balance (node->ledger.balance (transaction, block->previous ())); if (block->balance () < prev_balance) { // State send @@ -1080,7 +1086,7 @@ int main (int argc, char * const * argv) // State change block_details_error = sideband.details.is_send || sideband.details.is_receive || sideband.details.is_epoch; } - else if (block->balance () == prev_balance && node.node->ledger.is_epoch_link (block->link ())) + else if (block->balance () == prev_balance && node->ledger.is_epoch_link (block->link ())) { // State epoch block_details_error = !sideband.details.is_epoch || sideband.details.is_send || sideband.details.is_receive; @@ -1089,7 +1095,7 @@ int main (int argc, char * const * argv) { // State receive block_details_error = !sideband.details.is_receive || sideband.details.is_send || sideband.details.is_epoch; - block_details_error |= !node.node->store.source_exists (transaction, block->link ()); + block_details_error |= !node->store.source_exists (transaction, block->link ()); } } } @@ -1120,11 +1126,11 @@ int main (int argc, char * const * argv) calculated_representative = block->representative (); } // Retrieving successor block hash - hash = node.node->store.block_successor (transaction, hash); + hash = node->store.block_successor (transaction, hash); // Retrieving block data if (!hash.is_zero ()) { - block = node.node->store.block_get (transaction, hash); + block = node->store.block_get (transaction, hash); } } // Check if required block exists @@ -1150,14 +1156,14 @@ int main (int argc, char * const * argv) } std::cout << boost::str (boost::format ("%1% accounts validated\n") % count); // Validate total block count - auto ledger_block_count (node.node->store.block_count (transaction).sum ()); + auto ledger_block_count (node->store.block_count (transaction).sum ()); if (block_count != ledger_block_count) { std::cerr << boost::str (boost::format ("Incorrect total block count. Blocks validated %1%. Block count in database: %2%\n") % block_count % ledger_block_count); } // Validate pending blocks count = 0; - for (auto i (node.node->store.pending_begin (transaction)), n (node.node->store.pending_end ()); i != n; ++i) + for (auto i (node->store.pending_begin (transaction)), n (node->store.pending_end ()); i != n; ++i) { ++count; if ((count % 200000) == 0) @@ -1167,7 +1173,7 @@ int main (int argc, char * const * argv) nano::pending_key const & key (i->first); nano::pending_info const & info (i->second); // Check block existance - auto block (node.node->store.block_get_no_sideband (transaction, key.hash)); + auto block (node->store.block_get_no_sideband (transaction, key.hash)); if (block == nullptr) { std::cerr << boost::str (boost::format ("Pending block does not exist %1%\n") % key.hash.to_string ()); @@ -1178,7 +1184,7 @@ int main (int argc, char * const * argv) nano::account destination (0); if (auto state = dynamic_cast (block.get ())) { - if (node.node->ledger.is_send (transaction, *state)) + if (node->ledger.is_send (transaction, *state)) { destination = state->hashables.link; } @@ -1196,13 +1202,13 @@ int main (int argc, char * const * argv) std::cerr << boost::str (boost::format ("Incorrect destination for pending block %1%\n") % key.hash.to_string ()); } // Check if pending source is correct - auto account (node.node->ledger.account (transaction, key.hash)); + auto account (node->ledger.account (transaction, key.hash)); if (info.source != account) { std::cerr << boost::str (boost::format ("Incorrect source for pending block %1%\n") % key.hash.to_string ()); } // Check if pending amount is correct - auto amount (node.node->ledger.amount (transaction, key.hash)); + auto amount (node->ledger.amount (transaction, key.hash)); if (info.amount != amount) { std::cerr << boost::str (boost::format ("Incorrect amount for pending block %1%\n") % key.hash.to_string ()); @@ -1216,17 +1222,18 @@ int main (int argc, char * const * argv) auto node_flags = nano::inactive_node_flag_defaults (); node_flags.read_only = false; nano::update_flags (node_flags, vm); - nano::inactive_node node2 (nano::unique_path (), 24001, node_flags); + nano::inactive_node node2 (nano::unique_path (), node_flags); nano::genesis genesis; auto begin (std::chrono::high_resolution_clock::now ()); uint64_t block_count (0); size_t count (0); { - nano::inactive_node node (data_path, 24000); - auto transaction (node.node->store.tx_begin_read ()); - block_count = node.node->store.block_count (transaction).sum (); + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; + auto transaction (node->store.tx_begin_read ()); + block_count = node->store.block_count (transaction).sum (); std::cout << boost::str (boost::format ("Performing bootstrap emulation, %1% blocks in ledger...") % block_count) << std::endl; - for (auto i (node.node->store.latest_begin (transaction)), n (node.node->store.latest_end ()); i != n; ++i) + for (auto i (node->store.latest_begin (transaction)), n (node->store.latest_end ()); i != n; ++i) { nano::account const & account (i->first); nano::account_info const & info (i->second); @@ -1234,7 +1241,7 @@ int main (int argc, char * const * argv) while (!hash.is_zero ()) { // Retrieving block data - auto block (node.node->store.block_get_no_sideband (transaction, hash)); + auto block (node->store.block_get_no_sideband (transaction, hash)); if (block != nullptr) { ++count; @@ -1278,10 +1285,11 @@ int main (int argc, char * const * argv) } else if (vm.count ("debug_peers")) { - nano::inactive_node node (data_path); - auto transaction (node.node->store.tx_begin_read ()); + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; + auto transaction (node->store.tx_begin_read ()); - for (auto i (node.node->store.peers_begin (transaction)), n (node.node->store.peers_end ()); i != n; ++i) + for (auto i (node->store.peers_begin (transaction)), n (node->store.peers_end ()); i != n; ++i) { std::cout << boost::str (boost::format ("%1%\n") % nano::endpoint (boost::asio::ip::address_v6 (i->first.address_bytes ()), i->first.port ())); } @@ -1290,7 +1298,8 @@ int main (int argc, char * const * argv) { auto node_flags = nano::inactive_node_flag_defaults (); node_flags.generate_cache.cemented_count = true; - nano::inactive_node node (data_path, 24000, node_flags); + nano::update_flags (node_flags, vm); + nano::inactive_node node (data_path, node_flags); std::cout << "Total cemented block count: " << node.node->ledger.cache.cemented_count << std::endl; } else if (vm.count ("debug_stacktrace")) @@ -1306,18 +1315,19 @@ int main (int argc, char * const * argv) return 1; } #endif - nano::inactive_node node (data_path); - node.node->logger.always_log (nano::severity_level::error, "Testing system logger"); + auto inactive_node = nano::default_inactive_node (data_path, vm); + inactive_node->node->logger.always_log (nano::severity_level::error, "Testing system logger"); } else if (vm.count ("debug_account_versions")) { - nano::inactive_node node (data_path); + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; - auto transaction (node.node->store.tx_begin_read ()); + auto transaction (node->store.tx_begin_read ()); std::vector> opened_account_versions (nano::normalized_epoch (nano::epoch::max)); // Cache the accounts in a collection to make searching quicker against unchecked keys. Group by epoch - for (auto i (node.node->store.latest_begin (transaction)), n (node.node->store.latest_end ()); i != n; ++i) + for (auto i (node->store.latest_begin (transaction)), n (node->store.latest_end ()); i != n; ++i) { auto const & account (i->first); auto const & account_info (i->second); @@ -1329,7 +1339,7 @@ int main (int argc, char * const * argv) // Iterate all pending blocks and collect the highest version for each unopened account std::unordered_map> unopened_highest_pending; - for (auto i (node.node->store.pending_begin (transaction)), n (node.node->store.pending_end ()); i != n; ++i) + for (auto i (node->store.pending_begin (transaction)), n (node->store.pending_end ()); i != n; ++i) { nano::pending_key const & key (i->first); nano::pending_info const & info (i->second); diff --git a/nano/node/cli.cpp b/nano/node/cli.cpp index edb1af4ad4..12cf05b612 100644 --- a/nano/node/cli.cpp +++ b/nano/node/cli.cpp @@ -120,8 +120,11 @@ std::error_code nano::update_flags (nano::node_flags & flags_a, boost::program_o flags_a.disable_lazy_bootstrap = (vm.count ("disable_lazy_bootstrap") > 0); flags_a.disable_legacy_bootstrap = (vm.count ("disable_legacy_bootstrap") > 0); flags_a.disable_wallet_bootstrap = (vm.count ("disable_wallet_bootstrap") > 0); - flags_a.disable_bootstrap_listener = (vm.count ("disable_bootstrap_listener") > 0); - flags_a.disable_tcp_realtime = (vm.count ("disable_tcp_realtime") > 0); + if (!flags_a.inactive_node) + { + flags_a.disable_bootstrap_listener = (vm.count ("disable_bootstrap_listener") > 0); + flags_a.disable_tcp_realtime = (vm.count ("disable_tcp_realtime") > 0); + } flags_a.disable_providing_telemetry_metrics = (vm.count ("disable_providing_telemetry_metrics") > 0); if ((vm.count ("disable_udp") > 0) && (vm.count ("enable_udp") > 0)) { @@ -193,7 +196,8 @@ bool copy_database (boost::filesystem::path const & data_path, boost::program_op auto node_flags = nano::inactive_node_flag_defaults (); node_flags.read_only = !needs_to_write; - nano::inactive_node node (data_path, 24000, node_flags); + nano::update_flags (node_flags, vm); + nano::inactive_node node (data_path, node_flags); if (!node.node->init_error ()) { if (vm.count ("unchecked_clear")) @@ -253,8 +257,8 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map { password = vm["password"].as (); } - inactive_node node (data_path); - auto wallet (node.node->wallets.open (wallet_id)); + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto wallet (inactive_node->node->wallets.open (wallet_id)); if (wallet != nullptr) { auto transaction (wallet->wallets.tx_begin_write ()); @@ -438,7 +442,8 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map boost::filesystem::path data_path = vm.count ("data_path") ? boost::filesystem::path (vm["data_path"].as ()) : nano::working_path (); auto node_flags = nano::inactive_node_flag_defaults (); node_flags.read_only = false; - nano::inactive_node node (data_path, 24000, node_flags); + nano::update_flags (node_flags, vm); + nano::inactive_node node (data_path, node_flags); if (!node.node->init_error ()) { auto transaction (node.node->store.tx_begin_write ()); @@ -455,7 +460,8 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map boost::filesystem::path data_path = vm.count ("data_path") ? boost::filesystem::path (vm["data_path"].as ()) : nano::working_path (); auto node_flags = nano::inactive_node_flag_defaults (); node_flags.read_only = false; - nano::inactive_node node (data_path, 24000, node_flags); + nano::update_flags (node_flags, vm); + nano::inactive_node node (data_path, node_flags); if (!node.node->init_error ()) { auto transaction (node.node->wallets.tx_begin_write ()); @@ -472,7 +478,8 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map boost::filesystem::path data_path = vm.count ("data_path") ? boost::filesystem::path (vm["data_path"].as ()) : nano::working_path (); auto node_flags = nano::inactive_node_flag_defaults (); node_flags.read_only = false; - nano::inactive_node node (data_path, 24000, node_flags); + nano::update_flags (node_flags, vm); + nano::inactive_node node (data_path, node_flags); if (!node.node->init_error ()) { auto transaction (node.node->store.tx_begin_write ()); @@ -489,7 +496,8 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map boost::filesystem::path data_path = vm.count ("data_path") ? boost::filesystem::path (vm["data_path"].as ()) : nano::working_path (); auto node_flags = nano::inactive_node_flag_defaults (); node_flags.read_only = false; - nano::inactive_node node (data_path, 24000, node_flags); + nano::update_flags (node_flags, vm); + nano::inactive_node node (data_path, node_flags); if (!node.node->init_error ()) { auto transaction (node.node->store.tx_begin_write ()); @@ -506,7 +514,8 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map boost::filesystem::path data_path = vm.count ("data_path") ? boost::filesystem::path (vm["data_path"].as ()) : nano::working_path (); auto node_flags = nano::inactive_node_flag_defaults (); node_flags.read_only = false; - nano::inactive_node node (data_path, 24000, node_flags); + nano::update_flags (node_flags, vm); + nano::inactive_node node (data_path, node_flags); if (!node.node->init_error ()) { auto account_it = vm.find ("account"); @@ -600,7 +609,7 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map } else if (vm.count ("diagnostics")) { - inactive_node node (data_path); + auto inactive_node = nano::default_inactive_node (data_path, vm); std::cout << "Testing hash function" << std::endl; nano::raw_key key; key.data.clear (); @@ -619,7 +628,7 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map environment.dump (std::cout); std::stringstream stream; environment.dump (stream); - node.node->logger.always_log (stream.str ()); + inactive_node->node->logger.always_log (stream.str ()); } else { @@ -663,8 +672,8 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map { password = vm["password"].as (); } - inactive_node node (data_path); - auto wallet (node.node->wallets.open (wallet_id)); + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto wallet (inactive_node->node->wallets.open (wallet_id)); if (wallet != nullptr) { auto transaction (wallet->wallets.tx_begin_write ()); @@ -717,8 +726,8 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map { password = vm["password"].as (); } - inactive_node node (data_path); - auto wallet (node.node->wallets.open (wallet_id)); + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto wallet (inactive_node->node->wallets.open (wallet_id)); if (wallet != nullptr) { auto transaction (wallet->wallets.tx_begin_write ()); @@ -799,9 +808,9 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map } if (!ec) { - inactive_node node (data_path); + auto inactive_node = nano::default_inactive_node (data_path, vm); auto wallet_key = nano::random_wallet_id (); - auto wallet (node.node->wallets.create (wallet_key)); + auto wallet (inactive_node->node->wallets.create (wallet_key)); if (wallet != nullptr) { if (vm.count ("password") > 0) @@ -841,9 +850,10 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map nano::wallet_id wallet_id; if (!wallet_id.decode_hex (vm["wallet"].as ())) { - inactive_node node (data_path); - auto existing (node.node->wallets.items.find (wallet_id)); - if (existing != node.node->wallets.items.end ()) + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; + auto existing (inactive_node->node->wallets.items.find (wallet_id)); + if (existing != inactive_node->node->wallets.items.end ()) { auto transaction (existing->second->wallets.tx_begin_write ()); if (!existing->second->enter_password (transaction, password)) @@ -896,10 +906,11 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map nano::wallet_id wallet_id; if (!wallet_id.decode_hex (vm["wallet"].as ())) { - inactive_node node (data_path); - if (node.node->wallets.items.find (wallet_id) != node.node->wallets.items.end ()) + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; + if (node->wallets.items.find (wallet_id) != node->wallets.items.end ()) { - node.node->wallets.destroy (wallet_id); + node->wallets.destroy (wallet_id); } else { @@ -945,13 +956,14 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map nano::wallet_id wallet_id; if (!wallet_id.decode_hex (vm["wallet"].as ())) { - inactive_node node (data_path); - auto existing (node.node->wallets.items.find (wallet_id)); - if (existing != node.node->wallets.items.end ()) + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; + auto existing (node->wallets.items.find (wallet_id)); + if (existing != node->wallets.items.end ()) { bool valid (false); { - auto transaction (node.node->wallets.tx_begin_write ()); + auto transaction (node->wallets.tx_begin_write ()); valid = existing->second->store.valid_password (transaction); if (!valid) { @@ -987,9 +999,9 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map { bool error (true); { - nano::lock_guard lock (node.node->wallets.mutex); - auto transaction (node.node->wallets.tx_begin_write ()); - nano::wallet wallet (error, transaction, node.node->wallets, wallet_id.to_string (), contents.str ()); + nano::lock_guard lock (node->wallets.mutex); + auto transaction (node->wallets.tx_begin_write ()); + nano::wallet wallet (error, transaction, node->wallets, wallet_id.to_string (), contents.str ()); } if (error) { @@ -998,9 +1010,9 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map } else { - node.node->wallets.reload (); - nano::lock_guard lock (node.node->wallets.mutex); - release_assert (node.node->wallets.items.find (wallet_id) != node.node->wallets.items.end ()); + node->wallets.reload (); + nano::lock_guard lock (node->wallets.mutex); + release_assert (node->wallets.items.find (wallet_id) != node->wallets.items.end ()); std::cout << "Import completed\n"; } } @@ -1032,8 +1044,9 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map } else if (vm.count ("wallet_list")) { - inactive_node node (data_path); - for (auto i (node.node->wallets.items.begin ()), n (node.node->wallets.items.end ()); i != n; ++i) + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; + for (auto i (node->wallets.items.begin ()), n (node->wallets.items.end ()); i != n; ++i) { std::cout << boost::str (boost::format ("Wallet ID: %1%\n") % i->first.to_string ()); auto transaction (i->second->wallets.tx_begin_read ()); @@ -1047,12 +1060,13 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map { if (vm.count ("wallet") == 1 && vm.count ("account") == 1) { - inactive_node node (data_path); + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; nano::wallet_id wallet_id; if (!wallet_id.decode_hex (vm["wallet"].as ())) { - auto wallet (node.node->wallets.items.find (wallet_id)); - if (wallet != node.node->wallets.items.end ()) + auto wallet (node->wallets.items.find (wallet_id)); + if (wallet != node->wallets.items.end ()) { nano::account account_id; if (!account_id.decode_account (vm["account"].as ())) @@ -1100,9 +1114,10 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map nano::wallet_id wallet_id; if (!wallet_id.decode_hex (vm["wallet"].as ())) { - inactive_node node (data_path); - auto wallet (node.node->wallets.items.find (wallet_id)); - if (wallet != node.node->wallets.items.end ()) + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; + auto wallet (node->wallets.items.find (wallet_id)); + if (wallet != node->wallets.items.end ()) { auto transaction (wallet->second->wallets.tx_begin_read ()); auto representative (wallet->second->store.representative (transaction)); @@ -1138,9 +1153,10 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map nano::account account; if (!account.decode_account (vm["account"].as ())) { - inactive_node node (data_path); - auto wallet (node.node->wallets.items.find (wallet_id)); - if (wallet != node.node->wallets.items.end ()) + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; + auto wallet (node->wallets.items.find (wallet_id)); + if (wallet != node->wallets.items.end ()) { auto transaction (wallet->second->wallets.tx_begin_write ()); wallet->second->store.representative_set (transaction, account); @@ -1177,9 +1193,10 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map } else if (vm.count ("vote_dump") == 1) { - inactive_node node (data_path); - auto transaction (node.node->store.tx_begin_read ()); - for (auto i (node.node->store.vote_begin (transaction)), n (node.node->store.vote_end ()); i != n; ++i) + auto inactive_node = nano::default_inactive_node (data_path, vm); + auto node = inactive_node->node; + auto transaction (node->store.tx_begin_read ()); + for (auto i (node->store.vote_begin (transaction)), n (node->store.vote_end ()); i != n; ++i) { auto const & vote (i->second); std::cerr << boost::str (boost::format ("%1%\n") % vote->to_json ()); @@ -1193,6 +1210,13 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map return ec; } +std::unique_ptr nano::default_inactive_node (boost::filesystem::path const & path_a, boost::program_options::variables_map const & vm_a) +{ + auto node_flags = nano::inactive_node_flag_defaults (); + nano::update_flags (node_flags, vm_a); + return std::make_unique (path_a, node_flags); +} + namespace { void reset_confirmation_heights (nano::block_store & store) diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 77e5041e59..a32a692614 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -1,7 +1,9 @@ +#include #include #include #include #include +#include #include #include #include @@ -1341,39 +1343,38 @@ bool nano::node::init_error () const return store.init_error () || wallets_store.init_error (); } -nano::inactive_node::inactive_node (boost::filesystem::path const & path_a, uint16_t peering_port_a, nano::node_flags const & node_flags) : -path (path_a), +nano::inactive_node::inactive_node (boost::filesystem::path const & path_a, nano::node_flags const & node_flags_a) : io_context (std::make_shared ()), alarm (*io_context), -work (1), -peering_port (peering_port_a) +work (1) { boost::system::error_code error_chmod; /* * @warning May throw a filesystem exception */ - boost::filesystem::create_directories (path); - nano::set_secure_perm_directory (path, error_chmod); - logging.max_size = std::numeric_limits::max (); - logging.init (path); - // Config overriding - nano::node_config config (peering_port, logging); - std::stringstream config_overrides_stream; - for (auto const & entry : node_flags.config_overrides) - { - config_overrides_stream << entry << std::endl; - } - config_overrides_stream << std::endl; - nano::tomlconfig toml; - toml.read (config_overrides_stream); - auto error = config.deserialize_toml (toml); + boost::filesystem::create_directories (path_a); + nano::set_secure_perm_directory (path_a, error_chmod); + nano::daemon_config daemon_config (path_a); + auto error = nano::read_node_config_toml (path_a, daemon_config, node_flags_a.config_overrides); if (error) { - std::cerr << "Error deserializing --config option" << std::endl; + std::cerr << "Error deserializing config file"; + if (!node_flags_a.config_overrides.empty ()) + { + std::cerr << " or --config option"; + } + std::cerr << "\n" + << error.get_message () << std::endl; std::exit (1); } - node = std::make_shared (*io_context, path, alarm, config, work, node_flags); + + auto & node_config = daemon_config.node; + node_config.peering_port = nano::get_available_port (); + node_config.logging.max_size = std::numeric_limits::max (); + node_config.logging.init (path_a); + + node = std::make_shared (*io_context, path_a, alarm, node_config, work, node_flags_a); node->active.stop (); } diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 619646964e..36a86b56c6 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -35,6 +34,7 @@ #include #include #include +#include #include #include @@ -48,7 +48,6 @@ namespace websocket { class listener; } - class node; class telemetry; class work_pool; @@ -212,14 +211,12 @@ nano::node_flags const & inactive_node_flag_defaults (); class inactive_node final { public: - inactive_node (boost::filesystem::path const & path = nano::working_path (), uint16_t = 24000, nano::node_flags const & = nano::inactive_node_flag_defaults ()); + inactive_node (boost::filesystem::path const & path_a, nano::node_flags const & node_flags_a = nano::inactive_node_flag_defaults ()); ~inactive_node (); - boost::filesystem::path path; std::shared_ptr io_context; nano::alarm alarm; - nano::logging logging; nano::work_pool work; - uint16_t peering_port; std::shared_ptr node; }; +std::unique_ptr default_inactive_node (boost::filesystem::path const &, boost::program_options::variables_map const &); } From c42709d86826dc4879489244b01af508c3b06e1a Mon Sep 17 00:00:00 2001 From: Sergey Kroshnin Date: Tue, 10 Mar 2020 18:52:59 +0300 Subject: [PATCH 32/43] Use attempts list for TCP channels (#2581) * Use attempts list for TCP channels to prevent multiple concurrent connections start to same peer * Erase from attempts list after realtime TCP connection failure (udp fallback function) or success (insert function) * Explicitly close sockets after realtime TCP connection start failure * Use tags for TCP & UDP attempts lists * New test for max attempts * Limit max peers per IP for live & beta networks to 5 * Debug assert if there is limit overflow in tests * And special flag to allow using more connections --- nano/core_test/network.cpp | 19 +++++++++++++++++++ nano/node/network.cpp | 8 +++++++- nano/node/network.hpp | 2 ++ nano/node/nodeconfig.hpp | 1 + nano/node/testing.cpp | 1 + nano/node/transport/tcp.cpp | 23 +++++++++++++++++++---- nano/node/transport/tcp.hpp | 13 ++++++++++--- nano/node/transport/transport.hpp | 2 -- nano/node/transport/udp.cpp | 13 +++++++++---- nano/node/transport/udp.hpp | 7 +++++-- nano/secure/common.cpp | 1 + nano/secure/common.hpp | 2 ++ nano/slow_test/node.cpp | 4 +++- 13 files changed, 79 insertions(+), 17 deletions(-) diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index 99b9b9f121..b480ff23b9 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -927,6 +927,25 @@ TEST (network, replace_port) node1->stop (); } +TEST (network, peer_max_tcp_attempts) +{ + nano::system system (1); + auto node (system.nodes[0]); + // Add nodes that can accept TCP connection, but not node ID handshake + nano::node_flags node_flags; + node_flags.disable_tcp_realtime = true; + for (auto i (0); i < node->network_params.node.max_peers_per_ip; ++i) + { + auto node2 (std::make_shared (system.io_ctx, nano::get_available_port (), nano::unique_path (), system.alarm, system.logging, system.work, node_flags)); + node2->start (); + system.nodes.push_back (node2); + // Start TCP attempt + node->network.merge_peer (node2->network.endpoint ()); + } + ASSERT_EQ (0, node->network.size ()); + ASSERT_TRUE (node->network.tcp_channels.reachout (nano::endpoint (node->network.endpoint ().address (), nano::get_available_port ()))); +} + // The test must be completed in less than 1 second TEST (bandwidth_limiter, validate) { diff --git a/nano/node/network.cpp b/nano/node/network.cpp index aba02256d9..95f7d25041 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -11,6 +11,7 @@ #include nano::network::network (nano::node & node_a, uint16_t port_a) : +syn_cookies (node_a.network_params.node.max_peers_per_ip), buffer_container (node_a.stats, nano::network::buffer_size, 4096), // 2Mb receive buffer resolver (node_a.io_ctx), limiter (node_a.config.bandwidth_limit), @@ -815,6 +816,11 @@ void nano::message_buffer_manager::stop () condition.notify_all (); } +nano::syn_cookies::syn_cookies (size_t max_cookies_per_ip_a) : +max_cookies_per_ip (max_cookies_per_ip_a) +{ +} + boost::optional nano::syn_cookies::assign (nano::endpoint const & endpoint_a) { auto ip_addr (endpoint_a.address ()); @@ -822,7 +828,7 @@ boost::optional nano::syn_cookies::assign (nano::endpoint c nano::lock_guard lock (syn_cookie_mutex); unsigned & ip_cookies = cookies_per_ip[ip_addr]; boost::optional result; - if (ip_cookies < nano::transport::max_peers_per_ip) + if (ip_cookies < max_cookies_per_ip) { if (cookies.find (endpoint_a) == cookies.end ()) { diff --git a/nano/node/network.hpp b/nano/node/network.hpp index 99c68bbc45..2ae8814077 100644 --- a/nano/node/network.hpp +++ b/nano/node/network.hpp @@ -71,6 +71,7 @@ class message_buffer_manager final class syn_cookies final { public: + syn_cookies (size_t); void purge (std::chrono::steady_clock::time_point const &); // Returns boost::none if the IP is rate capped on syn cookie requests, // or if the endpoint already has a syn cookie query @@ -90,6 +91,7 @@ class syn_cookies final mutable std::mutex syn_cookie_mutex; std::unordered_map cookies; std::unordered_map cookies_per_ip; + size_t max_cookies_per_ip; }; class network final { diff --git a/nano/node/nodeconfig.hpp b/nano/node/nodeconfig.hpp index dab89a6c38..2baee4d866 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -132,6 +132,7 @@ class node_flags final bool disable_block_processor_republishing{ false }; bool disable_ongoing_telemetry_requests{ false }; bool allow_bootstrap_peers_duplicates{ false }; + bool disable_max_peers_per_ip{ false }; // For testing only bool fast_bootstrap{ false }; bool read_only{ false }; nano::confirmation_height_mode confirmation_height_processor_mode{ nano::confirmation_height_mode::automatic }; diff --git a/nano/node/testing.cpp b/nano/node/testing.cpp index d4c86a268c..fa2f25bb5d 100644 --- a/nano/node/testing.cpp +++ b/nano/node/testing.cpp @@ -37,6 +37,7 @@ std::shared_ptr nano::system::add_node (nano::node_config const & no nodes.push_back (node); if (nodes.size () > 1) { + debug_assert (nodes.size () - 1 <= node->network_params.node.max_peers_per_ip); // Check that we don't start more nodes than limit for single IP address auto begin = nodes.end () - 2; for (auto i (begin), j (begin + 1), n (nodes.end ()); j != n; ++i, ++j) { diff --git a/nano/node/transport/tcp.cpp b/nano/node/transport/tcp.cpp index 450a266d6a..42d669f2da 100644 --- a/nano/node/transport/tcp.cpp +++ b/nano/node/transport/tcp.cpp @@ -116,6 +116,7 @@ bool nano::transport::tcp_channels::insert (std::shared_ptr ().erase (node_id); } channels.get ().emplace (channel_a, socket_a, bootstrap_server_a); + attempts.get ().erase (endpoint); error = false; lock.unlock (); node.network.channel_observer (channel_a); @@ -349,8 +350,16 @@ void nano::transport::tcp_channels::stop () bool nano::transport::tcp_channels::max_ip_connections (nano::tcp_endpoint const & endpoint_a) { - nano::unique_lock lock (mutex); - bool result (channels.get ().count (endpoint_a.address ()) >= nano::transport::max_peers_per_ip); + bool result (false); + if (!node.flags.disable_max_peers_per_ip) + { + nano::unique_lock lock (mutex); + result = channels.get ().count (endpoint_a.address ()) >= node.network_params.node.max_peers_per_ip; + if (!result) + { + result = attempts.get ().count (endpoint_a.address ()) >= node.network_params.node.max_peers_per_ip; + } + } return result; } @@ -396,8 +405,8 @@ void nano::transport::tcp_channels::purge (std::chrono::steady_clock::time_point auto disconnect_cutoff (channels.get ().lower_bound (cutoff_a)); channels.get ().erase (channels.get ().begin (), disconnect_cutoff); // Remove keepalive attempt tracking for attempts older than cutoff - auto attempts_cutoff (attempts.get<1> ().lower_bound (cutoff_a)); - attempts.get<1> ().erase (attempts.get<1> ().begin (), attempts_cutoff); + auto attempts_cutoff (attempts.get ().lower_bound (cutoff_a)); + attempts.get ().erase (attempts.get ().begin (), attempts_cutoff); // Cleanup any sockets which may still be existing from failed node id handshakes node_id_handshake_sockets.erase (std::remove_if (node_id_handshake_sockets.begin (), node_id_handshake_sockets.end (), [this](auto socket) { @@ -545,6 +554,7 @@ void nano::transport::tcp_channels::start_tcp (nano::endpoint const & endpoint_a if (auto socket_l = channel->socket.lock ()) { node_l->network.tcp_channels.remove_node_id_handshake_socket (socket_l); + socket_l->close (); } if (node_l->config.logging.network_node_id_handshake_logging ()) { @@ -576,6 +586,7 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptrnetwork.tcp_channels.remove_node_id_handshake_socket (socket_l); + socket_l->close (); } } }; @@ -677,6 +688,10 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr)> const & callback_a) { + { + nano::lock_guard lock (mutex); + attempts.get ().erase (nano::transport::map_endpoint_to_tcp (endpoint_a)); + } if (callback_a && !node.flags.disable_udp) { auto channel_udp (node.network.udp_channels.create (endpoint_a)); diff --git a/nano/node/transport/tcp.hpp b/nano/node/transport/tcp.hpp index b71dda5a0d..6dd4738a19 100644 --- a/nano/node/transport/tcp.hpp +++ b/nano/node/transport/tcp.hpp @@ -131,6 +131,9 @@ namespace transport class last_bootstrap_attempt_tag { }; + class last_attempt_tag + { + }; class node_id_tag { }; @@ -171,10 +174,12 @@ namespace transport { public: nano::tcp_endpoint endpoint; + boost::asio::ip::address address; std::chrono::steady_clock::time_point last_attempt{ std::chrono::steady_clock::now () }; explicit tcp_endpoint_attempt (nano::tcp_endpoint const & endpoint_a) : - endpoint (endpoint_a) + endpoint (endpoint_a), + address (endpoint_a.address ()) { } }; @@ -196,9 +201,11 @@ namespace transport channels; boost::multi_index_container, mi::member>, - mi::ordered_non_unique< + mi::hashed_non_unique, + mi::member>, + mi::ordered_non_unique, mi::member>>> attempts; // clang-format on diff --git a/nano/node/transport/transport.hpp b/nano/node/transport/transport.hpp index 6de3dafbb1..6a0e7e635e 100644 --- a/nano/node/transport/transport.hpp +++ b/nano/node/transport/transport.hpp @@ -43,8 +43,6 @@ namespace transport nano::tcp_endpoint map_endpoint_to_tcp (nano::endpoint const &); // Unassigned, reserved, self bool reserved_address (nano::endpoint const &, bool = false); - // Maximum number of peers per IP - static size_t constexpr max_peers_per_ip = 10; static std::chrono::seconds constexpr syn_cookie_cutoff = std::chrono::seconds (5); enum class transport_type : uint8_t { diff --git a/nano/node/transport/udp.cpp b/nano/node/transport/udp.cpp index 993ee9bc9c..08ee1fbbe3 100644 --- a/nano/node/transport/udp.cpp +++ b/nano/node/transport/udp.cpp @@ -116,6 +116,7 @@ std::shared_ptr nano::transport::udp_channels::ins { result = std::make_shared (*this, endpoint_a, network_version_a); channels.get ().insert (result); + attempts.get ().erase (endpoint_a); lock.unlock (); node.network.channel_observer (result); } @@ -633,8 +634,12 @@ std::shared_ptr nano::transport::udp_channels::create bool nano::transport::udp_channels::max_ip_connections (nano::endpoint const & endpoint_a) { - nano::unique_lock lock (mutex); - bool result (channels.get ().count (endpoint_a.address ()) >= nano::transport::max_peers_per_ip); + bool result (false); + if (!node.flags.disable_max_peers_per_ip) + { + nano::unique_lock lock (mutex); + result = channels.get ().count (endpoint_a.address ()) >= node.network_params.node.max_peers_per_ip; + } return result; } @@ -677,8 +682,8 @@ void nano::transport::udp_channels::purge (std::chrono::steady_clock::time_point auto disconnect_cutoff (channels.get ().lower_bound (cutoff_a)); channels.get ().erase (channels.get ().begin (), disconnect_cutoff); // Remove keepalive attempt tracking for attempts older than cutoff - auto attempts_cutoff (attempts.get<1> ().lower_bound (cutoff_a)); - attempts.get<1> ().erase (attempts.get<1> ().begin (), attempts_cutoff); + auto attempts_cutoff (attempts.get ().lower_bound (cutoff_a)); + attempts.get ().erase (attempts.get ().begin (), attempts_cutoff); } void nano::transport::udp_channels::ongoing_keepalive () diff --git a/nano/node/transport/udp.hpp b/nano/node/transport/udp.hpp index c201619a8a..50f0149244 100644 --- a/nano/node/transport/udp.hpp +++ b/nano/node/transport/udp.hpp @@ -124,6 +124,9 @@ namespace transport class last_bootstrap_attempt_tag { }; + class last_attempt_tag + { + }; class node_id_tag { }; @@ -191,9 +194,9 @@ namespace transport boost::multi_index_container< endpoint_attempt, mi::indexed_by< - mi::hashed_unique< + mi::hashed_unique, mi::member>, - mi::ordered_non_unique< + mi::ordered_non_unique, mi::member>>> attempts; // clang-format on diff --git a/nano/secure/common.cpp b/nano/secure/common.cpp index 4b732b4ec8..f75a57b80d 100644 --- a/nano/secure/common.cpp +++ b/nano/secure/common.cpp @@ -135,6 +135,7 @@ nano::node_constants::node_constants (nano::network_constants & network_constant peer_interval = search_pending_interval; unchecked_cleaning_interval = std::chrono::minutes (30); process_confirmed_interval = network_constants.is_test_network () ? std::chrono::milliseconds (50) : std::chrono::milliseconds (500); + max_peers_per_ip = network_constants.is_test_network () ? 10 : 5; max_weight_samples = network_constants.is_live_network () ? 4032 : 864; weight_period = 5 * 60; // 5 minutes } diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index be6e87497d..338237bccb 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -419,6 +419,8 @@ class node_constants std::chrono::seconds peer_interval; std::chrono::minutes unchecked_cleaning_interval; std::chrono::milliseconds process_confirmed_interval; + /** Maximum number of peers per IP */ + size_t max_peers_per_ip; /** The maximum amount of samples for a 2 week period on live or 3 days on beta */ uint64_t max_weight_samples; diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index 253cdd0ea6..9ee55a9509 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -187,7 +187,9 @@ TEST (store, load) // ulimit -n increasing may be required TEST (node, fork_storm) { - nano::system system (64); + nano::node_flags flags; + flags.disable_max_peers_per_ip = true; + nano::system system (64, nano::transport::transport_type::tcp, flags); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); auto previous (system.nodes[0]->latest (nano::test_genesis_key.pub)); auto balance (system.nodes[0]->balance (nano::test_genesis_key.pub)); From 90b754ca4898645ae0af4e3eef1747305feec767 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Tue, 10 Mar 2020 16:15:40 +0000 Subject: [PATCH 33/43] Fix wrong number of representatives in confirmation solicitor (#2648) --- nano/node/active_transactions.cpp | 2 +- nano/node/repcrawler.cpp | 18 ++++-------------- nano/node/repcrawler.hpp | 8 ++++---- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 3baf4e3773..ce3fb047b2 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -227,7 +227,7 @@ void nano::active_transactions::request_confirm (nano::unique_lock & // Only representatives ready to receive batched confirm_req nano::confirmation_solicitor solicitor (node.network, node.network_params.network); - solicitor.prepare (node.rep_crawler.representatives (node.network_params.protocol.tcp_realtime_protocol_version_min)); + solicitor.prepare (node.rep_crawler.principal_representatives (std::numeric_limits::max (), node.network_params.protocol.tcp_realtime_protocol_version_min)); auto election_ttl_cutoff_l (std::chrono::steady_clock::now () - election_time_to_live); auto roots_size_l (roots.size ()); diff --git a/nano/node/repcrawler.cpp b/nano/node/repcrawler.cpp index d1af4a7663..edc87d24b0 100644 --- a/nano/node/repcrawler.cpp +++ b/nano/node/repcrawler.cpp @@ -317,14 +317,14 @@ void nano::rep_crawler::update_weights () } } -std::vector nano::rep_crawler::representatives (size_t count_a, boost::optional const & opt_version_min_a) +std::vector nano::rep_crawler::representatives (size_t count_a, nano::uint128_t const weight_a, boost::optional const & opt_version_min_a) { auto version_min (opt_version_min_a.value_or (node.network_params.protocol.protocol_version_min)); std::vector result; nano::lock_guard lock (probable_reps_mutex); for (auto i (probable_reps.get ().begin ()), n (probable_reps.get ().end ()); i != n && result.size () < count_a; ++i) { - if (!i->weight.is_zero () && i->channel->get_network_version () >= version_min) + if (i->weight > weight_a && i->channel->get_network_version () >= version_min) { result.push_back (*i); } @@ -332,19 +332,9 @@ std::vector nano::rep_crawler::representatives (size_t cou return result; } -std::vector nano::rep_crawler::principal_representatives (size_t count_a) +std::vector nano::rep_crawler::principal_representatives (size_t count_a, boost::optional const & opt_version_min_a) { - std::vector result; - auto minimum = node.minimum_principal_weight (); - nano::lock_guard lock (probable_reps_mutex); - for (auto i (probable_reps.get ().begin ()), n (probable_reps.get ().end ()); i != n && result.size () < count_a; ++i) - { - if (i->weight > minimum) - { - result.push_back (*i); - } - } - return result; + return representatives (count_a, node.minimum_principal_weight (), opt_version_min_a); } std::vector> nano::rep_crawler::representative_endpoints (size_t count_a) diff --git a/nano/node/repcrawler.hpp b/nano/node/repcrawler.hpp index 06f1491037..5454b4f024 100644 --- a/nano/node/repcrawler.hpp +++ b/nano/node/repcrawler.hpp @@ -102,11 +102,11 @@ class rep_crawler final /** Get total available weight from representatives */ nano::uint128_t total_weight () const; - /** Request a list of the top \p count_a known representatives in descending order of weight, optionally with a minimum version \p opt_version_min_a */ - std::vector representatives (size_t count_a = std::numeric_limits::max (), boost::optional const & opt_version_min_a = boost::none); + /** Request a list of the top \p count_a known representatives in descending order of weight, with at least \p weight_a voting weight, and optionally with a minimum version \p opt_version_min_a */ + std::vector representatives (size_t count_a = std::numeric_limits::max (), nano::uint128_t const weight_a = 0, boost::optional const & opt_version_min_a = boost::none); - /** Request a list of the top \p count_a known principal representatives in descending order of weight. */ - std::vector principal_representatives (size_t count_a = std::numeric_limits::max ()); + /** Request a list of the top \p count_a known principal representatives in descending order of weight, optionally with a minimum version \p opt_version_min_a */ + std::vector principal_representatives (size_t count_a = std::numeric_limits::max (), boost::optional const & opt_version_min_a = boost::none); /** Request a list of the top \p count_a known representative endpoints. */ std::vector> representative_endpoints (size_t count_a); From a1eaad592e3c49a37e7b9d97bf7fce770ba1ce0c Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Tue, 10 Mar 2020 17:10:13 +0000 Subject: [PATCH 34/43] Bandwidth considerations following election refactor (#2646) * Bandwidth considerations with election refactor - Block broadcasting is used as a backup mechanism, now only done after and every 20 seconds - Reduced send_confirm_req period from 15 to 5 seconds, as it is the primary mechanism and only targets representatives that haven't voted yet - Increased time to activate dependencies ensuring at least one block broadcast is performed * Initial flood for elections created via node::block_confirm * Revert "Initial flood for elections created via node::block_confirm" Following Colin review, this reverts commit bdb200ab09ad3e2c0cb7b709045599c374c8695f. --- nano/node/election.cpp | 13 +++---------- nano/node/election.hpp | 5 ++--- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 8481bb0456..09ae2f2b2b 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -152,7 +152,7 @@ bool nano::election::state_change (nano::election::state_t expected_a, nano::ele void nano::election::send_confirm_req (nano::confirmation_solicitor & solicitor_a) { - if (last_req + std::chrono::seconds (15) < std::chrono::steady_clock::now ()) + if (base_latency () * 5 < std::chrono::steady_clock::now () - last_req) { if (!solicitor_a.add (*this)) { @@ -181,14 +181,7 @@ void nano::election::transition_active () void nano::election::transition_active_impl () { - if (!state_change (nano::election::state_t::idle, nano::election::state_t::active)) - { - if (base_latency () * 5 < std::chrono::steady_clock::now () - last_block) - { - last_block = std::chrono::steady_clock::now (); - node.network.flood_block (status.winner); - } - } + state_change (nano::election::state_t::idle, nano::election::state_t::active); } bool nano::election::idle () const @@ -247,7 +240,7 @@ void nano::election::activate_dependencies () void nano::election::broadcast_block (nano::confirmation_solicitor & solicitor_a) { - if (base_latency () * 5 < std::chrono::steady_clock::now () - last_block) + if (base_latency () * 20 < std::chrono::steady_clock::now () - last_block) { if (!solicitor_a.broadcast (*this)) { diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 6ea73c11ce..16b500b987 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -47,7 +47,7 @@ class election final : public std::enable_shared_from_this expired_unconfirmed }; static int constexpr passive_duration_factor = 5; - static int constexpr active_duration_factor = 20; + static int constexpr active_duration_factor = 30; static int constexpr confirmed_duration_factor = 10; static int constexpr confirmed_duration_factor_saturated = 1; std::atomic state_m = { state_t::idle }; @@ -55,8 +55,7 @@ class election final : public std::enable_shared_from_this // Protects state_start, last_vote and last_block std::mutex timepoints_mutex; std::chrono::steady_clock::time_point state_start = { std::chrono::steady_clock::now () }; - std::chrono::steady_clock::time_point last_vote = { std::chrono::steady_clock::time_point () }; - std::chrono::steady_clock::time_point last_block = { std::chrono::steady_clock::time_point () }; + std::chrono::steady_clock::time_point last_block = { std::chrono::steady_clock::now () }; std::chrono::steady_clock::time_point last_req = { std::chrono::steady_clock::time_point () }; bool valid_change (nano::election::state_t, nano::election::state_t) const; From c1eb776bc70346090b4efcdb3654a3cbbc9737e3 Mon Sep 17 00:00:00 2001 From: Wesley Shillingford Date: Tue, 10 Mar 2020 19:47:58 +0000 Subject: [PATCH 35/43] Telemetry results not correctly using cache timeout (#2650) --- nano/core_test/node_telemetry.cpp | 2 +- nano/node/telemetry.cpp | 109 ++++++++++++++++++++++-------- nano/node/telemetry.hpp | 14 +++- nano/slow_test/node.cpp | 2 +- 4 files changed, 95 insertions(+), 32 deletions(-) diff --git a/nano/core_test/node_telemetry.cpp b/nano/core_test/node_telemetry.cpp index 7e4152eb2c..bff5609f4e 100644 --- a/nano/core_test/node_telemetry.cpp +++ b/nano/core_test/node_telemetry.cpp @@ -598,7 +598,7 @@ TEST (node_telemetry, all_peers_use_single_request_cache) ASSERT_EQ (1, node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::in)); ASSERT_EQ (0, node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_req, nano::stat::dir::out)); - std::this_thread::sleep_for (nano::telemetry_cache_cutoffs::test); + std::this_thread::sleep_for (node_server->telemetry->cache_plus_buffer_cutoff_time ()); // Should be empty responses = node_client->telemetry->get_metrics (); diff --git a/nano/node/telemetry.cpp b/nano/node/telemetry.cpp index efbd39a413..bb30a9a226 100644 --- a/nano/node/telemetry.cpp +++ b/nano/node/telemetry.cpp @@ -13,6 +13,8 @@ #include #include +using namespace std::chrono_literals; + nano::telemetry::telemetry (nano::network & network_a, nano::alarm & alarm_a, nano::worker & worker_a, bool disable_ongoing_requests_a) : network (network_a), alarm (alarm_a), @@ -56,34 +58,69 @@ void nano::telemetry::set (nano::telemetry_data const & telemetry_data_a, nano:: } } +std::chrono::milliseconds nano::telemetry::cache_plus_buffer_cutoff_time () const +{ + // This include the waiting time for the response as well as a buffer (1 second) waiting for the alarm operation to be scheduled and completed + return cache_cutoff + response_time_cutoff + 1s; +} + +bool nano::telemetry::within_cache_plus_buffer_cutoff (telemetry_info const & telemetry_info) const +{ + auto is_within = (telemetry_info.last_response + cache_plus_buffer_cutoff_time ()) >= std::chrono::steady_clock::now (); + return !telemetry_info.awaiting_first_response () && is_within; +} + bool nano::telemetry::within_cache_cutoff (telemetry_info const & telemetry_info) const { - auto is_within = (telemetry_info.last_request + nano::telemetry_cache_cutoffs::network_to_time (network_params.network)) >= std::chrono::steady_clock::now (); + auto is_within = (telemetry_info.last_response + cache_cutoff) >= std::chrono::steady_clock::now (); return !telemetry_info.awaiting_first_response () && is_within; } void nano::telemetry::ongoing_req_all_peers (std::chrono::milliseconds next_request_interval) { - // Check if any peers actually need requesting alarm.add (std::chrono::steady_clock::now () + next_request_interval, [this_w = std::weak_ptr (shared_from_this ())]() { if (auto this_l = this_w.lock ()) { // Check if there are any peers which are in the peers list which haven't been request, or any which are below or equal to the cache cutoff time if (!this_l->stopped) { - auto peers = this_l->network.list (std::numeric_limits::max (), this_l->network_params.protocol.telemetry_protocol_version_min, false); + class tag_channel + { + }; + struct channel_wrapper { - std::unordered_set temp_peers; - std::transform (peers.begin (), peers.end (), std::inserter (temp_peers, temp_peers.end ()), [](auto const & channel_a) { - return channel_a->get_endpoint (); - }); + std::shared_ptr channel; + channel_wrapper (std::shared_ptr const & channel_a) : + channel (channel_a) + { + } + nano::endpoint endpoint () const + { + return channel->get_endpoint (); + } + }; + namespace mi = boost::multi_index; + boost::multi_index_container, + mi::const_mem_fun>, + mi::hashed_unique, + mi::member, &channel_wrapper::channel>>>> + peers; + + { + auto temp_peers = this_l->network.list (std::numeric_limits::max (), this_l->network_params.protocol.telemetry_protocol_version_min, false); + peers.insert (temp_peers.begin (), temp_peers.end ()); + } + + { // Cleanup any stale saved telemetry data for non-existent peers nano::lock_guard guard (this_l->mutex); for (auto it = this_l->recent_or_initial_request_telemetry_data.begin (); it != this_l->recent_or_initial_request_telemetry_data.end ();) { - if (!it->undergoing_request && !this_l->within_cache_cutoff (*it) && temp_peers.count (it->endpoint) == 0) + if (!it->undergoing_request && !this_l->within_cache_cutoff (*it) && peers.count (it->endpoint) == 0) { it = this_l->recent_or_initial_request_telemetry_data.erase (it); } @@ -93,32 +130,48 @@ void nano::telemetry::ongoing_req_all_peers (std::chrono::milliseconds next_requ } } - peers.erase (std::remove_if (peers.begin (), peers.end (), [&this_l](auto const & channel_a) { - // Remove from peers list if it exists and is within the cache cutoff - auto it = this_l->recent_or_initial_request_telemetry_data.find (channel_a->get_endpoint ()); - return it != this_l->recent_or_initial_request_telemetry_data.end () && this_l->within_cache_cutoff (*it); - }), - peers.end ()); + // Remove from peers list if it exists and is within the cache cutoff + for (auto peers_it = peers.begin (); peers_it != peers.end ();) + { + auto it = this_l->recent_or_initial_request_telemetry_data.find (peers_it->endpoint ()); + if (it != this_l->recent_or_initial_request_telemetry_data.cend () && this_l->within_cache_cutoff (*it)) + { + peers_it = peers.erase (peers_it); + } + else + { + ++peers_it; + } + } } // Request data from new peers, or ones which are out of date - for (auto const & peer : peers) + for (auto const & peer : boost::make_iterator_range (peers)) { - this_l->get_metrics_single_peer_async (peer, [](auto const &) { + this_l->get_metrics_single_peer_async (peer.channel, [](auto const &) { // Intentionally empty, just using to refresh the cache }); } nano::lock_guard guard (this_l->mutex); - long long next_round = std::chrono::duration_cast (nano::telemetry_cache_cutoffs::network_to_time (this_l->network_params.network)).count (); + long long next_round = std::chrono::duration_cast (this_l->cache_cutoff + this_l->response_time_cutoff).count (); if (!this_l->recent_or_initial_request_telemetry_data.empty ()) { // Use the default request time unless a telemetry request cache expires sooner - auto const cache_cutoff = nano::telemetry_cache_cutoffs::network_to_time (this_l->network_params.network); - auto const last_request = this_l->recent_or_initial_request_telemetry_data.get ().begin ()->last_request; - if (std::chrono::steady_clock::now () > last_request + cache_cutoff) + // Find the closest time with doesn't + auto range = boost::make_iterator_range (this_l->recent_or_initial_request_telemetry_data.get ()); + for (auto i : range) { - next_round = std::min (next_round, std::chrono::duration_cast (std::chrono::steady_clock::now () - (last_request + cache_cutoff)).count ()); + if (peers.count (i.endpoint) == 0) + { + auto const last_response = i.last_response; + auto now = std::chrono::steady_clock::now (); + if (now > last_response + this_l->cache_cutoff) + { + next_round = std::min (next_round, std::chrono::duration_cast (now - (last_response + this_l->cache_cutoff)).count ()); + } + break; + } } } @@ -134,10 +187,9 @@ std::unordered_map nano::telemetry::get_me nano::lock_guard guard (mutex); auto range = boost::make_iterator_range (recent_or_initial_request_telemetry_data); - // clang-format off nano::transform_if (range.begin (), range.end (), std::inserter (telemetry_data, telemetry_data.end ()), - [this](auto const & telemetry_info) { return this->within_cache_cutoff (telemetry_info); }, + [this](auto const & telemetry_info) { return this->within_cache_plus_buffer_cutoff (telemetry_info); }, [](auto const & telemetry_info) { return std::pair{ telemetry_info.endpoint, telemetry_info.data }; }); // clang-format on @@ -195,7 +247,6 @@ void nano::telemetry::get_metrics_single_peer_async (std::shared_ptrendpoint].push_back (callback_a); @@ -271,8 +322,12 @@ void nano::telemetry::fire_request_message (std::shared_ptr 0) + auto it = recent_or_initial_request_telemetry_data.find (endpoint_a); + if (it != recent_or_initial_request_telemetry_data.end ()) { + recent_or_initial_request_telemetry_data.modify (it, [](nano::telemetry_info & telemetry_info_a) { + telemetry_info_a.last_response = std::chrono::steady_clock::now (); + }); if (error_a) { recent_or_initial_request_telemetry_data.erase (endpoint_a); @@ -328,10 +383,10 @@ size_t nano::telemetry::telemetry_data_size () return recent_or_initial_request_telemetry_data.size (); } -nano::telemetry_info::telemetry_info (nano::endpoint const & endpoint_a, nano::telemetry_data const & data_a, std::chrono::steady_clock::time_point last_request_a, bool undergoing_request_a) : +nano::telemetry_info::telemetry_info (nano::endpoint const & endpoint_a, nano::telemetry_data const & data_a, std::chrono::steady_clock::time_point last_response_a, bool undergoing_request_a) : endpoint (endpoint_a), data (data_a), -last_request (last_request_a), +last_response (last_response_a), undergoing_request (undergoing_request_a) { } diff --git a/nano/node/telemetry.hpp b/nano/node/telemetry.hpp index 1f704af9b3..ebf9d4336c 100644 --- a/nano/node/telemetry.hpp +++ b/nano/node/telemetry.hpp @@ -38,12 +38,12 @@ class telemetry_info final { public: telemetry_info () = default; - telemetry_info (nano::endpoint const & endpoint, nano::telemetry_data const & data, std::chrono::steady_clock::time_point last_request, bool undergoing_request); + telemetry_info (nano::endpoint const & endpoint, nano::telemetry_data const & data, std::chrono::steady_clock::time_point last_response, bool undergoing_request); bool awaiting_first_response () const; nano::endpoint endpoint; nano::telemetry_data data; - std::chrono::steady_clock::time_point last_request; + std::chrono::steady_clock::time_point last_response; bool undergoing_request{ false }; uint64_t round{ 0 }; }; @@ -87,6 +87,11 @@ class telemetry : public std::enable_shared_from_this */ size_t telemetry_data_size (); + /* + * Returns the time for the cache, response and a small buffer for alarm operations to be scheduled and completed + */ + std::chrono::milliseconds cache_plus_buffer_cutoff_time () const; + private: class tag_endpoint { @@ -111,11 +116,13 @@ class telemetry : public std::enable_shared_from_this mi::hashed_unique, mi::member>, mi::ordered_non_unique, - mi::member>>> recent_or_initial_request_telemetry_data; + mi::member>>> recent_or_initial_request_telemetry_data; // clang-format on // Anything older than this requires requesting metrics from other nodes. std::chrono::seconds const cache_cutoff{ nano::telemetry_cache_cutoffs::network_to_time (network_params.network) }; + + // The maximum time spent waiting for a response to a telemetry request std::chrono::seconds const response_time_cutoff{ is_sanitizer_build || nano::running_within_valgrind () ? 6 : 3 }; std::unordered_map>> callbacks; @@ -128,6 +135,7 @@ class telemetry : public std::enable_shared_from_this void invoke_callbacks (nano::endpoint const &, bool); bool within_cache_cutoff (nano::telemetry_info const &) const; + bool within_cache_plus_buffer_cutoff (telemetry_info const & telemetry_info) const; friend std::unique_ptr collect_container_info (telemetry & telemetry, const std::string & name); }; diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index 9ee55a9509..d794c1261f 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -906,7 +906,7 @@ TEST (node_telemetry, ongoing_requests) // Wait till the next ongoing will be called, and add a 1s buffer for the actual processing auto time = std::chrono::steady_clock::now (); - while (std::chrono::steady_clock::now () < (time + nano::telemetry_cache_cutoffs::test + 1s)) + while (std::chrono::steady_clock::now () < (time + node_client->telemetry->cache_plus_buffer_cutoff_time () + 1s)) { ASSERT_NO_ERROR (system.poll ()); } From 2ca651f54b4c11a93482117ad1cf1f6e7c426708 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Tue, 10 Mar 2020 19:56:54 +0000 Subject: [PATCH 36/43] Increase active elections capacity with periodic full checks (#2641) * Confirmed to expiry fixed at 5 seconds * Only increase the election counter for unconfirmed elections * Increase default active elections size to 50k but only perform a full check periodically * Add timing logging for large number of elections (every 5 seconds max) * Adjust timing logging for block processor * Tune search_frontiers such that therre are no delays in aggressive mode but also not too many added * Add erroneously removed mutex lock in https://github.com/nanocurrency/nano-node/pull/2619 * Feedback on variable name * Empty commit to fix actions * Tweak blockprocessor logging to resemble previous behavior with disabled timing logging --- nano/node/active_transactions.cpp | 33 +++++++++++++++++++++---------- nano/node/active_transactions.hpp | 4 ++++ nano/node/blockprocessor.cpp | 33 ++++++++----------------------- nano/node/blockprocessor.hpp | 2 +- nano/node/election.cpp | 5 ++--- nano/node/election.hpp | 5 ++--- nano/node/json_handler.cpp | 1 + nano/node/nodeconfig.hpp | 2 +- 8 files changed, 42 insertions(+), 43 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index ce3fb047b2..35f5a76d47 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -19,6 +19,7 @@ confirmation_height_processor (confirmation_height_processor_a), node (node_a), multipliers_cb (20, 1.), trended_active_difficulty (node_a.network_params.network.publish_threshold), +check_all_elections_period (node_a.network_params.network.is_test_network () ? 10ms : 5s), election_time_to_live (node_a.network_params.network.is_test_network () ? 0s : 2s), thread ([this]() { nano::thread_role::set (nano::thread_role::name::request_loop); @@ -61,11 +62,11 @@ void nano::active_transactions::search_frontiers (nano::transaction const & tran nano::unique_lock lk (mutex); auto check_time_exceeded = std::chrono::steady_clock::now () >= next_frontier_check; lk.unlock (); - auto max_elections = (node.config.active_elections_size / 20); + auto max_elections = 1000; auto low_active_elections = roots_size < max_elections; bool wallets_check_required = (!skip_wallets || !priority_wallet_cementable_frontiers.empty ()) && !agressive_mode; // Minimise dropping real-time transactions, set the number of frontiers added to a factor of the total number of active elections - auto max_active = node.config.active_elections_size / 5; + auto max_active = node.config.active_elections_size / 20; if (roots_size <= max_active && (check_time_exceeded || wallets_check_required || (!is_test_network && low_active_elections && agressive_mode))) { // When the number of active elections is low increase max number of elections for setting confirmation height. @@ -229,11 +230,12 @@ void nano::active_transactions::request_confirm (nano::unique_lock & nano::confirmation_solicitor solicitor (node.network, node.network_params.network); solicitor.prepare (node.rep_crawler.principal_representatives (std::numeric_limits::max (), node.network_params.protocol.tcp_realtime_protocol_version_min)); - auto election_ttl_cutoff_l (std::chrono::steady_clock::now () - election_time_to_live); - auto roots_size_l (roots.size ()); - bool saturated_l (roots_size_l > node.config.active_elections_size / 2); - auto & sorted_roots_l = roots.get (); - size_t count_l{ 0 }; + auto & sorted_roots_l (roots.get ()); + auto const election_ttl_cutoff_l (std::chrono::steady_clock::now () - election_time_to_live); + bool const check_all_elections_l (std::chrono::steady_clock::now () - last_check_all_elections > check_all_elections_period); + size_t const this_loop_target_l (check_all_elections_l ? sorted_roots_l.size () : node.config.active_elections_size / 10); + size_t unconfirmed_count_l (0); + nano::timer elapsed (nano::timer_state::started); /* * Loop through active elections in descending order of proof-of-work difficulty, requesting confirmation @@ -242,11 +244,12 @@ void nano::active_transactions::request_confirm (nano::unique_lock & * Elections extending the soft config.active_elections_size limit are flushed after a certain time-to-live cutoff * Flushed elections are later re-activated via frontier confirmation */ - for (auto i = sorted_roots_l.begin (), n = sorted_roots_l.end (); i != n; ++count_l) + for (auto i = sorted_roots_l.begin (), n = sorted_roots_l.end (); i != n && unconfirmed_count_l < this_loop_target_l;) { auto & election_l (i->election); - bool const overflow_l (count_l >= node.config.active_elections_size && election_l->election_start < election_ttl_cutoff_l && !node.wallets.watcher->is_watched (i->root)); - if (overflow_l || election_l->transition_time (solicitor, saturated_l)) + unconfirmed_count_l += !election_l->confirmed (); + bool const overflow_l (unconfirmed_count_l > node.config.active_elections_size && election_l->election_start < election_ttl_cutoff_l && !node.wallets.watcher->is_watched (i->root)); + if (overflow_l || election_l->transition_time (solicitor)) { election_l->clear_blocks (); i = sorted_roots_l.erase (i); @@ -259,6 +262,16 @@ void nano::active_transactions::request_confirm (nano::unique_lock & lock_a.unlock (); solicitor.flush (); lock_a.lock (); + + // This is updated after the loop to ensure slow machines don't do the full check often + if (check_all_elections_l) + { + last_check_all_elections = std::chrono::steady_clock::now (); + if (node.config.logging.timing_logging () && this_loop_target_l > node.config.active_elections_size / 10) + { + node.logger.try_log (boost::str (boost::format ("Processed %1% elections (%2% were already confirmed) in %3% %4%") % this_loop_target_l % (this_loop_target_l - unconfirmed_count_l) % elapsed.value ().count () % elapsed.unit ())); + } + } } void nano::active_transactions::request_loop () diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index d1ffcb3d09..fee740091a 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -155,6 +155,10 @@ class active_transactions final bool started{ false }; std::atomic stopped{ false }; + // Periodically check all elections + std::chrono::milliseconds const check_all_elections_period; + std::chrono::steady_clock::time_point last_check_all_elections{}; + // Maximum time an election can be kept active if it is extending the container std::chrono::seconds const election_time_to_live; diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 5447645f39..583a446624 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -127,13 +127,13 @@ void nano::block_processor::process_blocks () } } -bool nano::block_processor::should_log (bool first_time) +bool nano::block_processor::should_log () { auto result (false); auto now (std::chrono::steady_clock::now ()); - if (first_time || next_log < now) + if (next_log < now) { - next_log = now + std::chrono::seconds (15); + next_log = now + (node.config.logging.timing_logging () ? std::chrono::seconds (2) : std::chrono::seconds (15)); result = true; } return result; @@ -238,9 +238,9 @@ void nano::block_processor::verify_state_blocks (nano::unique_lock & } items.pop_front (); } - if (node.config.logging.timing_logging ()) + if (node.config.logging.timing_logging () && timer_l.stop () > std::chrono::milliseconds (10)) { - node.logger.try_log (boost::str (boost::format ("Batch verified %1% state blocks in %2% %3%") % size % timer_l.stop ().count () % timer_l.unit ())); + node.logger.try_log (boost::str (boost::format ("Batch verified %1% state blocks in %2% %3%") % size % timer_l.value ().count () % timer_l.unit ())); } } else @@ -272,29 +272,12 @@ void nano::block_processor::process_batch (nano::unique_lock & lock_ timer_l.restart (); lock_a.lock (); // Processing blocks - auto first_time (true); unsigned number_of_blocks_processed (0), number_of_forced_processed (0); while ((!blocks.empty () || !forced.empty ()) && (timer_l.before_deadline (node.config.block_processor_batch_max_time) || (number_of_blocks_processed < node.flags.block_processor_batch_size)) && !awaiting_write) { - auto log_this_record (false); - if (node.config.logging.timing_logging ()) - { - if (should_log (first_time)) - { - log_this_record = true; - } - } - else - { - if (((blocks.size () + state_blocks.size () + forced.size ()) > 64 && should_log (false))) - { - log_this_record = true; - } - } - + bool log_this_record = (blocks.size () + state_blocks.size () + forced.size () > 64) && should_log (); if (log_this_record) { - first_time = false; node.logger.always_log (boost::str (boost::format ("%1% blocks (+ %2% state blocks) (+ %3% forced) in processing queue") % blocks.size () % state_blocks.size () % forced.size ())); } nano::unchecked_info info; @@ -358,9 +341,9 @@ void nano::block_processor::process_batch (nano::unique_lock & lock_ awaiting_write = false; lock_a.unlock (); - if (node.config.logging.timing_logging () && number_of_blocks_processed != 0) + if (node.config.logging.timing_logging () && number_of_blocks_processed != 0 && timer_l.stop () > std::chrono::milliseconds (10)) { - node.logger.always_log (boost::str (boost::format ("Processed %1% blocks (%2% blocks were forced) in %3% %4%") % number_of_blocks_processed % number_of_forced_processed % timer_l.stop ().count () % timer_l.unit ())); + node.logger.always_log (boost::str (boost::format ("Processed %1% blocks (%2% blocks were forced) in %3% %4%") % number_of_blocks_processed % number_of_forced_processed % timer_l.value ().count () % timer_l.unit ())); } } diff --git a/nano/node/blockprocessor.hpp b/nano/node/blockprocessor.hpp index 1395400d00..eb4416e8f9 100644 --- a/nano/node/blockprocessor.hpp +++ b/nano/node/blockprocessor.hpp @@ -39,7 +39,7 @@ class block_processor final void add (std::shared_ptr, uint64_t = 0); void force (std::shared_ptr); void wait_write (); - bool should_log (bool); + bool should_log (); bool have_blocks (); void process_blocks (); nano::process_return process_one (nano::write_transaction const &, nano::unchecked_info, const bool = false, const bool = false); diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 09ae2f2b2b..e85892340d 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -9,7 +9,6 @@ using namespace std::chrono; int constexpr nano::election::passive_duration_factor; int constexpr nano::election::active_duration_factor; int constexpr nano::election::confirmed_duration_factor; -int constexpr nano::election::confirmed_duration_factor_saturated; std::chrono::milliseconds nano::election::base_latency () const { @@ -249,7 +248,7 @@ void nano::election::broadcast_block (nano::confirmation_solicitor & solicitor_a } } -bool nano::election::transition_time (nano::confirmation_solicitor & solicitor_a, bool const saturated_a) +bool nano::election::transition_time (nano::confirmation_solicitor & solicitor_a) { debug_assert (!node.active.mutex.try_lock ()); nano::unique_lock lock (timepoints_mutex); @@ -282,7 +281,7 @@ bool nano::election::transition_time (nano::confirmation_solicitor & solicitor_a send_confirm_req (solicitor_a); break; case nano::election::state_t::confirmed: - if (base_latency () * (saturated_a ? confirmed_duration_factor_saturated : confirmed_duration_factor) < std::chrono::steady_clock::now () - state_start) + if (base_latency () * confirmed_duration_factor < std::chrono::steady_clock::now () - state_start) { result = true; state_change (nano::election::state_t::confirmed, nano::election::state_t::expired_confirmed); diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 16b500b987..117d70ddfd 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -48,8 +48,7 @@ class election final : public std::enable_shared_from_this }; static int constexpr passive_duration_factor = 5; static int constexpr active_duration_factor = 30; - static int constexpr confirmed_duration_factor = 10; - static int constexpr confirmed_duration_factor_saturated = 1; + static int constexpr confirmed_duration_factor = 5; std::atomic state_m = { state_t::idle }; // Protects state_start, last_vote and last_block @@ -82,7 +81,7 @@ class election final : public std::enable_shared_from_this void insert_inactive_votes_cache (nano::block_hash const &); public: // State transitions - bool transition_time (nano::confirmation_solicitor &, bool const saturated); + bool transition_time (nano::confirmation_solicitor &); void transition_passive (); void transition_active (); diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index e05e32df21..0a34ee2578 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1851,6 +1851,7 @@ void nano::json_handler::confirmation_info () if (!root.decode_hex (root_text)) { auto election (node.active.election (root)); + nano::lock_guard guard (node.active.mutex); if (election != nullptr && !election->confirmed ()) { response_l.put ("announcements", std::to_string (election->confirmation_request_count)); diff --git a/nano/node/nodeconfig.hpp b/nano/node/nodeconfig.hpp index 2baee4d866..b611790559 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -82,7 +82,7 @@ class node_config /** Timeout for initiated async operations */ std::chrono::seconds tcp_io_timeout{ (network_params.network.is_test_network () && !is_sanitizer_build) ? std::chrono::seconds (5) : std::chrono::seconds (15) }; std::chrono::nanoseconds pow_sleep_interval{ 0 }; - size_t active_elections_size{ 10000 }; + size_t active_elections_size{ 50000 }; /** Default maximum incoming TCP connections, including realtime network & bootstrap */ unsigned tcp_incoming_connections_max{ 1024 }; bool use_memory_pools{ true }; From 7f728824220da98a43b9c5364fc6f6c90e5f79de Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Wed, 11 Mar 2020 10:40:32 +0000 Subject: [PATCH 37/43] Network duplicate filter for publish messages (#2643) * Filter duplicate publish messages before deserializing When a message is unique, the digest is saved and passed around to network processing, which may drop it if the block processor is full. Cleaning up a long unchecked block erases its digest from the publish filter. The blocks_filter has been removed due to redundancy. The size of this filter is 256k, which uses about 4MB. * Batch erase in unchecked_cleanup due to a potentially large list --- nano/core_test/active_transactions.cpp | 51 ++++++++++++++++++++ nano/core_test/message_parser.cpp | 15 ++++-- nano/core_test/network.cpp | 61 ++++++++++++++++++++++++ nano/core_test/node.cpp | 14 +++++- nano/lib/numbers.cpp | 7 ++- nano/lib/stats.cpp | 6 +++ nano/lib/stats.hpp | 8 +++- nano/node/active_transactions.cpp | 13 ++--- nano/node/active_transactions.hpp | 1 + nano/node/blockprocessor.cpp | 38 ++++----------- nano/node/blockprocessor.hpp | 2 - nano/node/bootstrap/bootstrap_server.cpp | 21 +++++--- nano/node/common.cpp | 26 +++++++--- nano/node/common.hpp | 12 +++-- nano/node/election.cpp | 16 ++++++- nano/node/election.hpp | 3 +- nano/node/network.cpp | 25 ++++++---- nano/node/network.hpp | 3 +- nano/node/node.cpp | 7 +-- nano/node/transport/udp.cpp | 17 ++++--- nano/secure/network_filter.cpp | 35 +++++++++++--- nano/secure/network_filter.hpp | 13 ++++- 22 files changed, 296 insertions(+), 98 deletions(-) diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 5be7c22de3..85b8eeb337 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -772,3 +772,54 @@ TEST (active_transactions, activate_dependencies) ASSERT_TRUE (node1->ledger.block_confirmed (node1->store.tx_begin_read (), block2->hash ())); ASSERT_TRUE (node2->ledger.block_confirmed (node2->store.tx_begin_read (), block2->hash ())); } + +namespace nano +{ +// Tests that blocks are correctly cleared from the duplicate filter for unconfirmed elections +TEST (active_transactions, dropped_cleanup) +{ + nano::system system; + nano::node_config node_config (nano::get_available_port (), system.logging); + node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + auto & node (*system.add_node (node_config)); + + nano::genesis genesis; + auto block = genesis.open; + + // Add to network filter to ensure proper cleanup after the election is dropped + std::vector block_bytes; + { + nano::vectorstream stream (block_bytes); + block->serialize (stream); + } + ASSERT_FALSE (node.network.publish_filter.apply (block_bytes.data (), block_bytes.size ())); + ASSERT_TRUE (node.network.publish_filter.apply (block_bytes.data (), block_bytes.size ())); + + auto election (node.active.insert (block).first); + ASSERT_NE (nullptr, election); + + // Not yet removed + ASSERT_TRUE (node.network.publish_filter.apply (block_bytes.data (), block_bytes.size ())); + + // Now simulate dropping the election, which performs a cleanup in the background using the node worker + ASSERT_FALSE (election->confirmed ()); + { + nano::lock_guard guard (node.active.mutex); + election->cleanup (); + } + + // Push a worker task to ensure the cleanup is already performed + std::atomic flag{ false }; + node.worker.push_task ([&flag]() { + flag = true; + }); + system.deadline_set (5s); + while (!flag) + { + ASSERT_NO_ERROR (system.poll ()); + } + + // The filter must have been cleared + ASSERT_FALSE (node.network.publish_filter.apply (block_bytes.data (), block_bytes.size ())); +} +} diff --git a/nano/core_test/message_parser.cpp b/nano/core_test/message_parser.cpp index f67b2fd6e7..e13540a053 100644 --- a/nano/core_test/message_parser.cpp +++ b/nano/core_test/message_parser.cpp @@ -63,9 +63,10 @@ TEST (message_parser, exact_confirm_ack_size) { nano::system system (1); test_visitor visitor; + nano::network_filter filter (1); nano::block_uniquer block_uniquer; nano::vote_uniquer vote_uniquer (block_uniquer); - nano::message_parser parser (block_uniquer, vote_uniquer, visitor, system.work); + nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work); auto block (std::make_shared (1, 1, 2, nano::keypair ().prv, 4, *system.work.generate (nano::root (1)))); auto vote (std::make_shared (0, nano::keypair ().prv, 0, std::move (block))); nano::confirm_ack message (vote); @@ -96,9 +97,10 @@ TEST (message_parser, exact_confirm_req_size) { nano::system system (1); test_visitor visitor; + nano::network_filter filter (1); nano::block_uniquer block_uniquer; nano::vote_uniquer vote_uniquer (block_uniquer); - nano::message_parser parser (block_uniquer, vote_uniquer, visitor, system.work); + nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work); auto block (std::make_shared (1, 1, 2, nano::keypair ().prv, 4, *system.work.generate (nano::root (1)))); nano::confirm_req message (std::move (block)); std::vector bytes; @@ -128,9 +130,10 @@ TEST (message_parser, exact_confirm_req_hash_size) { nano::system system (1); test_visitor visitor; + nano::network_filter filter (1); nano::block_uniquer block_uniquer; nano::vote_uniquer vote_uniquer (block_uniquer); - nano::message_parser parser (block_uniquer, vote_uniquer, visitor, system.work); + nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work); nano::send_block block (1, 1, 2, nano::keypair ().prv, 4, *system.work.generate (nano::root (1))); nano::confirm_req message (block.hash (), block.root ()); std::vector bytes; @@ -160,9 +163,10 @@ TEST (message_parser, exact_publish_size) { nano::system system (1); test_visitor visitor; + nano::network_filter filter (1); nano::block_uniquer block_uniquer; nano::vote_uniquer vote_uniquer (block_uniquer); - nano::message_parser parser (block_uniquer, vote_uniquer, visitor, system.work); + nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work); auto block (std::make_shared (1, 1, 2, nano::keypair ().prv, 4, *system.work.generate (nano::root (1)))); nano::publish message (std::move (block)); std::vector bytes; @@ -192,9 +196,10 @@ TEST (message_parser, exact_keepalive_size) { nano::system system (1); test_visitor visitor; + nano::network_filter filter (1); nano::block_uniquer block_uniquer; nano::vote_uniquer vote_uniquer (block_uniquer); - nano::message_parser parser (block_uniquer, vote_uniquer, visitor, system.work); + nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work); nano::keepalive message; std::vector bytes; { diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index b480ff23b9..92535152b0 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -946,6 +946,67 @@ TEST (network, peer_max_tcp_attempts) ASSERT_TRUE (node->network.tcp_channels.reachout (nano::endpoint (node->network.endpoint ().address (), nano::get_available_port ()))); } +TEST (network, duplicate_detection) +{ + nano::system system; + nano::node_flags node_flags; + node_flags.disable_udp = false; + auto & node0 (*system.add_node (node_flags)); + auto & node1 (*system.add_node (node_flags)); + auto udp_channel (std::make_shared (node0.network.udp_channels, node1.network.endpoint (), node1.network_params.protocol.protocol_version)); + nano::genesis genesis; + nano::publish publish (genesis.open); + + // Publish duplicate detection through UDP + ASSERT_EQ (0, node1.stats.count (nano::stat::type::filter, nano::stat::detail::duplicate_publish)); + udp_channel->send (publish); + udp_channel->send (publish); + system.deadline_set (2s); + while (node1.stats.count (nano::stat::type::filter, nano::stat::detail::duplicate_publish) < 1) + { + ASSERT_NO_ERROR (system.poll ()); + } + + // Publish duplicate detection through TCP + auto tcp_channel (node0.network.tcp_channels.find_channel (nano::transport::map_endpoint_to_tcp (node1.network.endpoint ()))); + ASSERT_EQ (1, node1.stats.count (nano::stat::type::filter, nano::stat::detail::duplicate_publish)); + tcp_channel->send (publish); + system.deadline_set (2s); + while (node1.stats.count (nano::stat::type::filter, nano::stat::detail::duplicate_publish) < 2) + { + ASSERT_NO_ERROR (system.poll ()); + } +} + +TEST (network, duplicate_revert_publish) +{ + nano::system system; + nano::node_flags node_flags; + node_flags.block_processor_full_size = 0; + auto & node (*system.add_node (node_flags)); + ASSERT_TRUE (node.block_processor.full ()); + nano::genesis genesis; + nano::publish publish (genesis.open); + std::vector bytes; + { + nano::vectorstream stream (bytes); + publish.block->serialize (stream); + } + // Add to the blocks filter + // Should be cleared when dropping due to a full block processor, as long as the message has the optional digest attached + // Test network.duplicate_detection ensures that the digest is attached when deserializing messages + nano::uint128_t digest; + ASSERT_FALSE (node.network.publish_filter.apply (bytes.data (), bytes.size (), &digest)); + ASSERT_TRUE (node.network.publish_filter.apply (bytes.data (), bytes.size ())); + auto channel (std::make_shared (node.network.udp_channels, node.network.endpoint (), node.network_params.protocol.protocol_version)); + ASSERT_EQ (0, publish.digest); + node.network.process_message (publish, channel); + ASSERT_TRUE (node.network.publish_filter.apply (bytes.data (), bytes.size ())); + publish.digest = digest; + node.network.process_message (publish, channel); + ASSERT_FALSE (node.network.publish_filter.apply (bytes.data (), bytes.size ())); +} + // The test must be completed in less than 1 second TEST (bandwidth_limiter, validate) { diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index df082522c4..ca2511b499 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -3217,7 +3217,7 @@ TEST (node, block_processor_full) { nano::system system; nano::node_flags node_flags; - node_flags.block_processor_full_size = 2; + node_flags.block_processor_full_size = 3; auto & node = *system.add_node (nano::node_config (nano::get_available_port (), system.logging), node_flags); nano::genesis genesis; auto send1 (std::make_shared (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::test_genesis_key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0)); @@ -3245,7 +3245,7 @@ TEST (node, block_processor_half_full) { nano::system system; nano::node_flags node_flags; - node_flags.block_processor_full_size = 4; + node_flags.block_processor_full_size = 6; auto & node = *system.add_node (nano::node_config (nano::get_available_port (), system.logging), node_flags); nano::genesis genesis; auto send1 (std::make_shared (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::test_genesis_key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0)); @@ -3421,6 +3421,14 @@ TEST (node, unchecked_cleanup) nano::keypair key; auto & node (*system.nodes[0]); auto open (std::make_shared (key.pub, 0, key.pub, 1, key.pub, key.prv, key.pub, *system.work.generate (key.pub))); + std::vector bytes; + { + nano::vectorstream stream (bytes); + open->serialize (stream); + } + // Add to the blocks filter + // Should be cleared after unchecked cleanup + ASSERT_FALSE (node.network.publish_filter.apply (bytes.data (), bytes.size ())); node.process_active (open); node.block_processor.flush (); node.config.unchecked_cutoff_time = std::chrono::seconds (2); @@ -3432,6 +3440,7 @@ TEST (node, unchecked_cleanup) } std::this_thread::sleep_for (std::chrono::seconds (1)); node.unchecked_cleanup (); + ASSERT_TRUE (node.network.publish_filter.apply (bytes.data (), bytes.size ())); { auto transaction (node.store.tx_begin_read ()); auto unchecked_count (node.store.unchecked_count (transaction)); @@ -3440,6 +3449,7 @@ TEST (node, unchecked_cleanup) } std::this_thread::sleep_for (std::chrono::seconds (2)); node.unchecked_cleanup (); + ASSERT_FALSE (node.network.publish_filter.apply (bytes.data (), bytes.size ())); { auto transaction (node.store.tx_begin_read ()); auto unchecked_count (node.store.unchecked_count (transaction)); diff --git a/nano/lib/numbers.cpp b/nano/lib/numbers.cpp index fba48e109a..542f4805f3 100644 --- a/nano/lib/numbers.cpp +++ b/nano/lib/numbers.cpp @@ -430,8 +430,11 @@ bool nano::validate_message (nano::public_key const & public_key, nano::uint256_ bool nano::validate_message_batch (const unsigned char ** m, size_t * mlen, const unsigned char ** pk, const unsigned char ** RS, size_t num, int * valid) { - bool result (0 == ed25519_sign_open_batch (m, mlen, pk, RS, num, valid)); - return result; + for (size_t i{ 0 }; i < num; ++i) + { + valid[i] = (0 == ed25519_sign_open (m[i], mlen[i], pk[i], RS[i])); + } + return true; } nano::uint128_union::uint128_union (std::string const & string_a) diff --git a/nano/lib/stats.cpp b/nano/lib/stats.cpp index dccbfeda17..4d2cab087b 100644 --- a/nano/lib/stats.cpp +++ b/nano/lib/stats.cpp @@ -444,6 +444,9 @@ std::string nano::stat::type_to_string (uint32_t key) case nano::stat::type::requests: res = "requests"; break; + case nano::stat::type::filter: + res = "filter"; + break; } return res; } @@ -694,6 +697,9 @@ std::string nano::stat::detail_to_string (uint32_t key) case nano::stat::detail::requests_unknown: res = "requests_unknown"; break; + case nano::stat::detail::duplicate_publish: + res = "duplicate_publish"; + break; } return res; } diff --git a/nano/lib/stats.hpp b/nano/lib/stats.hpp index 26f78afa5f..c559db493f 100644 --- a/nano/lib/stats.hpp +++ b/nano/lib/stats.hpp @@ -199,7 +199,8 @@ class stat final confirmation_height, drop, aggregator, - requests + requests, + filter, }; /** Optional detail type */ @@ -314,7 +315,10 @@ class stat final requests_generated_hashes, requests_cached_votes, requests_generated_votes, - requests_unknown + requests_unknown, + + // duplicate + duplicate_publish }; /** Direction of the stat. If the direction is irrelevant, use in */ diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 35f5a76d47..a2c8466959 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -251,7 +251,7 @@ void nano::active_transactions::request_confirm (nano::unique_lock & bool const overflow_l (unconfirmed_count_l > node.config.active_elections_size && election_l->election_start < election_ttl_cutoff_l && !node.wallets.watcher->is_watched (i->root)); if (overflow_l || election_l->transition_time (solicitor)) { - election_l->clear_blocks (); + election_l->cleanup (); i = sorted_roots_l.erase (i); } else @@ -803,7 +803,7 @@ void nano::active_transactions::erase (nano::block const & block_a) auto root_it (roots.get ().find (block_a.qualified_root ())); if (root_it != roots.get ().end ()) { - root_it->election->clear_blocks (); + root_it->election->cleanup (); root_it->election->adjust_dependent_difficulty (); roots.get ().erase (root_it); node.logger.try_log (boost::str (boost::format ("Election erased for block block %1% root %2%") % block_a.hash ().to_string () % block_a.root ().to_string ())); @@ -949,18 +949,13 @@ nano::inactive_cache_information nano::active_transactions::find_inactive_votes_ } else { - return nano::inactive_cache_information{ std::chrono::steady_clock::time_point{}, 0, std::vector{} }; + return nano::inactive_cache_information{}; } } void nano::active_transactions::erase_inactive_votes_cache (nano::block_hash const & hash_a) { - auto & inactive_by_hash (inactive_votes_cache.get ()); - auto existing (inactive_by_hash.find (hash_a)); - if (existing != inactive_by_hash.end ()) - { - inactive_by_hash.erase (existing); - } + inactive_votes_cache.get ().erase (hash_a); } bool nano::active_transactions::inactive_votes_bootstrap_check (std::vector const & voters_a, nano::block_hash const & hash_a, bool & confirmed_a) diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index fee740091a..e1decad227 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -199,6 +199,7 @@ class active_transactions final bool inactive_votes_bootstrap_check (std::vector const &, nano::block_hash const &, bool &); boost::thread thread; + friend class active_transactions_dropped_cleanup_Test; friend class confirmation_height_prioritize_frontiers_Test; friend class confirmation_height_prioritize_frontiers_overwrite_Test; friend std::unique_ptr collect_container_info (active_transactions &, const std::string &); diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 583a446624..1345acd901 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -42,7 +42,6 @@ void nano::block_processor::flush () { condition.wait (lock); } - blocks_filter.clear (); flushing = false; } @@ -54,12 +53,12 @@ size_t nano::block_processor::size () bool nano::block_processor::full () { - return size () > node.flags.block_processor_full_size; + return size () >= node.flags.block_processor_full_size; } bool nano::block_processor::half_full () { - return size () > node.flags.block_processor_full_size / 2; + return size () >= node.flags.block_processor_full_size / 2; } void nano::block_processor::add (std::shared_ptr block_a, uint64_t origination) @@ -72,20 +71,14 @@ void nano::block_processor::add (nano::unchecked_info const & info_a) { debug_assert (!nano::work_validate (*info_a.block)); { - auto hash (info_a.block->hash ()); - auto filter_hash (filter_item (hash, info_a.block->block_signature ())); nano::lock_guard lock (mutex); - if (blocks_filter.find (filter_hash) == blocks_filter.end ()) + if (info_a.verified == nano::signature_verification::unknown && (info_a.block->type () == nano::block_type::state || info_a.block->type () == nano::block_type::open || !info_a.account.is_zero ())) { - if (info_a.verified == nano::signature_verification::unknown && (info_a.block->type () == nano::block_type::state || info_a.block->type () == nano::block_type::open || !info_a.account.is_zero ())) - { - state_blocks.push_back (info_a); - } - else - { - blocks.push_back (info_a); - } - blocks_filter.insert (filter_hash); + state_blocks.push_back (info_a); + } + else + { + blocks.push_back (info_a); } } condition.notify_all (); @@ -233,7 +226,6 @@ void nano::block_processor::verify_state_blocks (nano::unique_lock & } else { - blocks_filter.erase (filter_item (hashes[i], blocks_signatures[i])); requeue_invalid (hashes[i], item); } items.pop_front (); @@ -288,7 +280,6 @@ void nano::block_processor::process_batch (nano::unique_lock & lock_ info = blocks.front (); blocks.pop_front (); hash = info.block->hash (); - blocks_filter.erase (filter_item (hash, info.block->block_signature ())); } else { @@ -551,19 +542,6 @@ void nano::block_processor::queue_unchecked (nano::write_transaction const & tra node.gap_cache.erase (hash_a); } -nano::block_hash nano::block_processor::filter_item (nano::block_hash const & hash_a, nano::signature const & signature_a) -{ - static nano::random_constants constants; - nano::block_hash result; - blake2b_state state; - blake2b_init (&state, sizeof (result.bytes)); - blake2b_update (&state, constants.not_an_account.bytes.data (), constants.not_an_account.bytes.size ()); - blake2b_update (&state, signature_a.bytes.data (), signature_a.bytes.size ()); - blake2b_update (&state, hash_a.bytes.data (), hash_a.bytes.size ()); - blake2b_final (&state, result.bytes.data (), sizeof (result.bytes)); - return result; -} - void nano::block_processor::requeue_invalid (nano::block_hash const & hash_a, nano::unchecked_info const & info_a) { debug_assert (hash_a == info_a.block->hash ()); diff --git a/nano/node/blockprocessor.hpp b/nano/node/blockprocessor.hpp index eb4416e8f9..34d87e1b05 100644 --- a/nano/node/blockprocessor.hpp +++ b/nano/node/blockprocessor.hpp @@ -62,8 +62,6 @@ class block_processor final std::deque state_blocks; std::deque blocks; std::deque> forced; - nano::block_hash filter_item (nano::block_hash const &, nano::signature const &); - std::unordered_set blocks_filter; nano::condition_variable condition; nano::node & node; nano::write_database_queue & write_database_queue; diff --git a/nano/node/bootstrap/bootstrap_server.cpp b/nano/node/bootstrap/bootstrap_server.cpp index e8c9cc1168..d4f98f8868 100644 --- a/nano/node/bootstrap/bootstrap_server.cpp +++ b/nano/node/bootstrap/bootstrap_server.cpp @@ -408,15 +408,24 @@ void nano::bootstrap_server::receive_publish_action (boost::system::error_code c { if (!ec) { - auto error (false); - nano::bufferstream stream (receive_buffer->data (), size_a); - auto request (std::make_unique (error, stream, header_a)); - if (!error) + nano::uint128_t digest; + if (!node->network.publish_filter.apply (receive_buffer->data (), size_a, &digest)) { - if (is_realtime_connection ()) + auto error (false); + nano::bufferstream stream (receive_buffer->data (), size_a); + auto request (std::make_unique (error, stream, header_a, digest)); + if (!error) { - add_request (std::unique_ptr (request.release ())); + if (is_realtime_connection ()) + { + add_request (std::unique_ptr (request.release ())); + } + receive (); } + } + else + { + node->stats.inc (nano::stat::type::filter, nano::stat::detail::duplicate_publish); receive (); } } diff --git a/nano/node/common.cpp b/nano/node/common.cpp index d57558897e..9b07ffc1b9 100644 --- a/nano/node/common.cpp +++ b/nano/node/common.cpp @@ -312,6 +312,10 @@ std::string nano::message_parser::status_string () { return "invalid_network"; } + case nano::message_parser::parse_status::duplicate_publish_message: + { + return "duplicate_publish_message"; + } } debug_assert (false); @@ -319,7 +323,8 @@ std::string nano::message_parser::status_string () return "[unknown parse_status]"; } -nano::message_parser::message_parser (nano::block_uniquer & block_uniquer_a, nano::vote_uniquer & vote_uniquer_a, nano::message_visitor & visitor_a, nano::work_pool & pool_a) : +nano::message_parser::message_parser (nano::network_filter & publish_filter_a, nano::block_uniquer & block_uniquer_a, nano::vote_uniquer & vote_uniquer_a, nano::message_visitor & visitor_a, nano::work_pool & pool_a) : +publish_filter (publish_filter_a), block_uniquer (block_uniquer_a), vote_uniquer (vote_uniquer_a), visitor (visitor_a), @@ -355,7 +360,15 @@ void nano::message_parser::deserialize_buffer (uint8_t const * buffer_a, size_t } case nano::message_type::publish: { - deserialize_publish (stream, header); + nano::uint128_t digest; + if (!publish_filter.apply (buffer_a + header.size, size_a - header.size, &digest)) + { + deserialize_publish (stream, header, digest); + } + else + { + status = parse_status::duplicate_publish_message; + } break; } case nano::message_type::confirm_req: @@ -412,10 +425,10 @@ void nano::message_parser::deserialize_keepalive (nano::stream & stream_a, nano: } } -void nano::message_parser::deserialize_publish (nano::stream & stream_a, nano::message_header const & header_a) +void nano::message_parser::deserialize_publish (nano::stream & stream_a, nano::message_header const & header_a, nano::uint128_t const & digest_a) { auto error (false); - nano::publish incoming (error, stream_a, header_a, &block_uniquer); + nano::publish incoming (error, stream_a, header_a, digest_a, &block_uniquer); if (!error && at_end (stream_a)) { if (!nano::work_validate (*incoming.block)) @@ -593,8 +606,9 @@ bool nano::keepalive::operator== (nano::keepalive const & other_a) const return peers == other_a.peers; } -nano::publish::publish (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a, nano::block_uniquer * uniquer_a) : -message (header_a) +nano::publish::publish (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a, nano::uint128_t const & digest_a, nano::block_uniquer * uniquer_a) : +message (header_a), +digest (digest_a) { if (!error_a) { diff --git a/nano/node/common.hpp b/nano/node/common.hpp index 4cf7273208..cd9612fda8 100644 --- a/nano/node/common.hpp +++ b/nano/node/common.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -247,18 +248,20 @@ class message_parser final invalid_telemetry_ack_message, outdated_version, invalid_magic, - invalid_network + invalid_network, + duplicate_publish_message }; - message_parser (nano::block_uniquer &, nano::vote_uniquer &, nano::message_visitor &, nano::work_pool &); + message_parser (nano::network_filter &, nano::block_uniquer &, nano::vote_uniquer &, nano::message_visitor &, nano::work_pool &); void deserialize_buffer (uint8_t const *, size_t); void deserialize_keepalive (nano::stream &, nano::message_header const &); - void deserialize_publish (nano::stream &, nano::message_header const &); + void deserialize_publish (nano::stream &, nano::message_header const &, nano::uint128_t const & = 0); void deserialize_confirm_req (nano::stream &, nano::message_header const &); void deserialize_confirm_ack (nano::stream &, nano::message_header const &); void deserialize_node_id_handshake (nano::stream &, nano::message_header const &); void deserialize_telemetry_req (nano::stream &, nano::message_header const &); void deserialize_telemetry_ack (nano::stream &, nano::message_header const &); bool at_end (nano::stream &); + nano::network_filter & publish_filter; nano::block_uniquer & block_uniquer; nano::vote_uniquer & vote_uniquer; nano::message_visitor & visitor; @@ -282,13 +285,14 @@ class keepalive final : public message class publish final : public message { public: - publish (bool &, nano::stream &, nano::message_header const &, nano::block_uniquer * = nullptr); + publish (bool &, nano::stream &, nano::message_header const &, nano::uint128_t const & = 0, nano::block_uniquer * = nullptr); explicit publish (std::shared_ptr); void visit (nano::message_visitor &) const override; void serialize (nano::stream &) const override; bool deserialize (nano::stream &, nano::block_uniquer * = nullptr); bool operator== (nano::publish const &) const; std::shared_ptr block; + nano::uint128_t digest{ 0 }; }; class confirm_req final : public message { diff --git a/nano/node/election.cpp b/nano/node/election.cpp index e85892340d..828e176e5e 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -523,8 +524,9 @@ void nano::election::adjust_dependent_difficulty () } } -void nano::election::clear_blocks () +void nano::election::cleanup () { + bool unconfirmed (!confirmed ()); auto winner_hash (status.winner->hash ()); for (auto const & block : blocks) { @@ -534,11 +536,21 @@ void nano::election::clear_blocks () debug_assert (erased == 1); node.active.erase_inactive_votes_cache (hash); // Notify observers about dropped elections & blocks lost confirmed elections - if (!confirmed () || hash != winner_hash) + if (unconfirmed || hash != winner_hash) { node.observers.active_stopped.notify (hash); } } + if (unconfirmed) + { + // Clear network filter in another thread + node.worker.push_task ([node_l = node.shared (), blocks_l = std::move (blocks)]() { + for (auto const & block : blocks_l) + { + node_l->network.publish_filter.clear (block.second); + } + }); + } } void nano::election::insert_inactive_votes_cache (nano::block_hash const & hash_a) diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 117d70ddfd..5d51a9cb70 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -77,8 +77,9 @@ class election final : public std::enable_shared_from_this size_t last_votes_size (); void update_dependent (); void adjust_dependent_difficulty (); - void clear_blocks (); void insert_inactive_votes_cache (nano::block_hash const &); + // Erase all blocks from active and, if not confirmed, clear digests from network filters + void cleanup (); public: // State transitions bool transition_time (nano::confirmation_solicitor &); diff --git a/nano/node/network.cpp b/nano/node/network.cpp index 95f7d25041..bb12834854 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -16,6 +16,7 @@ buffer_container (node_a.stats, nano::network::buffer_size, 4096), // 2Mb receiv resolver (node_a.io_ctx), limiter (node_a.config.bandwidth_limit), node (node_a), +publish_filter (256 * 1024), udp_channels (node_a, port_a), tcp_channels (node_a), port (port_a), @@ -391,6 +392,7 @@ class network_message_visitor : public nano::message_visitor } else { + node.network.publish_filter.clear (message_a.digest); node.stats.inc (nano::stat::type::drop, nano::stat::detail::publish, nano::stat::dir::in); } } @@ -428,22 +430,25 @@ class network_message_visitor : public nano::message_visitor node.logger.try_log (boost::str (boost::format ("Received confirm_ack message from %1% for %2%sequence %3%") % channel->to_string () % message_a.vote->hashes_string () % std::to_string (message_a.vote->sequence))); } node.stats.inc (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::in); - for (auto & vote_block : message_a.vote->blocks) + if (!message_a.vote->account.is_zero ()) { - if (!vote_block.which ()) + for (auto & vote_block : message_a.vote->blocks) { - auto block (boost::get> (vote_block)); - if (!node.block_processor.full ()) + if (!vote_block.which ()) { - node.process_active (block); - } - else - { - node.stats.inc (nano::stat::type::drop, nano::stat::detail::confirm_ack, nano::stat::dir::in); + auto block (boost::get> (vote_block)); + if (!node.block_processor.full ()) + { + node.process_active (block); + } + else + { + node.stats.inc (nano::stat::type::drop, nano::stat::detail::confirm_ack, nano::stat::dir::in); + } } } + node.vote_processor.vote (message_a.vote, channel); } - node.vote_processor.vote (message_a.vote, channel); } void bulk_pull (nano::bulk_pull const &) override { diff --git a/nano/node/network.hpp b/nano/node/network.hpp index 2ae8814077..a1c8781011 100644 --- a/nano/node/network.hpp +++ b/nano/node/network.hpp @@ -3,13 +3,13 @@ #include #include #include +#include #include #include #include #include - namespace nano { class channel; @@ -154,6 +154,7 @@ class network final std::vector packet_processing_threads; nano::bandwidth_limiter limiter; nano::node & node; + nano::network_filter publish_filter; nano::transport::udp_channels udp_channels; nano::transport::tcp_channels tcp_channels; std::atomic port{ 0 }; diff --git a/nano/node/node.cpp b/nano/node/node.cpp index a32a692614..48c0d39c17 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -86,21 +86,18 @@ std::unique_ptr nano::collect_container_info (bl { size_t state_blocks_count; size_t blocks_count; - size_t blocks_filter_count; size_t forced_count; { nano::lock_guard guard (block_processor.mutex); state_blocks_count = block_processor.state_blocks.size (); blocks_count = block_processor.blocks.size (); - blocks_filter_count = block_processor.blocks_filter.size (); forced_count = block_processor.forced.size (); } auto composite = std::make_unique (name); composite->add_component (std::make_unique (container_info{ "state_blocks", state_blocks_count, sizeof (decltype (block_processor.state_blocks)::value_type) })); composite->add_component (std::make_unique (container_info{ "blocks", blocks_count, sizeof (decltype (block_processor.blocks)::value_type) })); - composite->add_component (std::make_unique (container_info{ "blocks_filter", blocks_filter_count, sizeof (decltype (block_processor.blocks_filter)::value_type) })); composite->add_component (std::make_unique (container_info{ "forced", forced_count, sizeof (decltype (block_processor.forced)::value_type) })); composite->add_component (collect_container_info (block_processor.generator, "generator")); return composite; @@ -938,6 +935,7 @@ void nano::node::bootstrap_wallet () void nano::node::unchecked_cleanup () { + std::vector digests; std::deque cleaning_list; auto attempt (bootstrap_initiator.current_attempt ()); bool long_attempt (attempt != nullptr && std::chrono::duration_cast (std::chrono::steady_clock::now () - attempt->attempt_start).count () > config.unchecked_cutoff_time.count ()); @@ -953,6 +951,7 @@ void nano::node::unchecked_cleanup () nano::unchecked_info const & info (i->second); if ((now - info.modified) > static_cast (config.unchecked_cutoff_time.count ())) { + digests.push_back (network.publish_filter.hash (info.block)); cleaning_list.push_back (key); } } @@ -978,6 +977,8 @@ void nano::node::unchecked_cleanup () } } } + // Delete from the duplicate filter + network.publish_filter.clear (digests); } void nano::node::ongoing_unchecked_cleanup () diff --git a/nano/node/transport/udp.cpp b/nano/node/transport/udp.cpp index 08ee1fbbe3..4610f93690 100644 --- a/nano/node/transport/udp.cpp +++ b/nano/node/transport/udp.cpp @@ -544,9 +544,17 @@ void nano::transport::udp_channels::receive_action (nano::message_buffer * data_ if (allowed_sender) { udp_message_visitor visitor (node, data_a->endpoint); - nano::message_parser parser (node.block_uniquer, node.vote_uniquer, visitor, node.work); + nano::message_parser parser (node.network.publish_filter, node.block_uniquer, node.vote_uniquer, visitor, node.work); parser.deserialize_buffer (data_a->buffer, data_a->size); - if (parser.status != nano::message_parser::parse_status::success) + if (parser.status == nano::message_parser::parse_status::success) + { + node.stats.add (nano::stat::type::traffic_udp, nano::stat::dir::in, data_a->size); + } + else if (parser.status == nano::message_parser::parse_status::duplicate_publish_message) + { + node.stats.inc (nano::stat::type::filter, nano::stat::detail::duplicate_publish); + } + else { node.stats.inc (nano::stat::type::error); @@ -592,15 +600,12 @@ void nano::transport::udp_channels::receive_action (nano::message_buffer * data_ case nano::message_parser::parse_status::outdated_version: node.stats.inc (nano::stat::type::udp, nano::stat::detail::outdated_version); break; + case nano::message_parser::parse_status::duplicate_publish_message: case nano::message_parser::parse_status::success: /* Already checked, unreachable */ break; } } - else - { - node.stats.add (nano::stat::type::traffic_udp, nano::stat::dir::in, data_a->size); - } } else { diff --git a/nano/secure/network_filter.cpp b/nano/secure/network_filter.cpp index b6fc7b3f2b..f9e6b0374a 100644 --- a/nano/secure/network_filter.cpp +++ b/nano/secure/network_filter.cpp @@ -40,6 +40,19 @@ void nano::network_filter::clear (nano::uint128_t const & digest_a) } } +void nano::network_filter::clear (std::vector const & digests_a) +{ + nano::lock_guard lock (mutex); + for (auto const & digest : digests_a) + { + auto & element (get_element (digest)); + if (element == digest) + { + element = nano::uint128_t{ 0 }; + } + } +} + void nano::network_filter::clear (uint8_t const * bytes_a, size_t count_a) { clear (hash (bytes_a, count_a)); @@ -48,12 +61,7 @@ void nano::network_filter::clear (uint8_t const * bytes_a, size_t count_a) template void nano::network_filter::clear (OBJECT const & object_a) { - std::vector bytes; - { - nano::vectorstream stream (bytes); - object_a->serialize (stream); - } - clear (bytes.data (), bytes.size ()); + clear (hash (object_a)); } void nano::network_filter::clear () @@ -62,6 +70,17 @@ void nano::network_filter::clear () items.assign (items.size (), nano::uint128_t{ 0 }); } +template +nano::uint128_t nano::network_filter::hash (OBJECT const & object_a) const +{ + std::vector bytes; + { + nano::vectorstream stream (bytes); + object_a->serialize (stream); + } + return hash (bytes.data (), bytes.size ()); +} + nano::uint128_t & nano::network_filter::get_element (nano::uint128_t const & hash_a) { debug_assert (!mutex.try_lock ()); @@ -77,3 +96,7 @@ nano::uint128_t nano::network_filter::hash (uint8_t const * bytes_a, size_t coun siphash.CalculateDigest (digest.bytes.data (), bytes_a, count_a); return digest.number (); } + +// Explicitly instantiate +template nano::uint128_t nano::network_filter::hash (std::shared_ptr const &) const; +template void nano::network_filter::clear (std::shared_ptr const &); diff --git a/nano/secure/network_filter.hpp b/nano/secure/network_filter.hpp index 60b99dbd08..0e42a2cce7 100644 --- a/nano/secure/network_filter.hpp +++ b/nano/secure/network_filter.hpp @@ -34,6 +34,11 @@ class network_filter final **/ void clear (nano::uint128_t const & digest_a); + /** + * Clear many digests from the filter + **/ + void clear (std::vector const &); + /** * Reads \p count_a bytes starting from \p bytes_a and digests the contents. * Then, sets the corresponding element in the filter to zero, if it matches the digest exactly. @@ -42,7 +47,7 @@ class network_filter final void clear (uint8_t const * bytes_a, size_t count_a); /** - * Serializes \p object_a and runs clears the resulting siphash digest. + * Serializes \p object_a and clears the resulting siphash digest from the filter. * @return a boolean representing the previous existence of the hash in the filter. **/ template @@ -51,6 +56,12 @@ class network_filter final /** Sets every element of the filter to zero, keeping its size and capacity. */ void clear (); + /** + * Serializes \p object_a and returns the resulting siphash digest + */ + template + nano::uint128_t hash (OBJECT const & object_a) const; + private: using siphash_t = CryptoPP::SipHash<2, 4, true>; From 54761e42bfcd655f30456746c6122896ee7a1dba Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Thu, 12 Mar 2020 09:07:12 +0000 Subject: [PATCH 38/43] Erase representatives with full queues when adding to confirmation solicitor (#2649) With this change less processing happens for elections that won't fit in the solicitor. --- nano/node/confirmation_solicitor.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/nano/node/confirmation_solicitor.cpp b/nano/node/confirmation_solicitor.cpp index f86019b0ae..5b83a97d62 100644 --- a/nano/node/confirmation_solicitor.cpp +++ b/nano/node/confirmation_solicitor.cpp @@ -37,8 +37,9 @@ bool nano::confirmation_solicitor::add (nano::election const & election_a) debug_assert (prepared); auto const max_channel_requests (max_confirm_req_batches * nano::network::confirm_req_hashes_max); unsigned count = 0; - for (auto i (representatives.begin ()), n (representatives.end ()); i != n && count < max_election_requests; ++i) + for (auto i (representatives.begin ()); i != representatives.end () && count < max_election_requests;) { + bool full_queue (false); auto rep (*i); if (election_a.last_votes.find (rep.account) == election_a.last_votes.end ()) { @@ -48,7 +49,12 @@ bool nano::confirmation_solicitor::add (nano::election const & election_a) request_queue.emplace_back (election_a.status.winner->hash (), election_a.status.winner->root ()); ++count; } + else + { + full_queue = true; + } } + i = !full_queue ? i + 1 : representatives.erase (i); } return count == 0; } From 6aea75e12b40cd02b795c88f2c189e50fac3dd49 Mon Sep 17 00:00:00 2001 From: Wesley Shillingford Date: Thu, 12 Mar 2020 14:02:52 +0000 Subject: [PATCH 39/43] Include requesting telemetry metrics from temporary channels (#2653) * Include requesting telemetry metrics from temporary channels * Read transaction no longer needed (Gui comment) --- nano/node/election.hpp | 2 +- nano/node/json_handler.cpp | 3 +-- nano/node/telemetry.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 5d51a9cb70..4d21435f29 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -51,7 +51,7 @@ class election final : public std::enable_shared_from_this static int constexpr confirmed_duration_factor = 5; std::atomic state_m = { state_t::idle }; - // Protects state_start, last_vote and last_block + // These time points must be protected by this mutex std::mutex timepoints_mutex; std::chrono::steady_clock::time_point state_start = { std::chrono::steady_clock::now () }; std::chrono::steady_clock::time_point last_block = { std::chrono::steady_clock::now () }; diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 0a34ee2578..ea11063f3a 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1226,8 +1226,7 @@ void nano::json_handler::block_account () void nano::json_handler::block_count () { - auto transaction (node.store.tx_begin_read ()); - response_l.put ("count", std::to_string (node.store.block_count (transaction).sum ())); + response_l.put ("count", std::to_string (node.ledger.cache.block_count)); response_l.put ("unchecked", std::to_string (node.ledger.cache.unchecked_count)); response_l.put ("cemented", std::to_string (node.ledger.cache.cemented_count)); response_errors (); diff --git a/nano/node/telemetry.cpp b/nano/node/telemetry.cpp index bb30a9a226..aa599b76c0 100644 --- a/nano/node/telemetry.cpp +++ b/nano/node/telemetry.cpp @@ -111,7 +111,7 @@ void nano::telemetry::ongoing_req_all_peers (std::chrono::milliseconds next_requ peers; { - auto temp_peers = this_l->network.list (std::numeric_limits::max (), this_l->network_params.protocol.telemetry_protocol_version_min, false); + auto temp_peers = this_l->network.list (std::numeric_limits::max (), this_l->network_params.protocol.telemetry_protocol_version_min); peers.insert (temp_peers.begin (), temp_peers.end ()); } From c1808b6fb464f83b1ddee3b6b472115c7d67021c Mon Sep 17 00:00:00 2001 From: Wesley Shillingford Date: Thu, 12 Mar 2020 14:03:28 +0000 Subject: [PATCH 40/43] Parallelize state block signature verification with block processor (#2570) * Parallelize state block signature verification with batch block processing * Make sure all state blocks are flushed too * Serg review comment about notification cleanup * Use half the amount of threads for the signature checker * Remove multithreaded cutoff * Use n/2 extra threads. Handles odd number of CPU threads too. * Formatting * Simplify expression (Gui comment) --- nano/lib/threading.cpp | 3 + nano/lib/threading.hpp | 3 +- nano/node/CMakeLists.txt | 10 +- nano/node/blockprocessor.cpp | 149 +++++----------- nano/node/blockprocessor.hpp | 6 +- nano/node/node.cpp | 21 --- nano/node/node.hpp | 1 - nano/node/nodeconfig.cpp | 2 +- nano/node/nodeconfig.hpp | 3 +- nano/node/signatures.cpp | 43 +---- nano/node/signatures.hpp | 5 +- .../state_block_signature_verification.cpp | 164 ++++++++++++++++++ .../state_block_signature_verification.hpp | 48 +++++ 13 files changed, 285 insertions(+), 173 deletions(-) create mode 100644 nano/node/state_block_signature_verification.cpp create mode 100644 nano/node/state_block_signature_verification.hpp diff --git a/nano/lib/threading.cpp b/nano/lib/threading.cpp index 84ce42840a..7a5482ab07 100644 --- a/nano/lib/threading.cpp +++ b/nano/lib/threading.cpp @@ -75,6 +75,9 @@ std::string nano::thread_role::get_string (nano::thread_role::name role) case nano::thread_role::name::request_aggregator: thread_role_name_string = "Req aggregator"; break; + case nano::thread_role::name::state_block_signature_verification: + thread_role_name_string = "State block sig"; + break; } /* diff --git a/nano/lib/threading.hpp b/nano/lib/threading.hpp index 7c4f01169e..2ea5138059 100644 --- a/nano/lib/threading.hpp +++ b/nano/lib/threading.hpp @@ -33,7 +33,8 @@ namespace thread_role work_watcher, confirmation_height_processing, worker, - request_aggregator + request_aggregator, + state_block_signature_verification }; /* * Get/Set the identifier for the current thread diff --git a/nano/node/CMakeLists.txt b/nano/node/CMakeLists.txt index e8b15a6eab..b5c03a4121 100644 --- a/nano/node/CMakeLists.txt +++ b/nano/node/CMakeLists.txt @@ -121,6 +121,12 @@ add_library (node repcrawler.cpp request_aggregator.hpp request_aggregator.cpp + signatures.hpp + signatures.cpp + socket.hpp + socket.cpp + state_block_signature_verification.hpp + state_block_signature_verification.cpp testing.hpp testing.cpp transport/tcp.hpp @@ -129,10 +135,6 @@ add_library (node transport/transport.cpp transport/udp.hpp transport/udp.cpp - signatures.hpp - signatures.cpp - socket.hpp - socket.cpp vote_processor.hpp vote_processor.cpp voting.hpp diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 1345acd901..af31c8cb8b 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -14,8 +15,12 @@ stopped (false), active (false), next_log (std::chrono::steady_clock::now ()), node (node_a), -write_database_queue (write_database_queue_a) +write_database_queue (write_database_queue_a), +state_block_signature_verification (node.checker, node.ledger.network_params.ledger.epochs, node.config, node.logger, node.flags.block_processor_verification_size) { + state_block_signature_verification.blocks_verified_callback = [this](std::deque & items, std::vector const & verifications, std::vector const & hashes, std::vector const & blocks_signatures) { + this->process_verified_state_blocks (items, verifications, hashes, blocks_signatures); + }; } nano::block_processor::~block_processor () @@ -31,6 +36,7 @@ void nano::block_processor::stop () stopped = true; } condition.notify_all (); + state_block_signature_verification.stop (); } void nano::block_processor::flush () @@ -38,7 +44,7 @@ void nano::block_processor::flush () node.checker.flush (); flushing = true; nano::unique_lock lock (mutex); - while (!stopped && (have_blocks () || active)) + while (!stopped && (have_blocks () || active || state_block_signature_verification.is_active ())) { condition.wait (lock); } @@ -48,7 +54,7 @@ void nano::block_processor::flush () size_t nano::block_processor::size () { nano::unique_lock lock (mutex); - return (blocks.size () + state_blocks.size () + forced.size ()); + return (blocks.size () + state_block_signature_verification.size () + forced.size ()); } bool nano::block_processor::full () @@ -69,19 +75,23 @@ void nano::block_processor::add (std::shared_ptr block_a, uint64_t void nano::block_processor::add (nano::unchecked_info const & info_a) { - debug_assert (!nano::work_validate (*info_a.block)); + bool should_notify{ false }; { nano::lock_guard lock (mutex); if (info_a.verified == nano::signature_verification::unknown && (info_a.block->type () == nano::block_type::state || info_a.block->type () == nano::block_type::open || !info_a.account.is_zero ())) { - state_blocks.push_back (info_a); + state_block_signature_verification.add (info_a); } else { + should_notify = true; blocks.push_back (info_a); } } - condition.notify_all (); + if (should_notify) + { + condition.notify_all (); + } } void nano::block_processor::force (std::shared_ptr block_a) @@ -104,7 +114,7 @@ void nano::block_processor::process_blocks () nano::unique_lock lock (mutex); while (!stopped) { - if (have_blocks ()) + if (!blocks.empty () || !forced.empty ()) { active = true; lock.unlock (); @@ -114,7 +124,7 @@ void nano::block_processor::process_blocks () } else { - condition.notify_all (); + condition.notify_one (); condition.wait (lock); } } @@ -135,71 +145,14 @@ bool nano::block_processor::should_log () bool nano::block_processor::have_blocks () { debug_assert (!mutex.try_lock ()); - return !blocks.empty () || !forced.empty () || !state_blocks.empty (); + return !blocks.empty () || !forced.empty () || state_block_signature_verification.size () != 0; } -void nano::block_processor::verify_state_blocks (nano::unique_lock & lock_a, size_t max_count) +void nano::block_processor::process_verified_state_blocks (std::deque & items, std::vector const & verifications, std::vector const & hashes, std::vector const & blocks_signatures) { - debug_assert (!mutex.try_lock ()); - nano::timer timer_l (nano::timer_state::started); - std::deque items; - if (state_blocks.size () <= max_count) - { - items.swap (state_blocks); - } - else { - for (auto i (0); i < max_count; ++i) - { - items.push_back (state_blocks.front ()); - state_blocks.pop_front (); - } - debug_assert (!state_blocks.empty ()); - } - lock_a.unlock (); - if (!items.empty ()) - { - auto size (items.size ()); - std::vector hashes; - hashes.reserve (size); - std::vector messages; - messages.reserve (size); - std::vector lengths; - lengths.reserve (size); - std::vector accounts; - accounts.reserve (size); - std::vector pub_keys; - pub_keys.reserve (size); - std::vector blocks_signatures; - blocks_signatures.reserve (size); - std::vector signatures; - signatures.reserve (size); - std::vector verifications; - verifications.resize (size, 0); - for (auto i (0); i < size; ++i) - { - auto & item (items[i]); - hashes.push_back (item.block->hash ()); - messages.push_back (hashes.back ().bytes.data ()); - lengths.push_back (sizeof (decltype (hashes)::value_type)); - nano::account account (item.block->account ()); - if (!item.block->link ().is_zero () && node.ledger.is_epoch_link (item.block->link ())) - { - account = node.ledger.epoch_signer (item.block->link ()); - } - else if (!item.account.is_zero ()) - { - account = item.account; - } - accounts.push_back (account); - pub_keys.push_back (accounts.back ().bytes.data ()); - blocks_signatures.push_back (item.block->block_signature ()); - signatures.push_back (blocks_signatures.back ().bytes.data ()); - } - nano::signature_check_set check = { size, messages.data (), lengths.data (), pub_keys.data (), signatures.data (), verifications.data () }; - node.checker.verify (check); - lock_a.lock (); - for (auto i (0); i < size; ++i) + nano::unique_lock lk (mutex); + for (auto i (0); i < verifications.size (); ++i) { debug_assert (verifications[i] == 1 || verifications[i] == 0); auto & item (items.front ()); @@ -230,47 +183,24 @@ void nano::block_processor::verify_state_blocks (nano::unique_lock & } items.pop_front (); } - if (node.config.logging.timing_logging () && timer_l.stop () > std::chrono::milliseconds (10)) - { - node.logger.try_log (boost::str (boost::format ("Batch verified %1% state blocks in %2% %3%") % size % timer_l.value ().count () % timer_l.unit ())); - } - } - else - { - lock_a.lock (); } + condition.notify_all (); } void nano::block_processor::process_batch (nano::unique_lock & lock_a) { - nano::timer timer_l; - lock_a.lock (); - timer_l.start (); - // Limit state blocks verification time - - { - if (!state_blocks.empty ()) - { - size_t max_verification_batch (node.flags.block_processor_verification_size != 0 ? node.flags.block_processor_verification_size : 2048 * (node.config.signature_checker_threads + 1)); - while (!state_blocks.empty () && timer_l.before_deadline (std::chrono::seconds (2))) - { - verify_state_blocks (lock_a, max_verification_batch); - } - } - } - lock_a.unlock (); auto scoped_write_guard = write_database_queue.wait (nano::writer::process_batch); auto transaction (node.store.tx_begin_write ({ tables::accounts, nano::tables::cached_counts, nano::tables::change_blocks, tables::frontiers, tables::open_blocks, tables::pending, tables::receive_blocks, tables::representation, tables::send_blocks, tables::state_blocks, tables::unchecked }, { tables::confirmation_height })); - timer_l.restart (); + nano::timer timer_l; lock_a.lock (); + timer_l.start (); // Processing blocks unsigned number_of_blocks_processed (0), number_of_forced_processed (0); while ((!blocks.empty () || !forced.empty ()) && (timer_l.before_deadline (node.config.block_processor_batch_max_time) || (number_of_blocks_processed < node.flags.block_processor_batch_size)) && !awaiting_write) { - bool log_this_record = (blocks.size () + state_blocks.size () + forced.size () > 64) && should_log (); - if (log_this_record) + if ((blocks.size () + state_block_signature_verification.size () + forced.size () > 64) && should_log ()) { - node.logger.always_log (boost::str (boost::format ("%1% blocks (+ %2% state blocks) (+ %3% forced) in processing queue") % blocks.size () % state_blocks.size () % forced.size ())); + node.logger.always_log (boost::str (boost::format ("%1% blocks (+ %2% state blocks) (+ %3% forced) in processing queue") % blocks.size () % state_block_signature_verification.size () % forced.size ())); } nano::unchecked_info info; nano::block_hash hash (0); @@ -322,12 +252,6 @@ void nano::block_processor::process_batch (nano::unique_lock & lock_ number_of_blocks_processed++; process_one (transaction, info); lock_a.lock (); - /* Verify more state blocks if blocks deque is empty - Because verification is long process, avoid large deque verification inside of write transaction */ - if (blocks.empty () && !state_blocks.empty ()) - { - verify_state_blocks (lock_a, 256 * (node.config.signature_checker_threads + 1)); - } } awaiting_write = false; lock_a.unlock (); @@ -547,3 +471,22 @@ void nano::block_processor::requeue_invalid (nano::block_hash const & hash_a, na debug_assert (hash_a == info_a.block->hash ()); node.bootstrap_initiator.lazy_requeue (hash_a, info_a.block->previous (), info_a.confirmed); } + +std::unique_ptr nano::collect_container_info (block_processor & block_processor, const std::string & name) +{ + size_t blocks_count; + size_t forced_count; + + { + nano::lock_guard guard (block_processor.mutex); + blocks_count = block_processor.blocks.size (); + forced_count = block_processor.forced.size (); + } + + auto composite = std::make_unique (name); + composite->add_component (collect_container_info (block_processor.state_block_signature_verification, "state_block_signature_verification")); + composite->add_component (std::make_unique (container_info{ "blocks", blocks_count, sizeof (decltype (block_processor.blocks)::value_type) })); + composite->add_component (std::make_unique (container_info{ "forced", forced_count, sizeof (decltype (block_processor.forced)::value_type) })); + composite->add_component (collect_container_info (block_processor.generator, "generator")); + return composite; +} diff --git a/nano/node/blockprocessor.hpp b/nano/node/blockprocessor.hpp index 34d87e1b05..e87882c4fa 100644 --- a/nano/node/blockprocessor.hpp +++ b/nano/node/blockprocessor.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -51,22 +52,23 @@ class block_processor final private: void queue_unchecked (nano::write_transaction const &, nano::block_hash const &); - void verify_state_blocks (nano::unique_lock &, size_t = std::numeric_limits::max ()); void process_batch (nano::unique_lock &); void process_live (nano::block_hash const &, std::shared_ptr, const bool = false, const bool = false); void requeue_invalid (nano::block_hash const &, nano::unchecked_info const &); + void process_verified_state_blocks (std::deque &, std::vector const &, std::vector const &, std::vector const &); bool stopped; bool active; bool awaiting_write{ false }; std::chrono::steady_clock::time_point next_log; - std::deque state_blocks; std::deque blocks; std::deque> forced; nano::condition_variable condition; nano::node & node; nano::write_database_queue & write_database_queue; std::mutex mutex; + nano::state_block_signature_verification state_block_signature_verification; friend std::unique_ptr collect_container_info (block_processor & block_processor, const std::string & name); }; +std::unique_ptr collect_container_info (block_processor & block_processor, const std::string & name); } diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 48c0d39c17..65cce5ad3d 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -82,27 +82,6 @@ std::unique_ptr nano::collect_container_info (re return composite; } -std::unique_ptr nano::collect_container_info (block_processor & block_processor, const std::string & name) -{ - size_t state_blocks_count; - size_t blocks_count; - size_t forced_count; - - { - nano::lock_guard guard (block_processor.mutex); - state_blocks_count = block_processor.state_blocks.size (); - blocks_count = block_processor.blocks.size (); - forced_count = block_processor.forced.size (); - } - - auto composite = std::make_unique (name); - composite->add_component (std::make_unique (container_info{ "state_blocks", state_blocks_count, sizeof (decltype (block_processor.state_blocks)::value_type) })); - composite->add_component (std::make_unique (container_info{ "blocks", blocks_count, sizeof (decltype (block_processor.blocks)::value_type) })); - composite->add_component (std::make_unique (container_info{ "forced", forced_count, sizeof (decltype (block_processor.forced)::value_type) })); - composite->add_component (collect_container_info (block_processor.generator, "generator")); - return composite; -} - nano::node::node (boost::asio::io_context & io_ctx_a, uint16_t peering_port_a, boost::filesystem::path const & application_path_a, nano::alarm & alarm_a, nano::logging const & logging_a, nano::work_pool & work_a, nano::node_flags flags_a) : node (io_ctx_a, application_path_a, alarm_a, nano::node_config (peering_port_a, logging_a), work_a, flags_a) { diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 36a86b56c6..a24bc7282b 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -83,7 +83,6 @@ class block_arrival final std::unique_ptr collect_container_info (block_arrival & block_arrival, const std::string & name); std::unique_ptr collect_container_info (rep_crawler & rep_crawler, const std::string & name); -std::unique_ptr collect_container_info (block_processor & block_processor, const std::string & name); class node final : public std::enable_shared_from_this { diff --git a/nano/node/nodeconfig.cpp b/nano/node/nodeconfig.cpp index 74ff6542c3..a00fea859c 100644 --- a/nano/node/nodeconfig.cpp +++ b/nano/node/nodeconfig.cpp @@ -75,7 +75,7 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const toml.put ("io_threads", io_threads, "Number of threads dedicated to I/O opeations. Defaults to the number of CPU threads, and at least 4.\ntype:uint64"); toml.put ("network_threads", network_threads, "Number of threads dedicated to processing network messages. Defaults to the number of CPU threads, and at least 4.\ntype:uint64"); toml.put ("work_threads", work_threads, "Number of threads dedicated to CPU generated work. Defaults to all available CPU threads.\ntype:uint64"); - toml.put ("signature_checker_threads", signature_checker_threads, "Number of additional threads dedicated to signature verification. Defaults to the number of CPU threads minus 1.\ntype:uint64"); + toml.put ("signature_checker_threads", signature_checker_threads, "Number of additional threads dedicated to signature verification. Defaults to number of CPU threads / 2.\ntype:uint64"); toml.put ("enable_voting", enable_voting, "Enable or disable voting. Enabling this option requires additional system resources, namely increased CPU, bandwidth and disk usage.\ntype:bool"); toml.put ("bootstrap_connections", bootstrap_connections, "Number of outbound bootstrap connections. Must be a power of 2. Defaults to 4.\nWarning: a larger amount of connections may use substantially more system memory.\ntype:uint64"); toml.put ("bootstrap_connections_max", bootstrap_connections_max, "Maximum number of inbound bootstrap connections. Defaults to 64.\nWarning: a larger amount of connections may use additional system memory.\ntype:uint64"); diff --git a/nano/node/nodeconfig.hpp b/nano/node/nodeconfig.hpp index b611790559..86d9e8de7f 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -60,7 +60,8 @@ class node_config unsigned io_threads{ std::max (4, std::thread::hardware_concurrency ()) }; unsigned network_threads{ std::max (4, std::thread::hardware_concurrency ()) }; unsigned work_threads{ std::max (4, std::thread::hardware_concurrency ()) }; - unsigned signature_checker_threads{ (std::thread::hardware_concurrency () != 0) ? std::thread::hardware_concurrency () - 1 : 0 }; /* The calling thread does checks as well so remove it from the number of threads used */ + /* Use half available threads on the system for signature checking. The calling thread does checks as well, so these are extra worker threads */ + unsigned signature_checker_threads{ std::thread::hardware_concurrency () / 2 }; bool enable_voting{ false }; unsigned bootstrap_connections{ 4 }; unsigned bootstrap_connections_max{ 64 }; diff --git a/nano/node/signatures.cpp b/nano/node/signatures.cpp index 30f9413819..ff4d2dea19 100644 --- a/nano/node/signatures.cpp +++ b/nano/node/signatures.cpp @@ -22,16 +22,13 @@ nano::signature_checker::~signature_checker () void nano::signature_checker::verify (nano::signature_check_set & check_a) { + // Don't process anything else if we have stopped + if (stopped) { - // Don't process anything else if we have stopped - nano::lock_guard guard (mutex); - if (stopped) - { - return; - } + return; } - if (check_a.size < multithreaded_cutoff || single_threaded) + if (check_a.size <= batch_size || single_threaded) { // Not dealing with many so just use the calling thread for checking signatures auto result = verify_batch (check_a, 0, check_a.size); @@ -76,32 +73,26 @@ void nano::signature_checker::verify (nano::signature_check_set & check_a) void nano::signature_checker::stop () { - nano::lock_guard guard (mutex); - if (!stopped) + if (!stopped.exchange (true)) { - stopped = true; thread_pool.join (); } } void nano::signature_checker::flush () { - nano::lock_guard guard (mutex); while (!stopped && tasks_remaining != 0) ; } bool nano::signature_checker::verify_batch (const nano::signature_check_set & check_a, size_t start_index, size_t size) { - /* Returns false if there are at least 1 invalid signature */ - auto code (nano::validate_message_batch (check_a.messages + start_index, check_a.message_lengths + start_index, check_a.pub_keys + start_index, check_a.signatures + start_index, size, check_a.verifications + start_index)); - (void)code; - + nano::validate_message_batch (check_a.messages + start_index, check_a.message_lengths + start_index, check_a.pub_keys + start_index, check_a.signatures + start_index, size, check_a.verifications + start_index); return std::all_of (check_a.verifications + start_index, check_a.verifications + start_index + size, [](int verification) { return verification == 0 || verification == 1; }); } /* This operates on a number of signatures of size (num_batches * batch_size) from the beginning of the check_a pointers. - * Caller should check the value of the promise which indicateswhen the work has been completed. + * Caller should check the value of the promise which indicates when the work has been completed. */ void nano::signature_checker::verify_async (nano::signature_check_set & check_a, size_t num_batches, std::promise & promise) { @@ -129,10 +120,6 @@ void nano::signature_checker::verify_async (nano::signature_check_set & check_a, // Set the names of all the threads in the thread pool for easier identification void nano::signature_checker::set_thread_names (unsigned num_threads) { - auto ready = false; - auto pending = num_threads; - nano::condition_variable cv; - std::vector> promises (num_threads); std::vector> futures; futures.reserve (num_threads); @@ -142,21 +129,8 @@ void nano::signature_checker::set_thread_names (unsigned num_threads) for (auto i = 0u; i < num_threads; ++i) { - boost::asio::post (thread_pool, [&cv, &ready, &pending, &mutex = mutex, &promise = promises[i]]() { - nano::unique_lock lk (mutex); + boost::asio::post (thread_pool, [& promise = promises[i]]() { nano::thread_role::set (nano::thread_role::name::signature_checking); - if (--pending == 0) - { - // All threads have been reached - ready = true; - lk.unlock (); - cv.notify_all (); - } - else - { - // We need to wait until the other threads are finished - cv.wait (lk, [&ready]() { return ready; }); - } promise.set_value (); }); } @@ -166,5 +140,4 @@ void nano::signature_checker::set_thread_names (unsigned num_threads) { future.wait (); } - debug_assert (pending == 0); } diff --git a/nano/node/signatures.hpp b/nano/node/signatures.hpp index 61a6bb7a97..72f26b9fc2 100644 --- a/nano/node/signatures.hpp +++ b/nano/node/signatures.hpp @@ -55,12 +55,9 @@ class signature_checker final void set_thread_names (unsigned num_threads); boost::asio::thread_pool thread_pool; std::atomic tasks_remaining{ 0 }; - /** minimum signature_check_set size eligible to be multithreaded */ - static constexpr size_t multithreaded_cutoff = 513; static constexpr size_t batch_size = 256; const bool single_threaded; unsigned num_threads; - std::mutex mutex; - bool stopped{ false }; + std::atomic stopped{ false }; }; } diff --git a/nano/node/state_block_signature_verification.cpp b/nano/node/state_block_signature_verification.cpp new file mode 100644 index 0000000000..4402ef222a --- /dev/null +++ b/nano/node/state_block_signature_verification.cpp @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +nano::state_block_signature_verification::state_block_signature_verification (nano::signature_checker & signature_checker, nano::epochs & epochs, nano::node_config & node_config, nano::logger_mt & logger, uint64_t state_block_signature_verification_size) : +signature_checker (signature_checker), +epochs (epochs), +node_config (node_config), +logger (logger), +thread ([this, state_block_signature_verification_size]() { + nano::thread_role::set (nano::thread_role::name::state_block_signature_verification); + this->run (state_block_signature_verification_size); +}) +{ +} + +nano::state_block_signature_verification::~state_block_signature_verification () +{ + stop (); +} + +void nano::state_block_signature_verification::stop () +{ + { + nano::lock_guard guard (mutex); + stopped = true; + } + + if (thread.joinable ()) + { + condition.notify_one (); + thread.join (); + } +} + +void nano::state_block_signature_verification::run (uint64_t state_block_signature_verification_size) +{ + nano::unique_lock lk (mutex); + while (!stopped) + { + if (!state_blocks.empty ()) + { + size_t const max_verification_batch (state_block_signature_verification_size != 0 ? state_block_signature_verification_size : 256 * (node_config.signature_checker_threads + 1)); + active = true; + while (!state_blocks.empty () && !stopped) + { + auto items = setup_items (max_verification_batch); + lk.unlock (); + verify_state_blocks (items); + lk.lock (); + } + active = false; + } + else + { + condition.wait (lk); + } + } +} + +bool nano::state_block_signature_verification::is_active () +{ + nano::lock_guard guard (mutex); + return active; +} + +void nano::state_block_signature_verification::add (nano::unchecked_info const & info_a) +{ + { + nano::lock_guard guard (mutex); + state_blocks.push_back (info_a); + } + condition.notify_one (); +} + +size_t nano::state_block_signature_verification::size () +{ + nano::lock_guard guard (mutex); + return state_blocks.size (); +} + +std::deque nano::state_block_signature_verification::setup_items (size_t max_count) +{ + std::deque items; + if (state_blocks.size () <= max_count) + { + items.swap (state_blocks); + } + else + { + for (auto i (0); i < max_count; ++i) + { + items.push_back (state_blocks.front ()); + state_blocks.pop_front (); + } + debug_assert (!state_blocks.empty ()); + } + return items; +} + +void nano::state_block_signature_verification::verify_state_blocks (std::deque & items) +{ + if (!items.empty ()) + { + nano::timer<> timer_l; + timer_l.start (); + auto size (items.size ()); + std::vector hashes; + hashes.reserve (size); + std::vector messages; + messages.reserve (size); + std::vector lengths; + lengths.reserve (size); + std::vector accounts; + accounts.reserve (size); + std::vector pub_keys; + pub_keys.reserve (size); + std::vector blocks_signatures; + blocks_signatures.reserve (size); + std::vector signatures; + signatures.reserve (size); + std::vector verifications; + verifications.resize (size, 0); + for (auto & item : items) + { + hashes.push_back (item.block->hash ()); + messages.push_back (hashes.back ().bytes.data ()); + lengths.push_back (sizeof (decltype (hashes)::value_type)); + nano::account account (item.block->account ()); + if (!item.block->link ().is_zero () && epochs.is_epoch_link (item.block->link ())) + { + account = epochs.signer (epochs.epoch (item.block->link ())); + } + else if (!item.account.is_zero ()) + { + account = item.account; + } + accounts.push_back (account); + pub_keys.push_back (accounts.back ().bytes.data ()); + blocks_signatures.push_back (item.block->block_signature ()); + signatures.push_back (blocks_signatures.back ().bytes.data ()); + } + nano::signature_check_set check = { size, messages.data (), lengths.data (), pub_keys.data (), signatures.data (), verifications.data () }; + signature_checker.verify (check); + if (node_config.logging.timing_logging () && timer_l.stop () > std::chrono::milliseconds (10)) + { + logger.try_log (boost::str (boost::format ("Batch verified %1% state blocks in %2% %3%") % size % timer_l.value ().count () % timer_l.unit ())); + } + blocks_verified_callback (items, verifications, hashes, blocks_signatures); + } +} + +std::unique_ptr nano::collect_container_info (state_block_signature_verification & state_block_signature_verification, const std::string & name) +{ + auto composite = std::make_unique (name); + composite->add_component (std::make_unique (container_info{ "state_blocks", state_block_signature_verification.size (), sizeof (nano::unchecked_info) })); + return composite; +} diff --git a/nano/node/state_block_signature_verification.hpp b/nano/node/state_block_signature_verification.hpp new file mode 100644 index 0000000000..7af89bfd8f --- /dev/null +++ b/nano/node/state_block_signature_verification.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace nano +{ +class epochs; +class logger_mt; +class node_config; +class signature_checker; + +class state_block_signature_verification +{ +public: + state_block_signature_verification (nano::signature_checker &, nano::epochs &, nano::node_config &, nano::logger_mt &, uint64_t); + ~state_block_signature_verification (); + void add (nano::unchecked_info const & info_a); + size_t size (); + void stop (); + bool is_active (); + + std::function &, std::vector const &, std::vector const &, std::vector const &)> blocks_verified_callback; + +private: + nano::signature_checker & signature_checker; + nano::epochs & epochs; + nano::node_config & node_config; + nano::logger_mt & logger; + + std::mutex mutex; + bool stopped{ false }; + bool active{ false }; + std::deque state_blocks; + nano::condition_variable condition; + std::thread thread; + + void run (uint64_t block_processor_verification_size); + std::deque setup_items (size_t); + void verify_state_blocks (std::deque &); +}; + +std::unique_ptr collect_container_info (state_block_signature_verification & state_block_signature_verification, const std::string & name); +} From 7348d1e71739bb7a5052375e6e918e92f5d1d6b0 Mon Sep 17 00:00:00 2001 From: cryptocode Date: Thu, 12 Mar 2020 15:22:55 +0100 Subject: [PATCH 41/43] Update tick count in timer::update (#2655) * Update tick count in timer::update * Return ticks from restart --- nano/core_test/timer.cpp | 2 ++ nano/lib/timer.cpp | 17 +++++++++++++---- nano/lib/timer.hpp | 8 +++++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/nano/core_test/timer.cpp b/nano/core_test/timer.cpp index ffd65f46b2..1aa29739cb 100644 --- a/nano/core_test/timer.cpp +++ b/nano/core_test/timer.cpp @@ -42,6 +42,8 @@ TEST (timer, measure_and_compare) ASSERT_LT (t1.since_start (), 200ms); ASSERT_GT (t1.since_start (), 10ms); ASSERT_GE (t1.stop (), 50ms); + std::this_thread::sleep_for (50ms); + ASSERT_GT (t1.restart (), 10ms); } TEST (timer, cummulative_child) diff --git a/nano/lib/timer.cpp b/nano/lib/timer.cpp index ed2988a2f2..2acd18819a 100644 --- a/nano/lib/timer.cpp +++ b/nano/lib/timer.cpp @@ -97,12 +97,14 @@ void nano::timer::start () } template -void nano::timer::restart () +UNIT nano::timer::restart () { + auto current = ticks; state = nano::timer_state::started; begin = CLOCK::now (); ticks = UNIT::zero (); measurements = 0; + return current; } template @@ -112,20 +114,27 @@ UNIT nano::timer::pause () return stop (); } +template +void nano::timer::update_ticks () +{ + auto end = CLOCK::now (); + ticks += std::chrono::duration_cast (end - begin); +} + template UNIT nano::timer::stop () { debug_assert (state == nano::timer_state::started); state = nano::timer_state::stopped; - auto end = CLOCK::now (); - ticks += std::chrono::duration_cast (end - begin); + update_ticks (); return ticks; } template -UNIT nano::timer::value () const +UNIT nano::timer::value () { + update_ticks (); return ticks; } diff --git a/nano/lib/timer.hpp b/nano/lib/timer.hpp index 61c201d3fb..423d6f6f73 100644 --- a/nano/lib/timer.hpp +++ b/nano/lib/timer.hpp @@ -42,8 +42,9 @@ class timer /** * Restarts the timer by setting start time to current time and resetting tick count. * This can be called in any timer state. + * @return duration */ - void restart (); + UNIT restart (); /** * Stops the timer and increases the measurement count. A timer can be started and paused @@ -59,9 +60,9 @@ class timer UNIT stop (); /** - * Return current tick count. + * Updates and returns current tick count. */ - UNIT value () const; + UNIT value (); /** Returns the duration in UNIT since the timer was last started. */ UNIT since_start () const; @@ -94,5 +95,6 @@ class timer UNIT ticks{ 0 }; UNIT minimum{ UNIT::zero () }; unsigned measurements{ 0 }; + void update_ticks (); }; } From 7dcb20b076d8f68841906cd6e4938897e6e77c4d Mon Sep 17 00:00:00 2001 From: Sergey Kroshnin Date: Thu, 12 Mar 2020 17:31:08 +0300 Subject: [PATCH 42/43] Delay inactive/gap cache bootstrap start for 30 seconds (#2631) instead of 5 seconds. To allow more frequent new blocks arrival with realtime network & kess usage for expensive bootstrap --- nano/node/active_transactions.cpp | 5 ++--- nano/node/gap_cache.cpp | 5 ++--- nano/secure/common.cpp | 1 + nano/secure/common.hpp | 1 + 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index a2c8466959..3ef813ca1b 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -975,11 +975,10 @@ bool nano::active_transactions::inactive_votes_bootstrap_check (std::vectornetwork_params.network.is_test_network () ? now + std::chrono::milliseconds (5) : now + std::chrono::seconds (5), [node_l, hash_a]() { + node.alarm.add (std::chrono::steady_clock::now () + node.network_params.bootstrap.gap_cache_bootstrap_start_interval, [node_l, hash_a]() { auto transaction (node_l->store.tx_begin_read ()); if (!node_l->store.block_exists (transaction, hash_a)) { diff --git a/nano/node/gap_cache.cpp b/nano/node/gap_cache.cpp index bf1be781ce..e57f2f9c13 100644 --- a/nano/node/gap_cache.cpp +++ b/nano/node/gap_cache.cpp @@ -86,11 +86,10 @@ bool nano::gap_cache::bootstrap_check (std::vector const & voters { start_bootstrap = true; } - if (start_bootstrap) + if (start_bootstrap && !node.ledger.block_exists (hash_a)) { auto node_l (node.shared ()); - auto now (std::chrono::steady_clock::now ()); - node.alarm.add (node_l->network_params.network.is_test_network () ? now + std::chrono::milliseconds (5) : now + std::chrono::seconds (5), [node_l, hash_a]() { + node.alarm.add (std::chrono::steady_clock::now () + node.network_params.bootstrap.gap_cache_bootstrap_start_interval, [node_l, hash_a]() { auto transaction (node_l->store.tx_begin_read ()); if (!node_l->store.block_exists (transaction, hash_a)) { diff --git a/nano/secure/common.cpp b/nano/secure/common.cpp index f75a57b80d..c66f472096 100644 --- a/nano/secure/common.cpp +++ b/nano/secure/common.cpp @@ -158,6 +158,7 @@ nano::bootstrap_constants::bootstrap_constants (nano::network_constants & networ frontier_retry_limit = network_constants.is_test_network () ? 2 : 16; lazy_retry_limit = network_constants.is_test_network () ? 2 : frontier_retry_limit * 10; lazy_destinations_retry_limit = network_constants.is_test_network () ? 1 : frontier_retry_limit / 4; + gap_cache_bootstrap_start_interval = network_constants.is_test_network () ? std::chrono::milliseconds (5) : std::chrono::milliseconds (30 * 1000); } /* Convenience constants for core_test which is always on the test network */ diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index 338237bccb..1781ba202a 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -455,6 +455,7 @@ class bootstrap_constants unsigned frontier_retry_limit; unsigned lazy_retry_limit; unsigned lazy_destinations_retry_limit; + std::chrono::milliseconds gap_cache_bootstrap_start_interval; }; /** Constants whose value depends on the active network */ From 7cd51dea484bdde9029a6fff396bec00daf7fa14 Mon Sep 17 00:00:00 2001 From: Wesley Shillingford Date: Thu, 12 Mar 2020 18:45:30 +0000 Subject: [PATCH 43/43] Signature checker blocking fix (#2659) * Signature checker blocking fix * Change variable name to remove shadowing --- nano/core_test/signing.cpp | 52 +++++++++++++++++ nano/node/signatures.cpp | 13 ++++- nano/node/signatures.hpp | 3 +- .../state_block_signature_verification.cpp | 2 +- nano/slow_test/node.cpp | 56 +++++++++++++++++++ 5 files changed, 121 insertions(+), 5 deletions(-) diff --git a/nano/core_test/signing.cpp b/nano/core_test/signing.cpp index 6ac902d3b5..30704623f1 100644 --- a/nano/core_test/signing.cpp +++ b/nano/core_test/signing.cpp @@ -147,3 +147,55 @@ TEST (signature_checker, one) block.signature.bytes[31] ^= 0x1; verify_block (block, 1); } + +TEST (signature_checker, boundary_checks) +{ + // sizes container must be in incrementing order + std::vector sizes{ 0, 1 }; + auto add_boundary = [&sizes](size_t boundary) { + sizes.insert (sizes.end (), { boundary - 1, boundary, boundary + 1 }); + }; + + for (auto i = 1; i <= 5; ++i) + { + add_boundary (nano::signature_checker::batch_size * i); + } + + nano::signature_checker checker (1); + auto max_size = *(sizes.end () - 1); + std::vector hashes; + hashes.reserve (max_size); + std::vector messages; + messages.reserve (max_size); + std::vector lengths; + lengths.reserve (max_size); + std::vector pub_keys; + pub_keys.reserve (max_size); + std::vector signatures; + signatures.reserve (max_size); + nano::keypair key; + nano::state_block block (key.pub, 0, key.pub, 0, 0, key.prv, key.pub, 0); + + auto last_size = 0; + for (auto size : sizes) + { + // The size needed to append to existing containers, saves re-initializing from scratch each iteration + auto extra_size = size - last_size; + + std::vector verifications; + verifications.resize (size); + for (auto i (0); i < extra_size; ++i) + { + hashes.push_back (block.hash ()); + messages.push_back (hashes.back ().bytes.data ()); + lengths.push_back (sizeof (decltype (hashes)::value_type)); + pub_keys.push_back (block.hashables.account.bytes.data ()); + signatures.push_back (block.signature.bytes.data ()); + } + nano::signature_check_set check = { size, messages.data (), lengths.data (), pub_keys.data (), signatures.data (), verifications.data () }; + checker.verify (check); + bool all_valid = std::all_of (verifications.cbegin (), verifications.cend (), [](auto verification) { return verification == 1; }); + ASSERT_TRUE (all_valid); + last_size = size; + } +} diff --git a/nano/node/signatures.cpp b/nano/node/signatures.cpp index ff4d2dea19..69d347c31b 100644 --- a/nano/node/signatures.cpp +++ b/nano/node/signatures.cpp @@ -50,9 +50,16 @@ void nano::signature_checker::verify (nano::signature_check_set & check_a) auto num_full_batches_thread = (num_base_batches_each * num_threads); if (num_full_overflow_batches > 0) { - size_calling_thread += batch_size; - auto remaining = num_full_overflow_batches - 1; - num_full_batches_thread += remaining; + if (overflow_size == 0) + { + // Give the calling thread priority over any batches when there is no excess remainder. + size_calling_thread += batch_size; + num_full_batches_thread += num_full_overflow_batches - 1; + } + else + { + num_full_batches_thread += num_full_overflow_batches; + } } release_assert (check_a.size == (num_full_batches_thread * batch_size + size_calling_thread)); diff --git a/nano/node/signatures.hpp b/nano/node/signatures.hpp index 72f26b9fc2..b1d65d7a38 100644 --- a/nano/node/signatures.hpp +++ b/nano/node/signatures.hpp @@ -35,6 +35,8 @@ class signature_checker final void stop (); void flush (); + static size_t constexpr batch_size = 256; + private: struct Task final { @@ -55,7 +57,6 @@ class signature_checker final void set_thread_names (unsigned num_threads); boost::asio::thread_pool thread_pool; std::atomic tasks_remaining{ 0 }; - static constexpr size_t batch_size = 256; const bool single_threaded; unsigned num_threads; std::atomic stopped{ false }; diff --git a/nano/node/state_block_signature_verification.cpp b/nano/node/state_block_signature_verification.cpp index 4402ef222a..5683a0b5ed 100644 --- a/nano/node/state_block_signature_verification.cpp +++ b/nano/node/state_block_signature_verification.cpp @@ -46,7 +46,7 @@ void nano::state_block_signature_verification::run (uint64_t state_block_signatu { if (!state_blocks.empty ()) { - size_t const max_verification_batch (state_block_signature_verification_size != 0 ? state_block_signature_verification_size : 256 * (node_config.signature_checker_threads + 1)); + size_t const max_verification_batch (state_block_signature_verification_size != 0 ? state_block_signature_verification_size : nano::signature_checker::batch_size * (node_config.signature_checker_threads + 1)); active = true; while (!state_blocks.empty () && !stopped) { diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index d794c1261f..42a9cf8755 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -986,3 +986,59 @@ namespace transport } } } + +// Similar to signature_checker.boundary_checks but more exhaustive. Can take up to 1 minute +TEST (signature_checker, mass_boundary_checks) +{ + // sizes container must be in incrementing order + std::vector sizes{ 0, 1 }; + auto add_boundary = [&sizes](size_t boundary) { + sizes.insert (sizes.end (), { boundary - 1, boundary, boundary + 1 }); + }; + + for (auto i = 1; i <= 10; ++i) + { + add_boundary (nano::signature_checker::batch_size * i); + } + + for (auto num_threads = 0; num_threads < 5; ++num_threads) + { + nano::signature_checker checker (num_threads); + auto max_size = *(sizes.end () - 1); + std::vector hashes; + hashes.reserve (max_size); + std::vector messages; + messages.reserve (max_size); + std::vector lengths; + lengths.reserve (max_size); + std::vector pub_keys; + pub_keys.reserve (max_size); + std::vector signatures; + signatures.reserve (max_size); + nano::keypair key; + nano::state_block block (key.pub, 0, key.pub, 0, 0, key.prv, key.pub, 0); + + auto last_size = 0; + for (auto size : sizes) + { + // The size needed to append to existing containers, saves re-initializing from scratch each iteration + auto extra_size = size - last_size; + + std::vector verifications; + verifications.resize (size); + for (auto i (0); i < extra_size; ++i) + { + hashes.push_back (block.hash ()); + messages.push_back (hashes.back ().bytes.data ()); + lengths.push_back (sizeof (decltype (hashes)::value_type)); + pub_keys.push_back (block.hashables.account.bytes.data ()); + signatures.push_back (block.signature.bytes.data ()); + } + nano::signature_check_set check = { size, messages.data (), lengths.data (), pub_keys.data (), signatures.data (), verifications.data () }; + checker.verify (check); + bool all_valid = std::all_of (verifications.cbegin (), verifications.cend (), [](auto verification) { return verification == 1; }); + ASSERT_TRUE (all_valid); + last_size = size; + } + } +}