diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ddd104cac..ad48da6b7e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,11 @@ jobs: script-id: native - name: x86_64 Linux [GOAL install] [GUI] [bionic] [no depends] script-id: native_old + - name: x86_64 Linux [ASan] [LSan] [UBSan] [integer] [jammy] [no depends] + script-id: native_asan + # FIXME: depends is unable to compile Qt with clang. + # - name: x86_64 Linux [TSan] [GUI] [jammy] + # script-id: native_tsan - name: macOS 10.14 [GOAL deploy] [GUI] [no tests] [focal] script-id: mac env: diff --git a/.github/workflows/cmake-ci.yml b/.github/workflows/cmake-ci.yml index 4105710601..cfd19b0446 100644 --- a/.github/workflows/cmake-ci.yml +++ b/.github/workflows/cmake-ci.yml @@ -192,3 +192,87 @@ jobs: name: testlog-macos-${{matrix.tag}} path: ${{github.workspace}}/build/Testing/Temporary/LastTest.log retention-days: 7 + + test-msys2: + runs-on: windows-latest + defaults: + run: + shell: msys2 {0} + env: + CCACHE_DIR: ${{github.workspace}}\ccache + CCACHE_MAXSIZE: 400M + CCACHE_COMPILERCHECK: content + strategy: + matrix: + tag: + - minimal + - no-asm + - gui-full + include: + - tag: no-asm + deps: null + options: -DUSE_ASM=OFF + - tag: gui-full + deps: >- + miniupnpc:p + qrencode:p + qt5-base:p + qt5-tools:p + options: >- + -DENABLE_GUI=ON + -DENABLE_QRENCODE=ON + -DENABLE_UPNP=ON + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + update: true + install: >- + make + ninja + pacboy: >- + ${{matrix.deps}} + boost:p + ccache:p + cmake:p + curl:p + libzip:p + openssl:p + toolchain:p + - name: Configure + run: | + cmake -B ./build -G Ninja \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + ${{matrix.options}} \ + -DBUILD_SHARED_LIBS=OFF -DENABLE_TESTS=ON + - name: Restore cache + uses: actions/cache/restore@v3 + if: always() + with: + path: ${{env.CCACHE_DIR}} + key: ccache-msys2-${{matrix.tag}}-${{github.run_id}} + restore-keys: | + ccache-msys2-${{matrix.tag}}- + - name: Build + run: | + cmake --build ./build -v -j $NUMBER_OF_PROCESSORS + - name: Save cache + uses: actions/cache/save@v3 + if: always() + with: + path: ${{env.CCACHE_DIR}} + key: ccache-msys2-${{matrix.tag}}-${{github.run_id}} + - name: Run tests + run: | + ctest --test-dir ./build -j $NUMBER_OF_PROCESSORS + - name: Upload test logs + uses: actions/upload-artifact@v3 + if: always() + with: + name: testlog-msys-${{matrix.tag}} + path: ${{github.workspace}}\build\Testing\Temporary\LastTest.log + retention-days: 7 diff --git a/CHANGELOG.md b/CHANGELOG.md index f194b20dad..216af62361 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/) and this project adheres to [Semantic Versioning](https://semver.org/). +## [5.4.8.0], 2024-04-10, leisure + +### Added + - build: add option for sanitizers #2553 (@div72) + - build: CMake: Initial Windows support (MSYS2) #2733 (@CyberTailor) + +### Changed + - build: enforce SSE2 on x86 targets #2746 (@div72) + - consensus: Update checkpoint data for mainnet and testnet #2756 (@jamescowens) + - gui, util: Enhance verify checkpoints fail handling; use RegistryBookmarks for DB passivation #2758 (@jamescowens) + +### Removed + +### Fixed + - build, depends: fix compilation with XCode 15 #2747 (@div72) + - Fix man page installation path for cmake builds #2749 (@theMarix) + - consensus, mrc, sidestake: add mrc fees to staker to rewards to be allocated via sidestaking #2753 (@jamescowens) + - Fix Systemd unit install location #2754 (@theMarix) + - scraper: Corrections to scraper_net after removal of cntPartsRcvd decrement and increment #2755 (@jamescowens) + - rpc: fix setban segfault #2757 (@div72) + ## [5.4.7.0], 2024-03-13, leisure ### Added diff --git a/CMakeLists.txt b/CMakeLists.txt index 0836128949..95398687fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,12 +7,31 @@ # CMake support is experimental. Use with caution and report any bugs. cmake_minimum_required(VERSION 3.18) +set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/build-aux/cmake") + + +# Hunter Package Manager +# ====================== + +# Windows doesn't yet have a package manager that can be used for managing +# dependencies, so we use Hunter on it. +option(HUNTER_ENABLED "Enable Hunter package manager" OFF) +include(HunterGate) +HunterGate( + URL "https://github.com/cpp-pm/hunter/archive/refs/tags/v0.25.3.tar.gz" + SHA1 "0dfbc2cb5c4cf7e83533733bdfd2125ff96680cb" + FILEPATH "${CMAKE_CURRENT_SOURCE_DIR}/build-aux/cmake/Hunter/config.cmake" +) + + +# Project configuration +# ===================== project("Gridcoin" - VERSION 5.4.7.0 + VERSION 5.4.8.0 DESCRIPTION "POS-based cryptocurrency that rewards BOINC computation" HOMEPAGE_URL "https://gridcoin.us" - LANGUAGES ASM C CXX + LANGUAGES C CXX ) set(CLIENT_VERSION_IS_RELEASE "true") @@ -24,33 +43,38 @@ set(COPYRIGHT_HOLDERS_FINAL "The Gridcoin developers") # ======================= set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_C_VISIBILITY_PRESET hidden) set(CMAKE_CXX_VISIBILITY_PRESET hidden) -set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded) - set(CMAKE_INCLUDE_CURRENT_DIR ON) -# Remove '-DNDEBUG' from flags because we need asserts -string(REPLACE "NDEBUG" "_NDEBUG" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") -string(REPLACE "NDEBUG" "_NDEBUG" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") - +if(MSVC) + if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + message(FATAL_ERROR "It's not yet possible to build Gridcoin with MSVC") + endif() + add_compile_options(/U NDEBUG) +else() + add_compile_options(-UNDEBUG) +endif() -# Load modules from the source tree -# ================================= -set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/build-aux/cmake") +# Load CMake modules +# ================== include(CheckCXXSymbolExists) include(CheckFunctionExists) include(CheckIncludeFile) include(CheckPIESupported) +include(CheckSymbolExists) + include(CheckSSE) include(CheckStrerrorR) -include(CheckSymbolExists) +include(HunterGate) include(VersionFromGit) + # Define options # ============== @@ -60,6 +84,8 @@ option(ENABLE_GUI "Enable Qt-based GUI" OFF) option(ENABLE_DOCS "Build Doxygen documentation" OFF) option(ENABLE_TESTS "Build tests" OFF) option(LUPDATE "Update translation files" OFF) +option(STATIC_LIBS "Prefer static variants of system libraries" ${WIN32}) +option(STATIC_RUNTIME "Link runtime statically" ${WIN32}) # CPU-dependent options option(ENABLE_SSE41 "Build code that uses SSE4.1 intrinsics" ${HAS_SSE41}) @@ -74,30 +100,51 @@ option(ENABLE_QRENCODE "Enable generation of QR Codes for receiving payments" O option(ENABLE_UPNP "Enable UPnP port mapping support" OFF) option(DEFAULT_UPNP "Turn UPnP on startup" OFF) option(USE_DBUS "Enable DBus support" OFF) + +# Bundled packages option(SYSTEM_BDB "Find system installation of Berkeley DB CXX 5.3" OFF) option(SYSTEM_LEVELDB "Find system installation of leveldb" OFF) option(SYSTEM_SECP256K1 "Find system installation of libsecp256k1 with pkg-config" OFF) option(SYSTEM_UNIVALUE "Find system installation of Univalue with pkg-config" OFF) option(SYSTEM_XXD "Find system xxd binary" OFF) +# Hunter packages +option(BUNDLED_BOOST "Use the bundled version of Boost" ${HUNTER_ENABLED}) +option(BUNDLED_CURL "Use the bundled version of cURL" ${HUNTER_ENABLED}) +option(BUNDLED_LIBZIP "Use the bundled version of libzip" ${HUNTER_ENABLED}) +option(BUNDLED_OPENSSL "Use the bundled version of OpenSSL" ${HUNTER_ENABLED}) +option(BUNDLED_QT "Use the bundled version of Qt" ${HUNTER_ENABLED}) -# Find dependencies -# ================= + +# Handle dependencies +# =================== + +set(QT5_MINIMUM_VERSION 5.9.5) +set(QT5_COMPONENTS Concurrent Core Gui LinguistTools Network Widgets) +set(QT5_HUNTER_COMPONENTS qtbase qttools) +if(USE_DBUS) + list(APPEND QT5_COMPONENTS DBus) +endif() +if(ENABLE_TESTS) + list(APPEND QT5_COMPONENTS Test) +endif() set(BOOST_MINIMUM_VERSION 1.63.0) -set(QT5_MINIMUM_VERSION 5.15.0) +set(BOOST_COMPONENTS filesystem iostreams thread) +set(BOOST_HUNTER_COMPONENTS ${BOOST_COMPONENTS}) +if(ENABLE_TESTS) + list(APPEND BOOST_COMPONENTS unit_test_framework) + list(APPEND BOOST_HUNTER_COMPONENTS test) +endif() find_package(Atomics REQUIRED) -find_package(Boost ${BOOST_MINIMUM_VERSION} COMPONENTS filesystem iostreams thread REQUIRED) -find_package(CURL COMPONENTS HTTP HTTPS SSL REQUIRED) -find_package(OpenSSL REQUIRED) find_package(Threads REQUIRED) -find_package(libzip REQUIRED) if(SYSTEM_BDB) find_package(BerkeleyDB 5.3...<5.4 COMPONENTS CXX REQUIRED) else() - find_program(MAKE_EXE NAMES gmake nmake make) + find_program(SH_EXE NAMES sh bash REQUIRED) + find_program(MAKE_EXE NAMES gmake nmake make REQUIRED) endif() if(SYSTEM_LEVELDB) @@ -114,27 +161,52 @@ if(SYSTEM_UNIVALUE) pkg_check_modules(UNIVALUE REQUIRED IMPORTED_TARGET libunivalue) endif() -if(ENABLE_GUI) - find_package(Qt5 ${QT5_MINIMUM_VERSION} REQUIRED COMPONENTS - Concurrent - Core - Gui - LinguistTools - Network - Widgets - ) - - if(USE_DBUS) - find_package(Qt5 ${QT5_MINIMUM_VERSION} COMPONENTS DBus REQUIRED) - endif() +if(BUNDLED_BOOST) + hunter_add_package(Boost COMPONENTS ${BOOST_HUNTER_COMPONENTS}) +endif() +find_package(Boost ${BOOST_MINIMUM_VERSION} COMPONENTS ${BOOST_COMPONENTS} CONFIG REQUIRED) + +if(BUNDLED_OPENSSL) + hunter_add_package(OpenSSL) +endif() +find_package(OpenSSL REQUIRED) + +if(BUNDLED_CURL) + hunter_add_package(CURL) + find_package(CURL CONFIG REQUIRED) +else() + find_package(CURL REQUIRED) +endif() + +if(BUNDLED_LIBZIP) + hunter_add_package(libzip) +endif() +find_package(libzip CONFIG REQUIRED) + +if(USE_ASM) + enable_language(ASM) +endif() - if(ENABLE_TESTS) - find_package(Qt5 ${QT5_MINIMUM_VERSION} COMPONENTS Test REQUIRED) +if(ENABLE_GUI) + if(BUNDLED_QT) + hunter_add_package(Qt COMPONENTS ${QT5_HUNTER_COMPONENTS}) endif() + find_package(Qt5 ${QT5_MINIMUM_VERSION} COMPONENTS ${QT5_COMPONENTS} REQUIRED) if(ENABLE_QRENCODE) pkg_check_modules(QRENCODE REQUIRED IMPORTED_TARGET libqrencode) endif() + + # Compatibility macros + if(Qt5Core_VERSION VERSION_LESS 5.15.0) + macro(qt_create_translation) + qt5_create_translation(${ARGN}) + endmacro() + + macro(qt_add_translation) + qt5_add_translation(${ARGN}) + endmacro() + endif() endif() if(ENABLE_UPNP) @@ -142,7 +214,6 @@ if(ENABLE_UPNP) endif() if(ENABLE_TESTS) - find_package(Boost ${BOOST_MINIMUM_VERSION} COMPONENTS unit_test_framework REQUIRED) enable_testing() if(SYSTEM_XXD) @@ -175,12 +246,25 @@ endif() set(CMAKE_POSITION_INDEPENDENT_CODE ${ENABLE_PIE}) # Set compiler flags -if (APPLE) +if(APPLE) add_compile_options(-Wno-error=deprecated-declarations) add_compile_options(-Wno-error=thread-safety-analysis) add_compile_options(-Wno-error=thread-safety-reference) endif() +if(STATIC_LIBS) + set(CMAKE_LINK_SEARCH_START_STATIC ON) + set(CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_STATIC_LIBRARY_SUFFIX}") +endif() + +if(STATIC_RUNTIME) + if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)\$") + list(APPEND RUNTIME_LIBS -static-libgcc -static-libstdc++) + elseif(MSVC) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() +endif() + # Set endianness if(CMAKE_CXX_BYTE_ORDER EQUAL BIG_ENDIAN) set(WORDS_BIGENDIAN 1) diff --git a/build-aux/cmake/Hunter/config.cmake b/build-aux/cmake/Hunter/config.cmake new file mode 100644 index 0000000000..e69de29bb2 diff --git a/build-aux/cmake/HunterGate.cmake b/build-aux/cmake/HunterGate.cmake new file mode 100644 index 0000000000..17c6d38038 --- /dev/null +++ b/build-aux/cmake/HunterGate.cmake @@ -0,0 +1,543 @@ +# Copyright (c) 2013-2019, Ruslan Baratov +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# This is a gate file to Hunter package manager. +# Include this file using `include` command and add package you need, example: +# +# cmake_minimum_required(VERSION 3.5) +# +# include("cmake/HunterGate.cmake") +# HunterGate( +# URL "https://github.com/path/to/hunter/archive.tar.gz" +# SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d" +# ) +# +# project(MyProject) +# +# hunter_add_package(Foo) +# hunter_add_package(Boo COMPONENTS Bar Baz) +# +# Projects: +# * https://github.com/cpp-pm/gate/ +# * https://github.com/cpp-pm/hunter + +option(HUNTER_ENABLED "Enable Hunter package manager support" ON) + +if(HUNTER_ENABLED) + if(CMAKE_VERSION VERSION_LESS "3.5") + message( + FATAL_ERROR + "At least CMake version 3.5 required for Hunter dependency management." + " Update CMake or set HUNTER_ENABLED to OFF." + ) + endif() +endif() + +include(CMakeParseArguments) # cmake_parse_arguments + +option(HUNTER_STATUS_PRINT "Print working status" ON) +option(HUNTER_STATUS_DEBUG "Print a lot info" OFF) +option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON) +set(HUNTER_ROOT "" CACHE FILEPATH "Override the HUNTER_ROOT.") + +set(HUNTER_ERROR_PAGE "https://hunter.readthedocs.io/en/latest/reference/errors") + +function(hunter_gate_status_print) + if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG) + foreach(print_message ${ARGV}) + message(STATUS "[hunter] ${print_message}") + endforeach() + endif() +endfunction() + +function(hunter_gate_status_debug) + if(HUNTER_STATUS_DEBUG) + foreach(print_message ${ARGV}) + string(TIMESTAMP timestamp) + message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}") + endforeach() + endif() +endfunction() + +function(hunter_gate_error_page error_page) + message("------------------------------ ERROR ------------------------------") + message(" ${HUNTER_ERROR_PAGE}/${error_page}.html") + message("-------------------------------------------------------------------") + message("") + message(FATAL_ERROR "") +endfunction() + +function(hunter_gate_internal_error) + message("") + foreach(print_message ${ARGV}) + message("[hunter ** INTERNAL **] ${print_message}") + endforeach() + message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") + message("") + hunter_gate_error_page("error.internal") +endfunction() + +function(hunter_gate_fatal_error) + cmake_parse_arguments(hunter "" "ERROR_PAGE" "" "${ARGV}") + if("${hunter_ERROR_PAGE}" STREQUAL "") + hunter_gate_internal_error("Expected ERROR_PAGE") + endif() + message("") + foreach(x ${hunter_UNPARSED_ARGUMENTS}) + message("[hunter ** FATAL ERROR **] ${x}") + endforeach() + message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") + message("") + hunter_gate_error_page("${hunter_ERROR_PAGE}") +endfunction() + +function(hunter_gate_user_error) + hunter_gate_fatal_error(${ARGV} ERROR_PAGE "error.incorrect.input.data") +endfunction() + +function(hunter_gate_self root version sha1 result) + string(COMPARE EQUAL "${root}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("root is empty") + endif() + + string(COMPARE EQUAL "${version}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("version is empty") + endif() + + string(COMPARE EQUAL "${sha1}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("sha1 is empty") + endif() + + string(SUBSTRING "${sha1}" 0 7 archive_id) + + if(EXISTS "${root}/cmake/Hunter") + set(hunter_self "${root}") + else() + set( + hunter_self + "${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked" + ) + endif() + + set("${result}" "${hunter_self}" PARENT_SCOPE) +endfunction() + +# Set HUNTER_GATE_ROOT cmake variable to suitable value. +function(hunter_gate_detect_root) + # Check CMake variable + if(HUNTER_ROOT) + set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE) + hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable") + return() + endif() + + # Check environment variable + if(DEFINED ENV{HUNTER_ROOT}) + set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE) + hunter_gate_status_debug("HUNTER_ROOT detected by environment variable") + return() + endif() + + # Check HOME environment variable + if(DEFINED ENV{HOME}) + set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE) + hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable") + return() + endif() + + # Check SYSTEMDRIVE and USERPROFILE environment variable (windows only) + if(WIN32) + if(DEFINED ENV{SYSTEMDRIVE}) + set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE) + hunter_gate_status_debug( + "HUNTER_ROOT set using SYSTEMDRIVE environment variable" + ) + return() + endif() + + if(DEFINED ENV{USERPROFILE}) + set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE) + hunter_gate_status_debug( + "HUNTER_ROOT set using USERPROFILE environment variable" + ) + return() + endif() + endif() + + hunter_gate_fatal_error( + "Can't detect HUNTER_ROOT" + ERROR_PAGE "error.detect.hunter.root" + ) +endfunction() + +function(hunter_gate_download dir) + string( + COMPARE + NOTEQUAL + "$ENV{HUNTER_DISABLE_AUTOINSTALL}" + "" + disable_autoinstall + ) + if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL) + hunter_gate_fatal_error( + "Hunter not found in '${dir}'" + "Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'" + "Settings:" + " HUNTER_ROOT: ${HUNTER_GATE_ROOT}" + " HUNTER_SHA1: ${HUNTER_GATE_SHA1}" + ERROR_PAGE "error.run.install" + ) + endif() + string(COMPARE EQUAL "${dir}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("Empty 'dir' argument") + endif() + + string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("HUNTER_GATE_SHA1 empty") + endif() + + string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("HUNTER_GATE_URL empty") + endif() + + set(done_location "${dir}/DONE") + set(sha1_location "${dir}/SHA1") + + set(build_dir "${dir}/Build") + set(cmakelists "${dir}/CMakeLists.txt") + + hunter_gate_status_debug("Locking directory: ${dir}") + file(LOCK "${dir}" DIRECTORY GUARD FUNCTION) + hunter_gate_status_debug("Lock done") + + if(EXISTS "${done_location}") + # while waiting for lock other instance can do all the job + hunter_gate_status_debug("File '${done_location}' found, skip install") + return() + endif() + + file(REMOVE_RECURSE "${build_dir}") + file(REMOVE_RECURSE "${cmakelists}") + + file(MAKE_DIRECTORY "${build_dir}") # check directory permissions + + # Disabling languages speeds up a little bit, reduces noise in the output + # and avoids path too long windows error + file( + WRITE + "${cmakelists}" + "cmake_minimum_required(VERSION 3.5)\n" + "if(POLICY CMP0114)\n" + " cmake_policy(SET CMP0114 NEW)\n" + "endif()\n" + "if(POLICY CMP0135)\n" + " cmake_policy(SET CMP0135 NEW)\n" + "endif()\n" + "project(HunterDownload LANGUAGES NONE)\n" + "include(ExternalProject)\n" + "ExternalProject_Add(\n" + " Hunter\n" + " URL\n" + " \"${HUNTER_GATE_URL}\"\n" + " URL_HASH\n" + " SHA1=${HUNTER_GATE_SHA1}\n" + " DOWNLOAD_DIR\n" + " \"${dir}\"\n" + " TLS_VERIFY\n" + " ${HUNTER_TLS_VERIFY}\n" + " SOURCE_DIR\n" + " \"${dir}/Unpacked\"\n" + " CONFIGURE_COMMAND\n" + " \"\"\n" + " BUILD_COMMAND\n" + " \"\"\n" + " INSTALL_COMMAND\n" + " \"\"\n" + ")\n" + ) + + if(HUNTER_STATUS_DEBUG) + set(logging_params "") + else() + set(logging_params OUTPUT_QUIET) + endif() + + hunter_gate_status_debug("Run generate") + + # Need to add toolchain file too. + # Otherwise on Visual Studio + MDD this will fail with error: + # "Could not find an appropriate version of the Windows 10 SDK installed on this machine" + if(EXISTS "${CMAKE_TOOLCHAIN_FILE}") + get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE) + set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}") + else() + # 'toolchain_arg' can't be empty + set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=") + endif() + + string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make) + if(no_make) + set(make_arg "") + else() + # Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM + set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}") + endif() + + execute_process( + COMMAND + "${CMAKE_COMMAND}" + "-H${dir}" + "-B${build_dir}" + "-G${CMAKE_GENERATOR}" + "${toolchain_arg}" + ${make_arg} + WORKING_DIRECTORY "${dir}" + RESULT_VARIABLE download_result + ${logging_params} + ) + + if(NOT download_result EQUAL 0) + hunter_gate_internal_error( + "Configure project failed." + "To reproduce the error run: ${CMAKE_COMMAND} -H${dir} -B${build_dir} -G${CMAKE_GENERATOR} ${toolchain_arg} ${make_arg}" + "In directory ${dir}" + ) + endif() + + hunter_gate_status_print( + "Initializing Hunter workspace (${HUNTER_GATE_SHA1})" + " ${HUNTER_GATE_URL}" + " -> ${dir}" + ) + execute_process( + COMMAND "${CMAKE_COMMAND}" --build "${build_dir}" + WORKING_DIRECTORY "${dir}" + RESULT_VARIABLE download_result + ${logging_params} + ) + + if(NOT download_result EQUAL 0) + hunter_gate_internal_error("Build project failed") + endif() + + file(REMOVE_RECURSE "${build_dir}") + file(REMOVE_RECURSE "${cmakelists}") + + file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}") + file(WRITE "${done_location}" "DONE") + + hunter_gate_status_debug("Finished") +endfunction() + +# Must be a macro so master file 'cmake/Hunter' can +# apply all variables easily just by 'include' command +# (otherwise PARENT_SCOPE magic needed) +macro(HunterGate) + if(HUNTER_GATE_DONE) + # variable HUNTER_GATE_DONE set explicitly for external project + # (see `hunter_download`) + set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) + endif() + + # First HunterGate command will init Hunter, others will be ignored + get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET) + + if(NOT HUNTER_ENABLED) + # Empty function to avoid error "unknown function" + function(hunter_add_package) + endfunction() + + set( + _hunter_gate_disabled_mode_dir + "${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode" + ) + if(EXISTS "${_hunter_gate_disabled_mode_dir}") + hunter_gate_status_debug( + "Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}" + ) + list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}") + endif() + elseif(_hunter_gate_done) + hunter_gate_status_debug("Secondary HunterGate (use old settings)") + hunter_gate_self( + "${HUNTER_CACHED_ROOT}" + "${HUNTER_VERSION}" + "${HUNTER_SHA1}" + _hunter_self + ) + include("${_hunter_self}/cmake/Hunter") + else() + set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}") + + string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name) + if(_have_project_name) + hunter_gate_fatal_error( + "Please set HunterGate *before* 'project' command. " + "Detected project: ${PROJECT_NAME}" + ERROR_PAGE "error.huntergate.before.project" + ) + endif() + + cmake_parse_arguments( + HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV} + ) + + string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1) + string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url) + string( + COMPARE + NOTEQUAL + "${HUNTER_GATE_UNPARSED_ARGUMENTS}" + "" + _have_unparsed + ) + string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global) + string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath) + + if(_have_unparsed) + hunter_gate_user_error( + "HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}" + ) + endif() + if(_empty_sha1) + hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory") + endif() + if(_empty_url) + hunter_gate_user_error("URL suboption of HunterGate is mandatory") + endif() + if(_have_global) + if(HUNTER_GATE_LOCAL) + hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)") + endif() + if(_have_filepath) + hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)") + endif() + endif() + if(HUNTER_GATE_LOCAL) + if(_have_global) + hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)") + endif() + if(_have_filepath) + hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)") + endif() + endif() + if(_have_filepath) + if(_have_global) + hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)") + endif() + if(HUNTER_GATE_LOCAL) + hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)") + endif() + endif() + + hunter_gate_detect_root() # set HUNTER_GATE_ROOT + + # Beautify path, fix probable problems with windows path slashes + get_filename_component( + HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE + ) + hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}") + if(NOT HUNTER_ALLOW_SPACES_IN_PATH) + string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces) + if(NOT _contain_spaces EQUAL -1) + hunter_gate_fatal_error( + "HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces." + "Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error" + "(Use at your own risk!)" + ERROR_PAGE "error.spaces.in.hunter.root" + ) + endif() + endif() + + string( + REGEX + MATCH + "[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*" + HUNTER_GATE_VERSION + "${HUNTER_GATE_URL}" + ) + string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty) + if(_is_empty) + set(HUNTER_GATE_VERSION "unknown") + endif() + + hunter_gate_self( + "${HUNTER_GATE_ROOT}" + "${HUNTER_GATE_VERSION}" + "${HUNTER_GATE_SHA1}" + _hunter_self + ) + + set(_master_location "${_hunter_self}/cmake/Hunter") + if(EXISTS "${HUNTER_GATE_ROOT}/cmake/Hunter") + # Hunter downloaded manually (e.g. by 'git clone') + set(_unused "xxxxxxxxxx") + set(HUNTER_GATE_SHA1 "${_unused}") + set(HUNTER_GATE_VERSION "${_unused}") + else() + get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE) + set(_done_location "${_archive_id_location}/DONE") + set(_sha1_location "${_archive_id_location}/SHA1") + + # Check Hunter already downloaded by HunterGate + if(NOT EXISTS "${_done_location}") + hunter_gate_download("${_archive_id_location}") + endif() + + if(NOT EXISTS "${_done_location}") + hunter_gate_internal_error("hunter_gate_download failed") + endif() + + if(NOT EXISTS "${_sha1_location}") + hunter_gate_internal_error("${_sha1_location} not found") + endif() + file(READ "${_sha1_location}" _sha1_value) + string(TOLOWER "${_sha1_value}" _sha1_value_lower) + string(TOLOWER "${HUNTER_GATE_SHA1}" _HUNTER_GATE_SHA1_lower) + string(COMPARE EQUAL "${_sha1_value_lower}" "${_HUNTER_GATE_SHA1_lower}" _is_equal) + if(NOT _is_equal) + hunter_gate_internal_error( + "Short SHA1 collision:" + " ${_sha1_value} (from ${_sha1_location})" + " ${HUNTER_GATE_SHA1} (HunterGate)" + ) + endif() + if(NOT EXISTS "${_master_location}") + hunter_gate_user_error( + "Master file not found:" + " ${_master_location}" + "try to update Hunter/HunterGate" + ) + endif() + endif() + include("${_master_location}") + set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) + endif() +endmacro() diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh new file mode 100644 index 0000000000..4426072659 --- /dev/null +++ b/ci/test/00_setup_env_native_asan.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2019-2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://opensource.org/licenses/mit-license.php. + +export LC_ALL=C.UTF-8 + +export CONTAINER_NAME=ci_native_asan +export PACKAGES="clang llvm libqt5gui5 libqt5core5a qtbase5-dev libqt5dbus5 qttools5-dev qttools5-dev-tools libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-iostreams-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libqrencode-dev libzip-dev zlib1g zlib1g-dev libcurl4 libcurl4-openssl-dev" +export DOCKER_NAME_TAG=ubuntu:22.04 +export NO_DEPENDS=1 +export GOAL="install" +export GRIDCOIN_CONFIG="--with-incompatible-bdb --with-gui=qt5 CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' --with-sanitizers=address,integer,undefined CC=clang CXX=clang++" diff --git a/ci/test/00_setup_env_native_tsan.sh b/ci/test/00_setup_env_native_tsan.sh new file mode 100644 index 0000000000..206af61e08 --- /dev/null +++ b/ci/test/00_setup_env_native_tsan.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2019-2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://opensource.org/licenses/mit-license.php. + +export LC_ALL=C.UTF-8 + +export CONTAINER_NAME=ci_native_tsan +export DOCKER_NAME_TAG=ubuntu:22.04 +export PACKAGES="clang-13 llvm-13 libc++abi-13-dev libc++-13-dev" +export DEP_OPTS="CC=clang-13 CXX='clang++-13 -stdlib=libc++'" +export GOAL="install" +export GRIDCOIN_CONFIG="CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER -DDEBUG_LOCKCONTENTION' CXXFLAGS='-g' --with-sanitizers=thread CC=clang-13 CXX='clang++-13 -stdlib=libc++'" diff --git a/ci/test/04_install.sh b/ci/test/04_install.sh index 288f60a7d5..d1ba771f6a 100755 --- a/ci/test/04_install.sh +++ b/ci/test/04_install.sh @@ -17,6 +17,11 @@ fi mkdir -p "${CCACHE_DIR}" mkdir -p "${PREVIOUS_RELEASES_DIR}" +export ASAN_OPTIONS="detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1" +export LSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/lsan" +export TSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/tsan:halt_on_error=1:log_path=${BASE_SCRATCH_DIR}/sanitizer-output/tsan" +export UBSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1" + env | grep -E '^(GRIDCOIN_CONFIG|BASE_|QEMU_|CCACHE_|LC_ALL|BOOST_TEST_RANDOM|DEBIAN_FRONTEND|CONFIG_SHELL|(ASAN|LSAN|TSAN|UBSAN)_OPTIONS|PREVIOUS_RELEASES_DIR)' | tee /tmp/env if [[ $HOST = *-mingw32 ]]; then DOCKER_ADMIN="--cap-add SYS_ADMIN" diff --git a/configure.ac b/configure.ac index fa97189dad..f2ff757821 100755 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 5) define(_CLIENT_VERSION_MINOR, 4) -define(_CLIENT_VERSION_REVISION, 7) +define(_CLIENT_VERSION_REVISION, 8) define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2024) @@ -209,6 +209,12 @@ AC_ARG_ENABLE([debug], [enable_debug=$enableval], [enable_debug=no]) +dnl Enable different -fsanitize options +AC_ARG_WITH([sanitizers], + [AS_HELP_STRING([--with-sanitizers], + [comma separated list of extra sanitizers to build with (default is none enabled)])], + [use_sanitizers=$withval]) + # Turn warnings into errors AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], @@ -230,6 +236,33 @@ if test "$enable_debug" = "yes"; then fi fi +if test "$use_sanitizers" != ""; then + dnl First check if the compiler accepts flags. If an incompatible pair like + dnl -fsanitize=address,thread is used here, this check will fail. This will also + dnl fail if a bad argument is passed, e.g. -fsanitize=undfeined + AX_CHECK_COMPILE_FLAG( + [-fsanitize=$use_sanitizers], + [SANITIZER_CXXFLAGS="-fsanitize=$use_sanitizers"], + [AC_MSG_ERROR([compiler did not accept requested flags])]) + + dnl Some compilers (e.g. GCC) require additional libraries like libasan, + dnl libtsan, libubsan, etc. Make sure linking still works with the sanitize + dnl flag. This is a separate check so we can give a better error message when + dnl the sanitize flags are supported by the compiler but the actual sanitizer + dnl libs are missing. + AX_CHECK_LINK_FLAG( + [-fsanitize=$use_sanitizers], + [SANITIZER_LDFLAGS="-fsanitize=$use_sanitizers"], + [AC_MSG_ERROR([linker did not accept requested flags, you are missing required libraries])], + [], + [AC_LANG_PROGRAM([[ + #include + #include + extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { return 0; } + __attribute__((weak)) // allow for libFuzzer linking + ]],[[]])]) +fi + ERROR_CXXFLAGS= if test "$enable_werror" = "yes"; then if test "$CXXFLAG_WERROR" = ""; then @@ -278,6 +311,16 @@ if test "$CXXFLAGS_overridden" = "no"; then AX_CHECK_COMPILE_FLAG([-Wdeprecated-copy],[CXXFLAGS="$CXXFLAGS -Wno-deprecated-copy"],,[[$CXXFLAG_WERROR]]) fi + +dnl x87 FP operations on x86 hosts can break assumptions made about the floating point values. +dnl See the commit message which introduced this change for more details. +case $host in + i?86-*|x86_64-*) + AX_CHECK_COMPILE_FLAG([-msse2],[CXXFLAGS="$CXXFLAGS -msse2 -mfpmath=sse"],[AC_MSG_ERROR([SSE2 support is required on x86 targets.])],[[$CXXFLAG_WERROR]]) + ;; + *) ;; +esac + enable_sse42=no enable_sse41=no enable_avx2=no @@ -1009,10 +1052,20 @@ dnl Check for Boost libs AX_BOOST_BASE([MINIMUM_REQUIRED_BOOST]) AX_BOOST_SYSTEM AX_BOOST_FILESYSTEM -AX_BOOST_THREAD AX_BOOST_ZLIB AX_BOOST_IOSTREAMS +dnl Prevent use of std::unary_function, which was removed in C++17, +dnl and will generate warnings with newer compilers for Boost +dnl older than 1.80. +dnl See: https://github.com/boostorg/config/pull/430. +AX_CHECK_PREPROC_FLAG([-DBOOST_NO_CXX98_FUNCTION_BASE], [BOOST_CPPFLAGS="$BOOST_CPPFLAGS -DBOOST_NO_CXX98_FUNCTION_BASE"], [], [$CXXFLAG_WERROR], + [AC_LANG_PROGRAM([[#include ]])]) + +dnl AX_BOOST_THREAD check also triggers the same issue, so it has to be checked after +dnl setting -DBOOST_NO_CXX98_FUNCTION_BASE. +AX_BOOST_THREAD + dnl Boost 1.56 through 1.62 allow using std::atomic instead of its own atomic dnl counter implementations. In 1.63 and later the std::atomic approach is default. m4_pattern_allow(DBOOST_AC_USE_STD_ATOMIC) dnl otherwise it's treated like a macro @@ -1292,6 +1345,8 @@ AC_SUBST(HARDENED_CPPFLAGS) AC_SUBST(HARDENED_LDFLAGS) AC_SUBST(PIC_FLAGS) AC_SUBST(PIE_FLAGS) +AC_SUBST(SANITIZER_CXXFLAGS) +AC_SUBST(SANITIZER_LDFLAGS) AC_SUBST(SSE42_CXXFLAGS) AC_SUBST(SSE41_CXXFLAGS) AC_SUBST(AVX2_CXXFLAGS) @@ -1398,6 +1453,7 @@ echo " with zmq = $use_zmq" echo " with test = $use_tests" echo " with bench = $use_bench" echo " with upnp = $use_upnp" +echo " sanitizers = $use_sanitizers" echo " debug enabled = $enable_debug" echo " werror = $enable_werror" echo diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index eef86df98a..a6f2dece64 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -28,7 +28,7 @@ ifneq (,$(findstring clang,$($(package)_cxx))) endif $(package)_archiver_$(host_os)=$($(package)_ar) $(package)_config_libraries=filesystem,system,thread,test,iostreams -$(package)_cxxflags+=-std=c++17 +$(package)_cxxflags+=-std=c++17 -DBOOST_NO_CXX98_FUNCTION_BASE $(package)_cxxflags_linux=-fPIC $(package)_cxxflags_android=-fPIC endef diff --git a/depends/packages/libzip.mk b/depends/packages/libzip.mk index 11ffdd414b..507d768ad2 100644 --- a/depends/packages/libzip.mk +++ b/depends/packages/libzip.mk @@ -42,5 +42,5 @@ define $(package)_build_cmds endef define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install + $(MAKE) DESTDIR=$($(package)_staging_dir) install -j1 endef diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 3f721988a1..d92752496d 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -17,6 +17,7 @@ $(package)_patches += qtbase-moc-ignore-gcc-macro.patch $(package)_patches += no_qrhi.patch $(package)_patches += drop_lrelease_dependency.patch $(package)_patches += subdirs.pro +$(package)_patches += fix-macos-linker.patch $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) $(package)_qttranslations_sha256_hash=c92af4171397a0ed272330b4fa0669790fcac8d050b07c8b8cc565ebeba6735e @@ -255,6 +256,7 @@ endef # CROSS_LIBRARY_PATH. See #15277. define $(package)_preprocess_cmds rm -f $(BASEDIR)/.qmake.stash && \ + patch -p1 -i $($(package)_patch_dir)/fix-macos-linker.patch && \ patch -p1 -i $($(package)_patch_dir)/drop_lrelease_dependency.patch && \ patch -p1 -i $($(package)_patch_dir)/dont_hardcode_pwd.patch && \ patch -p1 -i $($(package)_patch_dir)/dont_hardcode_x86_64.patch && \ diff --git a/depends/patches/qt/fix-macos-linker.patch b/depends/patches/qt/fix-macos-linker.patch new file mode 100644 index 0000000000..e439685656 --- /dev/null +++ b/depends/patches/qt/fix-macos-linker.patch @@ -0,0 +1,55 @@ +qmake: Don't error out if QMAKE_DEFAULT_LIBDIRS is empty on macOS + +The new linker in Xcode 15 doesn't provide any default linker or +framework paths when requested via -v, but still seems to use the +default paths documented in the ld man page. + +We trust that linker will do the right thing, even if we don't +know of its default linker paths. + +We also need to opt out of the default fallback logic to +set the libdirs to /lib and /usr/lib. + +This may result in UnixMakefileGenerator::findLibraries finding +different libraries than expected, if additional paths are +passed with -L, which will then take precedence for qmake, +even if the linker itself will use the library from the +SDK's default paths. This should hopefully not be an issue +in practice, as we don't turn -lFoo into absolute paths in +qmake, so the only risk is that we're picking up the wrong +prl files and adding additional dependencies that the lib +in the SDK doesn't have. + +Upstream commits: + - Qt 5.15.16: Not yet publicly available. + - Qt dev: cdf64b0e47115cc473e1afd1472b4b09e130b2a5 + +For other Qt branches see +https://codereview.qt-project.org/q/I2347b26e2df0828471373b0e15b8c9089274c65d + +--- old/qtbase/mkspecs/features/toolchain.prf ++++ new/qtbase/mkspecs/features/toolchain.prf +@@ -288,9 +288,12 @@ isEmpty($${target_prefix}.INCDIRS) { + } + } + } +- isEmpty(QMAKE_DEFAULT_LIBDIRS)|isEmpty(QMAKE_DEFAULT_INCDIRS): \ ++ isEmpty(QMAKE_DEFAULT_INCDIRS): \ + !integrity: \ +- error("failed to parse default search paths from compiler output") ++ error("failed to parse default include paths from compiler output") ++ isEmpty(QMAKE_DEFAULT_LIBDIRS): \ ++ !integrity:!darwin: \ ++ error("failed to parse default library paths from compiler output") + QMAKE_DEFAULT_LIBDIRS = $$unique(QMAKE_DEFAULT_LIBDIRS) + } else: ghs { + cmd = $$QMAKE_CXX $$QMAKE_CXXFLAGS -$${LITERAL_HASH} -o /tmp/fake_output /tmp/fake_input.cpp +@@ -412,7 +415,7 @@ isEmpty($${target_prefix}.INCDIRS) { + QMAKE_DEFAULT_INCDIRS = $$split(INCLUDE, $$QMAKE_DIRLIST_SEP) + } + +- unix:if(!cross_compile|host_build) { ++ unix:!darwin:if(!cross_compile|host_build) { + isEmpty(QMAKE_DEFAULT_INCDIRS): QMAKE_DEFAULT_INCDIRS = /usr/include /usr/local/include + isEmpty(QMAKE_DEFAULT_LIBDIRS): QMAKE_DEFAULT_LIBDIRS = /lib /usr/lib + } diff --git a/doc/README.md b/doc/README.md index eaa6ad72bd..f6eab9e075 100644 --- a/doc/README.md +++ b/doc/README.md @@ -27,6 +27,7 @@ The following are developer notes on how to build Gridcoin on your native platfo - [OS X Build Notes](build-macos.md) - [Unix Build Notes](build-unix.md) - [Windows Build Notes](build-windows.md) +- [Windows (MSYS2) Build Notes](build-msys2.md) - [FreeBSD Build Notes](build-freebsd.md) - [OpenBSD Build Notes](build-openbsd.md) diff --git a/doc/build-msys2.md b/doc/build-msys2.md new file mode 100644 index 0000000000..d4c885a233 --- /dev/null +++ b/doc/build-msys2.md @@ -0,0 +1,53 @@ +Windows (MSYS2) Build Notes +=========================== + +This guide describes how to build gridcoinresearchd, command-line utilities, and GUI on Windows. + +Preparing the Build +------------------- + +First, install [MSYS2](https://www.msys2.org/). All commands below are supposed to be run in the **MSYS2 UCRT64** shell. + +Run the following to install the base dependencies for building: + +```bash +pacman -S make ninja pactoys +pacboy -S boost:p cmake:p curl:p libzip:p openssl:p toolchain:p +pacboy -S qrencode:p qt5-base:p qt5-tools:p # optional for the GUI +``` + +To Build +-------- + +### 1. Configuration + +To configure with gridcoinresearchd: + +```bash +mkdir build && cd build +cmake .. -DBUILD_SHARED_LIBS=OFF +``` + +To configure with GUI: + +```bash +mkdir build && cd build +cmake .. -DENABLE_GUI=ON -DBUILD_SHARED_LIBS=OFF +``` + +> [!NOTE] +> See also: [CMake Build Options](cmake-options.md) + +### 2. Compile + +```bash +cmake --build . # use "-j N" here for N parallel jobs +``` + +### 3. Test + +```bash +cmake .. -DENABLE_TESTS=ON +cmake --build . +ctest . +``` diff --git a/doc/build-windows.md b/doc/build-windows.md index 5f58bf1603..06e13b6943 100644 --- a/doc/build-windows.md +++ b/doc/build-windows.md @@ -1,7 +1,8 @@ WINDOWS BUILD NOTES ==================== -> This document is outdated. +> [!NOTE] +> See also: [Windows (MSYS2) Build Notes](build-msys2.md) Below are some notes on how to build Gridcoin for Windows. diff --git a/doc/cmake-options.md b/doc/cmake-options.md index 95cd8c4163..b38031aad7 100644 --- a/doc/cmake-options.md +++ b/doc/cmake-options.md @@ -23,6 +23,10 @@ ccmake . `cmake .. -DENABLE_PIE=ON -DUSE_ASM=OFF` +* Build a static binary: + + `cmake .. -DSTATIC_LIBS=ON -DSTATIC_RUNTIME=ON` + * Build tests and docs, run `lupdate`: `cmake .. -DENABLE_DOCS=ON -DENABLE_TESTS=ON -DLUPDATE=ON` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 479d210bee..cd42521cce 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -78,10 +78,10 @@ else() endif() add_subdirectory(secp256k1 EXCLUDE_FROM_ALL) - target_include_directories(secp256k1 PUBLIC - "${CMAKE_CURRENT_SOURCE_DIR}/secp256k1/include" + target_include_directories(secp256k1_static PUBLIC + $ ) - set(LIBSECP256K1 secp256k1) + set(LIBSECP256K1 secp256k1_static) endif() if(SYSTEM_UNIVALUE) @@ -221,7 +221,7 @@ target_include_directories(gridcoin_util PUBLIC target_link_libraries(gridcoin_util PUBLIC -lm ${ATOMICS_LIBRARIES} ${RT_LIBRARIES} ${LIBBDB_CXX} ${LIBLEVELDB} ${LIBSECP256K1} ${LIBUNIVALUE} - Boost::filesystem Boost::headers Boost::iostreams Boost::thread + Boost::boost Boost::filesystem Boost::iostreams Boost::thread CURL::libcurl OpenSSL::Crypto OpenSSL::SSL Threads::Threads @@ -229,6 +229,10 @@ target_link_libraries(gridcoin_util PUBLIC ) target_link_libraries(gridcoin_util PUBLIC ${LIBGRIDCOIN_CRYPTO}) +if(WIN32) + target_link_libraries(gridcoin_util PUBLIC wsock32 Ws2_32) +endif() + target_compile_definitions(gridcoin_util PUBLIC HAVE_CONFIG_H HAVE_BUILD_INFO @@ -254,10 +258,13 @@ endif() if(ENABLE_DAEMON) add_executable(gridcoinresearchd gridcoinresearchd.cpp) - target_link_libraries(gridcoinresearchd PUBLIC gridcoin_util) + target_link_libraries(gridcoinresearchd PUBLIC ${RUNTIME_LIBS} gridcoin_util) if(WIN32) target_sources(gridcoinresearchd PRIVATE gridcoinresearchd-res.rc) + set_source_files_properties(gridcoinresearchd-res.rc PROPERTIES + COMPILE_DEFINITIONS WINDRES_PREPROC + ) endif() if(UNIX AND NOT APPLE) @@ -266,10 +273,10 @@ if(ENABLE_DAEMON) DESTINATION "${CMAKE_INSTALL_BINDIR}" ) install(FILES "${CMAKE_SOURCE_DIR}/doc/gridcoinresearchd.1" - DESTINATION "${CMAKE_INSTALL_MANDIR}" + DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" ) install(FILES "${CMAKE_SOURCE_DIR}/contrib/init/gridcoinresearchd.service" - DESTINATION /lib/systemd/system + DESTINATION "lib/systemd/system" ) endif() endif() diff --git a/src/Makefile.am b/src/Makefile.am index c521bd11fe..b92061afbc 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,8 +8,8 @@ print-%: FORCE DIST_SUBDIRS = univalue secp256k1 -AM_LDFLAGS = ${libcurl_LIBS} $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) -AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) +AM_LDFLAGS = ${libcurl_LIBS} $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(SANITIZER_LDFLAGS) +AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(SANITIZER_CXXFLAGS) AM_CPPFLAGS = ${libcurl_CFLAGS} $(HARDENED_CPPFLAGS) -DSTATICLIB -DCURL_STATICLIB -DMINIUPNP_STATICLIB -DZIP_STATIC -DNN_STATIC_LIB EXTRA_LIBRARIES = diff --git a/src/bdb53/CMakeLists.txt b/src/bdb53/CMakeLists.txt index 1c98273b73..5af5c845c7 100644 --- a/src/bdb53/CMakeLists.txt +++ b/src/bdb53/CMakeLists.txt @@ -1,7 +1,14 @@ include(ExternalProject) +set(LIBDB_DIST_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dist") +set(LIBDB_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/libdb_build") +set(libdb_cxx_library "${LIBDB_BUILD_DIR}/libdb_cxx.a") +set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM 1) + + # Configure flags # =============== + set(BDB_FLAGS --disable-java --disable-jdbc @@ -13,9 +20,13 @@ if(ENABLE_PIE) --with-pic ) endif() -if(MINGW) +if(WIN32) + # configure script doesn't support DOS-style paths + cmake_path(SET relative_dist_dir "${LIBDB_DIST_DIR}") + cmake_path(RELATIVE_PATH relative_dist_dir BASE_DIRECTORY "${LIBDB_BUILD_DIR}") list(APPEND BDB_FLAGS --enable-mingw + --srcdir "${relative_dist_dir}" ) endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") @@ -40,14 +51,10 @@ set(MAKEOPTS "-j${N}" CACHE STRING "Options for the 'make' program") # External project # ================ -set(LIBDB_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/libdb_build) -set(libdb_cxx_library ${LIBDB_BUILD_DIR}/libdb_cxx.a) -set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM 1) - ExternalProject_Add(BerkeleyDB_Project SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} BINARY_DIR ${LIBDB_BUILD_DIR} - CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/dist/configure ${BDB_FLAGS} + CONFIGURE_COMMAND ${SH_EXE} ${CMAKE_CURRENT_SOURCE_DIR}/dist/configure ${BDB_FLAGS} BUILD_COMMAND COMMAND ${MAKE_EXE} ${MAKEOPTS} clean COMMAND ${MAKE_EXE} ${MAKEOPTS} libdb_cxx.a diff --git a/src/chainparams.cpp b/src/chainparams.cpp index fa9e268052..53d8ca56d8 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -150,6 +150,8 @@ class CMainParams : public CChainParams { {2400000, uint256S("0xf67b595e02e22a02498dfab853e2fabe6e74298a8d83ddc6115c37eaa5808bf6")}, {2600000, uint256S("0xc86624a7f4dde5046d9b62aa2e177a46c60845684702ea3fd49ff40f4f2418f6")}, {2800000, uint256S("0x09af79c7da8880f7aa56687baf59f35a9d489037f3271938f85b3317d08a8476")}, + {3000000, uint256S("0xc991e619995b65af5af5fe13bb8d61d60c8bb0f4eb2f1472abc058c7aac28a13")}, + {3200000, uint256S("0xbf56ac3d117b7e24ca9d2082961a53bb4f77cd43eaa2a036628c97d2e1bc0cd2")}, } }; @@ -220,7 +222,8 @@ class CTestNetParams : public CChainParams { checkpointData = { { - {0, uint256S("00006e037d7b84104208ecf2a8638d23149d712ea810da604ee2f2cb39bae713")}, + {0, uint256S("0x00006e037d7b84104208ecf2a8638d23149d712ea810da604ee2f2cb39bae713")}, + {2400000, uint256S("0x962b7607f8ffceb5c77951d242caed3f94f465f8529d924338700895ff8ed458")}, } }; diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp index 7d15d9d52c..7fbe05adf8 100644 --- a/src/consensus/merkle.cpp +++ b/src/consensus/merkle.cpp @@ -37,118 +37,25 @@ root. */ -/* This implements a constant-space merkle root/path calculator, limited to 2^32 leaves. */ -static void MerkleComputation(const std::vector& leaves, uint256* proot, bool* pmutated, uint32_t branchpos, std::vector* pbranch) { - if (pbranch) pbranch->clear(); - if (leaves.size() == 0) { - if (pmutated) *pmutated = false; - if (proot) *proot = uint256(); - return; - } - bool mutated = false; - // count is the number of leaves processed so far. - uint32_t count = 0; - // inner is an array of eagerly computed subtree hashes, indexed by tree - // level (0 being the leaves). - // For example, when count is 25 (11001 in binary), inner[4] is the hash of - // the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to - // the last leaf. The other inner entries are undefined. - uint256 inner[32]; - // Which position in inner is a hash that depends on the matching leaf. - int matchlevel = -1; - // First process all leaves into 'inner' values. - while (count < leaves.size()) { - uint256 h = leaves[count]; - bool matchh = count == branchpos; - count++; - int level; - // For each of the lower bits in count that are 0, do 1 step. Each - // corresponds to an inner value that existed before processing the - // current leaf, and each needs a hash to combine it. - for (level = 0; !(count & (((uint32_t)1) << level)); level++) { - if (pbranch) { - if (matchh) { - pbranch->push_back(inner[level]); - } else if (matchlevel == level) { - pbranch->push_back(h); - matchh = true; - } +uint256 ComputeMerkleRoot(std::vector hashes, bool* mutated) { + bool mutation = false; + while (hashes.size() > 1) { + if (mutated) { + for (size_t pos = 0; pos + 1 < hashes.size(); pos += 2) { + if (hashes[pos] == hashes[pos + 1]) mutation = true; } - mutated |= (inner[level] == h); - CHash256().Write(inner[level]).Write(h).Finalize(h); - } - // Store the resulting hash at inner position level. - inner[level] = h; - if (matchh) { - matchlevel = level; - } - } - // Do a final 'sweep' over the rightmost branch of the tree to process - // odd levels, and reduce everything to a single top value. - // Level is the level (counted from the bottom) up to which we've sweeped. - int level = 0; - // As long as bit number level in count is zero, skip it. It means there - // is nothing left at this level. - while (!(count & (((uint32_t)1) << level))) { - level++; - } - uint256 h = inner[level]; - bool matchh = matchlevel == level; - while (count != (((uint32_t)1) << level)) { - // If we reach this point, h is an inner value that is not the top. - // We combine it with itself (Bitcoin's special rule for odd levels in - // the tree) to produce a higher level one. - if (pbranch && matchh) { - pbranch->push_back(h); } - CHash256().Write(h).Write(h).Finalize(h); - // Increment count to the value it would have if two entries at this - // level had existed. - count += (((uint32_t)1) << level); - level++; - // And propagate the result upwards accordingly. - while (!(count & (((uint32_t)1) << level))) { - if (pbranch) { - if (matchh) { - pbranch->push_back(inner[level]); - } else if (matchlevel == level) { - pbranch->push_back(h); - matchh = true; - } - } - CHash256().Write(inner[level]).Write(h).Finalize(h); - level++; + if (hashes.size() & 1) { + hashes.push_back(hashes.back()); } + SHA256D64(hashes[0].begin(), hashes[0].begin(), hashes.size() / 2); + hashes.resize(hashes.size() / 2); } - // Return result. - if (pmutated) *pmutated = mutated; - if (proot) *proot = h; -} - -uint256 ComputeMerkleRoot(const std::vector& leaves, bool* mutated) { - uint256 hash; - MerkleComputation(leaves, &hash, mutated, -1, nullptr); - return hash; -} - -std::vector ComputeMerkleBranch(const std::vector& leaves, uint32_t position) { - std::vector ret; - MerkleComputation(leaves, nullptr, nullptr, position, &ret); - return ret; + if (mutated) *mutated = mutation; + if (hashes.size() == 0) return uint256(); + return hashes[0]; } -uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector& vMerkleBranch, uint32_t nIndex) { - uint256 hash = leaf; - for (std::vector::const_iterator it = vMerkleBranch.begin(); it != vMerkleBranch.end(); ++it) { - if (nIndex & 1) { - hash = Hash(*it, hash); - } else { - hash = Hash(hash, *it); - } - nIndex >>= 1; - } - return hash; -} uint256 BlockMerkleRoot(const CBlock& block, bool* mutated) { @@ -157,15 +64,6 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated) for (size_t s = 0; s < block.vtx.size(); s++) { leaves[s] = block.vtx[s].GetHash(); } - return ComputeMerkleRoot(leaves, mutated); + return ComputeMerkleRoot(std::move(leaves), mutated); } -std::vector BlockMerkleBranch(const CBlock& block, uint32_t position) -{ - std::vector leaves; - leaves.resize(block.vtx.size()); - for (size_t s = 0; s < block.vtx.size(); s++) { - leaves[s] = block.vtx[s].GetHash(); - } - return ComputeMerkleBranch(leaves, position); -} diff --git a/src/consensus/merkle.h b/src/consensus/merkle.h index 439e60ed30..beb78eeeda 100644 --- a/src/consensus/merkle.h +++ b/src/consensus/merkle.h @@ -5,15 +5,12 @@ #ifndef BITCOIN_CONSENSUS_MERKLE_H #define BITCOIN_CONSENSUS_MERKLE_H -#include #include #include "main.h" #include -uint256 ComputeMerkleRoot(const std::vector& leaves, bool* mutated = nullptr); -std::vector ComputeMerkleBranch(const std::vector& leaves, uint32_t position); -uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector& branch, uint32_t position); +uint256 ComputeMerkleRoot(std::vector leaves, bool* mutated = nullptr); /* * Compute the Merkle root of the transactions in a block. @@ -21,11 +18,4 @@ uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector BlockMerkleBranch(const CBlock& block, uint32_t position); - #endif // BITCOIN_CONSENSUS_MERKLE_H diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index f7972902be..7f9811e744 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -22,21 +22,27 @@ if(ENABLE_SSE41) list(APPEND LIBGRIDCOIN_CRYPTO gridcoin_crypto_sse41) add_library(gridcoin_crypto_sse41 STATIC sha256_sse41.cpp) target_compile_definitions(gridcoin_crypto_sse41 PRIVATE ENABLE_SSE41) - target_compile_options(gridcoin_crypto_sse41 PRIVATE -msse4.1) + if(NOT MSVC) + target_compile_options(gridcoin_crypto_sse41 PRIVATE -msse4.1) + endif() endif() if(ENABLE_AVX2) list(APPEND LIBGRIDCOIN_CRYPTO gridcoin_crypto_avx2) add_library(gridcoin_crypto_avx2 STATIC sha256_avx2.cpp) target_compile_definitions(gridcoin_crypto_avx2 PRIVATE ENABLE_AVX2) - target_compile_options(gridcoin_crypto_avx2 PRIVATE -mavx -mavx2) + if(NOT MSVC) + target_compile_options(gridcoin_crypto_avx2 PRIVATE -mavx -mavx2) + endif() endif() if(ENABLE_X86_SHANI) list(APPEND LIBGRIDCOIN_CRYPTO gridcoin_crypto_x86_shani) add_library(gridcoin_crypto_x86_shani STATIC sha256_x86_shani.cpp) target_compile_definitions(gridcoin_crypto_x86_shani PRIVATE ENABLE_X86_SHANI) - target_compile_options(gridcoin_crypto_x86_shani PRIVATE -msse4 -msha) + if(NOT MSVC) + target_compile_options(gridcoin_crypto_x86_shani PRIVATE -msse4 -msha) + endif() endif() if(ENABLE_ARM_SHANI) diff --git a/src/gridcoin/beacon.cpp b/src/gridcoin/beacon.cpp index e529fdff07..3fdcfc9e61 100644 --- a/src/gridcoin/beacon.cpp +++ b/src/gridcoin/beacon.cpp @@ -1391,21 +1391,6 @@ BeaconRegistry::BeaconDB &BeaconRegistry::GetBeaconDB() return m_beacon_db; } -// This is static and called by the scheduler. -void BeaconRegistry::RunDBPassivation() -{ - TRY_LOCK(cs_main, locked_main); - - if (!locked_main) - { - return; - } - - BeaconRegistry& beacons = GetBeaconRegistry(); - - beacons.PassivateDB(); -} - template<> const std::string BeaconRegistry::BeaconDB::KeyType() { return std::string("beacon"); diff --git a/src/gridcoin/beacon.h b/src/gridcoin/beacon.h index 64970d7bac..e97d1fd0f3 100644 --- a/src/gridcoin/beacon.h +++ b/src/gridcoin/beacon.h @@ -768,7 +768,7 @@ class BeaconRegistry : public IContractHandler //! //! \return The number of elements passivated. //! - uint64_t PassivateDB(); + uint64_t PassivateDB() override; //! //! \brief This function walks the linked beacon entries back (using the m_previous_hash member) from a provided @@ -795,11 +795,6 @@ class BeaconRegistry : public IContractHandler //! bool SetNeedsIsContractCorrection(bool flag); - //! - //! \brief A static function that is called by the scheduler to run the beacon database passivation. - //! - static void RunDBPassivation(); - //! //! \brief Specializes the template RegistryDB for the ScraperEntry class //! diff --git a/src/gridcoin/contract/handler.h b/src/gridcoin/contract/handler.h index 9d2d462d5a..5bb121e1bf 100644 --- a/src/gridcoin/contract/handler.h +++ b/src/gridcoin/contract/handler.h @@ -5,6 +5,7 @@ #ifndef GRIDCOIN_CONTRACT_HANDLER_H #define GRIDCOIN_CONTRACT_HANDLER_H +#include #include class CBlockIndex; @@ -153,6 +154,20 @@ struct IContractHandler //! \param height //! virtual void SetDBHeight(int& height); + + //! + //! \brief Passivates the elements in the underlying db, which means remove from memory elements in the + //! historical map that are not referenced by any of the in memory maps. The backing store of + //! the element removed from memory is retained and will be transparently restored if find() + //! is called on the hash key for the element. + //! + //! \return The number of elements passivated. + //! + virtual uint64_t PassivateDB() + { + // The default method here does nothing. + return uint64_t {0}; + }; }; } // namespace GRC diff --git a/src/gridcoin/cpid.h b/src/gridcoin/cpid.h index b644abbd8c..c8b3df5417 100644 --- a/src/gridcoin/cpid.h +++ b/src/gridcoin/cpid.h @@ -454,9 +454,9 @@ namespace std { //! This enables the use of GRC::Cpid as a key in a std::unordered_map object. //! //! CONSENSUS: Don't use the hash produced by this routine (or by any std::hash -//! specialization) in protocol-specific implementations. It ignores endianness -//! and outputs a value with a chance of collision probably too great for usage -//! besides the intended local look-up functionality. +//! specialization) in protocol-specific implementations. It outputs a value +//! with a chance of collision probably too great for usage besides the intended +//! local look-up functionality. //! template<> struct hash @@ -473,8 +473,16 @@ struct hash // Just convert the CPID into a value that we can store in a size_t // object. CPIDs are already unique identifiers. // - return *reinterpret_cast(cpid.Raw().data()) - + *reinterpret_cast(cpid.Raw().data() + 8); + const auto& data = cpid.Raw(); + size_t ret = ((size_t)(data[0] & 255) | (size_t)(data[1] & 255) << 8 | + (size_t)(data[2] & 255) << 16 | (size_t)(data[3] & 255) << 24); + + if (sizeof(size_t) == 8) { + ret |= ((size_t)(data[4] & 255) << 32 | (size_t)(data[5] & 255) << 40 | + (size_t)(data[6] & 255) << 48 | (size_t)(data[7] & 255) << 56); + } + + return ret; } }; } diff --git a/src/gridcoin/gridcoin.cpp b/src/gridcoin/gridcoin.cpp index 39853e113f..693eb318fd 100644 --- a/src/gridcoin/gridcoin.cpp +++ b/src/gridcoin/gridcoin.cpp @@ -27,6 +27,7 @@ extern bool fExplorer; extern unsigned int nScraperSleep; extern unsigned int nActiveBeforeSB; extern bool fScraperActive; +extern bool fQtActive; void Scraper(bool bSingleShot = false); void ScraperSubscriber(); @@ -40,17 +41,29 @@ namespace { //! void ShowChainCorruptedMessage() { - uiInterface.ThreadSafeMessageBox( - _("WARNING: Blockchain data may be corrupted.\n\n" - "Gridcoin detected bad index entries. This may occur because of an " - "unexpected exit, a power failure, or a late software upgrade.\n\n" - "Please exit Gridcoin, open the data directory, and delete:\n" - " - the blk****.dat files\n" - " - the txleveldb folder\n\n" - "Your wallet will re-download the blockchain. Your balance may " - "appear incorrect until the synchronization finishes.\n" ), - "Gridcoin", - CClientUIInterface::BTN_OK | CClientUIInterface::MODAL); + fResetBlockchainRequest = true; + + if (fQtActive) { + uiInterface.ThreadSafeMessageBox( + _("ERROR: Checkpoint mismatch: Blockchain data may be corrupted.\n\n" + "Gridcoin detected bad index entries. This may occur because of a " + "late software upgrade, unexpected exit, or a power failure. " + "Your blockchain data is being reset and your wallet will resync " + "from genesis when you restart. Your balance may appear incorrect " + "until the synchronization finishes."), + "Gridcoin", + CClientUIInterface::BTN_OK | CClientUIInterface::MODAL); + } else { + uiInterface.ThreadSafeMessageBox( + _("ERROR: Checkpoint mismatch: Blockchain data may be corrupted.\n\n" + "Gridcoin detected bad index entries. This may occur because of a " + "late software upgrade, unexpected exit, or a power failure. " + "Please run gridcoinresearchd with the -resetblockchaindata " + "parameter. Your wallet will re-download the blockchain. Your " + "balance may appear incorrect until the synchronization finishes." ), + "Gridcoin", + CClientUIInterface::BTN_OK | CClientUIInterface::MODAL); + } } //! @@ -361,20 +374,23 @@ void InitializeExplorerFeatures() //! whether the index contains invalid state caused by an unclean shutdown. //! This condition was originally detected by an assertion in a routine for //! stake modifier checksum verification. Because Gridcoin removed modifier -//! checksums and checkpoints, we reinstate that assertion here as a formal -//! inspection. +//! checksums, we reinstate that assertion here as a formal inspection done +//! at initialization before the VerifyCheckpoints. //! //! This function checks that no block index entries contain a null pointer //! to a previous block. The symptom may indicate a deeper problem that can //! be resolved by tuning disk synchronization in LevelDB. Until then, this //! heuristic has proven itself to be effective for identifying a corrupted -//! database. +//! database. This type of error has not been seen in the wild in several +//! years as of Gridcoin 5.4.7.0, but is retained for thoroughness. //! -void CheckBlockIndexJob() +bool CheckBlockIndex() { LogPrintf("Gridcoin: checking block index..."); + uiInterface.InitMessage(_("Checking block index...")); - bool corrupted = false; + // Block index integrity status + bool status = true; if (pindexGenesisBlock) { LOCK(cs_main); @@ -383,18 +399,20 @@ void CheckBlockIndexJob() const CBlockIndex* const pindex = index_pair.second; if (!pindex || !(pindex->pprev || pindex == pindexGenesisBlock)) { - corrupted = true; + status = false; break; } } } - if (!corrupted) { + if (status) { LogPrintf("Gridcoin: block index is clean"); - return; + return status; } ShowChainCorruptedMessage(); + + return status; } //! @@ -466,11 +484,8 @@ void ScheduleRegistriesPassivation(CScheduler& scheduler) { // Run registry database passivation every 5 minutes. This is a very thin call most of the time. // Please see the PassivateDB function and passivate_db. - // TODO: Turn into a loop using extension of RegistryBookmarks - scheduler.scheduleEvery(BeaconRegistry::RunDBPassivation, std::chrono::minutes{5}); - scheduler.scheduleEvery(ScraperRegistry::RunDBPassivation, std::chrono::minutes{5}); - scheduler.scheduleEvery(ProtocolRegistry::RunDBPassivation, std::chrono::minutes{5}); - scheduler.scheduleEvery(Whitelist::RunDBPassivation, std::chrono::minutes{5}); + + scheduler.scheduleEvery(RunDBPassivation, std::chrono::minutes{5}); } } // Anonymous namespace @@ -490,6 +505,9 @@ bool GRC::Initialize(ThreadHandlerPtr threads, CBlockIndex* pindexBest) { LogPrintf("Gridcoin: initializing..."); + if (!CheckBlockIndex()) { + return false; + } if (!VerifyCheckpoints(pindexBest)) { return false; } @@ -518,8 +536,6 @@ void GRC::CloseResearcherRegistryFile() void GRC::ScheduleBackgroundJobs(CScheduler& scheduler) { - scheduler.schedule(CheckBlockIndexJob, std::chrono::system_clock::now()); - // Primitive, but this is what the scraper does in the scraper housekeeping // loop. It checks to see if the logs need to be archived by default every // 5 mins. Note that passing false to the archive function means that if we @@ -589,3 +605,13 @@ skip:; return true; } +void GRC::RunDBPassivation() +{ + LOCK(cs_main); + + for (const auto& contract_type : RegistryBookmarks::CONTRACT_TYPES_WITH_REG_DB) { + Registry& registry = RegistryBookmarks::GetRegistryWithDB(contract_type); + + registry.PassivateDB(); + } +} diff --git a/src/gridcoin/gridcoin.h b/src/gridcoin/gridcoin.h index dbd6bde049..d9c17e21bf 100644 --- a/src/gridcoin/gridcoin.h +++ b/src/gridcoin/gridcoin.h @@ -40,6 +40,12 @@ void ScheduleBackgroundJobs(CScheduler& scheduler); //! \return \c true if no errors occurred. //! bool CleanConfig(); + +//! +//! \brief Function to allow cycling through DB passivation for all contract types backed +//! with registry db. +//! +void RunDBPassivation(); } // namespace GRC #endif // GRIDCOIN_GRIDCOIN_H diff --git a/src/gridcoin/mrc.h b/src/gridcoin/mrc.h index bcc966d036..578c631302 100644 --- a/src/gridcoin/mrc.h +++ b/src/gridcoin/mrc.h @@ -125,14 +125,14 @@ class MRC : public IContractPayload //! incoming reward claims and can index those calculated values without //! this field. It can be considered informational. //! - CAmount m_research_subsidy; + CAmount m_research_subsidy = 0; //! //! \brief The value of the fees charged to the MRC claimant. These will be //! subtracted from the research subsidy and distributed to the staker and //! the foundation according to protocol rules encapsulated in ComputeMRCFee(). //! - CAmount m_fee; + CAmount m_fee = 0; //! //! \brief The researcher magnitude value from the superblock at the time @@ -145,14 +145,14 @@ class MRC : public IContractPayload //! //! Previous protocol versions used the magnitude in reward calculations. //! - uint16_t m_magnitude; + uint16_t m_magnitude = 0; //! //! \brief The magnitude ratio of the network at the time of the claim. //! //! Informational. //! - double m_magnitude_unit; + double m_magnitude_unit = 0.0; //! //! \brief The hash of the last block (head of the chain) for the MRC diff --git a/src/gridcoin/project.cpp b/src/gridcoin/project.cpp index 6db6511067..cf5c805e18 100644 --- a/src/gridcoin/project.cpp +++ b/src/gridcoin/project.cpp @@ -541,21 +541,6 @@ Whitelist::ProjectEntryDB &Whitelist::GetProjectDB() return m_project_db; } -// This is static and called by the scheduler. -void Whitelist::RunDBPassivation() -{ - TRY_LOCK(cs_main, locked_main); - - if (!locked_main) - { - return; - } - - Whitelist& project_entries = GetWhitelist(); - - project_entries.PassivateDB(); -} - template<> const std::string Whitelist::ProjectEntryDB::KeyType() { return std::string("project"); diff --git a/src/gridcoin/project.h b/src/gridcoin/project.h index 5b6ff5fae2..9e4c55ae87 100644 --- a/src/gridcoin/project.h +++ b/src/gridcoin/project.h @@ -603,19 +603,14 @@ class Whitelist : public IContractHandler void ResetInMemoryOnly(); //! - //! \brief Passivates the elements in the scraper db, which means remove from memory elements in the + //! \brief Passivates the elements in the project db, which means remove from memory elements in the //! historical map that are not referenced by m_projects. The backing store of the element removed //! from memory is retained and will be transparently restored if find() is called on the hash key //! for the element. //! //! \return The number of elements passivated. //! - uint64_t PassivateDB(); - - //! - //! \brief A static function that is called by the scheduler to run the project entry database passivation. - //! - static void RunDBPassivation(); + uint64_t PassivateDB() override; //! //! \brief Specializes the template RegistryDB for the ScraperEntry class. Note that std::set is not diff --git a/src/gridcoin/protocol.cpp b/src/gridcoin/protocol.cpp index ec5f0831e9..bd32a6838d 100644 --- a/src/gridcoin/protocol.cpp +++ b/src/gridcoin/protocol.cpp @@ -528,21 +528,6 @@ ProtocolRegistry::ProtocolEntryDB &ProtocolRegistry::GetProtocolEntryDB() return m_protocol_db; } -// This is static and called by the scheduler. -void ProtocolRegistry::RunDBPassivation() -{ - TRY_LOCK(cs_main, locked_main); - - if (!locked_main) - { - return; - } - - ProtocolRegistry& protocol_entries = GetProtocolRegistry(); - - protocol_entries.PassivateDB(); -} - template<> const std::string ProtocolRegistry::ProtocolEntryDB::KeyType() { return std::string("protocol"); diff --git a/src/gridcoin/protocol.h b/src/gridcoin/protocol.h index 37aa39bc53..3e8f400e17 100644 --- a/src/gridcoin/protocol.h +++ b/src/gridcoin/protocol.h @@ -559,12 +559,7 @@ class ProtocolRegistry : public IContractHandler //! //! \return The number of elements passivated. //! - uint64_t PassivateDB(); - - //! - //! \brief A static function that is called by the scheduler to run the protocol entry database passivation. - //! - static void RunDBPassivation(); + uint64_t PassivateDB() override; //! //! \brief Specializes the template RegistryDB for the ProtocolEntry class. Note that std::set diff --git a/src/gridcoin/scraper/scraper.cpp b/src/gridcoin/scraper/scraper.cpp index 9a65484b78..d654f88d4a 100755 --- a/src/gridcoin/scraper/scraper.cpp +++ b/src/gridcoin/scraper/scraper.cpp @@ -4479,7 +4479,7 @@ EXCLUSIVE_LOCKS_REQUIRED(cs_StructScraperFileManifest, CScraperManifest::cs_mapM CDataStream part(std::move(vchData), SER_NETWORK, 1); - manifest->addPartData(std::move(part)); + manifest->addPartData(std::move(part), true); iPartNum++; @@ -4512,7 +4512,7 @@ EXCLUSIVE_LOCKS_REQUIRED(cs_StructScraperFileManifest, CScraperManifest::cs_mapM part << ScraperVerifiedBeacons.mVerifiedMap; - manifest->addPartData(std::move(part)); + manifest->addPartData(std::move(part), true); iPartNum++; } @@ -4592,7 +4592,7 @@ EXCLUSIVE_LOCKS_REQUIRED(cs_StructScraperFileManifest, CScraperManifest::cs_mapM CDataStream part(vchData, SER_NETWORK, 1); - manifest->addPartData(std::move(part)); + manifest->addPartData(std::move(part), true); iPartNum++; } @@ -4602,6 +4602,7 @@ EXCLUSIVE_LOCKS_REQUIRED(cs_StructScraperFileManifest, CScraperManifest::cs_mapM LOCK(CSplitBlob::cs_mapParts); + // addManifest will also call Complete() after signing to send the manifest over the network. bool bAddManifestSuccessful = CScraperManifest::addManifest(manifest, Key); if (bAddManifestSuccessful) diff --git a/src/gridcoin/scraper/scraper_net.cpp b/src/gridcoin/scraper/scraper_net.cpp index 25cc1360e4..ccb46dc9f2 100644 --- a/src/gridcoin/scraper/scraper_net.cpp +++ b/src/gridcoin/scraper/scraper_net.cpp @@ -97,7 +97,7 @@ bool CSplitBlob::RecvPart(CNode* pfrom, CDataStream& vRecv) bool CSplitBlob::isComplete() const EXCLUSIVE_LOCKS_REQUIRED(CSplitBlob::cs_manifest) { - return cntPartsRcvd == vParts.size(); + return (!m_publish_in_progress && cntPartsRcvd == vParts.size()); } void CSplitBlob::addPart(const uint256& ihash) @@ -119,10 +119,12 @@ void CSplitBlob::addPart(const uint256& ihash) part.refs.emplace(this, n); } -int CSplitBlob::addPartData(CDataStream&& vData) +int CSplitBlob::addPartData(CDataStream&& vData, const bool& publish_in_progress) { LOCK2(cs_mapParts, cs_manifest); + m_publish_in_progress = publish_in_progress; + uint256 hash(Hash(vData)); auto it = mapParts.emplace(hash, CPart(hash)); @@ -138,11 +140,9 @@ int CSplitBlob::addPartData(CDataStream&& vData) if (!part.present()) { /* missing data; use the supplied data */ - /* prevent calling the Complete callback FIXME: make this look better */ - cntPartsRcvd--; CSplitBlob::RecvPart(nullptr, vData); - cntPartsRcvd++; } + return n; } @@ -802,7 +802,7 @@ EXCLUSIVE_LOCKS_REQUIRED(CScraperManifest::cs_mapManifest, cs_mapParts) if (it.second == false) return false; - // Release lock on cs_manifest before taking a lonk on cs_ConvergedScraperStatsCache to avoid potential deadlocks. + // Release lock on cs_manifest before taking a lock on cs_ConvergedScraperStatsCache to avoid potential deadlocks. { CScraperManifest& manifest = *it.first->second; @@ -836,6 +836,8 @@ EXCLUSIVE_LOCKS_REQUIRED(CScraperManifest::cs_mapManifest, cs_mapParts) void CScraperManifest::Complete() EXCLUSIVE_LOCKS_REQUIRED(CSplitBlob::cs_manifest, CSplitBlob::cs_mapParts) { + m_publish_in_progress = false; + // Notify peers that we have a new manifest LogPrint(BCLog::LogFlags::MANIFEST, "manifest %s complete with %u parts", phash->GetHex(), (unsigned)vParts.size()); { diff --git a/src/gridcoin/scraper/scraper_net.h b/src/gridcoin/scraper/scraper_net.h index 3959a6e889..ed2b1681a9 100755 --- a/src/gridcoin/scraper/scraper_net.h +++ b/src/gridcoin/scraper/scraper_net.h @@ -65,7 +65,7 @@ class CSplitBlob void addPart(const uint256& ihash); /** Create a part from specified data and add reference to it into vParts. */ - int addPartData(CDataStream&& vData); + int addPartData(CDataStream&& vData, const bool &publish_in_progress = false); /** Unref all parts referenced by this. Removes parts with no references */ virtual ~CSplitBlob(); @@ -86,6 +86,11 @@ class CSplitBlob std::vector vParts GUARDED_BY(cs_manifest); size_t cntPartsRcvd GUARDED_BY(cs_manifest) = 0; + + //! + //! \brief Used by the scraper when building manifests part by part to prevent triggering Complete() prematurely. + //! + bool m_publish_in_progress GUARDED_BY(cs_manifest) = false; }; /** An objects holding info about the scraper data file we have or are downloading. */ diff --git a/src/gridcoin/scraper/scraper_registry.cpp b/src/gridcoin/scraper/scraper_registry.cpp index bf481e0216..5a22fb11ad 100644 --- a/src/gridcoin/scraper/scraper_registry.cpp +++ b/src/gridcoin/scraper/scraper_registry.cpp @@ -573,21 +573,6 @@ ScraperRegistry::ScraperEntryDB &ScraperRegistry::GetScraperDB() return m_scraper_db; } -// This is static and called by the scheduler. -void ScraperRegistry::RunDBPassivation() -{ - TRY_LOCK(cs_main, locked_main); - - if (!locked_main) - { - return; - } - - ScraperRegistry& scraper_entries = GetScraperRegistry(); - - scraper_entries.PassivateDB(); -} - template<> const std::string ScraperRegistry::ScraperEntryDB::KeyType() { return std::string("scraper"); diff --git a/src/gridcoin/scraper/scraper_registry.h b/src/gridcoin/scraper/scraper_registry.h index 5686c25991..8256b7e11b 100644 --- a/src/gridcoin/scraper/scraper_registry.h +++ b/src/gridcoin/scraper/scraper_registry.h @@ -598,12 +598,7 @@ class ScraperRegistry : public IContractHandler //! //! \return The number of elements passivated. //! - uint64_t PassivateDB(); - - //! - //! \brief A static function that is called by the scheduler to run the scraper entry database passivation. - //! - static void RunDBPassivation(); + uint64_t PassivateDB() override; //! //! \brief Specializes the template RegistryDB for the ScraperEntry class. Note that std::set is diff --git a/src/gridcoin/sidestake.cpp b/src/gridcoin/sidestake.cpp index f96bd840db..0c553d6eef 100644 --- a/src/gridcoin/sidestake.cpp +++ b/src/gridcoin/sidestake.cpp @@ -453,6 +453,33 @@ std::string SideStake::GetDescription() const return std::string {}; } +int64_t SideStake::GetTimeStamp() const +{ + if (m_type == Type::MANDATORY && m_mandatory_sidestake_ptr != nullptr) { + return m_mandatory_sidestake_ptr->m_timestamp; + } + + return int64_t {0}; +} + +uint256 SideStake::GetHash() const +{ + if (m_type == Type::MANDATORY && m_mandatory_sidestake_ptr != nullptr) { + return m_mandatory_sidestake_ptr->m_hash; + } + + return uint256 {}; +} + +uint256 SideStake::GetPreviousHash() const +{ + if (m_type == Type::MANDATORY && m_mandatory_sidestake_ptr != nullptr) { + return m_mandatory_sidestake_ptr->m_previous_hash; + } + + return uint256 {}; +} + SideStake::Status SideStake::GetStatus() const { // For trivial initializer case @@ -1137,21 +1164,6 @@ SideStakeRegistry::SideStakeDB &SideStakeRegistry::GetSideStakeDB() return m_sidestake_db; } -// This is static and called by the scheduler. -void SideStakeRegistry::RunDBPassivation() -{ - TRY_LOCK(cs_main, locked_main); - - if (!locked_main) - { - return; - } - - SideStakeRegistry& SideStake_entries = GetSideStakeRegistry(); - - SideStake_entries.PassivateDB(); -} - template<> const std::string SideStakeRegistry::SideStakeDB::KeyType() { return std::string("SideStake"); diff --git a/src/gridcoin/sidestake.h b/src/gridcoin/sidestake.h index 42613e938c..d909e7f27a 100644 --- a/src/gridcoin/sidestake.h +++ b/src/gridcoin/sidestake.h @@ -428,6 +428,21 @@ class SideStake //! std::string GetDescription() const; //! + //! \brief Gets the timestamp of the transaction that contains the mandatory sidestake contract (now entry) + //! \return + //! + int64_t GetTimeStamp() const; + //! + //! \brief Gets the hash of the transaction that contains the mandatory sidestake contract (now entry) + //! \return uint256 hash + //! + uint256 GetHash() const; + //! + //! \brief Gets the hash of the transaction that contains the previous mandatory sidestake contract for the same key (address) + //! \return uint256 hash + //! + uint256 GetPreviousHash() const; + //! //! \brief Gets a variant containing either the mandatory sidestake status or local sidestake status, whichever //! is applicable. //! \return std::variant of the applicable sidestake status @@ -815,7 +830,7 @@ class SideStakeRegistry : public IContractHandler //! //! \return The number of elements passivated. //! - uint64_t PassivateDB(); + uint64_t PassivateDB() override; //! //! \brief This method parses the config file for local sidestakes. It is based on the original GetSideStakingStatusAndAlloc() @@ -823,11 +838,6 @@ class SideStakeRegistry : public IContractHandler //! void LoadLocalSideStakesFromConfig(); - //! - //! \brief A static function that is called by the scheduler to run the sidestake entry database passivation. - //! - static void RunDBPassivation(); - //! //! \brief Specializes the template RegistryDB for the SideStake class. Note that std::set //! is not actually used. diff --git a/src/gridcoin/staking/kernel.cpp b/src/gridcoin/staking/kernel.cpp index 7f1952140d..ccb154862e 100644 --- a/src/gridcoin/staking/kernel.cpp +++ b/src/gridcoin/staking/kernel.cpp @@ -603,21 +603,21 @@ bool GRC::CheckProofOfStakeV8( CTransaction txPrev; if (!ReadStakedInput(txdb, prevout.hash, header, txPrev, pindexPrev)) - return tx.DoS(1, error("%s: read staked input failed", __func__)); + return tx.DoS(10, error("%s: read staked input failed", __func__)); if (!VerifySignature(txPrev, tx, 0, 0)) return tx.DoS(100, error("%s: VerifySignature failed on coinstake %s", __func__, tx.GetHash().ToString())); // Check times if (tx.nTime < txPrev.nTime) - return error("%s: nTime violation", __func__); + return tx.DoS(100, error("%s: nTime violation", __func__)); if (header.nTime + nStakeMinAge > tx.nTime) - return error("%s: min age violation", __func__); + return tx.DoS(100, error("%s: min age violation", __func__)); if (Block.nVersion >= 12) { if (tx.nTime != MaskStakeTime(tx.nTime)) { - return error("%s: mask violation", __func__); + return tx.DoS(100, error("%s: mask violation", __func__)); } } @@ -649,5 +649,9 @@ bool GRC::CheckProofOfStakeV8( ); // Now check if proof-of-stake hash meets target protocol - return bnHashProof <= bnTarget; + if (bnHashProof > bnTarget) { + return tx.DoS(100, error("%s: invalid proof (proof: %s, target: %s)", __func__, bnHashProof.GetHex(), bnTarget.GetHex())); + } + + return true; } diff --git a/src/gridcoin/superblock.cpp b/src/gridcoin/superblock.cpp index 4b9e7c61d7..c2f8251de6 100644 --- a/src/gridcoin/superblock.cpp +++ b/src/gridcoin/superblock.cpp @@ -456,8 +456,9 @@ class LegacySuperblockParser for (size_t x = 0; x < binary_size && binary_size - x >= 18; x += 18) { magnitudes.AddLegacy( - *reinterpret_cast(byte_ptr + x), - be16toh(*reinterpret_cast(byte_ptr + x + 16))); + Cpid(std::vector(byte_ptr + x, byte_ptr + x + 16)), + (((uint16_t)byte_ptr[x + 16] & 0xFF) << 8) | ((uint16_t)byte_ptr[x + 17] & 0xFF) + ); } return magnitudes; diff --git a/src/main.h b/src/main.h index 5af4e3e47a..3afe3e45a5 100644 --- a/src/main.h +++ b/src/main.h @@ -478,7 +478,7 @@ class CBlockIndex int nHeight; unsigned int nFlags; // ppcoin: block index flags - enum + enum : uint32_t { BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier @@ -630,7 +630,7 @@ class CBlockIndex { if (nEntropyBit > 1) return false; - nFlags |= (nEntropyBit? BLOCK_STAKE_ENTROPY : 0); + nFlags |= (nEntropyBit ? BLOCK_STAKE_ENTROPY : (uint32_t) 0); return true; } diff --git a/src/miner.cpp b/src/miner.cpp index bc0ef2deb7..1d1e8a3f7f 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -150,7 +150,7 @@ bool TrySignClaim( // This is in anonymous namespace because it is only to be used by miner code here in this file. bool CreateMRCRewards(CBlock &blocknew, std::map>& mrc_map, - std::map& mrc_tx_map, uint32_t& claim_contract_version, + std::map& mrc_tx_map, CAmount& reward, uint32_t& claim_contract_version, GRC::Claim& claim, CWallet* pwallet) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { // For convenience @@ -160,7 +160,7 @@ bool CreateMRCRewards(CBlock &blocknew, std::map 0) { // TODO: Make foundation address a defaulted but protocol overridable parameter. @@ -287,7 +290,7 @@ bool CreateMRCRewards(CBlock &blocknew, std::map( @@ -310,8 +313,8 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev, int nHeight = pindexPrev->nHeight + 1; // This is specifically for BlockValidateContracts, and only the nHeight is filled in. - CBlockIndex* pindex_contract_validate = new CBlockIndex(); - pindex_contract_validate->nHeight = nHeight; + CBlockIndex pindex_contract_validate; + pindex_contract_validate.nHeight = nHeight; // Create coinbase tx CTransaction &CoinBase = block.vtx[0]; @@ -385,7 +388,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev, // pindex_contract_validate only has the block height filled out. // int DoS = 0; // Unused here. - if (!tx.GetContracts().empty() && !GRC::BlockValidateContracts(pindex_contract_validate, tx, DoS)) { + if (!tx.GetContracts().empty() && !GRC::BlockValidateContracts(&pindex_contract_validate, tx, DoS)) { LogPrint(BCLog::LogFlags::MINER, "%s: contract failed contextual validation. Skipped tx %s", __func__, @@ -1439,7 +1442,8 @@ void StakeMiner(CWallet *pwallet) // * Add MRC outputs to coinstake. This has to be done before the coinstake splitting/sidestaking, because // Some of the MRC fees go to the miner as part of the reward, and this affects the SplitCoinStakeOutput calculation. - if (!CreateMRCRewards(StakeBlock, mrc_map, mrc_tx_map, claim_contract_version, claim, pwallet)) continue; + // Note that nReward here now includes the mrc fees to the staker. + if (!CreateMRCRewards(StakeBlock, mrc_map, mrc_tx_map, nReward, claim_contract_version, claim, pwallet)) continue; g_timer.GetTimes(function + "CreateMRC", "miner"); @@ -1467,6 +1471,28 @@ void StakeMiner(CWallet *pwallet) g_miner_status.IncrementBlocksCreated(); + if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE)) { + COutPoint stake_prevout = StakeBlock.vtx[1].vin[0].prevout; + CTransaction stake_input; + ReadTxFromDisk(stake_input, stake_prevout); + + LogPrintf("INFO: %s: stake input = %s", + __func__, + FormatMoney(stake_input.vout[stake_prevout.n].nValue)); + + for (unsigned int i = 1; i < StakeBlock.vtx[1].vout.size(); ++i) { + CTxDestination destination; + + ExtractDestination(StakeBlock.vtx[1].vout[i].scriptPubKey, destination); + + LogPrintf("INFO: %s: stake output[%u] = %s, destination = %s", + __func__, + i, + FormatMoney(StakeBlock.vtx[1].vout[i].nValue), + CBitcoinAddress(destination).ToString()); + } + } + // * delegate to ProcessBlock if (!ProcessBlock(nullptr, &StakeBlock, true)) { error("%s: Block vehemently rejected", __func__); diff --git a/src/miner.h b/src/miner.h index c713a8de89..6e439c099a 100644 --- a/src/miner.h +++ b/src/miner.h @@ -31,6 +31,7 @@ bool GetStakeSplitStatusAndParams(int64_t& nMinStakeSplitValue, double& dEfficie bool CreateMRCRewards(CBlock &blocknew, std::map>& mrc_map, std::map& mrc_tx_map, + CAmount& reward, uint32_t& claim_contract_version, GRC::Claim& claim, CWallet* pwallet) EXCLUSIVE_LOCKS_REQUIRED(cs_main); diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 9a763d4e37..976fc405ef 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -102,6 +102,9 @@ add_library(gridcoinqt STATIC if(WIN32) target_sources(gridcoinqt PRIVATE res/gridcoinresearch.rc) + set_source_files_properties(res/gridcoinresearch.rc PROPERTIES + COMPILE_DEFINITIONS WINDRES_PREPROC + ) elseif(APPLE) target_sources(gridcoinqt PRIVATE macdockiconhandler.mm @@ -152,6 +155,8 @@ if(APPLE) "-framework ApplicationServices" "-framework AppKit" ) +elseif(WIN32) + target_link_libraries(gridcoinqt PUBLIC shlwapi) endif() target_compile_definitions(gridcoinqt PUBLIC HAVE_CONFIG_H) @@ -177,6 +182,7 @@ add_dependencies(gridcoinqt gridcoinqt_l10n) add_executable(gridcoinresearch WIN32 MACOSX_BUNDLE bitcoin.cpp) target_link_libraries(gridcoinresearch PRIVATE + ${RUNTIME_LIBS} Qt5::Widgets gridcoin_util gridcoinqt @@ -194,7 +200,7 @@ if(UNIX AND NOT APPLE) DESTINATION "${CMAKE_INSTALL_DATADIR}/applications" ) install(FILES "${CMAKE_SOURCE_DIR}/doc/gridcoinresearch.1" - DESTINATION "${CMAKE_INSTALL_MANDIR}" + DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" ) endif() diff --git a/src/qt/test/CMakeLists.txt b/src/qt/test/CMakeLists.txt index 4717e74597..f6ff90608d 100644 --- a/src/qt/test/CMakeLists.txt +++ b/src/qt/test/CMakeLists.txt @@ -8,6 +8,7 @@ set_target_properties(test_gridcoin-qt PROPERTIES ) target_link_libraries(test_gridcoin-qt PRIVATE + ${RUNTIME_LIBS} Qt5::Test Qt5::Widgets gridcoin_util diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 8c3964a9a4..32ee83380c 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -2700,6 +2700,41 @@ UniValue listprotocolentries(const UniValue& params, bool fHelp) return res; } +UniValue listmandatorysidestakes(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "listprotocolentries\n" + "\n" + "Displays the mandatory sidestakes on the network.\n"); + + UniValue res(UniValue::VOBJ); + UniValue scraper_entries(UniValue::VARR); + + for (const auto& sidestake : GRC::GetSideStakeRegistry().ActiveSideStakeEntries(GRC::SideStake::FilterFlag::MANDATORY, false)) { + UniValue entry(UniValue::VOBJ); + + entry.pushKV("mandatory_sidestake_entry_address", CBitcoinAddress(sidestake->GetDestination()).ToString()); + entry.pushKV("mandatory_sidestake_entry_allocation", sidestake->GetAllocation().ToPercent()); + entry.pushKV("mandatory_sidestake_entry_tx_hash", sidestake->GetHash().ToString()); + if (sidestake->GetPreviousHash().IsNull()) { + entry.pushKV("previous_mandatory_sidestake_entry_tx_hash", "null"); + } else { + entry.pushKV("previous_mandatory_sidestake_entry_tx_hash", sidestake->GetPreviousHash().ToString()); + } + + entry.pushKV("mandatory_sidestake_entry_timestamp", sidestake->GetTimeStamp()); + entry.pushKV("mandatory_sidestake_entry_time", DateTimeStrFormat(sidestake->GetTimeStamp())); + entry.pushKV("mandatory_sidestake_entry_status", sidestake->StatusToString()); + + scraper_entries.push_back(entry); + } + + res.pushKV("current_mandatory_sidestake_entries", scraper_entries); + + return res; +} + UniValue network(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 65180d6139..13127761d3 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -241,8 +241,9 @@ UniValue setban(const UniValue& params, bool fHelp) if (!isSubnet) { std::vector resolved; - LookupHost(params[0].get_str().c_str(), resolved, 1, false); - netAddr = resolved[0]; + if (LookupHost(params[0].get_str().c_str(), resolved, 1, false)) { + netAddr = resolved[0]; + } } else LookupSubNet(params[0].get_str().c_str(), subNet); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 5bf4a69b26..08640a3d93 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -388,6 +388,7 @@ static const CRPCCommand vRPCCommands[] = { "listprotocolentries", &listprotocolentries, cat_developer }, { "listresearcheraccounts", &listresearcheraccounts, cat_developer }, { "listscrapers", &listscrapers, cat_developer }, + { "listmandatorysidestakes", &listmandatorysidestakes, cat_developer }, { "listsettings", &listsettings, cat_developer }, { "logging", &logging, cat_developer }, { "network", &network, cat_developer }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 6aa1c4850f..a4b6f2d571 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -199,6 +199,7 @@ extern UniValue listprojects(const UniValue& params, bool fHelp); extern UniValue listprotocolentries(const UniValue& params, bool fHelp); extern UniValue listresearcheraccounts(const UniValue& params, bool fHelp); extern UniValue listscrapers(const UniValue& params, bool fHelp); +extern UniValue listmandatorysidestakes(const UniValue& params, bool fHelp); extern UniValue listsettings(const UniValue& params, bool fHelp); extern UniValue logging(const UniValue& params, bool fHelp); extern UniValue network(const UniValue& params, bool fHelp); diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index a2b03b3eab..ef05f222b6 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -24,6 +24,10 @@ #include #include +#ifdef ARENA_DEBUG +#include +#include +#endif LockedPoolManager* LockedPoolManager::_instance = nullptr; std::once_flag LockedPoolManager::init_flag; @@ -138,7 +142,7 @@ Arena::Stats Arena::stats() const } #ifdef ARENA_DEBUG -static void printchunk(char* base, size_t sz, bool used) { +static void printchunk(void* base, size_t sz, bool used) { std::cout << "0x" << std::hex << std::setw(16) << std::setfill('0') << base << " 0x" << std::hex << std::setw(16) << std::setfill('0') << sz << @@ -150,7 +154,7 @@ void Arena::walk() const printchunk(chunk.first, chunk.second, true); std::cout << std::endl; for (const auto& chunk: chunks_free) - printchunk(chunk.first, chunk.second, false); + printchunk(chunk.first, chunk.second->first, false); std::cout << std::endl; } #endif diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index ebc70baa79..ed69323da4 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -57,6 +57,7 @@ add_executable(test_gridcoin add_subdirectory(data) target_link_libraries(test_gridcoin PRIVATE + ${RUNTIME_LIBS} Boost::unit_test_framework gridcoin_util ) diff --git a/src/test/accounting_tests.cpp b/src/test/accounting_tests.cpp index 50259351a1..a89a266fbf 100644 --- a/src/test/accounting_tests.cpp +++ b/src/test/accounting_tests.cpp @@ -78,13 +78,13 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) wtx.mapValue["comment"] = "y"; - --wtx.nLockTime; // Just to change the hash :) + ++wtx.nLockTime; // Just to change the hash :) pwalletMain->AddToWallet(wtx, &walletdb); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); vpwtx[1]->nTimeReceived = (unsigned int)1333333336; wtx.mapValue["comment"] = "x"; - --wtx.nLockTime; // Just to change the hash :) + ++wtx.nLockTime; // Just to change the hash :) pwalletMain->AddToWallet(wtx, &walletdb); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); vpwtx[2]->nTimeReceived = (unsigned int)1333333329; diff --git a/src/test/gridcoin/beacon_tests.cpp b/src/test/gridcoin/beacon_tests.cpp index e216919f3a..770cddb0d7 100644 --- a/src/test/gridcoin/beacon_tests.cpp +++ b/src/test/gridcoin/beacon_tests.cpp @@ -1268,10 +1268,10 @@ BOOST_AUTO_TEST_CASE(beacon_registry_GetBeaconChainletRoot_test) tx1.nTime = int64_t {1}; uint256 tx1_hash = tx1.GetHash(); - CBlockIndex* pindex1 = new CBlockIndex; - pindex1->nVersion = 13; - pindex1->nHeight = 1; - pindex1->nTime = tx1.nTime; + CBlockIndex pindex1 {}; + pindex1.nVersion = 13; + pindex1.nHeight = 1; + pindex1.nTime = tx1.nTime; GRC::Beacon beacon1 {TestKey::Public(), tx1.nTime, tx1_hash}; beacon1.m_cpid = TestKey::Cpid(); @@ -1280,7 +1280,7 @@ BOOST_AUTO_TEST_CASE(beacon_registry_GetBeaconChainletRoot_test) beacon_payload1.m_signature = TestKey::Signature(beacon_payload1); GRC::Contract contract1 = GRC::MakeContract(3, GRC::ContractAction::ADD, beacon_payload1); - GRC::ContractContext ctx1 {contract1, tx1, pindex1}; + GRC::ContractContext ctx1 {contract1, tx1, &pindex1}; BOOST_CHECK(ctx1.m_contract.CopyPayloadAs().m_cpid == TestKey::Cpid()); BOOST_CHECK(ctx1.m_contract.CopyPayloadAs().m_beacon.m_status == GRC::BeaconStatusForStorage::PENDING); @@ -1305,18 +1305,18 @@ BOOST_AUTO_TEST_CASE(beacon_registry_GetBeaconChainletRoot_test) } // Activation - CBlockIndex* pindex2 = new CBlockIndex; - pindex2->nVersion = 13; - pindex2->nHeight = 2; - pindex2->nTime = int64_t {2}; - uint256* block2_phash = new uint256 {rng.rand256()}; - pindex2->phashBlock = block2_phash; + CBlockIndex pindex2 {}; + pindex2.nVersion = 13; + pindex2.nHeight = 2; + pindex2.nTime = int64_t {2}; + uint256 block2_phash = rng.rand256(); + pindex2.phashBlock = &block2_phash; std::vector beacon_ids {TestKey::Public().GetID()}; - registry.ActivatePending(beacon_ids, pindex2->nTime, *pindex2->phashBlock, pindex2->nHeight); + registry.ActivatePending(beacon_ids, pindex2.nTime, *pindex2.phashBlock, pindex2.nHeight); - uint256 activated_beacon_hash = Hash(*block2_phash, pending_beacons[0]->m_hash); + uint256 activated_beacon_hash = Hash(block2_phash, pending_beacons[0]->m_hash); BOOST_CHECK(registry.GetBeaconDB().size() == 2); @@ -1475,10 +1475,10 @@ BOOST_AUTO_TEST_CASE(beacon_registry_GetBeaconChainletRoot_test_2) tx1.nTime = int64_t {1}; uint256 tx1_hash = tx1.GetHash(); - CBlockIndex* pindex1 = new CBlockIndex; - pindex1->nVersion = 13; - pindex1->nHeight = 1; - pindex1->nTime = tx1.nTime; + CBlockIndex pindex1 {}; + pindex1.nVersion = 13; + pindex1.nHeight = 1; + pindex1.nTime = tx1.nTime; GRC::Beacon beacon1 {TestKey::Public(), tx1.nTime, tx1_hash}; beacon1.m_cpid = TestKey::Cpid(); @@ -1487,7 +1487,7 @@ BOOST_AUTO_TEST_CASE(beacon_registry_GetBeaconChainletRoot_test_2) beacon_payload1.m_signature = TestKey::Signature(beacon_payload1); GRC::Contract contract1 = GRC::MakeContract(3, GRC::ContractAction::ADD, beacon_payload1); - GRC::ContractContext ctx1 {contract1, tx1, pindex1}; + GRC::ContractContext ctx1 {contract1, tx1, &pindex1}; BOOST_CHECK(ctx1.m_contract.CopyPayloadAs().m_cpid == TestKey::Cpid()); BOOST_CHECK(ctx1.m_contract.CopyPayloadAs().m_beacon.m_status == GRC::BeaconStatusForStorage::PENDING); @@ -1512,18 +1512,18 @@ BOOST_AUTO_TEST_CASE(beacon_registry_GetBeaconChainletRoot_test_2) } // Activation - CBlockIndex* pindex2 = new CBlockIndex; - pindex2->nVersion = 13; - pindex2->nHeight = 2; - pindex2->nTime = int64_t {2}; - uint256* block2_phash = new uint256 {rng.rand256()}; - pindex2->phashBlock = block2_phash; + CBlockIndex pindex2 {}; + pindex2.nVersion = 13; + pindex2.nHeight = 2; + pindex2.nTime = int64_t {2}; + uint256 block2_phash = rng.rand256(); + pindex2.phashBlock = &block2_phash; std::vector beacon_ids {TestKey::Public().GetID()}; - registry.ActivatePending(beacon_ids, pindex2->nTime, *pindex2->phashBlock, pindex2->nHeight); + registry.ActivatePending(beacon_ids, pindex2.nTime, *pindex2.phashBlock, pindex2.nHeight); - uint256 activated_beacon_hash = Hash(*block2_phash, pending_beacons[0]->m_hash); + uint256 activated_beacon_hash = Hash(block2_phash, pending_beacons[0]->m_hash); BOOST_CHECK(registry.GetBeaconDB().size() == 2); diff --git a/src/test/gridcoin/claim_tests.cpp b/src/test/gridcoin/claim_tests.cpp index 33d1e204ad..0a9b5f1af4 100644 --- a/src/test/gridcoin/claim_tests.cpp +++ b/src/test/gridcoin/claim_tests.cpp @@ -97,26 +97,6 @@ static CKey GetTestPrivateKey() return key; } - -// Unfortunately, GCC 13 on openSUSE i386 is misbehaving and exhibiting weird errors in the last decimal places for things -// even as straightforward as -// -// double foo = 0.0; -// text >> foo. -// -// This comparison function works around that by allowing a small error band to pass the tests, but not enough to invalidate -// the tests on compilers that work. -bool comp_double(double lhs, double rhs) -{ - // Require exact match if 0=0. - if (std::min(lhs, rhs) == 0.0) { - return (lhs == rhs); - } else { - double unsigned_rel_error = std::abs(lhs - rhs) / std::min(lhs, rhs); - - return (unsigned_rel_error <= double {1e-8}); - } -} } // anonymous namespace // ----------------------------------------------------------------------------- @@ -138,7 +118,7 @@ BOOST_AUTO_TEST_CASE(it_initializes_to_an_empty_claim) BOOST_CHECK(claim.m_magnitude == 0); BOOST_CHECK(claim.m_research_subsidy == 0); - BOOST_CHECK(comp_double(claim.m_magnitude_unit, 0.0)); + BOOST_CHECK(claim.m_magnitude_unit == 0.0); BOOST_CHECK(claim.m_signature.empty() == true); @@ -161,7 +141,7 @@ BOOST_AUTO_TEST_CASE(it_initializes_to_the_specified_version) BOOST_CHECK(claim.m_magnitude == 0); BOOST_CHECK(claim.m_research_subsidy == 0); - BOOST_CHECK(comp_double(claim.m_magnitude_unit, 0.0)); + BOOST_CHECK(claim.m_magnitude_unit == 0.0); BOOST_CHECK(claim.m_signature.empty() == true); @@ -240,7 +220,7 @@ BOOST_AUTO_TEST_CASE(it_parses_a_legacy_boincblock_string_for_researcher) BOOST_CHECK(claim.m_magnitude == 123); BOOST_CHECK(claim.m_research_subsidy == 47.25 * COIN); - BOOST_CHECK(comp_double(claim.m_magnitude_unit, 0.123456)); + BOOST_CHECK(claim.m_magnitude_unit == 0.123456); BOOST_CHECK(claim.m_signature == signature); @@ -523,7 +503,7 @@ BOOST_AUTO_TEST_CASE(it_deserializes_from_a_stream_for_investor) BOOST_CHECK(claim.m_research_subsidy == 0); BOOST_CHECK(claim.m_magnitude == 0.0); - BOOST_CHECK(comp_double(claim.m_magnitude_unit, 0.0)); + BOOST_CHECK(claim.m_magnitude_unit == 0.0); BOOST_CHECK(claim.m_signature.empty() == true); BOOST_CHECK(claim.m_quorum_address.empty() == true); BOOST_CHECK(claim.m_superblock->WellFormed() == false); @@ -565,7 +545,7 @@ BOOST_AUTO_TEST_CASE(it_deserializes_from_a_stream_for_investor_with_superblock) BOOST_CHECK(claim.m_research_subsidy == 0); BOOST_CHECK(claim.m_magnitude == 0.0); - BOOST_CHECK(comp_double(claim.m_magnitude_unit, 0.0)); + BOOST_CHECK(claim.m_magnitude_unit == 0.0); BOOST_CHECK(claim.m_signature.empty() == true); } @@ -650,7 +630,7 @@ BOOST_AUTO_TEST_CASE(it_deserializes_from_a_stream_for_researcher) BOOST_CHECK(claim.m_research_subsidy == expected.m_research_subsidy); BOOST_CHECK(claim.m_magnitude == 0.0); - BOOST_CHECK(comp_double(claim.m_magnitude_unit, 0.0)); + BOOST_CHECK(claim.m_magnitude_unit == 0.0); BOOST_CHECK(claim.m_signature == expected.m_signature); BOOST_CHECK(claim.m_quorum_hash == expected.m_quorum_hash); @@ -690,7 +670,7 @@ BOOST_AUTO_TEST_CASE(it_deserializes_from_a_stream_for_researcher_with_superbloc BOOST_CHECK(claim.m_research_subsidy == expected.m_research_subsidy); BOOST_CHECK(claim.m_magnitude == 0.0); - BOOST_CHECK(comp_double(claim.m_magnitude_unit, 0.0)); + BOOST_CHECK(claim.m_magnitude_unit == 0.0); BOOST_CHECK(claim.m_signature == expected.m_signature); BOOST_CHECK(claim.m_quorum_hash == expected.m_quorum_hash); diff --git a/src/test/gridcoin/cpid_tests.cpp b/src/test/gridcoin/cpid_tests.cpp index 6becf0cc51..c76af4f76a 100644 --- a/src/test/gridcoin/cpid_tests.cpp +++ b/src/test/gridcoin/cpid_tests.cpp @@ -249,7 +249,7 @@ BOOST_AUTO_TEST_CASE(it_is_hashable_to_key_a_lookup_map) std::hash hasher; // CPID halves, little endian - const size_t expected = static_cast(0x0706050403020100ull + 0x1514131211100908ull); + const size_t expected = static_cast(0x0706050403020100ull); BOOST_CHECK_EQUAL(hasher(cpid), expected); } diff --git a/src/test/gridcoin/mrc_tests.cpp b/src/test/gridcoin/mrc_tests.cpp index 4a427fd3be..f80c5c4495 100644 --- a/src/test/gridcoin/mrc_tests.cpp +++ b/src/test/gridcoin/mrc_tests.cpp @@ -103,6 +103,7 @@ struct Setup { mapBlockIndex.erase(pindexGenesisBlock->GetBlockHash()); delete pindexGenesisBlock->phashBlock; + delete wallet; } }; } // Anonymous namespace @@ -263,7 +264,7 @@ BOOST_AUTO_TEST_CASE(it_creates_valid_mrc_claims) BOOST_CHECK(CreateGridcoinReward(block, pindex->pprev, reward, claim)); - BOOST_CHECK(CreateMRCRewards(block, mrc_map, mrc_tx_map, claim_contract_version, claim, wallet)); + BOOST_CHECK(CreateMRCRewards(block, mrc_map, mrc_tx_map, reward, claim_contract_version, claim, wallet)); // TODO(div72): Separate this test into pieces and actually have it do // some useful testing by testing the validation logic against it. diff --git a/src/test/gridcoin/researcher_tests.cpp b/src/test/gridcoin/researcher_tests.cpp index e163fb5862..352c2b464f 100644 --- a/src/test/gridcoin/researcher_tests.cpp +++ b/src/test/gridcoin/researcher_tests.cpp @@ -183,26 +183,6 @@ void AddProtocolEntry(const uint32_t& payload_version, const std::string& key, c registry.Add({contract, dummy_tx, &dummy_index}); } - -// Unfortunately, GCC 13 on openSUSE i386 is misbehaving and exhibiting weird errors in the last decimal places for things -// even as straightforward as -// -// double foo = 0.0; -// text >> foo. -// -// This comparison function works around that by allowing a small error band to pass the tests, but not enough to invalidate -// the tests on compilers that work. -bool comp_double(double lhs, double rhs) -{ - // Require exact match if 0=0. - if (std::min(lhs, rhs) == 0.0) { - return (lhs == rhs); - } else { - double unsigned_rel_error = std::abs(lhs - rhs) / std::min(lhs, rhs); - - return (unsigned_rel_error <= double {1e-8}); - } -} } // anonymous namespace // ----------------------------------------------------------------------------- @@ -225,7 +205,7 @@ BOOST_AUTO_TEST_CASE(it_initializes_with_project_data) BOOST_CHECK(project.m_cpid == expected); BOOST_CHECK(project.m_team == "team name"); BOOST_CHECK(project.m_url == "url"); - BOOST_CHECK(comp_double(project.m_rac, 0.0)); + BOOST_CHECK(project.m_rac == 0.0); BOOST_CHECK(project.m_error == GRC::MiningProject::Error::NONE); } @@ -254,7 +234,7 @@ BOOST_AUTO_TEST_CASE(it_parses_a_project_xml_string) BOOST_CHECK(project.m_cpid == cpid); BOOST_CHECK(project.m_team == "team name"); BOOST_CHECK(project.m_url == "https://example.com/"); - BOOST_CHECK(comp_double(project.m_rac, 123.45)); + BOOST_CHECK(project.m_rac == 123.45); BOOST_CHECK(project.m_error == GRC::MiningProject::Error::NONE); // Clean up: @@ -289,7 +269,7 @@ BOOST_AUTO_TEST_CASE(it_falls_back_to_compute_a_missing_external_cpid) BOOST_CHECK(project.m_cpid == cpid); BOOST_CHECK(project.m_team == "team name"); BOOST_CHECK(project.m_url == "https://example.com/"); - BOOST_CHECK(comp_double(project.m_rac, 123.45)); + BOOST_CHECK(project.m_rac == 123.45); BOOST_CHECK(project.m_error == GRC::MiningProject::Error::NONE); // Clean up: @@ -508,7 +488,7 @@ BOOST_AUTO_TEST_CASE(it_parses_a_set_of_project_xml_sections) BOOST_CHECK(project1->m_cpid == cpid_1); BOOST_CHECK(project1->m_team == "gridcoin"); BOOST_CHECK(project1->m_url == "https://example.com/1"); - BOOST_CHECK(comp_double(project1->m_rac, 123.45)); + BOOST_CHECK(project1->m_rac == 123.45); BOOST_CHECK(project1->m_error == GRC::MiningProject::Error::NONE); BOOST_CHECK(project1->Eligible() == true); } else { @@ -520,7 +500,7 @@ BOOST_AUTO_TEST_CASE(it_parses_a_set_of_project_xml_sections) BOOST_CHECK(project2->m_cpid == cpid_2); BOOST_CHECK(project2->m_team == "gridcoin"); BOOST_CHECK(project2->m_url == "https://example.com/2"); - BOOST_CHECK(comp_double(project2->m_rac, 567.89)); + BOOST_CHECK(project2->m_rac == 567.89); BOOST_CHECK(project2->m_error == GRC::MiningProject::Error::NONE); BOOST_CHECK(project2->Eligible() == true); } else { @@ -875,7 +855,7 @@ BOOST_AUTO_TEST_CASE(it_parses_project_xml_to_a_global_researcher_singleton) BOOST_CHECK(project1->m_cpid == cpid_1); BOOST_CHECK(project1->m_team == "gridcoin"); BOOST_CHECK(project1->m_url == "https://example.com/1"); - BOOST_CHECK(comp_double(project1->m_rac, 1.1)); + BOOST_CHECK(project1->m_rac == 1.1); BOOST_CHECK(project1->m_error == GRC::MiningProject::Error::NONE); BOOST_CHECK(project1->Eligible() == true); } else { @@ -887,7 +867,7 @@ BOOST_AUTO_TEST_CASE(it_parses_project_xml_to_a_global_researcher_singleton) BOOST_CHECK(project2->m_cpid == cpid_2); BOOST_CHECK(project2->m_team == "gridcoin"); BOOST_CHECK(project2->m_url == "https://example.com/2"); - BOOST_CHECK(comp_double(project2->m_rac, 2.2)); + BOOST_CHECK(project2->m_rac == 2.2); BOOST_CHECK(project2->m_error == GRC::MiningProject::Error::NONE); BOOST_CHECK(project2->Eligible() == true); } else { @@ -929,7 +909,7 @@ BOOST_AUTO_TEST_CASE(it_looks_up_loaded_boinc_projects_by_name) BOOST_CHECK(project->m_cpid == cpid); BOOST_CHECK(project->m_team == "gridcoin"); BOOST_CHECK(project->m_url == "https://example.com/"); - BOOST_CHECK(comp_double(project->m_rac, 1.1)); + BOOST_CHECK(project->m_rac == 1.1); BOOST_CHECK(project->m_error == GRC::MiningProject::Error::NONE); BOOST_CHECK(project->Eligible() == true); } else { @@ -1068,7 +1048,7 @@ BOOST_AUTO_TEST_CASE(it_tags_invalid_projects_with_errors_when_parsing_xml) BOOST_CHECK(project1->m_name == "project name 1"); BOOST_CHECK(project1->m_cpid == cpid); BOOST_CHECK(project1->m_team == "not gridcoin"); - BOOST_CHECK(comp_double(project1->m_rac, 1.1)); + BOOST_CHECK(project1->m_rac == 1.1); BOOST_CHECK(project1->m_error == GRC::MiningProject::Error::INVALID_TEAM); BOOST_CHECK(project1->Eligible() == false); } else { @@ -1079,7 +1059,7 @@ BOOST_AUTO_TEST_CASE(it_tags_invalid_projects_with_errors_when_parsing_xml) BOOST_CHECK(project2->m_name == "project name 2"); BOOST_CHECK(project2->m_cpid == cpid); BOOST_CHECK(project2->m_team.empty() == true); - BOOST_CHECK(comp_double(project2->m_rac, 2.2)); + BOOST_CHECK(project2->m_rac == 2.2); BOOST_CHECK(project2->m_error == GRC::MiningProject::Error::INVALID_TEAM); BOOST_CHECK(project2->Eligible() == false); } else { @@ -1090,7 +1070,7 @@ BOOST_AUTO_TEST_CASE(it_tags_invalid_projects_with_errors_when_parsing_xml) BOOST_CHECK(project3->m_name == "project name 3"); BOOST_CHECK(project3->m_cpid == GRC::Cpid()); BOOST_CHECK(project3->m_team == "gridcoin"); - BOOST_CHECK(comp_double(project3->m_rac, 3.3)); + BOOST_CHECK(project3->m_rac == 3.3); BOOST_CHECK(project3->m_error == GRC::MiningProject::Error::MALFORMED_CPID); BOOST_CHECK(project3->Eligible() == false); } else { @@ -1101,7 +1081,7 @@ BOOST_AUTO_TEST_CASE(it_tags_invalid_projects_with_errors_when_parsing_xml) BOOST_CHECK(project4->m_name == "project name 4"); BOOST_CHECK(project4->m_cpid == GRC::Cpid()); BOOST_CHECK(project4->m_team == "gridcoin"); - BOOST_CHECK(comp_double(project4->m_rac, 4.4)); + BOOST_CHECK(project4->m_rac == 4.4); BOOST_CHECK(project4->m_error == GRC::MiningProject::Error::MALFORMED_CPID); BOOST_CHECK(project4->Eligible() == false); } else { @@ -1112,7 +1092,7 @@ BOOST_AUTO_TEST_CASE(it_tags_invalid_projects_with_errors_when_parsing_xml) BOOST_CHECK(project5->m_name == "project name 5"); BOOST_CHECK(project5->m_cpid == cpid); BOOST_CHECK(project5->m_team == "gridcoin"); - BOOST_CHECK(comp_double(project5->m_rac, 5.5)); + BOOST_CHECK(project5->m_rac == 5.5); BOOST_CHECK(project5->m_error == GRC::MiningProject::Error::MISMATCHED_CPID); BOOST_CHECK(project5->Eligible() == false); } else { @@ -1123,7 +1103,7 @@ BOOST_AUTO_TEST_CASE(it_tags_invalid_projects_with_errors_when_parsing_xml) BOOST_CHECK(project6->m_name == "project name 6"); BOOST_CHECK(project6->m_cpid == cpid); BOOST_CHECK(project6->m_team == "gridcoin"); - BOOST_CHECK(comp_double(project6->m_rac, 6.6)); + BOOST_CHECK(project6->m_rac == 6.6); BOOST_CHECK(project6->m_error == GRC::MiningProject::Error::MISMATCHED_CPID); BOOST_CHECK(project6->Eligible() == false); } else { @@ -1132,7 +1112,7 @@ BOOST_AUTO_TEST_CASE(it_tags_invalid_projects_with_errors_when_parsing_xml) if (const GRC::ProjectOption project7 = projects.Try("project name 7")) { BOOST_CHECK(project7->m_name == "project name 7"); - BOOST_CHECK(comp_double(project7->m_rac, 7.7)); + BOOST_CHECK(project7->m_rac == 7.7); BOOST_CHECK(project7->m_error == GRC::MiningProject::Error::POOL); BOOST_CHECK(project7->Eligible() == false); } else { @@ -1142,7 +1122,7 @@ BOOST_AUTO_TEST_CASE(it_tags_invalid_projects_with_errors_when_parsing_xml) if (const GRC::ProjectOption project8 = projects.Try("project name 8")) { BOOST_CHECK(project8->m_name == "project name 8"); BOOST_CHECK(project8->m_cpid.IsZero() == true); - BOOST_CHECK(comp_double(project8->m_rac, 8.8)); + BOOST_CHECK(project8->m_rac == 8.8); BOOST_CHECK(project8->m_error == GRC::MiningProject::Error::POOL); BOOST_CHECK(project8->Eligible() == false); } else { @@ -1153,7 +1133,7 @@ BOOST_AUTO_TEST_CASE(it_tags_invalid_projects_with_errors_when_parsing_xml) BOOST_CHECK(project9->m_name == "project name 9"); BOOST_CHECK(project9->m_cpid == cpid); BOOST_CHECK(project9->m_team == "not gridcoin"); - BOOST_CHECK(comp_double(project9->m_rac, 0.0)); + BOOST_CHECK(project9->m_rac == 0.0); BOOST_CHECK(project9->m_error == GRC::MiningProject::Error::INVALID_TEAM); BOOST_CHECK(project9->Eligible() == false); } else { @@ -1232,7 +1212,7 @@ BOOST_AUTO_TEST_CASE(it_skips_the_team_requirement_when_set_by_protocol) BOOST_CHECK(project1->m_name == "project name 1"); BOOST_CHECK(project1->m_cpid == cpid); BOOST_CHECK(project1->m_team == "! not gridcoin !"); - BOOST_CHECK(comp_double(project1->m_rac, 1.1)); + BOOST_CHECK(project1->m_rac == 1.1); BOOST_CHECK(project1->m_error == GRC::MiningProject::Error::NONE); BOOST_CHECK(project1->Eligible() == true); } else { @@ -1302,7 +1282,7 @@ BOOST_AUTO_TEST_CASE(it_applies_the_team_whitelist_when_set_by_the_protocol) BOOST_CHECK(project1->m_name == "project name 1"); BOOST_CHECK(project1->m_cpid == cpid); BOOST_CHECK(project1->m_team == "! not gridcoin !"); - BOOST_CHECK(comp_double(project1->m_rac, 1.1)); + BOOST_CHECK(project1->m_rac == 1.1); BOOST_CHECK(project1->m_error == GRC::MiningProject::Error::INVALID_TEAM); BOOST_CHECK(project1->Eligible() == false); } else { @@ -1313,7 +1293,7 @@ BOOST_AUTO_TEST_CASE(it_applies_the_team_whitelist_when_set_by_the_protocol) BOOST_CHECK(project2->m_name == "project name 2"); BOOST_CHECK(project2->m_cpid == cpid); BOOST_CHECK(project2->m_team == "team 1"); - BOOST_CHECK(comp_double(project2->m_rac, 0)); + BOOST_CHECK(project2->m_rac == 0); BOOST_CHECK(project2->m_error == GRC::MiningProject::Error::NONE); BOOST_CHECK(project2->Eligible() == true); } else { @@ -1324,7 +1304,7 @@ BOOST_AUTO_TEST_CASE(it_applies_the_team_whitelist_when_set_by_the_protocol) BOOST_CHECK(project3->m_name == "project name 3"); BOOST_CHECK(project3->m_cpid == cpid); BOOST_CHECK(project3->m_team == "team 2"); - BOOST_CHECK(comp_double(project3->m_rac, 0)); + BOOST_CHECK(project3->m_rac == 0); BOOST_CHECK(project3->m_error == GRC::MiningProject::Error::NONE); BOOST_CHECK(project3->Eligible() == true); } else { @@ -1649,7 +1629,7 @@ void it_parses_project_xml_from_a_client_state_xml_file() BOOST_CHECK(project1->m_name == "valid project 1"); BOOST_CHECK(project1->m_cpid == cpid_1); BOOST_CHECK(project1->m_team == "gridcoin"); - BOOST_CHECK(comp_double(project1->m_rac, 1.1)); + BOOST_CHECK(project1->m_rac == 1.1); BOOST_CHECK(project1->m_url == "https://project1.example.com/boinc/"); BOOST_CHECK(project1->m_error == GRC::MiningProject::Error::NONE); BOOST_CHECK(project1->Eligible() == true); @@ -1661,7 +1641,7 @@ void it_parses_project_xml_from_a_client_state_xml_file() BOOST_CHECK(project2->m_name == "valid project 2"); BOOST_CHECK(project2->m_cpid == cpid_2); BOOST_CHECK(project2->m_team == "gridcoin"); - BOOST_CHECK(comp_double(project2->m_rac, 2.2)); + BOOST_CHECK(project2->m_rac == 2.2); BOOST_CHECK(project2->m_url == "https://project2.example.com/boinc/"); BOOST_CHECK(project2->m_error == GRC::MiningProject::Error::NONE); BOOST_CHECK(project2->Eligible() == true); @@ -1674,7 +1654,7 @@ void it_parses_project_xml_from_a_client_state_xml_file() BOOST_CHECK(project3->m_name == "invalid project 3"); BOOST_CHECK(project3->m_cpid == cpid_2); BOOST_CHECK(project3->m_team == "gridcoin"); - BOOST_CHECK(comp_double(project3->m_rac, 3.3)); + BOOST_CHECK(project3->m_rac == 3.3); BOOST_CHECK(project3->m_url == "https://project3.example.com/boinc/"); BOOST_CHECK(project3->m_error == GRC::MiningProject::Error::MISMATCHED_CPID); BOOST_CHECK(project3->Eligible() == false); diff --git a/src/test/gridcoin/superblock_tests.cpp b/src/test/gridcoin/superblock_tests.cpp index 61a8a7642c..eea417b628 100644 --- a/src/test/gridcoin/superblock_tests.cpp +++ b/src/test/gridcoin/superblock_tests.cpp @@ -33,7 +33,7 @@ struct Legacy struct BinaryResearcher { std::array cpid; - int16_t magnitude; + uint16_t magnitude; }; static std::string ExtractValue(std::string data, std::string delimiter, int pos) diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp index 36463d42d6..4fc02a1818 100644 --- a/src/test/merkle_tests.cpp +++ b/src/test/merkle_tests.cpp @@ -9,6 +9,129 @@ BOOST_AUTO_TEST_SUITE(merkle_tests) +/* This implements a constant-space merkle root/path calculator, limited to 2^32 leaves. */ +static void MerkleComputation(const std::vector& leaves, uint256* proot, bool* pmutated, uint32_t branchpos, std::vector* pbranch) { + if (pbranch) pbranch->clear(); + if (leaves.size() == 0) { + if (pmutated) *pmutated = false; + if (proot) *proot = uint256(); + return; + } + bool mutated = false; + // count is the number of leaves processed so far. + uint32_t count = 0; + // inner is an array of eagerly computed subtree hashes, indexed by tree + // level (0 being the leaves). + // For example, when count is 25 (11001 in binary), inner[4] is the hash of + // the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to + // the last leaf. The other inner entries are undefined. + uint256 inner[32]; + // Which position in inner is a hash that depends on the matching leaf. + int matchlevel = -1; + // First process all leaves into 'inner' values. + while (count < leaves.size()) { + uint256 h = leaves[count]; + bool matchh = count == branchpos; + count++; + int level; + // For each of the lower bits in count that are 0, do 1 step. Each + // corresponds to an inner value that existed before processing the + // current leaf, and each needs a hash to combine it. + for (level = 0; !(count & (((uint32_t)1) << level)); level++) { + if (pbranch) { + if (matchh) { + pbranch->push_back(inner[level]); + } else if (matchlevel == level) { + pbranch->push_back(h); + matchh = true; + } + } + mutated |= (inner[level] == h); + CHash256().Write(inner[level]).Write(h).Finalize(h); + } + // Store the resulting hash at inner position level. + inner[level] = h; + if (matchh) { + matchlevel = level; + } + } + // Do a final 'sweep' over the rightmost branch of the tree to process + // odd levels, and reduce everything to a single top value. + // Level is the level (counted from the bottom) up to which we've sweeped. + int level = 0; + // As long as bit number level in count is zero, skip it. It means there + // is nothing left at this level. + while (!(count & (((uint32_t)1) << level))) { + level++; + } + uint256 h = inner[level]; + bool matchh = matchlevel == level; + while (count != (((uint32_t)1) << level)) { + // If we reach this point, h is an inner value that is not the top. + // We combine it with itself (Bitcoin's special rule for odd levels in + // the tree) to produce a higher level one. + if (pbranch && matchh) { + pbranch->push_back(h); + } + CHash256().Write(h).Write(h).Finalize(h); + // Increment count to the value it would have if two entries at this + // level had existed. + count += (((uint32_t)1) << level); + level++; + // And propagate the result upwards accordingly. + while (!(count & (((uint32_t)1) << level))) { + if (pbranch) { + if (matchh) { + pbranch->push_back(inner[level]); + } else if (matchlevel == level) { + pbranch->push_back(h); + matchh = true; + } + } + CHash256().Write(inner[level]).Write(h).Finalize(h); + level++; + } + } + // Return result. + if (pmutated) *pmutated = mutated; + if (proot) *proot = h; +} + +uint256 ComputeMerkleRoot(const std::vector& leaves, bool* mutated) { + uint256 hash; + MerkleComputation(leaves, &hash, mutated, -1, nullptr); + return hash; +} + +std::vector ComputeMerkleBranch(const std::vector& leaves, uint32_t position) { + std::vector ret; + MerkleComputation(leaves, nullptr, nullptr, position, &ret); + return ret; +} + +uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector& vMerkleBranch, uint32_t nIndex) { + uint256 hash = leaf; + for (std::vector::const_iterator it = vMerkleBranch.begin(); it != vMerkleBranch.end(); ++it) { + if (nIndex & 1) { + hash = Hash(*it, hash); + } else { + hash = Hash(hash, *it); + } + nIndex >>= 1; + } + return hash; +} + +std::vector BlockMerkleBranch(const CBlock& block, uint32_t position) +{ + std::vector leaves; + leaves.resize(block.vtx.size()); + for (size_t s = 0; s < block.vtx.size(); s++) { + leaves[s] = block.vtx[s].GetHash(); + } + return ComputeMerkleBranch(leaves, position); +} + // Older version of the merkle root computation code, for comparison. static uint256 BlockBuildMerkleTree(const CBlock& block, std::vector& vMerkleTree) { diff --git a/src/test/test_gridcoin.cpp b/src/test/test_gridcoin.cpp index cacb3e12ec..7a9f325e3d 100644 --- a/src/test/test_gridcoin.cpp +++ b/src/test/test_gridcoin.cpp @@ -14,6 +14,8 @@ #include "random.h" #include "wallet/wallet.h" +leveldb::Env* txdb_env; + extern CWallet* pwalletMain; extern leveldb::DB *txdb; extern CClientUIInterface uiInterface; @@ -48,7 +50,7 @@ struct TestingSetup { // TODO: Refactor CTxDB to something like bitcoin's current CDBWrapper and remove this workaround. leveldb::Options db_options; - db_options.env = leveldb::NewMemEnv(leveldb::Env::Default()); // Use a memory environment to avoid polluting the production leveldb. + db_options.env = txdb_env = leveldb::NewMemEnv(leveldb::Env::Default()); // Use a memory environment to avoid polluting the production leveldb. db_options.create_if_missing = true; db_options.error_if_exists = true; assert(leveldb::DB::Open(db_options, "", &txdb).ok()); @@ -71,7 +73,9 @@ struct TestingSetup { bitdb.Flush(true); g_banman.reset(); delete txdb; + delete txdb_env; txdb = nullptr; + txdb_env = nullptr; g_mock_deterministic_tests = false; ECC_Stop(); } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 26809b7214..dbb899992f 100755 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) break; } - COutPoint outpoint(uint256S(vinput[0].get_str()), vinput[1].get_int()); + COutPoint outpoint(uint256S(vinput[0].get_str()), uint32_t(vinput[1].get_int())); mapprevOutScriptPubKeys[outpoint] = ParseScript(vinput[2].get_str()); } @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) fValid = false; break; } - COutPoint outpoint(uint256S(vinput[0].get_str()), vinput[1].get_int()); + COutPoint outpoint(uint256S(vinput[0].get_str()), uint32_t(vinput[1].get_int())); mapprevOutScriptPubKeys[outpoint] = ParseScript(vinput[2].get_str()); } if (!fValid) diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 5ccb4fc05e..1aeccd2785 100755 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1173,7 +1173,7 @@ BOOST_AUTO_TEST_CASE(Fraction_msb_performance_test) g_timer.InitTimer("msb_test", true); for (unsigned int i = 0; i < iterations; ++i) { - msb(rand.rand64()); + msb((int64_t)rand.rand64()); } int64_t msb_test_time = g_timer.GetTimes(strprintf("msb %u iterations", iterations), "msb_test").time_since_last_check; @@ -1181,7 +1181,7 @@ BOOST_AUTO_TEST_CASE(Fraction_msb_performance_test) FastRandomContext rand2(uint256 {0}); for (unsigned int i = 0; i < iterations; ++i) { - msb2(rand2.rand64()); + msb2((int64_t)rand2.rand64()); } int64_t msb2_test_time = g_timer.GetTimes(strprintf("msb2 %u iterations", iterations), "msb_test").time_since_last_check; @@ -1189,7 +1189,7 @@ BOOST_AUTO_TEST_CASE(Fraction_msb_performance_test) FastRandomContext rand3(uint256 {0}); for (unsigned int i = 0; i < iterations; ++i) { - msb3(rand3.rand64()); + msb3((int64_t)rand3.rand64()); } int64_t msb3_test_time = g_timer.GetTimes(strprintf("msb3 %u iterations", iterations), "msb_test").time_since_last_check; diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp index c5e390e531..a741d2a127 100755 --- a/src/test/wallet_tests.cpp +++ b/src/test/wallet_tests.cpp @@ -13,6 +13,8 @@ using namespace std; +std::vector> wtxn; + typedef set > CoinSet; BOOST_AUTO_TEST_SUITE(wallet_tests) @@ -23,13 +25,12 @@ static vector vCoins; static void add_coin(int64_t nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) { static int i; - CTransaction* tx = new CTransaction; - tx->nLockTime = i++; // so all transactions get different hashes - tx->nTime = 0; - tx->vout.resize(nInput+1); - tx->vout[nInput].nValue = nValue; - CWalletTx* wtx = new CWalletTx(&wallet, *tx); - delete tx; + CTransaction tx; + tx.nLockTime = i++; // so all transactions get different hashes + tx.nTime = 0; + tx.vout.resize(nInput+1); + tx.vout[nInput].nValue = nValue; + std::unique_ptr wtx(new CWalletTx(&wallet, tx)); if (fIsFromMe) { // IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(), @@ -38,15 +39,15 @@ static void add_coin(int64_t nValue, int nAge = 6*24, bool fIsFromMe = false, in wtx->fDebitCached = true; wtx->nDebitCached = 1; } - COutput output(wtx, nInput, nAge); + COutput output(wtx.get(), nInput, nAge); vCoins.push_back(output); + wtxn.emplace_back(std::move(wtx)); } static void empty_wallet() { - for(COutput& output : vCoins) - delete output.tx; vCoins.clear(); + wtxn.clear(); } static bool equal_sets(CoinSet a, CoinSet b) @@ -298,6 +299,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) BOOST_CHECK_NE(fails, RANDOM_REPEATS); } } + empty_wallet(); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index db89881f26..d2f0a4852f 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -112,7 +112,7 @@ void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { // if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator bool fHaveColon = colon != in.npos; bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe - bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos); + bool fMultiColon = fHaveColon && (colon != 0) && (in.find_last_of(':',colon-1) != in.npos); if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) { int32_t n; if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) { diff --git a/test/sanitizer_suppressions/lsan b/test/sanitizer_suppressions/lsan new file mode 100644 index 0000000000..7ccb22515f --- /dev/null +++ b/test/sanitizer_suppressions/lsan @@ -0,0 +1,2 @@ +# Suppress warnings triggered in dependencies +leak:libQt5Widgets diff --git a/test/sanitizer_suppressions/tsan b/test/sanitizer_suppressions/tsan new file mode 100644 index 0000000000..82c6885bc4 --- /dev/null +++ b/test/sanitizer_suppressions/tsan @@ -0,0 +1,45 @@ +# ThreadSanitizer suppressions +# ============================ +# +# https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions + +# race (TODO fix) +race:LoadWallet +race:WalletBatch::WriteHDChain +race:BerkeleyBatch +race:BerkeleyDatabase +race:DatabaseBatch +race:zmq::* +race:bitcoin-qt + +# deadlock (TODO fix) +# To reproduce, see: +# https://github.com/bitcoin/bitcoin/issues/19303#issuecomment-1514926359 +deadlock:Chainstate::ConnectTip + +# Intentional deadlock in tests +deadlock:sync_tests::potential_deadlock_detected + +# Wildcard for all gui tests, should be replaced with non-wildcard suppressions +race:src/qt/test/* +deadlock:src/qt/test/* + +# External libraries +# https://github.com/bitcoin/bitcoin/pull/27658#issuecomment-1547639621 +deadlock:libdb +race:libzmq + +# Intermittent issues +# ------------------- +# +# Suppressions that follow might only happen intermittently, thus they are not +# reproducible. Make sure to include a link to a full trace. + +# https://github.com/bitcoin/bitcoin/issues/20618 +race:CZMQAbstractPublishNotifier::SendZmqMessage + +# https://github.com/bitcoin/bitcoin/pull/27498#issuecomment-1517410478 +race:epoll_ctl + +# https://github.com/bitcoin/bitcoin/issues/23366 +race:std::__1::ios_base::* diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan new file mode 100644 index 0000000000..600d2dc8a2 --- /dev/null +++ b/test/sanitizer_suppressions/ubsan @@ -0,0 +1,61 @@ +# Suppressions should use `sanitize-type:ClassName::MethodName`. + +# -fsanitize=undefined suppressions +# ================================= + +# -fsanitize=integer suppressions +# =============================== +# Dependencies +# ------------ +# Suppressions in dependencies that are developed outside this repository. +unsigned-integer-overflow:*/include/c++/ +unsigned-integer-overflow:FuzzedDataProvider::ConsumeIntegralInRange +unsigned-integer-overflow:leveldb/ +unsigned-integer-overflow:minisketch/ +unsigned-integer-overflow:test/fuzz/crypto_diff_fuzz_chacha20.cpp +implicit-integer-sign-change:*/include/boost/ +implicit-integer-sign-change:*/include/c++/ +implicit-integer-sign-change:*/new_allocator.h +implicit-integer-sign-change:crc32c/ +implicit-integer-sign-change:minisketch/ +implicit-signed-integer-truncation:*/include/c++/ +implicit-signed-integer-truncation:leveldb/ +implicit-unsigned-integer-truncation:*/include/c++/ +implicit-unsigned-integer-truncation:leveldb/ +implicit-unsigned-integer-truncation:test/fuzz/crypto_diff_fuzz_chacha20.cpp +shift-base:*/include/c++/ +shift-base:leveldb/ +shift-base:minisketch/ +shift-base:test/fuzz/crypto_diff_fuzz_chacha20.cpp +# Unsigned integer overflow occurs when the result of an unsigned integer +# computation cannot be represented in its type. Unlike signed integer overflow, +# this is not undefined behavior, but it is often unintentional. The list below +# contains files in which we expect unsigned integer overflows to occur. The +# list is used to suppress -fsanitize=integer warnings when running our CI UBSan +# job. +unsigned-integer-overflow:arith_uint256.h +unsigned-integer-overflow:common/bloom.cpp +unsigned-integer-overflow:coins.cpp +unsigned-integer-overflow:compressor.cpp +unsigned-integer-overflow:crypto/ +unsigned-integer-overflow:hash.cpp +unsigned-integer-overflow:policy/fees.cpp +unsigned-integer-overflow:prevector.h +unsigned-integer-overflow:script.cpp +unsigned-integer-overflow:xoroshiro128plusplus.h +implicit-integer-sign-change:compat/stdin.cpp +implicit-integer-sign-change:compressor.h +implicit-integer-sign-change:crypto/ +implicit-integer-sign-change:policy/fees.cpp +implicit-integer-sign-change:prevector.h +implicit-integer-sign-change:script/bitcoinconsensus.cpp +implicit-integer-sign-change:script.cpp +implicit-integer-sign-change:serialize.h +implicit-signed-integer-truncation:crypto/ +implicit-unsigned-integer-truncation:crypto/ +shift-base:arith_uint256.cpp +shift-base:crypto/ +shift-base:hash.cpp +shift-base:streams.h +shift-base:util/bip32.cpp +shift-base:xoroshiro128plusplus.h