diff --git a/.travis.yml b/.travis.yml index f997848bbe489..6d611f940e0cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,14 +14,14 @@ # # These caches can be manually removed if necessary. This is one of the very # few manual operations that is possible with Travis, and it can be done by a -# PIVX GitHub member via the Travis web interface [0]. +# Phore GitHub member via the Travis web interface [0]. # # Travis CI uploads the cache after the script phase of the build [1]. # However, the build is terminated without saving the chache if it takes over # 50 minutes [2]. Thus, if we spent too much time in early build stages, fail # with an error and save the cache. # -# [0] https://travis-ci.org/pivx-project/pivx/caches +# [0] https://travis-ci.org/phoreproject/Phore/caches # [1] https://docs.travis-ci.com/user/caching/#build-phases # [2] https://docs.travis-ci.com/user/customizing-the-build#build-timeouts @@ -33,15 +33,18 @@ cache: directories: - $TRAVIS_BUILD_DIR/depends/built - $TRAVIS_BUILD_DIR/depends/sdk-sources + - $TRAVIS_BUILD_DIR/cmake-build-debug - $HOME/.ccache stages: - lint + - cmake - test + env: global: - MAKEJOBS=-j3 - RUN_UNIT_TESTS=true - - RUN_FUNCTIONAL_TESTS=false # Not Yet Implemented + - RUN_FUNCTIONAL_TESTS=false - RUN_BENCH=false # Set to true for any one job that has debug enabled, to quickly check bench is not crashing or hitting assertions - DOCKER_NAME_TAG=ubuntu:18.04 - BOOST_TEST_RANDOM=1$TRAVIS_BUILD_ID @@ -64,12 +67,16 @@ script: - export CONTINUE=1 - if [ $SECONDS -gt 1200 ]; then export CONTINUE=0; fi # Likely the depends build took very long - if [ $CONTINUE = "1" ]; then set -o errexit; source .travis/test_06_script_a.sh; else set +o errexit; echo "$CACHE_ERR_MSG"; false; fi - - if [ $SECONDS -gt 1500 ]; then export CONTINUE=0; fi # Likely the build took very long; The tests take about 1000s, so we should abort if we have less than 50*60-1000=2000s left + - if [ -n ${BUILD_TIMEOUT+x} ] && [ $SECONDS -gt $BUILD_TIMEOUT ]; then export CONTINUE=0; fi + - if [ $SECONDS -gt 2000 ]; then export CONTINUE=0; fi # Likely the build took very long; The tests take about 1000s, so we should abort if we have less than 50*60-1000=2000s left - if [ $CONTINUE = "1" ]; then set -o errexit; source .travis/test_06_script_b.sh; else set +o errexit; echo "$CACHE_ERR_MSG"; false; fi after_script: - echo $TRAVIS_COMMIT_RANGE - echo $TRAVIS_COMMIT_LOG jobs: + allow_failures: + - stage: cmake + fast_finish: true include: - stage: lint @@ -95,7 +102,7 @@ jobs: GOAL="install" # -Wno-psabi is to disable ABI warnings: "note: parameter passing for argument of type ... changed in GCC 7.1" # This could be removed once the ABI change warning does not show up by default - BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CXXFLAGS=-Wno-psabi" + BITCOIN_CONFIG="--with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports CXXFLAGS=-Wno-psabi" - stage: test name: 'ARM 64-bit [GOAL:install] [no unit or functional tests]' @@ -105,7 +112,7 @@ jobs: RUN_UNIT_TESTS=false RUN_FUNCTIONAL_TESTS=false GOAL="install" - BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" + BITCOIN_CONFIG="--with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports" - stage: test name: 'Win32 [GOAL: deploy] [no functional tests]' @@ -115,7 +122,7 @@ jobs: PACKAGES="python3 nsis g++-mingw-w64-i686 wine-binfmt wine32" RUN_FUNCTIONAL_TESTS=false GOAL="deploy" - BITCOIN_CONFIG="--enable-reduce-exports" + BITCOIN_CONFIG="--with-gui=qt5 --enable-reduce-exports" - stage: test name: 'Win64 [GOAL: deploy] [no functional tests]' @@ -124,10 +131,10 @@ jobs: PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64" RUN_FUNCTIONAL_TESTS=false GOAL="deploy" - BITCOIN_CONFIG="--enable-reduce-exports" + BITCOIN_CONFIG="--with-gui=qt5 --enable-reduce-exports" - stage: test - name: '32-bit + dash [GOAL: install] [no gui]' + name: '32-bit + dash [GOAL: install]' env: >- HOST=i686-pc-linux-gnu PACKAGES="g++-multilib python3-zmq" @@ -207,3 +214,54 @@ jobs: RUN_FUNCTIONAL_TESTS=false GOAL="deploy" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" + + - stage: cmake + name: 'CMake Linux' + language: cpp + compiler: + - gcc + addons: + apt: + packages: + - python3-zmq qtbase5-dev qttools5-dev-tools libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev libgmp-dev + before_install: + install: + before_script: + script: + - mkdir -p $TRAVIS_BUILD_DIR/cmake-build-debug && cd $TRAVIS_BUILD_DIR/cmake-build-debug + - cmake -DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - Unix Makefiles" $TRAVIS_BUILD_DIR + - cmake --build $TRAVIS_BUILD_DIR/cmake-build-debug --target all -- -j 3 + + - stage: cmake + name: 'CMake macOS' + os: osx + osx_image: xcode10.1 + language: cpp + compiler: + - clang + addons: + homebrew: + packages: + - autoconf + - automake + - berkeley-db4 + - libtool + - boost + - miniupnpc + - openssl + - pkg-config + - protobuf + - python3 + - qt5 + - zmq + - libevent + - qrencode + - gmp + update: true + before_install: + install: + before_script: + script: + - mkdir -p $TRAVIS_BUILD_DIR/cmake-build-debug && cd $TRAVIS_BUILD_DIR/cmake-build-debug + - cmake -DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - Unix Makefiles" $TRAVIS_BUILD_DIR + - cmake --build $TRAVIS_BUILD_DIR/cmake-build-debug --target all -- -j 3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 43797e3b5c722..6d167026fdcd9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,19 @@ cmake_minimum_required(VERSION 3.10) -project(PIVX) +project(Phore) set(BDB_VER "4.8.30") set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/contrib/cmake") set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ") include(${CMAKE_ROOT}/Modules/ExternalProject.cmake) +if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") + message(FATAL_ERROR "Native Windows CMake is not supported yet, use WSL instead") +endif() + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") message(STATUS "Compiling on macOS") + set(ENV{target} "Mac") list(APPEND CMAKE_PREFIX_PATH /usr/local/opt/qt5) list(APPEND CMAKE_PREFIX_PATH /usr/local/opt/openssl) list(APPEND CMAKE_PREFIX_PATH /usr/local/Cellar/berkeley-db@4) @@ -16,93 +21,72 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(ENV{CPPFLAGS} "-I${OPENSSL_ROOT_DIR}/include") set(ENV{LDFLAGS} "-L${OPENSSL_ROOT_DIR}/lib") set(BerkeleyDB_ROOT_DIR "/usr/local/Cellar/berkeley-db@4/${BDB_VER}/") + set(Boost_USE_MULTITHREADED ON) + set(Boost_NO_BOOST_CMAKE ON) elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set(ENV{target} "Linux") file(READ "/proc/version" _SYS_VERSION) std::string(REGEX MATCH "Microsoft" _SYSTEM_VERSION_PARSED "${_SYS_VERSION}") if(${_SYSTEM_VERSION_PARSED} MATCHES "Microsoft") message(STATUS "Compiling on WSL") - set(ENV{triple} x86_64-w64-mingw32) - set(ENV{target} Windows) - execute_process( - COMMAND make HOST=x86_64-w64-mingw32 -j16 - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/depends - ) - file(REMOVE ${CMAKE_BINARY_DIR}/CMakeFiles/${CMAKE_VERSION}/CMakeCXXCompiler.cmake) - file(REMOVE ${CMAKE_BINARY_DIR}/CMakeFiles/${CMAKE_VERSION}/CMakeCCompiler.cmake) - set(CMAKE_C_COMPILER /usr/bin/$ENV{triple}-gcc) - set(CMAKE_CXX_COMPILER /usr/bin/$ENV{triple}-g++) - set(CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR}/depends/$ENV{triple}) - set(BOOST_INCLUDEDIR ${CMAKE_CURRENT_SOURCE_DIR}/depends/$ENV{triple}/include/) - set(BOOST_LIBRARYDIR ${CMAKE_CURRENT_SOURCE_DIR}/depends/$ENV{triple}/lib) - set(Boost_USE_STATIC_RUNTIME ON) - set(Boost_THREADAPI "win32") + set(ENV{DISPLAY} ":0") + set(ENV{LIBGL_ALWAYS_INDIRECT} 1) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY /home/$ENV{USER}/pivx-run) + message(WARNING "WSL Runtime support requires VcXsrv to be installed and running") else() message(STATUS "Compiling on Linux") - list(APPEND CMAKE_PREFIX_PATH /usr/lib/x86_64-linux-gnu/cmake/Qt5) - set(Qt5core_DIR "/usr/lib/x86_64-linux-gnu/cmake/Qt5/QtCore") - include_directories("/usr/include") - include_directories("/usr/include/x86_64-linux-gnu") - add_definitions("-D__BYTE_ORDER -D__LITTLE_ENDIAN") endif() + list(APPEND CMAKE_PREFIX_PATH /usr/lib/x86_64-linux-gnu/cmake/Qt5) + set(Qt5core_DIR "/usr/lib/x86_64-linux-gnu/cmake/Qt5/QtCore") + add_definitions("-D__BYTE_ORDER -D__LITTLE_ENDIAN -D__GLIBC__ -DHAVE_DECL_BSWAP_16=0 -DHAVE_DECL_BSWAP_32=0 -DHAVE_DECL_BSWAP_64=0") endif() # Find Dependencies find_package(BerkeleyDB ${BDB_VER} REQUIRED) -if (BerkeleyDB_FOUND) +if(BerkeleyDB_FOUND) if(NOT ${BerkeleyDB_VERSION} MATCHES "${BDB_VER}") message(WARNING "BerkeleyDB version other than ${BDB_VER} found!") set(BDB_CONFIGURE_FLAGS "--with-incompatible-bdb") endif() - include_directories( ${BerkeleyDB_INCLUDE_DIRS} ) - link_directories( ${BerkeleyDB_ROOT_DIR}/lib ) -endif () +endif() find_package(OpenSSL REQUIRED) -if (OPENSSL_FOUND) +if(OPENSSL_FOUND) message(STATUS "Found OpenSSL (${OPENSSL_VERSION}): ${OPENSSL_LIBRARIES}") - include_directories( ${OPENSSL_INCLUDE_DIR} ) + if(OPENSSL_VERSION VERSION_GREATER_EQUAL 1.1) + message(STATUS "Found unsupported OpenSSL version!") + set(SSL_CONFIGURE_FLAGS "--with-unsupported-ssl") + endif() endif() find_package(LibEvent REQUIRED) -if (LibEvent_FOUND) - include_directories ( ${LibEvent_INCLUDE_DIR} ) - link_directories ( ${LibEvent_LIBRARY_DIRS} ) -endif() find_package(GMP) -if (GMP_FOUND) - include_directories ( ${GMP_INCLUDE_DIR} ) - link_directories ( ${GMP_LIBRARY_DIRS} ) +if(GMP_FOUND) else() message(WARNING "GMP not found, falling back to OpenSSL for bignum!") set(BIGNUM_CONFIGURE_FLAGS "--with-zerocoin-bignum=openssl") endif() find_package(ZMQ) -if (ZMQ_Found) - include_directories ( ${ZMQ_INCLUDE_DIR} ) - link_directories ( ${ZMQ_LIB_DIRS} ) -endif() - +find_package(Miniupnp) find_package(Boost COMPONENTS system filesystem thread program_options REQUIRED) -include_directories( ${Boost_INCLUDE_DIRS} ) -link_directories ( ${Boost_LIBRARY_DIRS} ) # run autogen.sh if missing header files from configure on Linux/Mac -if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/configure") +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/configure") else() execute_process( - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/autogen.sh - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/autogen.sh + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) endif() -# run configure if pivx_config.h doesn't exist -if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/config/pivx-config.h") +# run configure if phore_config.h doesn't exist +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/config/phore-config.h") else() execute_process( - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/configure ${BDB_CONFIGURE_FLAGS} ${BIGNUM_CONFIGURE_FLAGS} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/configure ${CONFIGSITE} ${BDB_CONFIGURE_FLAGS} ${BIGNUM_CONFIGURE_FLAGS} ${SSL_CONFIGURE_FLAGS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) endif() add_definitions(-DHAVE_CONFIG_H) @@ -111,7 +95,7 @@ ExternalProject_Add ( libunivalue SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/univalue CONFIGURE_COMMAND "" - BUILD_COMMAND make + BUILD_COMMAND $(MAKE) BUILD_IN_SOURCE 1 INSTALL_COMMAND "" ) @@ -120,31 +104,13 @@ ExternalProject_Add ( libsecp256k1 SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/secp256k1 CONFIGURE_COMMAND "" - BUILD_COMMAND make + BUILD_COMMAND $(MAKE) BUILD_IN_SOURCE 1 INSTALL_COMMAND "" -) - -link_directories( - ${CMAKE_SOURCE_DIR}/src/univalue/.libs - ${CMAKE_SOURCE_DIR}/src/secp256k1/.libs -) - -include_directories( - ${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/src/compat - ${CMAKE_SOURCE_DIR}/src/config - ${CMAKE_SOURCE_DIR}/src/obj - ${CMAKE_SOURCE_DIR}/src/leveldb - ${CMAKE_SOURCE_DIR}/src/leveldb/include - ${CMAKE_SOURCE_DIR}/src/leveldb/helpers/memenv - ${CMAKE_SOURCE_DIR}/src/secp256k1 - ${CMAKE_SOURCE_DIR}/src/secp256k1/include - ${CMAKE_SOURCE_DIR}/src/univalue/include -) + ) set(libleveldb_a_headers - ${CMAKE_CURRENT_SOURCE_DIR}/src/config/pivx-config.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/config/phore-config.h ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/port/atomic_pointer.h ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/port/port_example.h ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/port/port_posix.h @@ -244,22 +210,27 @@ set(libleveldb_a_sources ) set(libleveldb_cpp_flags) -if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") - set(libleveldb_cpp_flags -DOS_WINDOWS -DLEVELDB_PLATFORM_WINDOWS -DWINVER=0x0500 -D__USE_MINGW_ANSI_STDIO=1) - list(APPEND libleveldb_a_sources ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/util/env_win.cc) - list(APPEND libleveldb_a_srouces ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/port/port_win.cc) +if($ENV{target} MATCHES "Windows") + set(libleveldb_cpp_flags -DOS_WINDOWS -DLEVELDB_PLATFORM_WINDOWS -DWINVER=0x0500 -D__USE_MINGW_ANSI_STDIO=1) + set(libleveldb_a_sources ${libleveldb_a_sources} ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/util/env_win.cc + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/port/port_win.cc) else() if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(libleveldb_cpp_flags -DOS_MACOSX -DLEVELDB_PLATFORM_POSIX) else() set(libleveldb_cpp_flags -DOS_LINUX -DLEVELDB_PLATFORM_POSIX) endif() - list(APPEND libleveldb_a_sources ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/port/port_posix.cc) + set(libleveldb_a_sources ${libleveldb_a_sources} ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/port/port_posix.cc) endif() add_library(leveldb STATIC ${libleveldb_a_headers} ${libleveldb_a_sources}) target_compile_definitions(leveldb PUBLIC ${libleveldb_cpp_flags} -DLEVELDB_ATOMIC_PRESENT -D__STDC_LIMIT_MACROS) -target_compile_options(leveldb PUBLIC -I${CMAKE_CURRENT_SOURCE_DIR}/src/compat) +target_include_directories(leveldb PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/include + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/helpers/memenv + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/compat + ) set(libmemenv_a_sources ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/helpers/memenv/memenv.cc @@ -267,11 +238,17 @@ set(libmemenv_a_sources ) add_library(memenv STATIC ${libmemenv_a_sources}) target_compile_definitions(memenv PUBLIC ${libleveldb_cpp_flags}) +target_include_directories(memenv PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/include + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/helpers/memenv + ) set(libleveldb_sse42_a_sources ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/port/port_posix_sse.cc) add_library(leveldb_sse42 ${libleveldb_sse42_a_sources}) - -link_directories ( ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb ) +target_include_directories(leveldb_sse42 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/include + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/helpers/memenv + ) file(GLOB HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h) file(GLOB CRYPTO_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/*.h) @@ -328,14 +305,25 @@ set(SERVER_SOURCES ./src/zpivchain.cpp ) add_library(SERVER_A STATIC ${BitcoinHeaders} ${SERVER_SOURCES}) +if(MINIUPNP_FOUND) + target_compile_definitions(SERVER_A PUBLIC "-DSTATICLIB -DMINIUPNP_STATICLIB") +endif() +target_include_directories(SERVER_A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/univalue/include + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/include + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/helpers/memenv + ${ZMQ_INCLUDE_DIR} ${LIBEVENT_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR} ${BerkeleyDB_INCLUDE_DIRS} + ) -if (ZMQ_FOUND) +if(ZMQ_FOUND) set(ZMQ_SOURCES ./src/zmq/zmqabstractnotifier.cpp ./src/zmq/zmqnotificationinterface.cpp ./src/zmq/zmqpublishnotifier.cpp ) - add_library(ZMQ_A STATIC ${BitcoinHeaders} ${ZMQ_SOURCES}) + add_library(ZMQ_A STATIC ${BitcoinHeaders} ${ZMQ_SOURCES} ${ZMQ_LIB}) + target_include_directories(ZMQ_A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src ${ZMQ_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR}) + target_compile_definitions(ZMQ_A PUBLIC "-DZMQ_STATIC") endif() set(WALLET_SOURCES @@ -370,6 +358,13 @@ set(WALLET_SOURCES ./src/lightzpivthread.cpp ) add_library(WALLET_A STATIC ${BitcoinHeaders} ${WALLET_SOURCES}) +target_include_directories(WALLET_A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/secp256k1/include + ${CMAKE_CURRENT_SOURCE_DIR}/src/univalue/include + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/include + ${OPENSSL_INCLUDE_DIR} + ${BerkeleyDB_INCLUDE_DIRS} + ) set(BITCOIN_CRYPTO_SOURCES ./src/crypto/sha1.cpp @@ -405,6 +400,7 @@ set(BITCOIN_CRYPTO_SOURCES ./src/crypto/sph_types.h ) add_library(BITCOIN_CRYPTO_A STATIC ${BITCOIN_CRYPTO_SOURCES}) +target_include_directories(BITCOIN_CRYPTO_A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src ${OPENSSL_INCLUDE_DIR}) set(ZEROCOIN_SOURCES ./src/libzerocoin/Accumulator.h @@ -436,6 +432,9 @@ else() list(APPEND ZEROCOIN_SOURCES ./src/libzerocoin/bignum_openssl.cpp) endif() add_library(ZEROCOIN_A STATIC ${ZEROCOIN_SOURCES}) +target_include_directories(ZEROCOIN_A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src + ${OPENSSL_INCLUDE_DIR} + ) set(COMMON_SOURCES ./src/zpiv/accumulators.cpp @@ -449,6 +448,7 @@ set(COMMON_SOURCES ./src/chainparams.cpp ./src/coins.cpp ./src/compressor.cpp + ./src/consensus/merkle.cpp ./src/primitives/block.cpp ./src/zpiv/deterministicmint.cpp ./src/primitives/transaction.cpp @@ -472,6 +472,13 @@ set(COMMON_SOURCES ./src/sporkdb.cpp ) add_library(COMMON_A STATIC ${BitcoinHeaders} ${COMMON_SOURCES}) +target_include_directories(COMMON_A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/secp256k1/include + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/include + ${CMAKE_CURRENT_SOURCE_DIR}/src/univalue/include + ${OPENSSL_INCLUDE_DIR} + ${BerkeleyDB_INCLUDE_DIRS} + ) set(UTIL_SOURCES ./src/allocators.cpp @@ -491,23 +498,46 @@ set(UTIL_SOURCES ./src/support/cleanse.cpp ) add_library(UTIL_A STATIC ${BitcoinHeaders} ${UTIL_SOURCES}) +target_include_directories(UTIL_A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/univalue/include + ${OPENSSL_INCLUDE_DIR} + ) -set(CLI_SOURCES ./src/rpc/client.cpp) -add_library(CLI_A STATIC ${BitcoinHeaders} ${CLI_SOURCES}) +set(CLI_A_SOURCES ./src/rpc/client.cpp) +add_library(CLI_A STATIC ${BitcoinHeaders} ${CLI_A_SOURCES}) +target_include_directories(CLI_A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/univalue/include + ) + +link_directories( + ${CMAKE_SOURCE_DIR}/src/univalue/.libs + ${CMAKE_SOURCE_DIR}/src/secp256k1/.libs + ) -add_executable(pivx-cli ${CMAKE_CURRENT_SOURCE_DIR}/src/pivx-cli.cpp) -add_dependencies(pivx-cli libunivalue) -target_link_libraries(pivx-cli +set(phore-cli_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/phore-cli.cpp) +if($ENV{target} MATCHES "Windows") + list(APPEND phore-cli_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/phore-cli-res.rc) +endif() +add_executable(phore-cli ${phore-cli_SOURCES}) +add_dependencies(phore-cli libunivalue) +target_link_libraries(phore-cli CLI_A univalue UTIL_A BITCOIN_CRYPTO_A ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${LIBEVENT_LIB} ) +if($ENV{target} MATCHES "Windows") + target_link_libraries(phore-cli ${WINDOWS_LDADD}) +endif() -add_executable(pivx-tx ${CMAKE_CURRENT_SOURCE_DIR}/src/pivx-tx.cpp) -add_dependencies(pivx-tx libunivalue libsecp256k1) -target_link_libraries(pivx-tx +set(phore-tx_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/phore-tx.cpp) +if($ENV{target} MATCHES "Windows") + list(APPEND phore-tx_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/phore-tx-res.rc) +endif() +add_executable(phore-tx ${phore-tx_SOURCES}) +add_dependencies(phore-tx libunivalue libsecp256k1) +target_link_libraries(phore-tx univalue COMMON_A ZEROCOIN_A @@ -516,13 +546,28 @@ target_link_libraries(pivx-tx secp256k1 ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${LIBEVENT_LIB} ) +if($ENV{target} MATCHES "Windows") + target_link_libraries(phore-tx ${WINDOWS_LDADD}) +endif() if(GMP_FOUND) - target_link_libraries(pivx-tx ${GMP_LIBRARY}) + target_link_libraries(phore-tx ${GMP_LIBRARY}) endif() -add_executable(pivxd ${CMAKE_CURRENT_SOURCE_DIR}/src/pivxd.cpp) -add_dependencies(pivxd libunivalue libsecp256k1 leveldb leveldb_sse42 memenv) -target_link_libraries(pivxd +link_directories(${CMAKE_BINARY_DIR}) + +set(phored_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/phored.cpp) +if($ENV{target} MATCHES "Windows") + list(APPEND phored_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/phored-res.rc) +endif() +add_executable(phored ${phored_SOURCES} ${BitcoinHeaders}) +add_dependencies(phored libunivalue libsecp256k1 leveldb leveldb_sse42 memenv) +target_include_directories(phored PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/include + ${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/helpers/memenv + ${LIBEVENT_INCLUDE_DIR}) +target_link_libraries(phored + pthread SERVER_A COMMON_A univalue @@ -530,15 +575,30 @@ target_link_libraries(pivxd UTIL_A WALLET_A BITCOIN_CRYPTO_A - leveldb leveldb_sse42 memenv secp256k1 - ${BerkeleyDB_LIBRARIES} ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} ${LIBEVENT_LIB} miniupnpc pthread + ${CMAKE_BINARY_DIR}/libleveldb.a + ${CMAKE_BINARY_DIR}/libleveldb_sse42.a + ${CMAKE_BINARY_DIR}/libmemenv.a + secp256k1 + ${BerkeleyDB_LIBRARIES} ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} ${LIBEVENT_LIB} pthread ) +if($ENV{target} MATCHES "Windows") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wstack-protector -fstack-protector-all -fPIE -pipe -O2 -pthread -Wl,--dynamicbase -Wl,--nxcompat -Wl,--high-entropy-va -pie --static") + target_link_libraries(phored ${WINDOWS_LDADD}) +endif() if(GMP_FOUND) - target_link_libraries(pivxd ${GMP_LIBRARY}) + target_link_libraries(phored ${GMP_LIBRARY}) + target_include_directories(phored PUBLIC ${GMP_INCLUDE_DIR}) endif() if(ZMQ_FOUND) - target_link_libraries(pivxd ZMQ_A ${ZMQ_LIB}) -endif () + target_link_libraries(phored ZMQ_A ${ZMQ_LIB}) + target_include_directories(phored PUBLIC ${ZMQ_INCLUDE_DIR}) +endif() +if(MINIUPNP_FOUND) + target_compile_definitions(phored PUBLIC "-DSTATICLIB -DMINIUPNP_STATICLIB") + target_link_libraries(phored ${MINIUPNP_LIBRARY}) + target_include_directories(phored PUBLIC ${MINIUPNP_INCLUDE_DIR}) +endif() +target_link_libraries(phored -lpthread) -add_subdirectory(src/qt) +add_subdirectory(src/qt) \ No newline at end of file diff --git a/build-aux/snap/local/desktop/pivx-qt.desktop b/build-aux/snap/local/desktop/pivx-qt.desktop new file mode 100644 index 0000000000000..48887dbd4fa7f --- /dev/null +++ b/build-aux/snap/local/desktop/pivx-qt.desktop @@ -0,0 +1,15 @@ +# http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html + +[Desktop Entry] +Encoding=UTF-8 +Name=PIVX Core +Comment=PIVX Core QT main network +GenericName=PIVX Digital Currency QT client main network +Exec=pivx-core.qt %u +Terminal=false +Type=Application +Icon=${SNAP}/share/pixmaps/pivx.png +MimeType=x-scheme-handler/pivx; +Categories=X-Office;X-Finance; +Keywords=internet;pivx;zpiv;piv;core;wallet;qt;main;snap;launchpad;crypto;digital;currency;coin;token;zerocoin; +Version=1.0 diff --git a/build-aux/snap/local/desktop/pivx-qt_testnet.desktop b/build-aux/snap/local/desktop/pivx-qt_testnet.desktop new file mode 100644 index 0000000000000..a2c5c0fc95b3f --- /dev/null +++ b/build-aux/snap/local/desktop/pivx-qt_testnet.desktop @@ -0,0 +1,15 @@ +# http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html + +[Desktop Entry] +Encoding=UTF-8 +Name=PIVX Core +Comment=PIVX Core QT test network +GenericName=PIVX Digital Currency QT client testnet network +Exec=pivx-core.qt-testnet %u +Terminal=false +Type=Application +Icon=${SNAP}/share/pixmaps/pivx_testnet.png +MimeType=x-scheme-handler/piv; +Categories=X-Office;X-Finance; +Keywords=internet;pivx;zpiv;tpiv;core;wallet;qt;testnet;snap;launchpad;crypto;digital;currency;coin;token;zerocoin; +Version=1.0 diff --git a/build-aux/snap/local/desktop/pivxd.desktop b/build-aux/snap/local/desktop/pivxd.desktop new file mode 100755 index 0000000000000..52a34224c9f02 --- /dev/null +++ b/build-aux/snap/local/desktop/pivxd.desktop @@ -0,0 +1,15 @@ +# http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html + +[Desktop Entry] +Encoding=UTF-8 +Name=PIVX Core Daemon SNAP +Comment=main +GenericName=PIVX Digital Currency Daemon Client main network +Exec=pivx-core.daemon %u +Terminal=true +Type=Application +Icon=${SNAP}/share/pixmaps/pivx.png +MimeType=x-scheme-handler/piv; +Categories=X-Office;X-Finance; +Keywords=internet;pivx;zpiv;core;wallet;daemon;main;snap;launchpad;crypto;digital;currency;coin;token;zerocoin; +Version=1.0 diff --git a/build-aux/snap/local/desktop/pivxd_testnet.desktop b/build-aux/snap/local/desktop/pivxd_testnet.desktop new file mode 100755 index 0000000000000..6cf6a55ab828f --- /dev/null +++ b/build-aux/snap/local/desktop/pivxd_testnet.desktop @@ -0,0 +1,15 @@ +# http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html + +[Desktop Entry] +Encoding=UTF-8 +Name=PIVX Core Daemon SNAP +Comment=testnet +GenericName=PIVX Digital Currency Daemon Client testnet network +Exec=pivx-core.daemon-testnet %u +Terminal=true +Type=Application +Icon=${SNAP}/share/pixmaps/pivx_testnet.png +MimeType=x-scheme-handler/piv; +Categories=X-Office;X-Finance; +Keywords=internet;pivx;zpiv;core;wallet;daemon;testnet;snap;launchpad;crypto;digital;currency;coin;token;zerocoin; +Version=1.0 diff --git a/build-aux/snap/local/patches/X002-fix-bdb-tmp-folder.patch b/build-aux/snap/local/patches/X002-fix-bdb-tmp-folder.patch new file mode 100644 index 0000000000000..b561f58569831 --- /dev/null +++ b/build-aux/snap/local/patches/X002-fix-bdb-tmp-folder.patch @@ -0,0 +1,36 @@ +From dcfe31b190ab8a28f2617914afdf15c6be2a74d2 Mon Sep 17 00:00:00 2001 +From: cevap +Date: Mon, 4 Feb 2019 07:41:47 +0100 +Subject: [PATCH] fix-bdb-tmp-folder + +--- + depends/packages/bdb.mk | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk +index 6c9876c..291111c 100644 +--- a/depends/packages/bdb.mk ++++ b/depends/packages/bdb.mk +@@ -13,6 +13,19 @@ $(package)_cxxflags=-std=c++11 + endef + + define $(package)_preprocess_cmds ++ sed -i.old 's/\/var\/tmp/$HOME\/snap\/pivx\/common\/var\/tmp/g' csharp/DatabaseEnvironment.cs && \ ++ sed -i.old 's/\/var\/tmp/$HOME\/snap\/pivx\/common\/var\/tmp/g' csharp/DatabaseEnvironmentConfig.cs && \ ++ sed -i.old 's/\/var\/tmp/$HOME\/snap\/pivx\/common\/var\/tmp/g' csharp/doc/libdb_dotnet48.XML && \ ++ sed -i.old 's/\/var\/tmp/$HOME\/snap\/pivx\/common\/var\/tmp/g' docs/api_reference/C/envset_tmp_dir.html && \ ++ sed -i.old 's/\/var\/tmp/$HOME\/snap\/pivx\/common\/var\/tmp/g' docs/api_reference/CXX/envset_tmp_dir.html && \ ++ sed -i.old 's/\/var\/tmp/$HOME\/snap\/pivx\/common\/var\/tmp/g' docs/csharp/html/F_BerkeleyDB_DatabaseEnvironmentConfig_TempDir.htm && \ ++ sed -i.old 's/\/var\/tmp/$HOME\/snap\/pivx\/common\/var\/tmp/g' docs/csharp/html/P_BerkeleyDB_DatabaseEnvironment_TempDir.htm && \ ++ sed -i.old 's/\/var\/tmp/$HOME\/snap\/pivx\/common\/var\/tmp/g' docs/java/com/sleepycat/db/EnvironmentConfig.html && \ ++ sed -i.old 's/\/var\/tmp/$HOME\/snap\/pivx\/common\/var\/tmp/g' docs/programmer_reference/test.html && \ ++ sed -i.old 's/\/var\/tmp/$HOME\/snap\/pivx\/common\/var\/tmp/g' java/src/com/sleepycat/db/EnvironmentConfig.java && \ ++ sed -i.old 's/\/var\/tmp/$HOME\/snap\/pivx\/common\/var\/tmp/g' os/os_tmpdir.c && \ ++ sed -i.old 's/\/var\/tmp/$HOME\/snap\/pivx\/common\/var\/tmp/g' php_db4/samples/simple_counter.php && \ ++ sed -i.old 's/\/var\/tmp/$HOME\/snap\/pivx\/common\/var\/tmp/g' php_db4/samples/transactional_counter.php && \ + sed -i.old 's/__atomic_compare_exchange/__atomic_compare_exchange_db/' dbinc/atomic.h && \ + sed -i.old 's/atomic_init/atomic_init_db/' dbinc/atomic.h mp/mp_region.c mp/mp_mvcc.c mp/mp_fget.c mutex/mut_method.c mutex/mut_tas.c && \ + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub dist +-- +2.17.1 + diff --git a/build-aux/snap/local/patches/X003-fix-use-snap-instead-of-dirty.patch b/build-aux/snap/local/patches/X003-fix-use-snap-instead-of-dirty.patch new file mode 100644 index 0000000000000..755604057a67c --- /dev/null +++ b/build-aux/snap/local/patches/X003-fix-use-snap-instead-of-dirty.patch @@ -0,0 +1,25 @@ +From ec230421e7107448ca9ac2421eb702924e12a879 Mon Sep 17 00:00:00 2001 +From: observerdev +Date: Thu, 11 Apr 2019 00:12:00 +0200 +Subject: [PATCH] fix-use-snap-instead--of-dirty + +--- + share/genbuild.sh | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/share/genbuild.sh b/share/genbuild.sh +index 519cc6e..de56b8d 100755 +--- a/share/genbuild.sh ++++ b/share/genbuild.sh +@@ -37,7 +37,7 @@ if [ "${BITCOIN_GENBUILD_NO_GIT}" != "1" -a -e "$(which git 2>/dev/null)" -a "$( + + # otherwise generate suffix from git, i.e. string like "59887e8-dirty" + SUFFIX=$(git rev-parse --short HEAD) +- git diff-index --quiet HEAD -- || SUFFIX="$SUFFIX-dirty" ++ git diff-index --quiet HEAD -- || SUFFIX="$SUFFIX-snap" + + # get a string like "2012-04-10 16:27:19 +0200" + LAST_COMMIT_DATE="$(git log -n 1 --format="%ci")" +-- +2.17.1 + diff --git a/build-aux/snap/snapcraft.yaml b/build-aux/snap/snapcraft.yaml new file mode 100644 index 0000000000000..a6bd2315abad1 --- /dev/null +++ b/build-aux/snap/snapcraft.yaml @@ -0,0 +1,290 @@ +# Copyright (c) 2018-2019 The Ion developers +# Copyright (c) 2019 The PIVX developers +name: pivx-core +base: core18 +version: 3.4.99 +summary: PIVX (Private – Instant – Verified – Transaction) +description: | + PIVX is an MIT licensed, + open source, blockchain-based cryptocurrency with + ultra fast transactions, low fees, high network decentralization, and + Zero Knowledge cryptography proofs for industry-leading + transaction anonymity. + + - [Download](https://pivx.org/wp-content/uploads/2018/10/PIVX-White.pdf) PIVX White Paper PDF +grade: devel +confinement: strict +apps: + daemon: + command: pivxd + plugs: [network, network-bind, home, removable-media] + desktop: pivxd.desktop + environment: + XDG_DATA_DIRS: $SNAP_USER_DATA:$SNAP/usr/share:$XDG_DATA_DIRS + HOME: $SNAP_USER_COMMON + daemon-testnet: + command: pivxd --testnet + plugs: [network, network-bind, home, removable-media] + desktop: pivxd_testnet.desktop + environment: + XDG_DATA_DIRS: $SNAP_USER_DATA:$SNAP/usr/share:$XDG_DATA_DIRS + HOME: $SNAP_USER_COMMON + qt: + command: pivx-qt + plugs: [network, network-bind, network-status, unity7, desktop, desktop-legacy, wayland, x11, mir, opengl, home, gsettings, removable-media, screen-inhibit-control, pulseaudio, media-hub] + desktop: pivx-qt.desktop + environment: + QT_XKB_CONFIG_ROOT: $SNAP_USER_DATA:$SNAP/usr/share:$QT_XKB_CONFIG_ROOT + XDG_DATA_DIRS: $SNAP_USER_DATA:$SNAP/usr/share:$XDG_DATA_DIRS + HOME: $SNAP_USER_COMMON + qt-testnet: + command: pivx-qt --testnet + plugs: [network, network-bind, network-status, unity7, desktop, desktop-legacy, wayland, x11, mir, opengl, home, gsettings, removable-media, screen-inhibit-control, pulseaudio, media-hub] + desktop: pivx-qt_testnet.desktop + environment: + QT_XKB_CONFIG_ROOT: $SNAP_USER_DATA:$SNAP/usr/share:$QT_XKB_CONFIG_ROOT + XDG_DATA_DIRS: $SNAP_USER_DATA:$SNAP/usr/share:$XDG_DATA_DIRS + HOME: $SNAP_USER_COMMON + cli: + command: pivx-cli + plugs: [network, network-bind, home, removable-media] + environment: + XDG_DATA_DIRS: $SNAP_USER_DATA:$SNAP/usr/share:$XDG_DATA_DIRS + HOME: $SNAP_USER_COMMON + cli-testnet: + command: pivx-cli --testnet + plugs: [network, network-bind, home, removable-media] + environment: + XDG_DATA_DIRS: $SNAP_USER_DATA:$SNAP/usr/share:$XDG_DATA_DIRS + HOME: $SNAP_USER_COMMON + tx: + command: pivx-tx + plugs: [home] + test: + command: test_pivx + plugs: [home] + testqt: + command: test_pivx-qt + plugs: [home] +parts: + pivx-core: + source: https://github.com/PIVX-Project/PIVX + source-type: git + source-tag: master + plugin: nil + override-build: | + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + echo "CUSTOM SETTINGS" + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + # use override prefix if binaries differ from project name, for ion it does, + # project is called ioncore because ion exists already as package. For anybody + # offering non official release and using own SNAP, this variable should be used + # default value is "false", to enable it, use your projects binary prefix + OVERRIDEBINPREFIX="pivx" + OVERRIDEDATADIR="false" + OVERRIDECONF="${OVERRIDEDATADIR}" + COPYCONF=0 # copy example config into users data folder, 1 = enabled + JOBS=4 # 0 means off and make will run without -j + PATCH=1 # 1 = enabled + SPLASHPNGS=0 # patch pngs to differ visaully from those installed from self compilation or other source like deb + FIXPPCBUILD=1 # if ppc builds fail due to failed qt, apps part will return error and build will fail, 1 = enabled + EXTRALOG=0 # prints env and all installed files at the end of current script + RUNTESTS=1 # run make check after post install part + echo "-----------------------------------------------" + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + echo "SET OVERRIDE VARIABLES IF SET" # checks OVERRIDEBINPREFIX, OVERRIDEDATADIR and OVERRIDECONF + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + if [ $OVERRIDEBINPREFIX = "false" ]; then + BINPREF="${SNAPCRAFT_PROJECT_NAME}" + else + BINPREF="${OVERRIDEBINPREFIX}" + fi + if [ $OVERRIDEDATADIR = "false" ]; then + DATADIR="${SNAPCRAFT_PROJECT_NAME}" + else + DATADIR="${OVERRIDEDATADIR}" + fi + if [ $OVERRIDECONF = "false" ]; then + CONF="${SNAPCRAFT_PROJECT_NAME}" + else + CONF="${OVERRIDECONF}" + fi + echo "BINPREF VARIABLE - ${BINPREF}" + echo "DATADIR VARIABLE - ${DATADIR}" + echo "CONF VARIABLE - ${CONF}" + echo "-----------------------------------------------" + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + echo "OVERRIDE ARCHITECTURE" # applies currently only on i386, for i386 architecture, we will use i686-linux-gnu to compile dependencies + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + if [ $SNAPCRAFT_ARCH_TRIPLET = "i386-linux-gnu" ]; then + HOST="i686-linux-gnu" + else + HOST="${SNAPCRAFT_ARCH_TRIPLET}" + fi + echo "-----------------------------------------------" + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + echo "APPLY PATCHES ${BINPREF}-${SNAPCRAFT_PROJECT_VERSION}" # patches which have to be applied for compilation, replace splash screen + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + cd ${SNAPCRAFT_PART_BUILD} + if [ $PATCH = 1 ]; then + echo "apply patches:" + git apply $SNAPCRAFT_PART_BUILD/build-aux/snap/local/patches/X002-fix-bdb-tmp-folder.patch + git apply $SNAPCRAFT_PART_BUILD/build-aux/snap/local/patches/X003-fix-use-snap-instead-of-dirty.patch + sed -i 's/tar --strip-components/tar --no-same-owner --strip-components/' ${SNAPCRAFT_PART_BUILD}/depends/funcs.mk + else + echo "APPLY .patch files during compilation is disabled, skipping" + fi + if [ $SPLASHPNGS = 1 ]; then + echo "replace splash screen with snapcrafts" + mv -f $SNAPCRAFT_PART_BUILD/contrib/snap/images/splash.png $SNAPCRAFT_PART_BUILD/src/qt/res/images/splash.png + mv -f $SNAPCRAFT_PART_BUILD/contrib/snap/images/splash_regtest.png $SNAPCRAFT_PART_BUILD/src/qt/res/images/splash_regtest.png + mv -f $SNAPCRAFT_PART_BUILD/contrib/snap/images/splash_testnet.png $SNAPCRAFT_PART_BUILD/src/qt/res/images/splash_testnet.png + mv -f $SNAPCRAFT_PART_BUILD/contrib/snap/icons/favicon.ico $SNAPCRAFT_PART_BUILD/share/pixmaps/favicon.ico + mv -f $SNAPCRAFT_PART_BUILD/contrib/snap/icons/favicon_testnet.ico $SNAPCRAFT_PART_BUILD/share/pixmaps/favicon_testnet.ico + mv -f $SNAPCRAFT_PART_BUILD/contrib/snap/icons/favicon_regtest.ico $SNAPCRAFT_PART_BUILD/share/pixmaps/favicon_regtest.ico + mv -f $SNAPCRAFT_PART_BUILD/build-aux/snap/local/desktop/${BINPREF}.png $SNAPCRAFT_PART_BUILD/src/qt/res/icons/${BINPREF}.png + mv -f $SNAPCRAFT_PART_BUILD/build-aux/snap/local/desktop/${BINPREF}_testnet.png $SNAPCRAFT_PART_BUILD/src/qt/res/icons/${BINPREF}_testnet.png + mv -f $SNAPCRAFT_PART_BUILD/build-aux/snap/local/desktop/${BINPREF}_regtest.png $SNAPCRAFT_PART_BUILD/src/qt/res/icons/${BINPREF}_regtest.png + else + echo "patch icons and images is disabled, skipping" + fi + echo "-----------------------------------------------" + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + echo "BUILD DEPENDENCIES" + echo "PRECOMPILE ${SNAPCRAFT_ARCH_TRIPLET} DEPENDENCIES FOR ${BINPREF}-${SNAPCRAFT_PROJECT_VERSION}" # cd to depends folder, download and precompile + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + cd ${SNAPCRAFT_PART_BUILD}/depends + make download-linux + if [ $JOBS = 0 ]; then + make + else + make -j${JOBS} HOST=${HOST} + fi + echo "-----------------------------------------------" + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + echo "CONFIGURATION OF ${BINPREF}-${SNAPCRAFT_PROJECT_VERSION}" # run configure with prefix to include depends + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + cd ${SNAPCRAFT_PART_BUILD} + echo "Configure and build ${BINPREF}" + ./autogen.sh + ./configure --prefix=`pwd`/depends/${HOST} + echo "-----------------------------------------------" + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + echo "COMPILATION OF ${BINPREF}-${SNAPCRAFT_PROJECT_VERSION}" # run make to compile using -j + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + if [ $JOBS = 0 ]; then + make + else + make -j${JOBS} HOST=${HOST} + fi + echo "-----------------------------------------------" + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + echo "INSTALLATION OF ${BINPREF}-${SNAPCRAFT_PROJECT_VERSION}" # run make install with a prefix where to install + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + make install prefix=${SNAPCRAFT_PART_INSTALL} + install -m 0644 -D -T $SNAPCRAFT_PART_BUILD/src/qt/res/icons/bitcoin.png $SNAPCRAFT_PART_INSTALL/share/pixmaps/pivx.png + install -m 0644 -D -T $SNAPCRAFT_PART_BUILD/src/qt/res/icons/bitcoin_testnet.png $SNAPCRAFT_PART_INSTALL/share/pixmaps/pivx_testnet.png + echo "-----------------------------------------------" + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + echo "POST INSTALL SCRIPTS OF ${BINPREF}-${SNAPCRAFT_PROJECT_VERSION}" # run make install with a prefix where to install + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + if [ $FIXPPCBUILD = 1 ]; then + if [ $SNAPCRAFT_ARCH_TRIPLET = "powerpc64le-linux-gnu" ]; then + if [ -e $SNAPCRAFT_PART_BUILD/src/qt/${BINPREF}-qt ]; then + echo "PPC64EL fix - ${BINPREF}-qt exists, fix unrequired" + else + echo "echo ${BINPREF}-qt is not installed" > ${SNAPCRAFT_PART_INSTALL}/usr/bin/${BINPREF}-qt + chmod +x ${SNAPCRAFT_PART_INSTALL}/usr/bin/${BINPREF}-qt + echo "PPC64EL fix - ${BINPREF}-qt does not exist, fix required, dummy as ${BINPREF}-qt" + fi + if [ -e $SNAPCRAFT_PART_BUILD/src/qt/test/test_${BINPREF}-qt ]; then + echo "PPC64EL fix - test_${BINPREF}-qt exists, fix unrequired" + else + echo "echo test_${BINPREF}-qt is not installed" > ${SNAPCRAFT_PART_INSTALL}/usr/bin/test_${BINPREF}-qt + chmod +x ${SNAPCRAFT_PART_INSTALL}/usr/bin/test_${BINPREF}-qt + echo "PPC64EL fix - test_${BINPREF}-qt does not exist, fix required, dummy as test_${BINPREF}-qt" + fi + if [ -e $SNAPCRAFT_PART_BUILD/src/test/test_${BINPREF} ]; then + echo "PPC64EL fix - test_${BINPREF} exists, fix unrequired" + else + echo "echo test_${BINPREF} is not installed" > ${SNAPCRAFT_PART_INSTALL}/usr/bin/test_${BINPREF} + chmod +x ${SNAPCRAFT_PART_INSTALL}/usr/bin/test_${BINPREF} + echo "PPC64EL fix - test_${BINPREF} does not exist, fix required, dummy as test_${BINPREF}" + fi + fi + else + echo "PPC64EL fix disabled, skipping" + fi + echo "-----------------------------------------------" + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + echo "CONFIG FILE" # create .pivx folder and copy example config - !!!warning!!!: do not copy as pivx.conf + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + mkdir -p ${SNAP_USER_COMMON}/.${DATADIR} + if [ $COPYCONF = 1 ]; then + cp ${SNAPCRAFT_PART_BUILD}/contrib/debian/manpages/${CONF}.conf.5 ${SNAP_USER_COMMON}/.${DATADIR}/${CONF}-example.conf + else + echo "COPY CONFIG FILE disabled, skipping" + fi + echo "-----------------------------------------------" + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + echo "RUN TESTS" # if tests fail to pass, build and release will fail + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + cd ${SNAPCRAFT_PART_BUILD} + if [ ! $SNAPCRAFT_ARCH_TRIPLET = "s390x-linux-gnu" ]; then + if [ $RUNTESTS = 1 ]; then + make check + else + echo "RUN TESTS disabled, skipping" + fi + fi + echo "-----------------------------------------------" + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + echo "INSTALATION OF ${BINPREF}-${SNAPCRAFT_PROJECT_VERSION}-snap has finished" # List installed files and vars when installation is finished + echo "+++++++++++++++++++++++++++++++++++++++++++++++" + if [ $EXTRALOG = 1 ]; then + find ${SNAPCRAFT_PART_INSTALL} -type f + env + else + echo "EXTRA LOG disabled, skipping" + fi + echo "-----------------------------------------------+" + build-packages: + - curl + - wget + - gcc + - gcc-8 + - g++ + - g++-8 + - make + - autoconf + - automake + - cmake + - pkg-config + - libtool + - bsdmainutils + - binutils + - python3 + - help2man + - doxygen + stage-packages: + - libxkbcommon0 + - ttf-ubuntu-font-family + - dmz-cursor-theme + - light-themes + - shared-mime-info + - libqt5gui5 + - libgdk-pixbuf2.0-0 + - libqt5svg5 # for loading icon themes which are svg + - locales-all + - qtwayland5 + - ca-certificates + after: + - patches + patches: + source: build-aux/snap/local/patches + plugin: dump + prime: + - -* + desktop: + source: build-aux/snap/local/desktop + plugin: dump \ No newline at end of file diff --git a/configure.ac b/configure.ac index 3bb293f22b24b..e6b346ee1ecd3 100644 --- a/configure.ac +++ b/configure.ac @@ -4,9 +4,10 @@ define(_CLIENT_VERSION_MAJOR, 1) define(_CLIENT_VERSION_MINOR, 8) define(_CLIENT_VERSION_REVISION, 0) define(_CLIENT_VERSION_BUILD, 0) +define(_CLIENT_VERSION_RC, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2019) -AC_INIT([Phore Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[www.phore.io],[phore]) +AC_INIT([Phore Core],m4_join([.], _CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MINOR, _CLIENT_VERSION_REVISION, m4_if(_CLIENT_VERSION_BUILD, [0], [], _CLIENT_VERSION_BUILD))m4_if(_CLIENT_VERSION_RC, [0], [], [rc]_CLIENT_VERSION_RC),[https://github.com/phoreproject/Phore/issues],[phore],[https://phore.io/]) AC_CONFIG_SRCDIR([src/main.cpp]) AC_CONFIG_HEADERS([src/config/phore-config.h]) AC_CONFIG_AUX_DIR([build-aux]) @@ -746,6 +747,22 @@ AC_CHECK_DECLS([bswap_16, bswap_32, bswap_64],,, AC_CHECK_DECLS([__builtin_clz, __builtin_clzl, __builtin_clzll]) +dnl Check for MSG_NOSIGNAL +AC_MSG_CHECKING(for MSG_NOSIGNAL) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[ int f = MSG_NOSIGNAL; ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_MSG_NOSIGNAL, 1,[Define this symbol if you have MSG_NOSIGNAL]) ], + [ AC_MSG_RESULT(no)] +) + +dnl Check for MSG_DONTWAIT +AC_MSG_CHECKING(for MSG_DONTWAIT) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[ int f = MSG_DONTWAIT; ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_MSG_DONTWAIT, 1,[Define this symbol if you have MSG_DONTWAIT]) ], + [ AC_MSG_RESULT(no)] +) + dnl Check for malloc_info (for memory statistics information in getmemoryinfo) AC_MSG_CHECKING(for getmemoryinfo) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], @@ -801,6 +818,40 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([ ) LDFLAGS="$TEMP_LDFLAGS" +# Check for different ways of gathering OS randomness +AC_MSG_CHECKING(for Linux getrandom syscall) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + #include + #include ]], + [[ syscall(SYS_getrandom, nullptr, 32, 0); ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYS_GETRANDOM, 1,[Define this symbol if the Linux getrandom system call is available]) ], + [ AC_MSG_RESULT(no)] +) + +AC_MSG_CHECKING(for getentropy) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[ getentropy(nullptr, 32) ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_GETENTROPY, 1,[Define this symbol if the BSD getentropy system call is available]) ], + [ AC_MSG_RESULT(no)] +) + +AC_MSG_CHECKING(for getentropy via random.h) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + #include ]], + [[ getentropy(nullptr, 32) ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_GETENTROPY_RAND, 1,[Define this symbol if the BSD getentropy system call is available with sys/random.h]) ], + [ AC_MSG_RESULT(no)] +) + +AC_MSG_CHECKING(for sysctl KERN_ARND) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + #include ]], + [[ static const int name[2] = {CTL_KERN, KERN_ARND}; + sysctl(name, 2, nullptr, nullptr, nullptr, 0); ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSCTL_ARND, 1,[Define this symbol if the BSD sysctl(KERN_ARND) is available]) ], + [ AC_MSG_RESULT(no)] +) + # Check for reduced exports if test x$use_reduce_exports = xyes; then AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"], diff --git a/contrib/cmake/FindLibEvent.cmake b/contrib/cmake/FindLibEvent.cmake index d0ffdf0ad4720..7d02ded579865 100644 --- a/contrib/cmake/FindLibEvent.cmake +++ b/contrib/cmake/FindLibEvent.cmake @@ -4,10 +4,12 @@ # LIBEVENT_LIB, LibEvent libraries # LibEvent_FOUND, If false, do not try to use libevent -if(($ENV{triple}) AND (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/depends/$ENV{triple}")) - set(LIBEVENT_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/depends/$ENV{triple}/include") - set(LIBEVENT_LIB "${CMAKE_CURRENT_SOURCE_DIR}/depends/$ENV{triple}/lib/libevent.a") - set(LIBEVENT_PTHREAD_LIB "${CMAKE_CURRENT_SOURCE_DIR}/depends/$ENV{triple}/lib/libevent.a") +if($ENV{target} MATCHES "Windows") + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/depends/$ENV{triple}") + set(LIBEVENT_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/depends/$ENV{triple}/include") + set(LIBEVENT_LIB "${CMAKE_CURRENT_SOURCE_DIR}/depends/$ENV{triple}/lib/libevent.a") + set(LIBEVENT_PTHREAD_LIB "${CMAKE_CURRENT_SOURCE_DIR}/depends/$ENV{triple}/lib/libevent.a") + endif() else() set(LibEvent_EXTRA_PREFIXES /usr/local /opt/local "$ENV{HOME}") foreach(prefix ${LibEvent_EXTRA_PREFIXES}) diff --git a/contrib/cmake/FindMiniupnp.cmake b/contrib/cmake/FindMiniupnp.cmake new file mode 100644 index 0000000000000..31e2e39e58aaa --- /dev/null +++ b/contrib/cmake/FindMiniupnp.cmake @@ -0,0 +1,33 @@ +# - Find Miniupnp +# This module defines +# MINIUPNP_INCLUDE_DIR, where to find Miniupnp headers +# MINIUPNP_LIBRARY, Miniupnp libraries +# MINIUPNP_FOUND, If false, do not try to use Miniupnp + +set(MINIUPNP_PREFIX "" CACHE PATH "path ") + +find_path(MINIUPNP_INCLUDE_DIR miniupnpc/miniupnpc.h + PATHS ${MINIUPNP_PREFIX}/include /usr/include /usr/local/include ) + +find_library(MINIUPNP_LIBRARY NAMES miniupnpc libminiupnpc + PATHS ${MINIUPNP_PREFIX}/lib /usr/lib /usr/local/lib) + +if(MINIUPNP_INCLUDE_DIR AND MINIUPNP_LIBRARY) + get_filename_component(MINIUPNP_LIBRARY_DIR ${MINIUPNP_LIBRARY} PATH) + set(MINIUPNP_FOUND TRUE) +endif() + +if(MINIUPNP_FOUND) + if(NOT Miniupnp_FIND_QUIETLY) + MESSAGE(STATUS "Found Miniupnp: ${MINIUPNP_LIBRARY}") + endif() +else() + if(MINIUPNP_FIND_REQUIRED) + message(FATAL_ERROR "Could not find Miniupnp") + endif() +endif() + +mark_as_advanced( + MINIUPNP_LIBRARY + MINIUPNP_INCLUDE_DIR +) \ No newline at end of file diff --git a/contrib/devtools/lint-whitespace.sh b/contrib/devtools/lint-whitespace.sh index 128aa10da5efa..adf43c083cb93 100755 --- a/contrib/devtools/lint-whitespace.sh +++ b/contrib/devtools/lint-whitespace.sh @@ -31,14 +31,14 @@ if [ -z "${TRAVIS_COMMIT_RANGE}" ]; then fi showdiff() { - if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)depends/patches/" ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/"; then + if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)depends/patches/" ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)build-aux/snap/local/patches/"; then echo "Failed to get a diff" exit 1 fi } showcodediff() { - if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- *.cpp *.h *.md *.py *.sh ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/"; then + if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- *.cpp *.h *.md *.py *.sh ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)build-aux/snap/local/patches/"; then echo "Failed to get a diff" exit 1 fi diff --git a/doc/release-process.md b/doc/release-process.md index 4543b39a5803d..aea7e6fd24d80 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -1,13 +1,12 @@ Release Process ==================== -Before every release candidate: +### Before every release candidate * Update translations (ping Fuzzbawls on Slack) see [translation_process.md](https://github.com/phoreproject/Phore/blob/master/doc/translation_process.md#synchronising-translations). -Before every minor and major release: - -* Update version in `configure.ac` (don't forget to set `CLIENT_VERSION_IS_RELEASE` to `true`) +* Update release candidate version in `configure.ac` (`CLIENT_VERSION_RC`) +* Update version in `configure.ac` (don't forget to set `CLIENT_VERSION_IS_RELEASE` to `true`) (don't forget to set `CLIENT_VERSION_RC` to `0`) * Write release notes (see below) Before every major release: diff --git a/share/qt/Info.plist.in b/share/qt/Info.plist.in index 95d1361810a0f..2beda514e8549 100644 --- a/share/qt/Info.plist.in +++ b/share/qt/Info.plist.in @@ -3,7 +3,7 @@ LSMinimumSystemVersion - 10.8.0 + 10.10.0 LSArchitecturePriority @@ -17,8 +17,7 @@ APPL CFBundleGetInfoString - @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@, Copyright © 2009-@COPYRIGHT_YEAR@ The Bitcoin Core developers, 2014-@COPYRIGHT_YEAR@ The Dash Core developers, 2015-@COPYRIGHT_YEAR@ The PIVX Core developers, 2017-@COPYRIGHT_YEAR@ The Phore Developers - + @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@.@CLIENT_VERSION_BUILD@, Copyright © 2009-@COPYRIGHT_YEAR@ The Bitcoin Core developers, 2014-@COPYRIGHT_YEAR@ The Dash Core developers, 2015-@COPYRIGHT_YEAR@ The PIVX Core developers, 2017-@COPYRIGHT_YEAR@ The Phore Developers CFBundleShortVersionString @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@ @@ -30,7 +29,6 @@ CFBundleExecutable Phore-Qt - CFBundleName Phore-Qt @@ -100,6 +98,9 @@ LSAppNapIsDisabled True + NSRequiresAquaSystemAppearance + True + LSApplicationCategoryType public.app-category.finance diff --git a/share/setup.nsi.in b/share/setup.nsi.in index e7c627ddbb234..53c7d8d2ed6cd 100644 --- a/share/setup.nsi.in +++ b/share/setup.nsi.in @@ -5,7 +5,6 @@ SetCompressor /SOLID lzma # General Symbol Definitions !define REGKEY "SOFTWARE\$(^Name)" -!define VERSION @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@ !define COMPANY "@PACKAGE_NAME@ project" !define URL https://www.phore.org @@ -49,7 +48,7 @@ Var StartMenuGroup !insertmacro MUI_LANGUAGE English # Installer attributes -OutFile @abs_top_srcdir@/@PACKAGE_TARNAME@-${VERSION}-win@WINDOWS_BITS@-setup.exe +OutFile @abs_top_srcdir@/@PACKAGE_TARNAME@-@PACKAGE_VERSION@-win@WINDOWS_BITS@-setup.exe !if "@WINDOWS_BITS@" == "64" InstallDir $PROGRAMFILES64\Phore !else @@ -59,12 +58,12 @@ CRCCheck on XPStyle on BrandingText " " ShowInstDetails show -VIProductVersion ${VERSION}.@CLIENT_VERSION_BUILD@ +VIProductVersion @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@.@CLIENT_VERSION_BUILD@ VIAddVersionKey ProductName "@PACKAGE_NAME@" -VIAddVersionKey ProductVersion "${VERSION}" +VIAddVersionKey ProductVersion "@PACKAGE_VERSION@" VIAddVersionKey CompanyName "${COMPANY}" VIAddVersionKey CompanyWebsite "${URL}" -VIAddVersionKey FileVersion "${VERSION}" +VIAddVersionKey FileVersion "@PACKAGE_VERSION@" VIAddVersionKey FileDescription "" VIAddVersionKey LegalCopyright "" InstallDirRegKey HKCU "${REGKEY}" Path @@ -97,7 +96,7 @@ Section -post SEC0001 CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall $(^Name).lnk" $INSTDIR\uninstall.exe !insertmacro MUI_STARTMENU_WRITE_END WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)" - WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "${VERSION}" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "@PACKAGE_VERSION@" WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}" WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}" WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\uninstall.exe diff --git a/src/Makefile.am b/src/Makefile.am index 66f9cbfec1c17..ce91e7b0d87ee 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -106,6 +106,7 @@ BITCOIN_CORE_H = \ consensus/validation.h \ compressor.h \ cuckoocache.h \ + consensus/merkle.h \ primitives/block.h \ primitives/deterministicmint.h \ primitives/transaction.h \ @@ -160,6 +161,7 @@ BITCOIN_CORE_H = \ spork.h \ sporkdb.h \ stakeinput.h \ + sporkid.h \ streams.h \ support/cleanse.h \ sync.h \ @@ -291,6 +293,8 @@ crypto_libbitcoin_crypto_a_SOURCES = \ crypto/sha256.cpp \ crypto/sha256_sse4.cpp \ crypto/sha512.cpp \ + crypto/chacha20.h \ + crypto/chacha20.cpp \ crypto/hmac_sha256.cpp \ crypto/pkcs5_pbkdf2.h \ crypto/pkcs5_pbkdf2.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 677f010cfb0c2..6eed4b48e6203 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -52,10 +52,12 @@ BITCOIN_TESTS =\ test/key_tests.cpp \ test/main_tests.cpp \ test/mempool_tests.cpp \ + test/merkle_tests.cpp \ test/mruset_tests.cpp \ test/multisig_tests.cpp \ test/netbase_tests.cpp \ test/pmt_tests.cpp \ + test/random_tests.cpp \ test/reverselock_tests.cpp \ test/rpc_tests.cpp \ test/sanity_tests.cpp \ diff --git a/src/activemasternode.cpp b/src/activemasternode.cpp index 5611b67f063d5..505d4a3602ccb 100644 --- a/src/activemasternode.cpp +++ b/src/activemasternode.cpp @@ -198,7 +198,7 @@ bool CActiveMasternode::SendMasternodePing(std::string& errorMessage) * AFTER MIGRATION TO V12 IS DONE */ - if (IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)) return true; + if (sporkManager.IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)) return true; // for migration purposes ping our node on old masternodes network too std::string retErrorMessage; std::vector vchMasterNodeSignature; @@ -300,7 +300,7 @@ bool CActiveMasternode::CreateBroadcast(CTxIn vin, CService service, CKey keyCol * AFTER MIGRATION TO V12 IS DONE */ - if (IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)) return true; + if (sporkManager.IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)) return true; // for migration purposes inject our node in old masternodes' list too std::string retErrorMessage; std::vector vchMasterNodeSignature; diff --git a/src/addrman.cpp b/src/addrman.cpp index 720652de64c0f..f4c8b63d89dab 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -346,8 +346,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly) int nKBucket = GetRandInt(ADDRMAN_TRIED_BUCKET_COUNT); int nKBucketPos = GetRandInt(ADDRMAN_BUCKET_SIZE); while (vvTried[nKBucket][nKBucketPos] == -1) { - nKBucket = (nKBucket + insecure_rand()) % ADDRMAN_TRIED_BUCKET_COUNT; - nKBucketPos = (nKBucketPos + insecure_rand()) % ADDRMAN_BUCKET_SIZE; + nKBucket = (nKBucket + insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) % ADDRMAN_TRIED_BUCKET_COUNT; + nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE; } int nId = vvTried[nKBucket][nKBucketPos]; assert(mapInfo.count(nId) == 1); @@ -363,8 +363,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly) int nUBucket = GetRandInt(ADDRMAN_NEW_BUCKET_COUNT); int nUBucketPos = GetRandInt(ADDRMAN_BUCKET_SIZE); while (vvNew[nUBucket][nUBucketPos] == -1) { - nUBucket = (nUBucket + insecure_rand()) % ADDRMAN_NEW_BUCKET_COUNT; - nUBucketPos = (nUBucketPos + insecure_rand()) % ADDRMAN_BUCKET_SIZE; + nUBucket = (nUBucket + insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) % ADDRMAN_NEW_BUCKET_COUNT; + nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE; } int nId = vvNew[nUBucket][nUBucketPos]; assert(mapInfo.count(nId) == 1); diff --git a/src/addrman.h b/src/addrman.h index 947ab6dd900af..442cb1d161697 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -130,13 +130,13 @@ class CAddrInfo : public CAddress */ //! total number of buckets for tried addresses -#define ADDRMAN_TRIED_BUCKET_COUNT 256 +#define ADDRMAN_TRIED_BUCKET_COUNT_LOG2 8 //! total number of buckets for new addresses -#define ADDRMAN_NEW_BUCKET_COUNT 1024 +#define ADDRMAN_NEW_BUCKET_COUNT_LOG2 10 //! maximum allowed number of entries in buckets for new and tried addresses -#define ADDRMAN_BUCKET_SIZE 64 +#define ADDRMAN_BUCKET_SIZE_LOG2 6 //! over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread #define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8 @@ -165,6 +165,11 @@ class CAddrInfo : public CAddress //! the maximum number of nodes to return in a getaddr call #define ADDRMAN_GETADDR_MAX 2500 +//! Convenience +#define ADDRMAN_TRIED_BUCKET_COUNT (1 << ADDRMAN_TRIED_BUCKET_COUNT_LOG2) +#define ADDRMAN_NEW_BUCKET_COUNT (1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2) +#define ADDRMAN_BUCKET_SIZE (1 << ADDRMAN_BUCKET_SIZE_LOG2) + /** * Stochastical (IP) address manager */ @@ -202,6 +207,9 @@ class CAddrMan //! secret key to randomize bucket select with uint256 nKey; + //! Source of random numbers for randomization in inner loops + FastRandomContext insecure_rand; + //! Find an entry. CAddrInfo* Find(const CNetAddr& addr, int* pnId = NULL); diff --git a/src/bip38.cpp b/src/bip38.cpp index 07c856b803448..523641f27cf3f 100644 --- a/src/bip38.cpp +++ b/src/bip38.cpp @@ -11,7 +11,6 @@ #include "random.h" #include -#include #include #include diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 6355b3d1bf482..86e7e473f0655 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -8,6 +8,7 @@ #include "libzerocoin/Params.h" #include "chainparams.h" +#include "consensus/merkle.h" #include "random.h" #include "util.h" #include "utilstrencodings.h" @@ -197,7 +198,7 @@ class CMainParams : public CChainParams txNew.vout[0].SetEmpty(); genesis.vtx.push_back(txNew); genesis.hashPrevBlock = 0; - genesis.hashMerkleRoot = genesis.BuildMerkleTree(); + genesis.hashMerkleRoot = BlockMerkleRoot(genesis); genesis.nVersion = 1; genesis.nTime = 1505224800; genesis.nBits = 0x207fffff;; diff --git a/src/compressor.h b/src/compressor.h index 97b5ea0ee1f90..0f25d71c65596 100644 --- a/src/compressor.h +++ b/src/compressor.h @@ -91,8 +91,14 @@ class CScriptCompressor return; } nSize -= nSpecialScripts; - script.resize(nSize); - s >> REF(CFlatData(script)); + if (nSize > MAX_SCRIPT_SIZE) { + // Overly long script, replace with a short invalid one + script << OP_RETURN; + s.ignore(nSize); + } else { + script.resize(nSize); + s >> REF(CFlatData(script)); + } } }; diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp index 35f7d2e05a937..c2b0cd787a2f5 100644 --- a/src/consensus/merkle.cpp +++ b/src/consensus/merkle.cpp @@ -1,7 +1,6 @@ // Copyright (c) 2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. - #include "merkle.h" #include "hash.h" #include "utilstrencodings.h" @@ -10,13 +9,11 @@ and/or designing a new system that will use merkle trees, keep in mind that the following merkle tree algorithm has a serious flaw related to duplicate txids, resulting in a vulnerability (CVE-2012-2459). - The reason is that if the number of hashes in the list at a given time is odd, the last one is duplicated before computing the next level (which is unusual in Merkle trees). This results in certain sequences of transactions leading to the same merkle root. For example, these two trees: - A A / \ / \ B C B C @@ -24,11 +21,9 @@ D E F D E F F / \ / \ / \ / \ / \ / \ / \ 1 2 3 4 5 6 1 2 3 4 5 6 5 6 - for transaction lists [1,2,3,4,5,6] and [1,2,3,4,5,6,5,6] (where 5 and 6 are repeated) result in the same root hash A (because the hash of both of (F) and (F,F) is C). - The vulnerability results from being able to send a block with such a transaction list, with the same merkle root, and the same block hash as the original without duplication, resulting in failed validation. If the @@ -184,4 +179,4 @@ std::vector BlockMerkleBranch(const CBlock& block, uint32_t position) leaves[s] = block.vtx[s].GetHash(); } return ComputeMerkleBranch(leaves, position); -} +} \ No newline at end of file diff --git a/src/core_write.cpp b/src/core_write.cpp index 0669457a42632..2f4c617cf6ba2 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -18,7 +18,6 @@ - std::string FormatScript(const CScript& script) { std::string ret; diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp new file mode 100644 index 0000000000000..70c472b74b9d8 --- /dev/null +++ b/src/crypto/chacha20.cpp @@ -0,0 +1,128 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Based on the public domain implementation 'merged' by D. J. Bernstein +// See https://cr.yp.to/chacha.html. + +#include "crypto/common.h" +#include "crypto/chacha20.h" + +#include + +constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | (v >> (32 - c)); } + +#define QUARTERROUND(a,b,c,d) \ + a += b; d = rotl32(d ^ a, 16); \ + c += d; b = rotl32(b ^ c, 12); \ + a += b; d = rotl32(d ^ a, 8); \ + c += d; b = rotl32(b ^ c, 7); + +static const unsigned char sigma[] = "expand 32-byte k"; +static const unsigned char tau[] = "expand 16-byte k"; + +void ChaCha20::SetKey(const unsigned char* k, size_t keylen) +{ + const unsigned char *constants; + + input[4] = ReadLE32(k + 0); + input[5] = ReadLE32(k + 4); + input[6] = ReadLE32(k + 8); + input[7] = ReadLE32(k + 12); + if (keylen == 32) { /* recommended */ + k += 16; + constants = sigma; + } else { /* keylen == 16 */ + constants = tau; + } + input[8] = ReadLE32(k + 0); + input[9] = ReadLE32(k + 4); + input[10] = ReadLE32(k + 8); + input[11] = ReadLE32(k + 12); + input[0] = ReadLE32(constants + 0); + input[1] = ReadLE32(constants + 4); + input[2] = ReadLE32(constants + 8); + input[3] = ReadLE32(constants + 12); + input[12] = 0; + input[13] = 0; + input[14] = 0; + input[15] = 0; +} + +ChaCha20::ChaCha20() +{ + memset(input, 0, sizeof(input)); +} + +ChaCha20::ChaCha20(const unsigned char* k, size_t keylen) +{ + SetKey(k, keylen); +} + +void ChaCha20::SetIV(uint64_t iv) +{ + input[14] = iv; + input[15] = iv >> 32; +} + +void ChaCha20::Seek(uint64_t pos) +{ + input[12] = pos; + input[13] = pos >> 32; +} + +void ChaCha20::Output(unsigned char* c, size_t bytes) +{ + uint32_t x[16]; + uint32_t j[16]; + unsigned char *ctarget = nullptr; + unsigned char tmp[64]; + unsigned int i; + + if (!bytes) return; + + for (uint32_t i=0; i<16; i++) { + j[i] = input[i]; + } + + for (;;) { + if (bytes < 64) { + ctarget = c; + c = tmp; + } + for (uint32_t i=0; i<16; i++) { + x[i] = j[i]; + } + for (i = 20;i > 0;i -= 2) { + QUARTERROUND( x[0], x[4], x[8],x[12]) + QUARTERROUND( x[1], x[5], x[9],x[13]) + QUARTERROUND( x[2], x[6],x[10],x[14]) + QUARTERROUND( x[3], x[7],x[11],x[15]) + QUARTERROUND( x[0], x[5],x[10],x[15]) + QUARTERROUND( x[1], x[6],x[11],x[12]) + QUARTERROUND( x[2], x[7], x[8],x[13]) + QUARTERROUND( x[3], x[4], x[9],x[14]) + } + for (uint32_t i=0; i<16; i++) { + x[i] += j[i]; + } + + ++j[12]; + if (!j[12]) ++j[13]; + + for (uint32_t i=0; i<16; i++) { + WriteLE32(c + 4*i, x[i]); + } + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) ctarget[i] = c[i]; + } + input[12] = j[12]; + input[13] = j[13]; + return; + } + bytes -= 64; + c += 64; + } +} diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h new file mode 100644 index 0000000000000..a305977bcd5f4 --- /dev/null +++ b/src/crypto/chacha20.h @@ -0,0 +1,26 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_CHACHA20_H +#define BITCOIN_CRYPTO_CHACHA20_H + +#include +#include + +/** A PRNG class for ChaCha20. */ +class ChaCha20 +{ +private: + uint32_t input[16]; + +public: + ChaCha20(); + ChaCha20(const unsigned char* key, size_t keylen); + void SetKey(const unsigned char* key, size_t keylen); + void SetIV(uint64_t iv); + void Seek(uint64_t pos); + void Output(unsigned char* output, size_t bytes); +}; + +#endif // BITCOIN_CRYPTO_CHACHA20_H diff --git a/src/crypto/common.h b/src/crypto/common.h index 6acbb29586279..6940a9f40f8b1 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014 The Bitcoin developers +// Copyright (c) 2014-2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -10,111 +10,94 @@ #endif #include +#include -#if defined(HAVE_ENDIAN_H) -#include -#endif +#include + +uint16_t static inline ReadLE16(const unsigned char* ptr) +{ + uint16_t x; + memcpy((char*)&x, ptr, 2); + return le16toh(x); +} uint32_t static inline ReadLE32(const unsigned char* ptr) { -#if HAVE_DECL_LE32TOH == 1 - return le32toh(*((uint32_t*)ptr)); -#elif !defined(WORDS_BIGENDIAN) - return *((uint32_t*)ptr); -#else - return ((uint32_t)ptr[3] << 24 | (uint32_t)ptr[2] << 16 | (uint32_t)ptr[1] << 8 | (uint32_t)ptr[0]); -#endif + uint32_t x; + memcpy((char*)&x, ptr, 4); + return le32toh(x); } uint64_t static inline ReadLE64(const unsigned char* ptr) { -#if HAVE_DECL_LE64TOH == 1 - return le64toh(*((uint64_t*)ptr)); -#elif !defined(WORDS_BIGENDIAN) - return *((uint64_t*)ptr); -#else - return ((uint64_t)ptr[7] << 56 | (uint64_t)ptr[6] << 48 | (uint64_t)ptr[5] << 40 | (uint64_t)ptr[4] << 32 | - (uint64_t)ptr[3] << 24 | (uint64_t)ptr[2] << 16 | (uint64_t)ptr[1] << 8 | (uint64_t)ptr[0]); -#endif + uint64_t x; + memcpy((char*)&x, ptr, 8); + return le64toh(x); +} + +void static inline WriteLE16(unsigned char* ptr, uint16_t x) +{ + uint16_t v = htole16(x); + memcpy(ptr, (char*)&v, 2); } void static inline WriteLE32(unsigned char* ptr, uint32_t x) { -#if HAVE_DECL_HTOLE32 == 1 - *((uint32_t*)ptr) = htole32(x); -#elif !defined(WORDS_BIGENDIAN) - *((uint32_t*)ptr) = x; -#else - ptr[3] = x >> 24; - ptr[2] = x >> 16; - ptr[1] = x >> 8; - ptr[0] = x; -#endif + uint32_t v = htole32(x); + memcpy(ptr, (char*)&v, 4); } void static inline WriteLE64(unsigned char* ptr, uint64_t x) { -#if HAVE_DECL_HTOLE64 == 1 - *((uint64_t*)ptr) = htole64(x); -#elif !defined(WORDS_BIGENDIAN) - *((uint64_t*)ptr) = x; -#else - ptr[7] = x >> 56; - ptr[6] = x >> 48; - ptr[5] = x >> 40; - ptr[4] = x >> 32; - ptr[3] = x >> 24; - ptr[2] = x >> 16; - ptr[1] = x >> 8; - ptr[0] = x; -#endif + uint64_t v = htole64(x); + memcpy(ptr, (char*)&v, 8); } uint32_t static inline ReadBE32(const unsigned char* ptr) { -#if HAVE_DECL_BE32TOH == 1 - return be32toh(*((uint32_t*)ptr)); -#else - return ((uint32_t)ptr[0] << 24 | (uint32_t)ptr[1] << 16 | (uint32_t)ptr[2] << 8 | (uint32_t)ptr[3]); -#endif + uint32_t x; + memcpy((char*)&x, ptr, 4); + return be32toh(x); } uint64_t static inline ReadBE64(const unsigned char* ptr) { -#if HAVE_DECL_BE64TOH == 1 - return be64toh(*((uint64_t*)ptr)); -#else - return ((uint64_t)ptr[0] << 56 | (uint64_t)ptr[1] << 48 | (uint64_t)ptr[2] << 40 | (uint64_t)ptr[3] << 32 | - (uint64_t)ptr[4] << 24 | (uint64_t)ptr[5] << 16 | (uint64_t)ptr[6] << 8 | (uint64_t)ptr[7]); -#endif + uint64_t x; + memcpy((char*)&x, ptr, 8); + return be64toh(x); } void static inline WriteBE32(unsigned char* ptr, uint32_t x) { -#if HAVE_DECL_HTOBE32 == 1 - *((uint32_t*)ptr) = htobe32(x); -#else - ptr[0] = x >> 24; - ptr[1] = x >> 16; - ptr[2] = x >> 8; - ptr[3] = x; -#endif + uint32_t v = htobe32(x); + memcpy(ptr, (char*)&v, 4); } void static inline WriteBE64(unsigned char* ptr, uint64_t x) { -#if HAVE_DECL_HTOBE64 == 1 - *((uint64_t*)ptr) = htobe64(x); -#else - ptr[0] = x >> 56; - ptr[1] = x >> 48; - ptr[2] = x >> 40; - ptr[3] = x >> 32; - ptr[4] = x >> 24; - ptr[5] = x >> 16; - ptr[6] = x >> 8; - ptr[7] = x; + uint64_t v = htobe64(x); + memcpy(ptr, (char*)&v, 8); +} + +/** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */ +uint64_t static inline CountBits(uint64_t x) +{ +#if HAVE_DECL___BUILTIN_CLZL + if (sizeof(unsigned long) >= sizeof(uint64_t)) { + return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; + } +#endif +#if HAVE_DECL___BUILTIN_CLZLL + if (sizeof(unsigned long long) >= sizeof(uint64_t)) { + return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0; + } #endif + int ret = 0; + while (x) { + x >>= 1; + ++ret; + } + return ret; } -#endif // BITCOIN_CRYPTO_COMMON_H +#endif // BITCOIN_CRYPTO_COMMON_H \ No newline at end of file diff --git a/src/init.cpp b/src/init.cpp index 898642e16393e..7e6cbce19b1a7 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -63,7 +63,6 @@ #include #include #include -#include #if ENABLE_ZMQ @@ -304,7 +303,7 @@ void Shutdown() */ void HandleSIGTERM(int) { - fRequestShutdown = true; + StartShutdown(); } void HandleSIGHUP(int) @@ -312,6 +311,17 @@ void HandleSIGHUP(int) fReopenDebugLog = true; } +#ifndef WIN32 +static void registerSignalHandler(int signal, void(*handler)(int)) +{ + struct sigaction sa; + sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(signal, &sa, nullptr); +} +#endif + bool static Bind(const CService& addr, unsigned int flags) { @@ -709,9 +719,15 @@ bool InitSanityCheck(void) InitError("Elliptic curve cryptography sanity check failure. Aborting."); return false; } + if (!glibc_sanity_test() || !glibcxx_sanity_test()) return false; + if (!Random_SanityCheck()) { + InitError("OS cryptographic RNG sanity check failure. Aborting."); + return false; + } + return true; } @@ -732,10 +748,20 @@ bool AppInitServers() return true; } -/** Initialize phore. - * @pre Parameters should be parsed and config file should be read. - */ -bool AppInit2(const std::vector& words) +[[noreturn]] static void new_handler_terminate() +{ + // Rather than throwing std::bad-alloc if allocation fails, terminate + // immediately to (try to) avoid chain corruption. + // Since LogPrintf may itself allocate memory, set the handler directly + // to terminate first. + std::set_new_handler(std::terminate); + LogPrintf("Error: Out of memory. Terminating.\n"); + + // The log was successful, terminate now. + std::terminate(); +}; + +bool AppInitBasicSetup() { // ********************************************************* Step 1: setup #ifdef _MSC_VER @@ -748,7 +774,7 @@ bool AppInit2(const std::vector& words) _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); #endif #ifdef WIN32 -// Enable Data Execution Prevention (DEP) + // Enable Data Execution Prevention (DEP) // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008 // A failure is non-critical and needs no further attention! #ifndef PROCESS_DEP_ENABLE @@ -774,26 +800,31 @@ bool AppInit2(const std::vector& words) umask(077); } - - // Clean shutdown on SIGTERM - struct sigaction sa; - sa.sa_handler = HandleSIGTERM; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); + // Clean shutdown on SIGTERMx + registerSignalHandler(SIGTERM, HandleSIGTERM); + registerSignalHandler(SIGINT, HandleSIGTERM); // Reopen debug.log on SIGHUP - struct sigaction sa_hup; - sa_hup.sa_handler = HandleSIGHUP; - sigemptyset(&sa_hup.sa_mask); - sa_hup.sa_flags = 0; - sigaction(SIGHUP, &sa_hup, NULL); + registerSignalHandler(SIGHUP, HandleSIGHUP); // Ignore SIGPIPE, otherwise it will bring the daemon down if the client closes unexpectedly signal(SIGPIPE, SIG_IGN); #endif + std::set_new_handler(new_handler_terminate); + + return true; +} + +/** Initialize phore. + * @pre Parameters should be parsed and config file should be read. + */ +bool AppInit2(const std::vector& words) +{ + // ********************************************************* Step 1: setup + if (!AppInitBasicSetup()) + return false; + // ********************************************************* Step 2: parameter interactions // Set this early so that parameter interactions go to console fPrintToConsole = GetBoolArg("-printtoconsole", false); @@ -923,7 +954,6 @@ bool AppInit2(const std::vector& words) else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; - fServer = GetBoolArg("-server", false); setvbuf(stdout, NULL, _IOLBF, 0); /// ***TODO*** do we still need this after -printtoconsole is gone? // Staking needs a CWallet instance, so make sure wallet is enabled @@ -1015,6 +1045,7 @@ bool AppInit2(const std::vector& words) // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log // Initialize elliptic curve code + RandomInit(); ECC_Start(); globalVerifyHandle.reset(new ECCVerifyHandle()); @@ -1048,7 +1079,6 @@ bool AppInit2(const std::vector& words) ShrinkDebugFile(); LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); LogPrintf("Phore version %s (%s)\n", FormatFullVersion(), CLIENT_DATE); - LogPrintf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION)); #ifdef ENABLE_WALLET LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); #endif @@ -1081,14 +1111,12 @@ bool AppInit2(const std::vector& words) * that the server is there and will be ready later). Warmup mode will * be disabled when initialisation is finished. */ - if (fServer) { + if (GetBoolArg("-server", false)) { uiInterface.InitMessage.connect(SetRPCWarmupStatus); if (!AppInitServers()) return InitError(_("Unable to start HTTP server. See debug log for details.")); } - int64_t nStart; - // ********************************************************* Step 5: Backup wallet and verify wallet database integrity #ifdef ENABLE_WALLET if (!fDisableWallet) { @@ -1116,7 +1144,7 @@ bool AppInit2(const std::vector& words) try { boost::filesystem::copy_file(sourceFile, backupFile); LogPrintf("Creating backup of %s -> %s\n", sourceFile, backupFile); - } catch (boost::filesystem::filesystem_error& error) { + } catch (const boost::filesystem::filesystem_error& error) { LogPrintf("Failed to create backup %s\n", error.what()); } #else @@ -1152,7 +1180,7 @@ bool AppInit2(const std::vector& words) try { boost::filesystem::remove(file.second); LogPrintf("Old backup deleted: %s\n", file.second); - } catch (boost::filesystem::filesystem_error& error) { + } catch (const boost::filesystem::filesystem_error& error) { LogPrintf("Failed to delete backup %s\n", error.what()); } } @@ -1190,7 +1218,7 @@ bool AppInit2(const std::vector& words) boost::filesystem::remove_all(zerocoinDir); LogPrintf("-resync: folder deleted: %s\n", zerocoinDir.string().c_str()); } - } catch (boost::filesystem::filesystem_error& error) { + } catch (const boost::filesystem::filesystem_error& error) { LogPrintf("Failed to delete blockchain folders %s\n", error.what()); } } @@ -1205,7 +1233,7 @@ bool AppInit2(const std::vector& words) try { boost::filesystem::rename(pathDatabase, pathDatabaseBak); LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); - } catch (boost::filesystem::filesystem_error& error) { + } catch (const boost::filesystem::filesystem_error& error) { // failure is ok (well, not really, but it's not worse than what we started with) } @@ -1408,14 +1436,16 @@ bool AppInit2(const std::vector& words) nCoinCacheSize = nTotalCache / 300; // coins in memory require around 300 bytes bool fLoaded = false; - while (!fLoaded) { + int64_t nStart = GetTimeMillis(); + while (!fLoaded && !ShutdownRequested()) { bool fReset = fReindex; std::string strLoadError; uiInterface.InitMessage(_("Loading block index...")); - nStart = GetTimeMillis(); do { + const int64_t load_block_index_start_time = GetTimeMillis(); + try { UnloadBlockIndex(); delete pcoinsTip; @@ -1437,13 +1467,17 @@ bool AppInit2(const std::vector& words) if (fReindex) pblocktree->WriteReindexing(true); + // End loop if shutdown was requested + if (ShutdownRequested()) break; + // Phore: load previous sessions sporks if we have them. uiInterface.InitMessage(_("Loading sporks...")); - LoadSporksFromDB(); + sporkManager.LoadSporksFromDB(); uiInterface.InitMessage(_("Loading block index...")); std::string strBlockIndexError = ""; if (!LoadBlockIndex(strBlockIndexError)) { + if (ShutdownRequested()) break; strLoadError = _("Error loading block database"); strLoadError = strprintf("%s : %s", strLoadError, strBlockIndexError); break; @@ -1574,7 +1608,7 @@ bool AppInit2(const std::vector& words) fVerifyingBlocks = false; break; } - } catch (std::exception& e) { + } catch (const std::exception& e) { if (fDebug) LogPrintf("%s\n", e.what()); strLoadError = _("Error opening block database"); fVerifyingBlocks = false; @@ -1583,9 +1617,10 @@ bool AppInit2(const std::vector& words) fVerifyingBlocks = false; fLoaded = true; + LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time); } while (false); - if (!fLoaded) { + if (!fLoaded && !ShutdownRequested()) { // first suggest a reindex if (!fReset) { bool fRet = uiInterface.ThreadSafeMessageBox( @@ -1607,11 +1642,10 @@ bool AppInit2(const std::vector& words) // As LoadBlockIndex can take several minutes, it's possible the user // requested to kill the GUI during the last operation. If so, exit. // As the program has not fully started yet, Shutdown() is possibly overkill. - if (fRequestShutdown) { + if (ShutdownRequested()) { LogPrintf("Shutdown requested. Exiting.\n"); return false; } - LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart); boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; CAutoFile est_filein(fopen(est_path.string().c_str(), "rb"), SER_DISK, CLIENT_VERSION); @@ -1647,7 +1681,7 @@ bool AppInit2(const std::vector& words) uiInterface.InitMessage(_("Loading wallet...")); fVerifyingBlocks = true; - nStart = GetTimeMillis(); + const int64_t nWalletStartTime = GetTimeMillis(); bool fFirstRun = true; pwalletMain = new CWallet(strWalletFile); DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun); @@ -1692,12 +1726,12 @@ bool AppInit2(const std::vector& words) // ensure this wallet.dat can only be opened by clients supporting HD pwalletMain->SetMinVersion(FEATURE_HD); } - CPubKey newDefaultKey; - if (pwalletMain->GetKeyFromPool(newDefaultKey, false)) { - pwalletMain->SetDefaultKey(newDefaultKey); - if (!pwalletMain->SetAddressBook(pwalletMain->vchDefaultKey.GetID(), "", "receive")) - return InitError(_("Cannot write default address") += "\n"); + // Top up the keypool + if (!pwalletMain->TopUpKeyPool()) { + // Error generating keys + InitError(_("Unable to generate initial key") += "\n"); + return error("%s %s", __func__ , "Unable to generate initial key"); } pwalletMain->SetBestChain(chainActive.GetLocator()); @@ -1715,9 +1749,8 @@ bool AppInit2(const std::vector& words) InitWarning(_("Make sure to encrypt your wallet and delete all non-encrypted backups after you verified that wallet works!")); } - LogPrintf("%s", strErrors.str()); - LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); - + LogPrintf("Init errors: %s\n", strErrors.str()); + LogPrintf("Wallet completed loading in %15dms\n", GetTimeMillis() - nWalletStartTime); zwalletMain = new CzPHRWallet(pwalletMain->strWalletFile); pwalletMain->setZWallet(zwalletMain); @@ -1737,14 +1770,17 @@ bool AppInit2(const std::vector& words) if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { uiInterface.InitMessage(_("Rescanning...")); LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); - nStart = GetTimeMillis(); - pwalletMain->ScanForWalletTransactions(pindexRescan, true); - LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); + const int64_t nWalletRescanTime = GetTimeMillis(); + if (pwalletMain->ScanForWalletTransactions(pindexRescan, true, true) == -1) { + return error("Shutdown requested over the txs scan. Exiting."); + } + LogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nWalletRescanTime); pwalletMain->SetBestChain(chainActive.GetLocator()); nWalletDBUpdated++; // Restore wallet transaction metadata after -zapwallettxes=1 if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2") { + CWalletDB walletdb(strWalletFile); for (const CWalletTx& wtxOld : vWtx) { uint256 hash = wtxOld.GetHash(); std::map::iterator mi = pwalletMain->mapWallet.find(hash); @@ -1758,7 +1794,7 @@ bool AppInit2(const std::vector& words) copyTo->fFromMe = copyFrom->fFromMe; copyTo->strFromAccount = copyFrom->strFromAccount; copyTo->nOrderPos = copyFrom->nOrderPos; - copyTo->WriteToDisk(); + copyTo->WriteToDisk(&walletdb); } } } @@ -1977,6 +2013,11 @@ bool AppInit2(const std::vector& words) threadGroup.create_thread(boost::bind(&ThreadCheckObfuScationPool)); + if (ShutdownRequested()) { + LogPrintf("Shutdown requested. Exiting.\n"); + return false; + } + // ********************************************************* Step 11: start node if (!CheckDiskSpace()) @@ -2018,7 +2059,7 @@ bool AppInit2(const std::vector& words) #ifdef ENABLE_WALLET if (pwalletMain) { // Add wallet transactions that aren't already in a block to mapTransactions - pwalletMain->ReacceptWalletTransactions(); + pwalletMain->ReacceptWalletTransactions(/*fFirstLoad*/true); // Run a thread to flush wallet periodically threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile))); diff --git a/src/init.h b/src/init.h index 671b4810d4fb5..e98bc14491f00 100644 --- a/src/init.h +++ b/src/init.h @@ -29,6 +29,12 @@ void Shutdown(); void PrepareShutdown(); bool AppInit2(const std::vector& words); +/** Initialize PIVX core: Basic context setup. + * @note This can be done before daemonization. Do not call Shutdown() if this function fails. + * @pre Parameters should be parsed and config file should be read. + */ +bool AppInitBasicSetup(); + /** The help message mode determines what help message to show */ enum HelpMessageMode { HMM_BITCOIND, diff --git a/src/kernel.cpp b/src/kernel.cpp index e37f230c3f6a6..d2c8995f89bce 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -161,6 +161,11 @@ bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeMod { nStakeModifier = 0; fGeneratedStakeModifier = false; + + // modifier 0 on RegTest + if (Params().NetworkID() == CBaseChainParams::REGTEST) { + return true; + } if (!pindexPrev) { fGeneratedStakeModifier = true; return true; // genesis block's modifier is 0 @@ -255,6 +260,10 @@ bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeMod bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake) { nStakeModifier = 0; + // modifier 0 on RegTest + if (Params().NetworkID() == CBaseChainParams::REGTEST) { + return true; + } if (!mapBlockIndex.count(hashBlockFrom)) return error("GetKernelStakeModifier() : block not indexed"); const CBlockIndex* pindexFrom = mapBlockIndex[hashBlockFrom]; @@ -475,7 +484,7 @@ bool CheckProofOfStake(const CBlock block, uint256& hashProofOfStake, std::uniqu if (!pindexfrom) return error("%s: Failed to find the block index for stake origin", __func__); - if(GetSporkValue(SPORK_20_KERNEL_EXTRA_STAKING_CHECK) >= pindexfrom->nHeight) { + if(sporkManager.GetSporkValue(SPORK_20_KERNEL_EXTRA_STAKING_CHECK) >= pindexfrom->nHeight) { const CTxIn& txin = tx.vin[0]; uint256 hashBlock; CTransaction txPrev; diff --git a/src/key.cpp b/src/key.cpp index 6aefada0c18fc..21763fa48e2e3 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -158,7 +158,7 @@ bool CKey::Check(const unsigned char* vch) void CKey::MakeNewKey(bool fCompressedIn) { do { - GetRandBytes(keydata.data(), keydata.size()); + GetStrongRandBytes(keydata.data(), keydata.size()); } while (!Check(keydata.data())); fValid = true; fCompressed = fCompressedIn; diff --git a/src/main.cpp b/src/main.cpp index 0b060e467b4c5..54ff9b5fa8f8c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1532,10 +1532,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa *pfMissingInputs = false; //Temporarily disable zerocoin for maintenance - if (GetAdjustedTime() > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE) && tx.ContainsZerocoins()) + if (sporkManager.IsSporkActive(SPORK_16_ZEROCOIN_MAINTENANCE_MODE) && tx.ContainsZerocoins()) return state.DoS(10, error("AcceptToMemoryPool : Zerocoin transactions are temporarily disabled for maintenance"), REJECT_INVALID, "bad-tx"); - if (!CheckTransaction(tx, chainActive.Height() >= Params().Zerocoin_StartHeight(), true, state, GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < chainActive.Tip()->nTime)) { + if (!CheckTransaction(tx, chainActive.Height() >= Params().Zerocoin_StartHeight(), true, state, sporkManager.GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < chainActive.Tip()->nTime)) { return state.DoS(100, error("AcceptToMemoryPool: : CheckTransaction failed"), REJECT_INVALID, "bad-tx"); } @@ -1581,7 +1581,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa } // Don't accept witness transactions before the final threshold passes - if (!GetBoolArg("-prematurewitness", false) && !tx.wit.IsNull() && !IsSporkActive(SPORK_17_SEGWIT_ACTIVATION)) { + if (!GetBoolArg("-prematurewitness", false) && !tx.wit.IsNull() && !sporkManager.IsSporkActive(SPORK_17_SEGWIT_ACTIVATION)) { return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true); } @@ -1803,7 +1803,7 @@ bool ReadTransaction(CTransaction& tx, const CDiskTxPos &pos, uint256 &hashBlock file >> header; fseek(file.Get(), pos.nTxOffset, SEEK_CUR); file >> tx; - } catch (std::exception &e) { + } catch (const std::exception& e) { return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); } hashBlock = header.GetHash(); @@ -1840,7 +1840,7 @@ bool AcceptableInputs(CTxMemPool& pool, CValidationState& state, const CTransact if (pfMissingInputs) *pfMissingInputs = false; - if (!CheckTransaction(tx, chainActive.Height() >= Params().Zerocoin_StartHeight(), true, state, GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < chainActive.Tip()->nTime)) + if (!CheckTransaction(tx, chainActive.Height() >= Params().Zerocoin_StartHeight(), true, state, sporkManager.GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < chainActive.Tip()->nTime)) return error("AcceptableInputs: : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction @@ -2063,7 +2063,7 @@ bool GetTransaction(const uint256& hash, CTransaction& txOut, uint256& hashBlock file >> header; fseek(file.Get(), postx.nTxOffset, SEEK_CUR); file >> txOut; - } catch (std::exception& e) { + } catch (const std::exception& e) { return error("%s : Deserialize or I/O error - %s", __func__, e.what()); } hashBlock = header.GetHash(); @@ -2144,7 +2144,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) // Read block try { filein >> block; - } catch (std::exception& e) { + } catch (const std::exception& e) { return error("%s : Deserialize or I/O error - %s", __func__, e.what()); } @@ -2906,7 +2906,7 @@ bool UpdateZPHRSupply(const CBlock& block, CBlockIndex* pindex) CWalletTx wtx(pwalletMain, tx); wtx.nTimeReceived = block.GetBlockTime(); wtx.SetMerkleBranch(block); - pwalletMain->AddToWallet(wtx); + pwalletMain->AddToWallet(wtx, false, nullptr); setAddedToWallet.insert(txid); } } @@ -3042,7 +3042,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG; - if (GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < block.nTime) { + if (sporkManager.GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < block.nTime) { flags |= SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; } @@ -3052,7 +3052,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin nInputs += tx.vin.size(); //Temporarily disable zerocoin transactions for maintenance - if (block.nTime > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE) && !IsInitialBlockDownload() && tx.ContainsZerocoins()) { + if (block.nTime > sporkManager.GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE) && !IsInitialBlockDownload() && tx.ContainsZerocoins()) { return state.DoS(100, error("ConnectBlock() : zerocoin transactions are currently in maintenance mode")); } if (tx.IsZerocoinSpend()) { @@ -3254,7 +3254,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CWalletTx wtx(pwalletMain, tx); wtx.nTimeReceived = pindex->GetBlockTime(); wtx.SetMerkleBranch(block); - pwalletMain->AddToWallet(wtx); + pwalletMain->AddToWallet(wtx, false, nullptr); setAddedTx.insert(pSpend.second); } } @@ -3561,19 +3561,50 @@ bool static ConnectTip(CValidationState& state, CBlockIndex* pindexNew, CBlock* return true; } -bool DisconnectBlocksAndReprocess(int blocks) +bool DisconnectBlocks(int nBlocks) { LOCK(cs_main); CValidationState state; - LogPrintf("DisconnectBlocksAndReprocess: Got command to replay %d blocks\n", blocks); - for (int i = 0; i <= blocks; i++) + LogPrintf("%s: Got command to replay %d blocks\n", __func__, nBlocks); + for (int i = 0; i <= nBlocks; i++) DisconnectTip(state, false); return true; } +void ReprocessBlocks(int nBlocks) +{ + std::map::iterator it = mapRejectedBlocks.begin(); + while (it != mapRejectedBlocks.end()) { + //use a window twice as large as is usual for the nBlocks we want to reset + if ((*it).second > GetTime() - (nBlocks * Params().TargetSpacing() * 2)) { + BlockMap::iterator mi = mapBlockIndex.find((*it).first); + if (mi != mapBlockIndex.end() && (*mi).second) { + LOCK(cs_main); + + CBlockIndex* pindex = (*mi).second; + LogPrintf("%s - %s\n", __func__, (*it).first.ToString()); + + CValidationState state; + ReconsiderBlock(state, pindex); + } + } + ++it; + } + + CValidationState state; + { + LOCK(cs_main); + DisconnectBlocks(nBlocks); + } + + if (state.IsValid()) { + ActivateBestChain(state); + } +} + /* DisconnectBlockAndInputs @@ -4139,6 +4170,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo const bool IsPoS = block.IsProofOfStake(); LogPrint("debug", "%s: block=%s is proof of stake=%d\n", __func__, block.GetHash().ToString().c_str(), IsPoS); + if (block.fChecked) + return true; + // Check that the header is valid (particularly PoW). This is mostly // redundant with the call in AcceptBlockHeader. if (!CheckBlockHeader(block, state, !IsPoS)) @@ -4157,7 +4191,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Check the merkle root. if (fCheckMerkleRoot) { bool mutated; - uint256 hashMerkleRoot2 = block.BuildMerkleTree(&mutated); + uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated); if (block.hashMerkleRoot != hashMerkleRoot2) return state.DoS(100, error("%s : hashMerkleRoot mismatch", __func__), REJECT_INVALID, "bad-txnmrklroot", true); @@ -4187,7 +4221,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo if (IsPoS) { int commitpos = GetWitnessCommitmentIndex(block); if (commitpos >= 0) { - if (IsSporkActive(SPORK_19_SEGWIT_ON_COINBASE)) { + if (sporkManager.IsSporkActive(SPORK_19_SEGWIT_ON_COINBASE)) { if (block.vtx[0].vout.size() != 2) return state.DoS(100, error("CheckBlock() : coinbase output has wrong size for proof-of-stake block")); if (!block.vtx[0].vout[1].scriptPubKey.IsUnspendable()) @@ -4214,7 +4248,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo } // ----------- swiftTX transaction scanning ----------- - if (IsSporkActive(SPORK_3_SWIFTTX_BLOCK_FILTERING)) { + if (sporkManager.IsSporkActive(SPORK_3_SWIFTTX_BLOCK_FILTERING)) { for (const CTransaction& tx : block.vtx) { if (!tx.IsCoinBase()) { //only reject blocks when it's based on complete consensus @@ -4264,7 +4298,6 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo } } - unsigned int nSigOps = 0; for (const CTransaction& tx : block.vtx) { nSigOps += GetLegacySigOpCount(tx); @@ -4273,6 +4306,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo return state.DoS(100, error("%s : out-of-bounds SigOpCount", __func__), REJECT_INVALID, "bad-blk-sigops", true); + if (fCheckPOW && fCheckMerkleRoot && fCheckSig) + block.fChecked = true; + return true; } @@ -4353,7 +4389,7 @@ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPr { int commitpos = GetWitnessCommitmentIndex(block); static const std::vector nonce(32, 0x00); - if (commitpos != -1 && GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < pindexPrev->nTime && block.vtx[0].wit.IsEmpty()) { + if (commitpos != -1 && sporkManager.GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < pindexPrev->nTime && block.vtx[0].wit.IsEmpty()) { block.vtx[0].wit.vtxinwit.resize(1); block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.resize(1); block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0] = nonce; @@ -4372,7 +4408,7 @@ std::vector GenerateCoinbaseCommitment(CBlock& block, const CBloc } } std::vector ret(32, 0x00); - if (fHaveWitness && GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < pindexPrev->nTime) { + if (fHaveWitness && sporkManager.GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < pindexPrev->nTime) { if (commitpos == -1) { uint256 witnessroot = BlockWitnessMerkleRoot(block, NULL); CHash256().Write(witnessroot.begin(), 32).Write(&ret[0], 32).Finalize(witnessroot.begin()); @@ -4431,8 +4467,8 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn std::vector vBlockSerials; for (const CTransaction& tx : block.vtx) { - if (!CheckTransaction(tx, true, chainActive.Height() + 1 >= Params().Zerocoin_StartHeight(), state, GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < block.nTime)) - return error("%s : CheckTransaction failed", __func__); + if (!CheckTransaction(tx, true, chainActive.Height() + 1 >= Params().Zerocoin_StartHeight(), state, sporkManager.GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < block.nTime)) + return error("CheckBlock() : CheckTransaction failed"); // double check that there are no double spent zPHR spends in this block if (tx.IsZerocoinSpend()) { @@ -4479,10 +4515,10 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256(witness root, witness nonce). In case there are // multiple, the last one is used. bool fHaveWitness = false; - if (pindexPrev != nullptr && GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < pindexPrev->nTime) { + if (pindexPrev != nullptr && sporkManager.GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < pindexPrev->nTime) { int commitpos = GetWitnessCommitmentIndex(block); if (commitpos != -1) { - if (!IsSporkActive(SPORK_19_SEGWIT_ON_COINBASE)) { + if (!sporkManager.IsSporkActive(SPORK_19_SEGWIT_ON_COINBASE)) { if (fDebug) { LogPrintf("CheckBlock() : staking-on-segwit is not enabled.\n"); } @@ -4740,7 +4776,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return state.Error("Failed to write block"); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("AcceptBlock() : ReceivedBlockTransactions failed"); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { return state.Error(std::string("System error: ") + e.what()); } @@ -4991,6 +5027,9 @@ bool static LoadBlockIndexDB(std::string& strError) } std::sort(vSortedByHeight.begin(), vSortedByHeight.end()); for (const PAIRTYPE(int, CBlockIndex*) & item : vSortedByHeight) { + // Stop if shutdown was requested + if (ShutdownRequested()) return false; + CBlockIndex* pindex = item.second; pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); if (pindex->nStatus & BLOCK_HAVE_DATA) { @@ -5177,7 +5216,7 @@ bool RewindBlockIndex(const CChainParams& params) int nHeight = 1; while (nHeight <= chainActive.Height()) { - if (GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < chainActive[nHeight - 1]->nTime && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) { + if (sporkManager.GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < chainActive[nHeight - 1]->nTime && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) { break; } nHeight++; @@ -5200,7 +5239,7 @@ bool RewindBlockIndex(const CChainParams& params) // to disk before writing the chainstate, resulting in a failure to continue if interrupted. for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { CBlockIndex* pindexIter = it->second; - if (GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < pindexIter->nTime && !(pindexIter->nStatus & BLOCK_OPT_WITNESS)) { + if (sporkManager.GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < pindexIter->nTime && !(pindexIter->nStatus & BLOCK_OPT_WITNESS)) { // Reduce validity pindexIter->nStatus = std::min(pindexIter->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (pindexIter->nStatus & ~BLOCK_VALID_MASK); // Remove have-data flags. @@ -5312,7 +5351,7 @@ bool InitBlockIndex() return error("LoadBlockIndex() : genesis block cannot be activated"); // Force a chainstate write so that when we VerifyDB in a moment, it doesnt check stale data return FlushStateToDisk(state, FLUSH_STATE_ALWAYS); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); } } @@ -5409,11 +5448,11 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos* dbp) mapBlocksUnknownParent.erase(it); } } - } catch (std::exception& e) { + } catch (const std::exception& e) { LogPrintf("%s : Deserialize or I/O error - %s", __func__, e.what()); } } - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { AbortNode(std::string("System error: ") + e.what()); } if (nLoaded > 0) @@ -5685,7 +5724,6 @@ bool static AlreadyHave(const CInv& inv) return true; } - void static ProcessGetData(CNode* pfrom) { std::deque::iterator it = pfrom->vRecvGetData.begin(); @@ -5921,8 +5959,7 @@ void static ProcessGetData(CNode* pfrom) bool fRequestedSporksIDB = false; bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { - if (fDebug) - LogPrintf("received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); + LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); return true; @@ -6023,13 +6060,14 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR // Advertise our address if (fListen && !IsInitialBlockDownload()) { CAddress addr = GetLocalAddress(&pfrom->addr); + FastRandomContext insecure_rand; if (addr.IsRoutable()) { - LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString()); - pfrom->PushAddress(addr); + LogPrintf("ProcessMessages: advertising address %s\n", addr.ToString()); + pfrom->PushAddress(addr, insecure_rand); } else if (IsPeerAddrLocalGood(pfrom)) { addr.SetIP(pfrom->addrLocal); - LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString()); - pfrom->PushAddress(addr); + LogPrintf("ProcessMessages: advertising address %s\n", addr.ToString()); + pfrom->PushAddress(addr, insecure_rand); } } @@ -6139,8 +6177,9 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR mapMix.insert(std::make_pair(hashKey, pnode)); } int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) + FastRandomContext insecure_rand; for (std::multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) - ((*mi).second)->PushAddress(addr); + ((*mi).second)->PushAddress(addr, insecure_rand); } } // Do not store addresses outside our network @@ -6197,7 +6236,7 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR // doing this will result in the received block being rejected as an orphan in case it is // not a direct successor. if (State(pfrom->GetId())->fHaveWitness && - (GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) > chainActive.Tip()->nTime || State(pfrom->GetId())->fHaveWitness)) { + (sporkManager.GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) > chainActive.Tip()->nTime || State(pfrom->GetId())->fHaveWitness)) { inv.type = MSG_WITNESS_BLOCK; } vToFetch.push_back(inv); @@ -6592,8 +6631,9 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR else if ((strCommand == NetMsgType::GETADDR) && (pfrom->fInbound)) { pfrom->vAddrToSend.clear(); std::vector vAddr = addrman.GetAddr(); + FastRandomContext insecure_rand; for (const CAddress& addr : vAddr) - pfrom->PushAddress(addr); + pfrom->PushAddress(addr, insecure_rand); } @@ -6807,7 +6847,7 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR budget.ProcessMessage(pfrom, strCommand, vRecv); masternodePayments.ProcessMessageMasternodePayments(pfrom, strCommand, vRecv); ProcessMessageSwiftTX(pfrom, strCommand, vRecv); - ProcessSpork(pfrom, strCommand, vRecv); + sporkManager.ProcessSpork(pfrom, strCommand, vRecv); masternodeSync.ProcessMessage(pfrom, strCommand, vRecv); } @@ -6821,8 +6861,10 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR // it was the one which was commented out int ActiveProtocol() { - if (IsSporkActive(SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2)) - return MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT; + // SPORK_14 is used for 70917 (v3.4+) + if (sporkManager.IsSporkActive(SPORK_14_NEW_PROTOCOL_ENFORCEMENT)) + return MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT; + return MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT; } @@ -6914,9 +6956,9 @@ bool ProcessMessages(CNode* pfrom) } else { PrintExceptionContinue(&e, "ProcessMessages()"); } - } catch (boost::thread_interrupted) { + } catch (const boost::thread_interrupted&) { throw; - } catch (std::exception& e) { + } catch (const std::exception& e) { PrintExceptionContinue(&e, "ProcessMessages()"); } catch (...) { PrintExceptionContinue(NULL, "ProcessMessages()"); @@ -7127,7 +7169,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); for (CBlockIndex *pindex : vToDownload) { - if (State(pto->GetId())->fHaveWitness || GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) > pindex->pprev->nTime) { + if (State(pto->GetId())->fHaveWitness || sporkManager.GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) > pindex->pprev->nTime) { vGetData.push_back(CInv(State(staller)->fHaveWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK, pindex->GetBlockHash())); MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex); LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), @@ -7204,7 +7246,7 @@ bool CBlockUndo::ReadFromDisk(const CDiskBlockPos& pos, const uint256& hashBlock try { filein >> *this; filein >> hashChecksum; - } catch (std::exception& e) { + } catch (const std::exception& e) { return error("%s : Deserialize or I/O error - %s", __func__, e.what()); } diff --git a/src/main.h b/src/main.h index e08c65d98cc25..037e5fc7f2da8 100644 --- a/src/main.h +++ b/src/main.h @@ -521,7 +521,8 @@ bool FindTransactionsByDestination(const CTxDestination &dest, std::set> hashIn; - } catch (std::exception& e) { + } catch (const std::exception& e) { error("%s : Deserialize or I/O error - %s", __func__, e.what()); return HashReadError; } @@ -383,7 +383,7 @@ CBudgetDB::ReadResult CBudgetDB::Read(CBudgetManager& objToLoad, bool fDryRun) // de-serialize data into CBudgetManager object ssObj >> objToLoad; - } catch (std::exception& e) { + } catch (const std::exception& e) { objToLoad.Clear(); error("%s : Deserialize or I/O error - %s", __func__, e.what()); return IncorrectFormat; diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index c9a27f38046f1..13f89640ddec0 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -55,7 +55,7 @@ bool CMasternodePaymentDB::Write(const CMasternodePayments& objToSave) // Write and commit header, data try { fileout << ssObj; - } catch (std::exception& e) { + } catch (const std::exception& e) { return error("%s : Serialize or I/O error - %s", __func__, e.what()); } fileout.fclose(); @@ -90,7 +90,7 @@ CMasternodePaymentDB::ReadResult CMasternodePaymentDB::Read(CMasternodePayments& try { filein.read((char*)&vchData[0], dataSize); filein >> hashIn; - } catch (std::exception& e) { + } catch (const std::exception& e) { error("%s : Deserialize or I/O error - %s", __func__, e.what()); return HashReadError; } @@ -129,7 +129,7 @@ CMasternodePaymentDB::ReadResult CMasternodePaymentDB::Read(CMasternodePayments& // de-serialize data into CMasternodePayments object ssObj >> objToLoad; - } catch (std::exception& e) { + } catch (const std::exception& e) { objToLoad.Clear(); error("%s : Deserialize or I/O error - %s", __func__, e.what()); return IncorrectFormat; @@ -206,7 +206,7 @@ bool IsBlockValueValid(const CBlock& block, CAmount nExpectedValue, CAmount nMin } else { // we're synced and have data so check the budget schedule //are these blocks even enabled - if (!IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS)) { + if (!sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS)) { return nMinted <= nExpectedValue; } @@ -235,7 +235,7 @@ bool IsBlockPayeeValid(const CBlock& block, int nBlockHeight) const CTransaction& txNew = (nBlockHeight > Params().LAST_POW_BLOCK() ? block.vtx[1] : block.vtx[0]); //check if it's a budget block - if (IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS)) { + if (sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS)) { if (budget.IsBudgetPaymentBlock(nBlockHeight)) { transactionStatus = budget.IsTransactionValid(txNew, nBlockHeight); if (transactionStatus == TrxValidationStatus::Valid) { @@ -244,7 +244,7 @@ bool IsBlockPayeeValid(const CBlock& block, int nBlockHeight) if (transactionStatus == TrxValidationStatus::Invalid) { LogPrint("masternode","Invalid budget payment detected %s\n", txNew.ToString().c_str()); - if (IsSporkActive(SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT)) + if (sporkManager.IsSporkActive(SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT)) return false; LogPrint("masternode","Budget enforcement is disabled, accepting block\n"); @@ -262,7 +262,7 @@ bool IsBlockPayeeValid(const CBlock& block, int nBlockHeight) return true; LogPrint("masternode","Invalid mn payment detected %s\n", txNew.ToString().c_str()); - if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) + if (sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) return false; LogPrint("masternode","Masternode payment enforcement is disabled, accepting block\n"); @@ -275,7 +275,7 @@ void FillBlockPayee(CMutableTransaction& txNew, CAmount nFees, bool fProofOfStak CBlockIndex* pindexPrev = chainActive.Tip(); if (!pindexPrev) return; - if (IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS) && budget.IsBudgetPaymentBlock(pindexPrev->nHeight + 1)) { + if (sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS) && budget.IsBudgetPaymentBlock(pindexPrev->nHeight + 1)) { budget.FillBlockPayee(txNew, nFees, fProofOfStake); } else { masternodePayments.FillBlockPayee(txNew, nFees, fProofOfStake); @@ -284,7 +284,7 @@ void FillBlockPayee(CMutableTransaction& txNew, CAmount nFees, bool fProofOfStak std::string GetRequiredPaymentsString(int nBlockHeight) { - if (IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS) && budget.IsBudgetPaymentBlock(nBlockHeight)) { + if (sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS) && budget.IsBudgetPaymentBlock(nBlockHeight)) { return budget.GetRequiredPaymentsString(nBlockHeight); } else { return masternodePayments.GetRequiredPaymentsString(nBlockHeight); @@ -360,7 +360,10 @@ void CMasternodePayments::FillBlockPayee(CMutableTransaction& txNew, int64_t nFe int CMasternodePayments::GetMinMasternodePaymentsProto() { - return ActiveProtocol(); + if (sporkManager.IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)) + return ActiveProtocol(); // Allow only updated peers + else + return MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT; // Also allow old peers as long as they are allowed to run } void CMasternodePayments::ProcessMessageMasternodePayments(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) @@ -539,7 +542,7 @@ bool CMasternodeBlockPayees::IsTransactionValid(const CTransaction& txNew) CAmount nReward = GetBlockValue(nBlockHeight); - if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { + if (sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { // Get a stable number of masternodes by ignoring newly activated (< 8000 sec old) masternodes nMasternode_Drift_Count = mnodeman.stable_size() + Params().MasternodeCountDrift(); } @@ -564,10 +567,11 @@ bool CMasternodeBlockPayees::IsTransactionValid(const CTransaction& txNew) bool found = false; for (CTxOut out : txNew.vout) { if (payee.scriptPubKey == out.scriptPubKey) { - if(out.nValue >= requiredMasternodePayment) + if(out.nValue == requiredMasternodePayment) found = true; else - LogPrint("masternode","Masternode payment is out of drift range. Paid=%s Min=%s\n", FormatMoney(out.nValue).c_str(), FormatMoney(requiredMasternodePayment).c_str()); + LogPrintf("%s : Masternode payment value (%s) different from required value (%s).\n", + __func__, FormatMoney(out.nValue).c_str(), FormatMoney(requiredMasternodePayment).c_str()); } } diff --git a/src/masternode-sync.cpp b/src/masternode-sync.cpp index ee46c99f4bcee..bbf66909160d9 100644 --- a/src/masternode-sync.cpp +++ b/src/masternode-sync.cpp @@ -31,28 +31,29 @@ bool CMasternodeSync::IsSynced() bool CMasternodeSync::IsBlockchainSynced() { - static bool fBlockchainSynced = false; - static int64_t lastProcess = GetTime(); + int64_t now = GetTime(); // if the last call to this function was more than 60 minutes ago (client was in sleep mode) reset the sync process - if (GetTime() - lastProcess > 60 * 60) { + if (now > lastProcess + 60 * 60) { Reset(); fBlockchainSynced = false; } - lastProcess = GetTime(); + lastProcess = now; if (fBlockchainSynced) return true; if (fImporting || fReindex) return false; - TRY_LOCK(cs_main, lockMain); - if (!lockMain) return false; - - CBlockIndex* pindex = chainActive.Tip(); - if (pindex == NULL) return false; - + int64_t blockTime = 0; + { + TRY_LOCK(cs_main, lockMain); + if (!lockMain) return false; + CBlockIndex *pindex = chainActive.Tip(); + if (pindex == nullptr) return false; + blockTime = pindex->nTime; + } - if (pindex->nTime + 60 * 60 < GetTime()) + if (blockTime + 60 * 60 < lastProcess) return false; fBlockchainSynced = true; @@ -62,6 +63,8 @@ bool CMasternodeSync::IsBlockchainSynced() void CMasternodeSync::Reset() { + fBlockchainSynced = false; + lastProcess = 0; lastMasternodeList = 0; lastMasternodeWinner = 0; lastBudgetItem = 0; @@ -309,8 +312,8 @@ void CMasternodeSync::Process() // timeout if (lastMasternodeList == 0 && (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3 || GetTime() - nAssetSyncStarted > MASTERNODE_SYNC_TIMEOUT * 5)) { - if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { - LogPrintf("CMasternodeSync::Process - ERROR - Sync has failed, will retry later\n"); + if (sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { + LogPrintf("CMasternodeSync::Process - ERROR - Sync has failed on %s, will retry later\n", "MASTERNODE_SYNC_LIST"); RequestedMasternodeAssets = MASTERNODE_SYNC_FAILED; RequestedMasternodeAttempt = 0; lastFailure = GetTime(); @@ -340,8 +343,8 @@ void CMasternodeSync::Process() // timeout if (lastMasternodeWinner == 0 && (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3 || GetTime() - nAssetSyncStarted > MASTERNODE_SYNC_TIMEOUT * 5)) { - if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { - LogPrintf("CMasternodeSync::Process - ERROR - Sync has failed, will retry later\n"); + if (sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { + LogPrintf("CMasternodeSync::Process - ERROR - Sync has failed on %s, will retry later\n", "MASTERNODE_SYNC_MNW"); RequestedMasternodeAssets = MASTERNODE_SYNC_FAILED; RequestedMasternodeAttempt = 0; lastFailure = GetTime(); @@ -354,9 +357,6 @@ void CMasternodeSync::Process() if (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3) return; - CBlockIndex* pindexPrev = chainActive.Tip(); - if (pindexPrev == NULL) return; - int nMnCount = mnodeman.CountEnabled(); pnode->PushMessage(NetMsgType::MNGET, nMnCount); //sync payees RequestedMasternodeAttempt++; diff --git a/src/masternode-sync.h b/src/masternode-sync.h index f81f207fe4897..ea7f2e7c1719d 100644 --- a/src/masternode-sync.h +++ b/src/masternode-sync.h @@ -39,6 +39,9 @@ class CMasternodeSync int64_t lastFailure; int nCountFailures; + std::atomic lastProcess; + std::atomic fBlockchainSynced; + // sum of all counts int sumMasternodeList; int sumMasternodeWinner; diff --git a/src/masternode.cpp b/src/masternode.cpp index a9262d42e586c..abf0a3d41b251 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -684,8 +684,16 @@ bool CMasternodeBroadcast::VerifySignature() { std::string errorMessage; - if(!obfuScationSigner.VerifyMessage(pubKeyCollateralAddress, sig, GetNewStrMessage(), errorMessage) - && !obfuScationSigner.VerifyMessage(pubKeyCollateralAddress, sig, GetOldStrMessage(), errorMessage)) + std::string strMessage; + if(protocolVersion < 70008) { + std::string vchPubKey(pubKeyCollateralAddress.begin(), pubKeyCollateralAddress.end()); + std::string vchPubKey2(pubKeyMasternode.begin(), pubKeyMasternode.end()); + strMessage = addr.ToString() + std::to_string(sigTime) + vchPubKey + vchPubKey2 + std::to_string(protocolVersion); + } else { + strMessage = addr.ToString() + std::to_string(sigTime) + pubKeyCollateralAddress.GetID().ToString() + pubKeyMasternode.GetID().ToString() + std::to_string(protocolVersion); + } + + if(!obfuScationSigner.VerifyMessage(pubKeyCollateralAddress, sig, strMessage, errorMessage)) return error("CMasternodeBroadcast::VerifySignature() - Error: %s", errorMessage); return true; diff --git a/src/masternodeman.cpp b/src/masternodeman.cpp index 25fbd81cc0f09..14c0308a000a1 100644 --- a/src/masternodeman.cpp +++ b/src/masternodeman.cpp @@ -73,7 +73,7 @@ bool CMasternodeDB::Write(const CMasternodeMan& mnodemanToSave) // Write and commit header, data try { fileout << ssMasternodes; - } catch (std::exception& e) { + } catch (const std::exception& e) { return error("%s : Serialize or I/O error - %s", __func__, e.what()); } // FileCommit(fileout); @@ -110,7 +110,7 @@ CMasternodeDB::ReadResult CMasternodeDB::Read(CMasternodeMan& mnodemanToLoad, bo try { filein.read((char*)&vchData[0], dataSize); filein >> hashIn; - } catch (std::exception& e) { + } catch (const std::exception& e) { error("%s : Deserialize or I/O error - %s", __func__, e.what()); return HashReadError; } @@ -148,7 +148,7 @@ CMasternodeDB::ReadResult CMasternodeDB::Read(CMasternodeMan& mnodemanToLoad, bo } // de-serialize data into CMasternodeMan object ssMasternodes >> mnodemanToLoad; - } catch (std::exception& e) { + } catch (const std::exception& e) { mnodemanToLoad.Clear(); error("%s : Deserialize or I/O error - %s", __func__, e.what()); return IncorrectFormat; @@ -359,7 +359,7 @@ int CMasternodeMan::stable_size () if (mn.protocolVersion < nMinProtocol) { continue; // Skip obsolete versions } - if (IsSporkActive (SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { + if (sporkManager.IsSporkActive (SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { nMasternode_Age = GetAdjustedTime() - mn.sigTime; if ((nMasternode_Age) < nMasternode_Min_Age) { continue; // Skip masternodes younger than (default) 8000 sec (MUST be > MASTERNODE_REMOVAL_SECONDS) @@ -608,7 +608,7 @@ int CMasternodeMan::GetMasternodeRank(const CTxIn& vin, int64_t nBlockHeight, in continue; // Skip obsolete versions } - if (IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { + if (sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { nMasternode_Age = GetAdjustedTime() - mn.sigTime; if ((nMasternode_Age) < nMasternode_Min_Age) { if (fDebug) LogPrint("masternode","Skipping just activated Masternode. Age: %ld\n", nMasternode_Age); @@ -857,7 +857,7 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData // Light version for OLD MASSTERNODES - fake pings, no self-activation else if (strCommand == NetMsgType::DSEE) { //ObfuScation Election Entry - if (IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)) return; + if (sporkManager.IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)) return; CTxIn vin; CService addr; @@ -1064,7 +1064,7 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData else if (strCommand == NetMsgType::DSEEP) { //ObfuScation Election Entry Ping - if (IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)) return; + if (sporkManager.IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)) return; CTxIn vin; std::vector vchSig; diff --git a/src/miner.cpp b/src/miner.cpp index 97fe0494bf283..f94df28ac824a 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -11,6 +11,7 @@ #include "amount.h" #include "consensus/validation.h" +#include "consensus/merkle.h" #include "hash.h" #include "main.h" #include "masternode-sync.h" @@ -134,7 +135,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, // -promiscuousmempoolflags is used. // TODO: replace this with a call to main to assess validity of a mempool // transaction (which in most cases can be a no-op). - bool fIncludeWitness = IsSporkActive(SPORK_17_SEGWIT_ACTIVATION); + bool fIncludeWitness = sporkManager.IsSporkActive(SPORK_17_SEGWIT_ACTIVATION); @@ -238,7 +239,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, if (tx.IsCoinBase() || tx.IsCoinStake() || !IsFinalTx(tx, nHeight)){ continue; } - if(GetAdjustedTime() > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE) && tx.ContainsZerocoins()){ + if(sporkManager.IsSporkActive(SPORK_16_ZEROCOIN_MAINTENANCE_MODE) && tx.ContainsZerocoins()){ continue; } @@ -529,7 +530,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(pblock->vtx[0]); if (fProofOfStake) { - if (! IsSporkActive(SPORK_19_SEGWIT_ON_COINBASE)) { + if (!sporkManager.IsSporkActive(SPORK_19_SEGWIT_ON_COINBASE)) { bool fHaveWitness = false; for (size_t t = 1; t < pblock->vtx.size(); t++) { if (!pblock->vtx[t].wit.IsNull()) { @@ -572,7 +573,7 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned assert(txCoinbase.vin[0].scriptSig.size() <= 100); pblock->vtx[0] = txCoinbase; - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); } #ifdef ENABLE_WALLET diff --git a/src/net.cpp b/src/net.cpp index ea09fdd59b619..275bcb5412898 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -236,8 +236,9 @@ void AdvertizeLocal(CNode* pnode) addrLocal.SetIP(pnode->addrLocal); } if (addrLocal.IsRoutable()) { - LogPrintf("AdvertizeLocal: advertizing address %s\n", addrLocal.ToString()); - pnode->PushAddress(addrLocal); + LogPrintf("%s: advertising address %s\n", __func__, addrLocal.ToString()); + FastRandomContext insecure_rand; + pnode->PushAddress(addrLocal, insecure_rand); } } } @@ -1186,7 +1187,7 @@ void ThreadMapPort() MilliSleep(20 * 60 * 1000); // Refresh every 20 minutes } - } catch (boost::thread_interrupted) { + } catch (const boost::thread_interrupted&) { r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); LogPrintf("UPNP_DeletePortMapping() returned : %d\n", r); freeUPNPDevlist(devlist); @@ -1979,7 +1980,7 @@ bool CAddrDB::Write(const CAddrMan& addr) // Write and commit header, data try { fileout << ssPeers; - } catch (std::exception& e) { + } catch (const std::exception& e) { return error("%s : Serialize or I/O error - %s", __func__, e.what()); } FileCommit(fileout.Get()); @@ -2010,7 +2011,7 @@ bool CAddrDB::Read(CAddrMan& addr) try { filein.read((char*)&vchData[0], dataSize); filein >> hashIn; - } catch (std::exception& e) { + } catch (const std::exception& e) { return error("%s : Deserialize or I/O error - %s", __func__, e.what()); } filein.fclose(); @@ -2033,7 +2034,7 @@ bool CAddrDB::Read(CAddrMan& addr) // de-serialize address data into one CAddrMan object ssPeers >> addr; - } catch (std::exception& e) { + } catch (const std::exception& e) { return error("%s : Deserialize or I/O error - %s", __func__, e.what()); } diff --git a/src/net.h b/src/net.h index 1190ea9fa3fef..75d57560e65e6 100644 --- a/src/net.h +++ b/src/net.h @@ -442,14 +442,14 @@ class CNode setAddrKnown.insert(addr); } - void PushAddress(const CAddress& addr) + void PushAddress(const CAddress& _addr, FastRandomContext &insecure_rand) { // Known checking here is only to save space from duplicates. // SendMessages will filter it again for knowns that were added // after addresses were pushed. if (addr.IsValid() && !setAddrKnown.count(addr)) { if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) { - vAddrToSend[insecure_rand() % vAddrToSend.size()] = addr; + vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr; } else { vAddrToSend.push_back(addr); } diff --git a/src/netbase.cpp b/src/netbase.cpp index c1ef243a666bb..82269558f584c 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -16,6 +16,8 @@ #include "util.h" #include "utilstrencodings.h" +#include + #ifdef HAVE_GETADDRINFO_A #include #endif @@ -537,8 +539,8 @@ static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDe // do socks negotiation if (proxy.randomize_credentials) { ProxyCredentials random_auth; - random_auth.username = strprintf("%i", insecure_rand()); - random_auth.password = strprintf("%i", insecure_rand()); + static std::atomic_int counter; + random_auth.username = random_auth.password = strprintf("%i", counter++); if (!Socks5(strDest, (unsigned short)port, &random_auth, hSocket)) return false; } else { diff --git a/src/obfuscation.cpp b/src/obfuscation.cpp index d326e251580da..ecadfdd205431 100644 --- a/src/obfuscation.cpp +++ b/src/obfuscation.cpp @@ -667,7 +667,7 @@ void ThreadCheckObfuScationPool() // Make this thread recognisable as the wallet flushing thread RenameThread("phore-obfuscation"); - + LogPrintf("Masternodes thread started\n"); unsigned int c = 0; while (true) { diff --git a/src/phore-cli.cpp b/src/phore-cli.cpp index c31fa8ac608cb..5777af77f48f1 100644 --- a/src/phore-cli.cpp +++ b/src/phore-cli.cpp @@ -91,7 +91,7 @@ static bool AppInitRPC(int argc, char* argv[]) } try { ReadConfigFile(mapArgs, mapMultiArgs); - } catch (std::exception& e) { + } catch (const std::exception& e) { fprintf(stderr, "Error reading configuration file: %s\n", e.what()); return false; } @@ -274,9 +274,9 @@ int CommandLineRPC(int argc, char* argv[]) throw; } } while (fWait); - } catch (boost::thread_interrupted) { + } catch (const boost::thread_interrupted&) { throw; - } catch (std::exception& e) { + } catch (const std::exception& e) { strPrint = std::string("error: ") + e.what(); nRet = EXIT_FAILURE; } catch (...) { @@ -301,7 +301,7 @@ int main(int argc, char* argv[]) try { if (!AppInitRPC(argc, argv)) return EXIT_FAILURE; - } catch (std::exception& e) { + } catch (const std::exception& e) { PrintExceptionContinue(&e, "AppInitRPC()"); return EXIT_FAILURE; } catch (...) { @@ -312,7 +312,7 @@ int main(int argc, char* argv[]) int ret = EXIT_FAILURE; try { ret = CommandLineRPC(argc, argv); - } catch (std::exception& e) { + } catch (const std::exception& e) { PrintExceptionContinue(&e, "CommandLineRPC()"); } catch (...) { PrintExceptionContinue(NULL, "CommandLineRPC()"); diff --git a/src/phore-tx.cpp b/src/phore-tx.cpp index e795a42613d64..a9da47d69e61a 100644 --- a/src/phore-tx.cpp +++ b/src/phore-tx.cpp @@ -613,9 +613,9 @@ static int CommandLineRawTx(int argc, char* argv[]) OutputTx(tx); } - catch (boost::thread_interrupted) { + catch (const boost::thread_interrupted&) { throw; - } catch (std::exception& e) { + } catch (const std::exception& e) { strPrint = std::string("error: ") + e.what(); nRet = EXIT_FAILURE; } catch (...) { @@ -636,7 +636,7 @@ int main(int argc, char* argv[]) try { if (!AppInitRawTx(argc, argv)) return EXIT_FAILURE; - } catch (std::exception& e) { + } catch (const std::exception& e) { PrintExceptionContinue(&e, "AppInitRawTx()"); return EXIT_FAILURE; } catch (...) { @@ -647,7 +647,7 @@ int main(int argc, char* argv[]) int ret = EXIT_FAILURE; try { ret = CommandLineRawTx(argc, argv); - } catch (std::exception& e) { + } catch (const std::exception& e) { PrintExceptionContinue(&e, "CommandLineRawTx()"); } catch (...) { PrintExceptionContinue(NULL, "CommandLineRawTx()"); diff --git a/src/phored.cpp b/src/phored.cpp index 25c4fa112481b..4bc0cc0ed006d 100644 --- a/src/phored.cpp +++ b/src/phored.cpp @@ -90,7 +90,7 @@ bool AppInit(int argc, char* argv[]) } try { ReadConfigFile(mapArgs, mapMultiArgs); - } catch (std::exception& e) { + } catch (const std::exception& e) { fprintf(stderr, "Error reading configuration file: %s\n", e.what()); return false; } @@ -136,7 +136,7 @@ bool AppInit(int argc, char* argv[]) std::vector words; fRet = AppInit2(words); - } catch (std::exception& e) { + } catch (const std::exception& e) { PrintExceptionContinue(&e, "AppInit()"); } catch (...) { PrintExceptionContinue(NULL, "AppInit()"); diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 8665ea3ca8496..e9b64a803c542 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -22,100 +22,6 @@ uint256 CBlockHeader::GetHash() const return Hash(BEGIN(nVersion), END(nAccumulatorCheckpoint)); } -uint256 CBlock::BuildMerkleTree(bool* fMutated) const -{ - /* WARNING! If you're reading this because you're learning about crypto - and/or designing a new system that will use merkle trees, keep in mind - that the following merkle tree algorithm has a serious flaw related to - duplicate txids, resulting in a vulnerability (CVE-2012-2459). - - The reason is that if the number of hashes in the list at a given time - is odd, the last one is duplicated before computing the next level (which - is unusual in Merkle trees). This results in certain sequences of - transactions leading to the same merkle root. For example, these two - trees: - - A A - / \ / \ - B C B C - / \ | / \ / \ - D E F D E F F - / \ / \ / \ / \ / \ / \ / \ - 1 2 3 4 5 6 1 2 3 4 5 6 5 6 - - for transaction lists [1,2,3,4,5,6] and [1,2,3,4,5,6,5,6] (where 5 and - 6 are repeated) result in the same root hash A (because the hash of both - of (F) and (F,F) is C). - - The vulnerability results from being able to send a block with such a - transaction list, with the same merkle root, and the same block hash as - the original without duplication, resulting in failed validation. If the - receiving node proceeds to mark that block as permanently invalid - however, it will fail to accept further unmodified (and thus potentially - valid) versions of the same block. We defend against this by detecting - the case where we would hash two identical hashes at the end of the list - together, and treating that identically to the block having an invalid - merkle root. Assuming no double-SHA256 collisions, this will detect all - known ways of changing the transactions without affecting the merkle - root. - */ - vMerkleTree.clear(); - vMerkleTree.reserve(vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes. - for (std::vector::const_iterator it(vtx.begin()); it != vtx.end(); ++it) - vMerkleTree.push_back(it->GetHash()); - int j = 0; - bool mutated = false; - for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) - { - for (int i = 0; i < nSize; i += 2) - { - int i2 = std::min(i+1, nSize-1); - if (i2 == i + 1 && i2 + 1 == nSize && vMerkleTree[j+i] == vMerkleTree[j+i2]) { - // Two identical hashes at the end of the list at a particular level. - mutated = true; - } - vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), - BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); - } - j += nSize; - } - if (fMutated) { - *fMutated = mutated; - } - return (vMerkleTree.empty() ? uint256() : vMerkleTree.back()); -} - -std::vector CBlock::GetMerkleBranch(int nIndex) const -{ - if (vMerkleTree.empty()) - BuildMerkleTree(); - std::vector vMerkleBranch; - int j = 0; - for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) - { - int i = std::min(nIndex^1, nSize-1); - vMerkleBranch.push_back(vMerkleTree[j+i]); - nIndex >>= 1; - j += nSize; - } - return vMerkleBranch; -} - -uint256 CBlock::CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) -{ - if (nIndex == -1) - return uint256(); - for (std::vector::const_iterator it(vMerkleBranch.begin()); it != vMerkleBranch.end(); ++it) - { - if (nIndex & 1) - hash = Hash(BEGIN(*it), END(*it), BEGIN(hash), END(hash)); - else - hash = Hash(BEGIN(hash), END(hash), BEGIN(*it), END(*it)); - nIndex >>= 1; - } - return hash; -} - std::string CBlock::ToString() const { std::stringstream s; @@ -130,10 +36,6 @@ std::string CBlock::ToString() const { s << " " << vtx[i].ToString() << "\n"; } - s << " vMerkleTree: "; - for (unsigned int i = 0; i < vMerkleTree.size(); i++) - s << " " << vMerkleTree[i].ToString(); - s << "\n"; return s.str(); } diff --git a/src/primitives/block.h b/src/primitives/block.h index 2d29fa45f36a4..e3cc6c71fea6c 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -94,7 +94,7 @@ class CBlock : public CBlockHeader // memory only mutable CScript payee; - mutable std::vector vMerkleTree; + mutable bool fChecked; CBlock() { @@ -121,7 +121,7 @@ class CBlock : public CBlockHeader { CBlockHeader::SetNull(); vtx.clear(); - vMerkleTree.clear(); + fChecked = false; payee = CScript(); vchBlockSig.clear(); } diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 71979b725f00d..fd2117bedd084 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -8,6 +8,10 @@ set(CMAKE_AUTOUIC ON) if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(CMAKE_POSITION_INDEPENDENT_CODE ON) + execute_process( + COMMAND sed -i /HAVE_DECL_BSWAP/d phore-config.h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src/config + ) endif () find_package(Qrcode) @@ -16,28 +20,22 @@ if (QRCODE_FOUND) link_directories ( ${QRCODE_LIBRARY_DIRS} ) endif() -if($ENV{target} MATCHES "Windows") - find_package(Qt5) -else() - unset(Qt5Enables) - foreach(comp Widgets Core Gui Network LinguistTools DBus) - find_package(Qt5${comp}) - set(Qt5Enables "${Qt5Enables};Qt5${comp}_FOUND") - if(Qt5${comp}_FOUND) - MESSAGE(STATUS "FOUND QT5${comp}") - include_directories(${include_directories} ${Qt5${comp}_INCLUDE_DIRS} ${QT_USE_FILE}) - add_definitions(${Qt5${comp}_DEFINITIONS}) - list(APPEND Qt5_LIBRARIES ${Qt5${comp}_LIBRARIES}) - else() - MESSAGE(WARNING "Cant find Qt5${comp}") - endif() - endforeach(comp) - - find_package(Qt5DBus) - if (Qt5DBus_FOUND) - include_directories(${include_directories} "/usr/local/opt/qt5/include/QtDBus/") - add_compile_options("-DUSE_DBUS") +unset(Qt5Enables) +foreach(comp Widgets Core Gui Network LinguistTools DBus) + find_package(Qt5${comp}) + set(Qt5Enables "${Qt5Enables};Qt5${comp}_FOUND") + if(Qt5${comp}_FOUND) + MESSAGE(STATUS "FOUND QT5${comp}") + include_directories(${include_directories} ${Qt5${comp}_INCLUDE_DIRS} ${QT_USE_FILE}) + add_definitions(${Qt5${comp}_DEFINITIONS}) + list(APPEND Qt5_LIBRARIES ${Qt5${comp}_LIBRARIES}) + else() + MESSAGE(WARNING "Cant find Qt5${comp}") endif() +endforeach(comp) + +if (Qt5DBus_FOUND) + add_compile_options("-DUSE_DBUS") endif() # Why isn't this done automatically?? @@ -49,6 +47,10 @@ file(GLOB LOCAL_QT_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h) source_group("QtHeaders" FILES ${LOCAL_QT_HEADERS}) find_package(Protobuf REQUIRED) +if(Protobuf_FOUND) + message(STATUS "Found Protobuf compiler: ${Protobuf_PROTOC_EXECUTABLE}") + message(STATUS "Found Protobuf library: ${Protobuf_LIBRARIES}") +endif() include_directories(${PROTOBUF_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) @@ -146,12 +148,15 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") list(APPEND QT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/macnotificationhandler.mm) endif() -if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") - list(APPEND QT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/winshutdownmonitor.cpp) -endif () - add_library(qt_stuff STATIC ${BitcoinHeaders} ${QtHeaders} ${QT_SOURCES} ${PROTO_SRCS} ${PROTO_HDRS}) -target_include_directories(qt_stuff PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(qt_stuff PUBLIC ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/src/leveldb/include + ${CMAKE_SOURCE_DIR}/src/univalue/include + ${CMAKE_SOURCE_DIR}/src/secp256k1/include + ${CMAKE_CURRENT_SOURCE_DIR} + ${OPENSSL_INCLUDE_DIR} + ${BerkeleyDB_INCLUDE_DIRS} + ) set_property(TARGET qt_stuff PROPERTY CXX_STANDARD 11) file(GLOB QT_TRANSLATIONS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/locale/*.ts) @@ -162,38 +167,41 @@ set_source_files_properties(${QT_TRANSLATIONS_FILES} PROPERTIES OUTPUT_LOCATION QT5_ADD_TRANSLATION(QM ${QT_TRANSLATIONS_FILES}) add_custom_target(translations_target ALL DEPENDS ${QM}) -QT5_ADD_RESOURCES(QRC_RESOURCE pivx.qrc) -QT5_ADD_RESOURCES(QRC_LOCALE_RESOURCE pivx_locale.qrc) +QT5_ADD_RESOURCES(QRC_RESOURCE phore.qrc) +QT5_ADD_RESOURCES(QRC_LOCALE_RESOURCE phore_locale.qrc) -add_executable(pivx-qt pivx.cpp ${QM} ${QRC_RESOURCE} ${QRC_LOCALE_RESOURCE}) -add_dependencies(pivx-qt translations_target libunivalue libsecp256k1 leveldb leveldb_sse42 memenv) -target_link_libraries(pivx-qt +add_executable(phore-qt phore.cpp ${QM} ${QRC_RESOURCE} ${QRC_LOCALE_RESOURCE}) +add_dependencies(phore-qt translations_target libunivalue libsecp256k1 leveldb leveldb_sse42 memenv) +target_link_libraries(phore-qt qt_stuff univalue SERVER_A UTIL_A WALLET_A CLI_A COMMON_A BITCOIN_CRYPTO_A ZEROCOIN_A - leveldb leveldb_sse42 memenv miniupnpc secp256k1 + leveldb leveldb_sse42 memenv secp256k1 ${BerkeleyDB_LIBRARIES} ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} ${PROTOBUF_LIBRARIES} ${LIBEVENT_LIB} pthread ) if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - target_link_libraries(pivx-qt "-framework Cocoa") + target_link_libraries(phore-qt "-framework Cocoa") endif() if(GMP_FOUND) - target_link_libraries(pivx-qt ${GMP_LIBRARY}) + target_link_libraries(phore-qt ${GMP_LIBRARY}) + target_include_directories(phore-qt PUBLIC ${GMP_INCLUDE_DIR}) endif() if(ZMQ_FOUND) - target_link_libraries(pivx-qt ZMQ_A ${ZMQ_LIB}) + target_link_libraries(phore-qt ZMQ_A ${ZMQ_LIB}) + target_include_directories(phore-qt PUBLIC ${ZMQ_INCLUDE_DIR}) endif() if (QRCODE_FOUND) - target_link_libraries(pivx-qt ${QRCODE_LIB}) + target_link_libraries(phore-qt ${QRCODE_LIB}) + target_include_directories(phore-qt PUBLIC ${QRCODE_INCLUDE_DIR}) +endif() +if(MINIUPNP_FOUND) + target_compile_definitions(phore-qt PUBLIC "-DSTATICLIB -DMINIUPNP_STATICLIB") + target_link_libraries(phore-qt ${MINIUPNP_LIBRARY}) + target_include_directories(phore-qt PUBLIC ${MINIUPNP_INCLUDE_DIR}) endif() -QT5_USE_Modules(pivx-qt Gui) -QT5_USE_Modules(pivx-qt Core) -QT5_USE_Modules(pivx-qt Widgets) -QT5_USE_Modules(pivx-qt Test) -QT5_USE_Modules(pivx-qt PrintSupport) -QT5_USE_Modules(pivx-qt Network) +target_link_libraries(phore-qt Qt5::Gui Qt5::Core Qt5::Widgets Qt5::Network ${QT_LIBRARIES}) if (Qt5DBus_FOUND) - QT5_USE_Modules(pivx-qt DBus) + target_link_libraries(phore-qt Qt5::DBus ${QT_LIBRARIES}) endif() diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index cd01903e6274c..92e468c8ca048 100644 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -7,7 +7,7 @@ 0 0 769 - 516 + 496 @@ -27,13 +27,6 @@ 12 - - - - N/A - - - @@ -94,9 +87,9 @@ - + - Using OpenSSL version + Using BerkeleyDB version 10 @@ -104,7 +97,7 @@ - + IBeamCursor @@ -120,17 +113,14 @@ - + - Using BerkeleyDB version - - - 10 + Build date - + IBeamCursor @@ -146,14 +136,14 @@ - + - Build date + Startup time - + IBeamCursor @@ -169,29 +159,20 @@ - + - Startup time + Data Directory - - - IBeamCursor - + N/A - - Qt::PlainText - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - + @@ -204,14 +185,14 @@ - + Name - + IBeamCursor @@ -227,14 +208,14 @@ - + Number of connections - + IBeamCursor @@ -250,14 +231,14 @@ - + Number of Masternodes - + IBeamCursor @@ -273,7 +254,7 @@ - + @@ -286,14 +267,14 @@ - + Current number of blocks - + IBeamCursor @@ -309,14 +290,14 @@ - + Last block time - + IBeamCursor @@ -332,7 +313,21 @@ - + + + + Last block hash + + + + + + + N/A + + + + Qt::Vertical @@ -345,7 +340,7 @@ - + @@ -358,7 +353,7 @@ - + Open the Phore debug log file from the current data directory. This can take a few seconds for large log files. @@ -371,27 +366,6 @@ - - - - Data Directory - - - - - - - Last block hash - - - - - - - N/A - - - diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 9dbe6902b3bcc..236e69a38eded 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -94,7 +94,7 @@ void FreespaceChecker::check() replyMessage = tr("Path already exists, and is not a directory."); } } - } catch (fs::filesystem_error& e) { + } catch (const fs::filesystem_error& e) { /* Parent directory does not exist or is not accessible */ replyStatus = ST_ERROR; replyMessage = tr("Cannot create data directory here."); @@ -173,7 +173,7 @@ bool Intro::pickDataDirectory() try { TryCreateDirectory(GUIUtil::qstringToBoostPath(dataDir)); break; - } catch (fs::filesystem_error& e) { + } catch (const fs::filesystem_error& e) { QMessageBox::critical(0, tr("Phore Core"), tr("Error: Specified data directory \"%1\" cannot be created.").arg(dataDir)); /* fall through, back to choosing screen */ diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index f0d8bef368bd5..81392e687622b 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -165,9 +165,6 @@ void OptionsModel::Init() SoftSetArg("-zeromintpercentage", settings.value("nZeromintPercentage").toString().toStdString()); if (settings.contains("nPreferredDenom")) SoftSetArg("-preferredDenom", settings.value("nPreferredDenom").toString().toStdString()); - if (settings.contains("nAnonymizePhoreAmount")) - SoftSetArg("-anonymizephoreamount", settings.value("nAnonymizePhoreAmount").toString().toStdString()); - language = settings.value("language").toString(); } diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 55a9fb989020e..5892f07189216 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -48,7 +48,6 @@ class OptionsModel : public QAbstractListModel ZeromintPrefDenom, // int HideZeroBalances, // bool HideOrphans, // bool - AnonymizePhoreAmount, //int ShowMasternodesTab, // bool Listen, // bool StakeSplitThreshold, // int @@ -101,7 +100,6 @@ class OptionsModel : public QAbstractListModel void zeromintEnableChanged(bool); void zeromintPercentageChanged(int); void preferredDenomChanged(int); - void anonymizePhoreAmountChanged(int); void coinControlFeaturesChanged(bool); void hideZeroBalancesChanged(bool); void hideOrphansChanged(bool); diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index a41363d672a2f..657a2f20903e5 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -11,8 +11,6 @@ #include -#include - #include #include #include diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 9e05191178cf1..16f24556d45a2 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -19,8 +19,6 @@ #include -#include - #include #include #include diff --git a/src/qt/phore.cpp b/src/qt/phore.cpp index b1d232cfaea51..de48185bba941 100644 --- a/src/qt/phore.cpp +++ b/src/qt/phore.cpp @@ -172,7 +172,7 @@ public slots: bool execute_restart; /// Pass fatal exception message to UI thread - void handleRunawayException(std::exception* e); + void handleRunawayException(const std::exception* e); std::vector words; }; @@ -244,7 +244,7 @@ BitcoinCore::BitcoinCore(std::vector& wordlist) : QObject(), words( { } -void BitcoinCore::handleRunawayException(std::exception* e) +void BitcoinCore::handleRunawayException(const std::exception* e) { PrintExceptionContinue(e, "Runaway exception"); emit runawayException(QString::fromStdString(strMiscWarning)); @@ -258,7 +258,7 @@ void BitcoinCore::initialize() qDebug() << __func__ << ": Running AppInit2 in thread"; int rv = AppInit2(words); emit initializeResult(rv); - } catch (std::exception& e) { + } catch (const std::exception& e) { handleRunawayException(&e); } catch (...) { handleRunawayException(NULL); @@ -279,7 +279,7 @@ void BitcoinCore::restart(QStringList args) QProcess::startDetached(QApplication::applicationFilePath(), args); qDebug() << __func__ << ": Restart initiated..."; QApplication::quit(); - } catch (std::exception& e) { + } catch (const std::exception& e) { handleRunawayException(&e); } catch (...) { handleRunawayException(NULL); @@ -295,7 +295,7 @@ void BitcoinCore::shutdown() Shutdown(); qDebug() << __func__ << ": Shutdown finished"; emit shutdownResult(1); - } catch (std::exception& e) { + } catch (const std::exception& e) { handleRunawayException(&e); } catch (...) { handleRunawayException(NULL); @@ -590,7 +590,7 @@ int main(int argc, char* argv[]) } try { ReadConfigFile(mapArgs, mapMultiArgs); - } catch (std::exception& e) { + } catch (const std::exception& e) { QMessageBox::critical(0, QObject::tr("Phore Core"), QObject::tr("Error: Cannot parse configuration file: %1. Only use key=value syntax.").arg(e.what())); return 0; @@ -663,7 +663,7 @@ int main(int argc, char* argv[]) app.exec(); app.requestShutdown(); app.exec(); - } catch (std::exception& e) { + } catch (const std::exception& e) { PrintExceptionContinue(&e, "Runaway exception"); app.handleRunawayException(QString::fromStdString(strMiscWarning)); } catch (...) { diff --git a/src/qt/privacydialog.cpp b/src/qt/privacydialog.cpp index 1df70161ab53c..7d35285f86a09 100644 --- a/src/qt/privacydialog.cpp +++ b/src/qt/privacydialog.cpp @@ -162,7 +162,7 @@ void PrivacyDialog::on_pushButtonMintzPHR_clicked() if (!walletModel || !walletModel->getOptionsModel()) return; - if(GetAdjustedTime() > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE)) { + if(sporkManager.IsSporkActive(SPORK_16_ZEROCOIN_MAINTENANCE_MODE)) { QMessageBox::information(this, tr("Mint Zerocoin"), tr("zPHR is currently undergoing maintenance."), QMessageBox::Ok, QMessageBox::Ok); @@ -272,7 +272,7 @@ void PrivacyDialog::on_pushButtonSpendzPHR_clicked() if (!walletModel || !walletModel->getOptionsModel() || !pwalletMain) return; - if(GetAdjustedTime() > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE)) { + if(sporkManager.IsSporkActive(SPORK_16_ZEROCOIN_MAINTENANCE_MODE)) { QMessageBox::information(this, tr("Mint Zerocoin"), tr("zPHR is currently undergoing maintenance."), QMessageBox::Ok, QMessageBox::Ok); return; @@ -738,8 +738,9 @@ void PrivacyDialog::keyPressEvent(QKeyEvent* event) void PrivacyDialog::updateSPORK16Status() { // Update/enable labels, buttons and tooltips depending on the current SPORK_16 status - bool fButtonsEnabled = ui->pushButtonMintzPHR->isEnabled(); - bool fMaintenanceMode = GetAdjustedTime() > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE); + //bool fButtonsEnabled = ui->pushButtonMintzPIV->isEnabled(); + bool fButtonsEnabled = false; + bool fMaintenanceMode = sporkManager.IsSporkActive(SPORK_16_ZEROCOIN_MAINTENANCE_MODE); if (fMaintenanceMode && fButtonsEnabled) { // Mint zPHR ui->pushButtonMintzPHR->setEnabled(false); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index edfe0406a44b6..c9cc471e96cb6 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -27,8 +27,6 @@ #include "wallet/wallet.h" #endif // ENABLE_WALLET -#include - #include #ifdef ENABLE_WALLET @@ -245,17 +243,17 @@ void RPCExecutor::request(const QString& command) strPrint = result.write(2); emit reply(RPCConsole::CMD_REPLY, QString::fromStdString(strPrint)); - } catch (UniValue& objError) { + } catch (const UniValue& objError) { try // Nice formatting for standard-format error { int code = find_value(objError, "code").get_int(); std::string message = find_value(objError, "message").get_str(); emit reply(RPCConsole::CMD_ERROR, QString::fromStdString(message) + " (code " + QString::number(code) + ")"); - } catch (std::runtime_error&) // raised when converting to invalid type, i.e. missing code or message + } catch (const std::runtime_error&) // raised when converting to invalid type, i.e. missing code or message { // Show raw JSON object emit reply(RPCConsole::CMD_ERROR, QString::fromStdString(objError.write())); } - } catch (std::exception& e) { + } catch (const std::exception& e) { emit reply(RPCConsole::CMD_ERROR, QString("Error: ") + QString::fromStdString(e.what())); } } @@ -295,7 +293,6 @@ RPCConsole::RPCConsole(QWidget* parent) : QDialog(parent, Qt::WindowSystemMenuHi connect(ui->btn_convert_to_hd_Wallet, SIGNAL(clicked()), this, SLOT(walletUpgradeToHd())); // set library version labels - ui->openSSLVersion->setText(SSLeay_version(SSLEAY_VERSION)); #ifdef ENABLE_WALLET std::string strPathCustom = GetArg("-backuppath", ""); std::string strzphrPathCustom = GetArg("-zphrbackuppath", ""); diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index 1a7123700539c..6d5a703fe37b5 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -11,9 +11,6 @@ #include "util.h" #include "utilstrencodings.h" -#include -#include - #include #include diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index d4b76d86075b1..208dfa22da9e8 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -33,14 +33,19 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) else return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime)); } else { - int signatures = wtx.GetTransactionLockSignatures(); + const int signatures = wtx.GetTransactionLockSignatures(); QString strUsingIX = ""; + bool fConflicted; + const int nDepth = wtx.GetDepthAndMempool(fConflicted); + + if (nDepth < 0 || fConflicted) + return tr("conflicted"); + + const bool isOffline = (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0); + if (signatures >= 0) { if (signatures >= SWIFTTX_SIGNATURES_REQUIRED) { - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < 0) - return tr("conflicted"); - else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + if (isOffline) return tr("%1/offline (verified via SwiftX)").arg(nDepth); else if (nDepth < 6) return tr("%1/confirmed (verified via SwiftX)").arg(nDepth); @@ -48,20 +53,14 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) return tr("%1 confirmations (verified via SwiftX)").arg(nDepth); } else { if (!wtx.IsTransactionLockTimedOut()) { - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < 0) - return tr("conflicted"); - else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + if (isOffline) return tr("%1/offline (SwiftX verification in progress - %2 of %3 signatures)").arg(nDepth).arg(signatures).arg(SWIFTTX_SIGNATURES_TOTAL); else if (nDepth < 6) return tr("%1/confirmed (SwiftX verification in progress - %2 of %3 signatures )").arg(nDepth).arg(signatures).arg(SWIFTTX_SIGNATURES_TOTAL); else return tr("%1 confirmations (SwiftX verification in progress - %2 of %3 signatures)").arg(nDepth).arg(signatures).arg(SWIFTTX_SIGNATURES_TOTAL); } else { - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < 0) - return tr("conflicted"); - else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + if (isOffline) return tr("%1/offline (SwiftX verification failed)").arg(nDepth); else if (nDepth < 6) return tr("%1/confirmed (SwiftX verification failed)").arg(nDepth); @@ -70,10 +69,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) } } } else { - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < 0) - return tr("conflicted"); - else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + if (isOffline) return tr("%1/offline").arg(nDepth); else if (nDepth < 6) return tr("%1/unconfirmed").arg(nDepth); diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index 1ff098453b95b..767f35b576386 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -63,6 +63,8 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex& void TransactionFilterProxy::setDateRange(const QDateTime& from, const QDateTime& to) { + if (from == this->dateFrom && to == this->dateTo) + return; // No need to set the range. this->dateFrom = from; this->dateTo = to; invalidateFilter(); diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 46ac18e153c19..f21e6b9dd3229 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -309,8 +309,15 @@ void TransactionRecord::updateStatus(const CWalletTx& wtx) (wtx.IsCoinBase() ? 1 : 0), wtx.nTimeReceived, idx); - status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0); - status.depth = wtx.GetDepthInMainChain(); + //status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0); + bool fConflicted; + status.depth = wtx.GetDepthAndMempool(fConflicted); + const bool isOffline = (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0); + + //Determine the depth of the block + int nBlocksToMaturity = wtx.GetBlocksToMaturity(); + + status.countsForBalance = wtx.IsTrusted() && !(nBlocksToMaturity > 0); status.cur_num_blocks = chainActive.Height(); status.cur_num_ix_locks = nCompleteTXLocks; @@ -332,7 +339,7 @@ void TransactionRecord::updateStatus(const CWalletTx& wtx) status.matures_in = wtx.GetBlocksToMaturity(); // Check if the block was requested by anyone - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + if (isOffline) status.status = TransactionStatus::MaturesWarning; } else { status.status = TransactionStatus::NotAccepted; @@ -341,9 +348,9 @@ void TransactionRecord::updateStatus(const CWalletTx& wtx) status.status = TransactionStatus::Confirmed; } } else { - if (status.depth < 0) { + if (status.depth < 0 || fConflicted) { status.status = TransactionStatus::Conflicted; - } else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) { + } else if (isOffline) { status.status = TransactionStatus::Offline; } else if (status.depth == 0) { status.status = TransactionStatus::Unconfirmed; @@ -370,3 +377,29 @@ int TransactionRecord::getOutputIndex() const { return idx; } + + +std::string TransactionRecord::statusToString(){ + switch (status.status){ + case TransactionStatus::MaturesWarning: + return "Abandoned (not mature because no nodes have confirmed)"; + case TransactionStatus::Confirmed: + return "Confirmed"; + case TransactionStatus::OpenUntilDate: + return "OpenUntilDate"; + case TransactionStatus::OpenUntilBlock: + return "OpenUntilBlock"; + case TransactionStatus::Unconfirmed: + return "Unconfirmed"; + case TransactionStatus::Confirming: + return "Confirming"; + case TransactionStatus::Conflicted: + return "Conflicted"; + case TransactionStatus::Immature: + return "Immature"; + case TransactionStatus::NotAccepted: + return "Not Accepted"; + default: + return "No status"; + } +} diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index a26a2a3b01bb9..ff9c67e39dee3 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -145,6 +145,10 @@ class TransactionRecord */ void updateStatus(const CWalletTx& wtx); + /** Return transaction status + */ + std::string statusToString(); + /** Return whether a status update is needed. */ bool statusUpdateNeeded(); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 46e8f0c41df59..4cf932d94079b 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -325,8 +325,8 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact CReserveKey* keyChange = transaction.getPossibleKeyChange(); - if (recipients[0].useSwiftTX && total > GetSporkValue(SPORK_5_MAX_VALUE) * COIN) { - emit message(tr("Send Coins"), tr("SwiftX doesn't support sending values that high yet. Transactions are currently limited to %1 PHR.").arg(GetSporkValue(SPORK_5_MAX_VALUE)), + if (recipients[0].useSwiftTX && total > sporkManager.GetSporkValue(SPORK_5_MAX_VALUE) * COIN) { + emit message(tr("Send Coins"), tr("SwiftX doesn't support sending values that high yet. Transactions are currently limited to %1 PIV.").arg(sporkManager.GetSporkValue(SPORK_5_MAX_VALUE)), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } @@ -334,8 +334,8 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, strFailReason, coinControl, recipients[0].inputType, recipients[0].useSwiftTX); transaction.setTransactionFee(nFeeRequired); - if (recipients[0].useSwiftTX && newTx->GetValueOut() > GetSporkValue(SPORK_5_MAX_VALUE) * COIN) { - emit message(tr("Send Coins"), tr("SwiftX doesn't support sending values that high yet. Transactions are currently limited to %1 PHR.").arg(GetSporkValue(SPORK_5_MAX_VALUE)), + if (recipients[0].useSwiftTX && newTx->GetValueOut() > sporkManager.GetSporkValue(SPORK_5_MAX_VALUE) * COIN) { + emit message(tr("Send Coins"), tr("SwiftX doesn't support sending values that high yet. Transactions are currently limited to %1 PIV.").arg(sporkManager.GetSporkValue(SPORK_5_MAX_VALUE)), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } @@ -673,8 +673,9 @@ void WalletModel::getOutputs(const std::vector& vOutpoints, std::vect LOCK2(cs_main, wallet->cs_wallet); for (const COutPoint& outpoint : vOutpoints) { if (!wallet->mapWallet.count(outpoint.hash)) continue; - int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); - if (nDepth < 0) continue; + bool fConflicted; + const int nDepth = wallet->mapWallet[outpoint.hash].GetDepthAndMempool(fConflicted); + if (nDepth < 0 || fConflicted) continue; COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true); vOutputs.push_back(out); } @@ -699,8 +700,9 @@ void WalletModel::listCoins(std::map >& mapCoins) // add locked coins for (const COutPoint& outpoint : vLockedCoins) { if (!wallet->mapWallet.count(outpoint.hash)) continue; - int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); - if (nDepth < 0) continue; + bool fConflicted; + int nDepth = wallet->mapWallet[outpoint.hash].GetDepthAndMempool(fConflicted); + if (nDepth < 0 || fConflicted) continue; COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true); if (outpoint.n < out.tx->vout.size() && wallet->IsMine(out.tx->vout[outpoint.n]) == ISMINE_SPENDABLE) vCoins.push_back(out); diff --git a/src/random.cpp b/src/random.cpp index c2d1f5d6c487a..7796c69d34495 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -14,26 +14,118 @@ #include "util.h" // for LogPrint() #include "utilstrencodings.h" // for GetTime() +#include #include +#include +#include #ifndef WIN32 #include #endif +#ifdef HAVE_SYS_GETRANDOM +#include +#include +#endif +#if defined(HAVE_GETENTROPY) || (defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX)) +#include +#endif +#if defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX) +#include +#endif +#ifdef HAVE_SYSCTL_ARND +#include +#endif + +#include + +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +#include +#include +#endif + #include #include +[[noreturn]] static void RandFailure() +{ + LogPrintf("Failed to read randomness, aborting\n"); + std::abort(); +} + static inline int64_t GetPerformanceCounter() { - int64_t nCounter = 0; -#ifdef WIN32 - QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); + // Read the hardware time stamp counter when available. + // See https://en.wikipedia.org/wiki/Time_Stamp_Counter for more information. +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) + return __rdtsc(); +#elif !defined(_MSC_VER) && defined(__i386__) + uint64_t r = 0; + __asm__ volatile ("rdtsc" : "=A"(r)); // Constrain the r variable to the eax:edx pair. + return r; +#elif !defined(_MSC_VER) && (defined(__x86_64__) || defined(__amd64__)) + uint64_t r1 = 0, r2 = 0; + __asm__ volatile ("rdtsc" : "=a"(r1), "=d"(r2)); // Constrain r1 to rax and r2 to rdx. + return (r2 << 32) | r1; +#else + // Fall back to using C++11 clock (usually microsecond or nanosecond precision) + return std::chrono::high_resolution_clock::now().time_since_epoch().count(); +#endif +} + + +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +static std::atomic hwrand_initialized{false}; +static bool rdrand_supported = false; +static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; +static void RDRandInit() +{ + uint32_t eax, ebx, ecx, edx; + if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) { + LogPrintf("Using RdRand as an additional entropy source\n"); + rdrand_supported = true; + } + hwrand_initialized.store(true); +} #else - timeval t; - gettimeofday(&t, NULL); - nCounter = (int64_t)(t.tv_sec * 1000000 + t.tv_usec); +static void RDRandInit() {} +#endif + +static bool GetHWRand(unsigned char* ent32) { +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) + assert(hwrand_initialized.load(std::memory_order_relaxed)); + if (rdrand_supported) { + uint8_t ok; + // Not all assemblers support the rdrand instruction, write it in hex. +#ifdef __i386__ + for (int iter = 0; iter < 4; ++iter) { + uint32_t r1, r2; + __asm__ volatile (".byte 0x0f, 0xc7, 0xf0;" // rdrand %eax + ".byte 0x0f, 0xc7, 0xf2;" // rdrand %edx + "setc %2" : + "=a"(r1), "=d"(r2), "=q"(ok) :: "cc"); + if (!ok) return false; + WriteLE32(ent32 + 8 * iter, r1); + WriteLE32(ent32 + 8 * iter + 4, r2); + } +#else + uint64_t r1, r2, r3, r4; + __asm__ volatile (".byte 0x48, 0x0f, 0xc7, 0xf0, " // rdrand %rax + "0x48, 0x0f, 0xc7, 0xf3, " // rdrand %rbx + "0x48, 0x0f, 0xc7, 0xf1, " // rdrand %rcx + "0x48, 0x0f, 0xc7, 0xf2; " // rdrand %rdx + "setc %4" : + "=a"(r1), "=b"(r2), "=c"(r3), "=d"(r4), "=q"(ok) :: "cc"); + if (!ok) return false; + WriteLE64(ent32, r1); + WriteLE64(ent32 + 8, r2); + WriteLE64(ent32 + 16, r3); + WriteLE64(ent32 + 24, r4); +#endif + return true; + } #endif - return nCounter; + return false; } void RandAddSeed() @@ -84,35 +176,143 @@ static void RandAddSeedPerfmon() #endif } +#ifndef WIN32 +/** Fallback: get 32 bytes of system entropy from /dev/urandom. The most + * compatible way to get cryptographic randomness on UNIX-ish platforms. + */ +void GetDevURandom(unsigned char *ent32) +{ + int f = open("/dev/urandom", O_RDONLY); + if (f == -1) { + RandFailure(); + } + int have = 0; + do { + ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have); + if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) { + close(f); + RandFailure(); + } + have += n; + } while (have < NUM_OS_RANDOM_BYTES); + close(f); +} +#endif + /** Get 32 bytes of system entropy. */ -static void GetOSRand(unsigned char *ent32) +void GetOSRand(unsigned char *ent32) { -#ifdef WIN32 +#if defined(WIN32) HCRYPTPROV hProvider; int ret = CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); - assert(ret); - ret = CryptGenRandom(hProvider, 32, ent32); - assert(ret); + if (!ret) { + RandFailure(); + } + ret = CryptGenRandom(hProvider, NUM_OS_RANDOM_BYTES, ent32); + if (!ret) { + RandFailure(); + } CryptReleaseContext(hProvider, 0); -#else - int f = open("/dev/urandom", O_RDONLY); - assert(f != -1); +#elif defined(HAVE_SYS_GETRANDOM) + /* Linux. From the getrandom(2) man page: + * "If the urandom source has been initialized, reads of up to 256 bytes + * will always return as many bytes as requested and will not be + * interrupted by signals." + */ + int rv = syscall(SYS_getrandom, ent32, NUM_OS_RANDOM_BYTES, 0); + if (rv != NUM_OS_RANDOM_BYTES) { + if (rv < 0 && errno == ENOSYS) { + /* Fallback for kernel <3.17: the return value will be -1 and errno + * ENOSYS if the syscall is not available, in that case fall back + * to /dev/urandom. + */ + GetDevURandom(ent32); + } else { + RandFailure(); + } + } +#elif defined(HAVE_GETENTROPY) && defined(__OpenBSD__) + /* On OpenBSD this can return up to 256 bytes of entropy, will return an + * error if more are requested. + * The call cannot return less than the requested number of bytes. + getentropy is explicitly limited to openbsd here, as a similar (but not + the same) function may exist on other platforms via glibc. + */ + if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { + RandFailure(); + } +#elif defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX) + // We need a fallback for OSX < 10.12 + if (&getentropy != NULL) { + if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { + RandFailure(); + } + } else { + GetDevURandom(ent32); + } +#elif defined(HAVE_SYSCTL_ARND) + /* FreeBSD and similar. It is possible for the call to return less + * bytes than requested, so need to read in a loop. + */ + static const int name[2] = {CTL_KERN, KERN_ARND}; int have = 0; do { - ssize_t n = read(f, ent32 + have, 32 - have); - assert(n > 0 && n <= 32 - have); - have += n; - } while (have < 32); - close(f); + size_t len = NUM_OS_RANDOM_BYTES - have; + if (sysctl(name, ARRAYLEN(name), ent32 + have, &len, NULL, 0) != 0) { + RandFailure(); + } + have += len; + } while (have < NUM_OS_RANDOM_BYTES); +#else + /* Fall back to /dev/urandom if there is no specific method implemented to + * get system entropy for this OS. + */ + GetDevURandom(ent32); #endif } void GetRandBytes(unsigned char* buf, int num) { if (RAND_bytes(buf, num) != 1) { - LogPrintf("%s: OpenSSL RAND_bytes() failed with error: %s\n", __func__, ERR_error_string(ERR_get_error(), NULL)); - assert(false); + RandFailure(); + } +} + +static void AddDataToRng(void* data, size_t len); + +void RandAddSeedSleep() +{ + int64_t nPerfCounter1 = GetPerformanceCounter(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + int64_t nPerfCounter2 = GetPerformanceCounter(); + + // Combine with and update state + AddDataToRng(&nPerfCounter1, sizeof(nPerfCounter1)); + AddDataToRng(&nPerfCounter2, sizeof(nPerfCounter2)); + + memory_cleanse(&nPerfCounter1, sizeof(nPerfCounter1)); + memory_cleanse(&nPerfCounter2, sizeof(nPerfCounter2)); +} + + +static std::mutex cs_rng_state; +static unsigned char rng_state[32] = {0}; +static uint64_t rng_counter = 0; + +static void AddDataToRng(void* data, size_t len) { + CSHA512 hasher; + hasher.Write((const unsigned char*)&len, sizeof(len)); + hasher.Write((const unsigned char*)data, len); + unsigned char buf[64]; + { + std::unique_lock lock(cs_rng_state); + hasher.Write(rng_state, sizeof(rng_state)); + hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter)); + ++rng_counter; + hasher.Finalize(buf); + memcpy(rng_state, buf + 32, 32); } + memory_cleanse(buf, 64); } void GetStrongRandBytes(unsigned char* out, int num) @@ -130,10 +330,24 @@ void GetStrongRandBytes(unsigned char* out, int num) GetOSRand(buf); hasher.Write(buf, 32); + // Third source: HW RNG, if available. + if (GetHWRand(buf)) { + hasher.Write(buf, 32); + } + + // Combine with and update state + { + std::unique_lock lock(cs_rng_state); + hasher.Write(rng_state, sizeof(rng_state)); + hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter)); + ++rng_counter; + hasher.Finalize(buf); + memcpy(rng_state, buf + 32, 32); + } + // Produce output - hasher.Finalize(buf); memcpy(out, buf, num); - memory_cleanse((void*)&buf, sizeof(buf)); + memory_cleanse(buf, 64); } uint64_t GetRand(uint64_t nMax) @@ -163,22 +377,107 @@ uint256 GetRandHash() return hash; } -uint32_t insecure_rand_Rz = 11; -uint32_t insecure_rand_Rw = 11; -void seed_insecure_rand(bool fDeterministic) +void FastRandomContext::RandomSeed() { - // The seed values have some unlikely fixed points which we avoid. - if (fDeterministic) { - insecure_rand_Rz = insecure_rand_Rw = 11; - } else { - uint32_t tmp; - do { - GetRandBytes((unsigned char*)&tmp, 4); - } while (tmp == 0 || tmp == 0x9068ffffU); - insecure_rand_Rz = tmp; - do { - GetRandBytes((unsigned char*)&tmp, 4); - } while (tmp == 0 || tmp == 0x464fffffU); - insecure_rand_Rw = tmp; + uint256 seed = GetRandHash(); + rng.SetKey(seed.begin(), 32); + requires_seed = false; +} + +uint256 FastRandomContext::rand256() +{ + if (bytebuf_size < 32) { + FillByteBuffer(); + } + uint256 ret; + memcpy(ret.begin(), bytebuf + 64 - bytebuf_size, 32); + bytebuf_size -= 32; + return ret; +} + +std::vector FastRandomContext::randbytes(size_t len) +{ + if (requires_seed) RandomSeed(); + std::vector ret(len); + if (len > 0) { + rng.Output(&ret[0], len); } + return ret; +} + +FastRandomContext::FastRandomContext(const uint256& seed) : requires_seed(false), bytebuf_size(0), bitbuf_size(0) +{ + rng.SetKey(seed.begin(), 32); +} + +bool Random_SanityCheck() +{ + uint64_t start = GetPerformanceCounter(); + + /* This does not measure the quality of randomness, but it does test that + * OSRandom() overwrites all 32 bytes of the output given a maximum + * number of tries. + */ + static const ssize_t MAX_TRIES = 1024; + uint8_t data[NUM_OS_RANDOM_BYTES]; + bool overwritten[NUM_OS_RANDOM_BYTES] = {}; /* Tracks which bytes have been overwritten at least once */ + int num_overwritten; + int tries = 0; + /* Loop until all bytes have been overwritten at least once, or max number tries reached */ + do { + memset(data, 0, NUM_OS_RANDOM_BYTES); + GetOSRand(data); + for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) { + overwritten[x] |= (data[x] != 0); + } + + num_overwritten = 0; + for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) { + if (overwritten[x]) { + num_overwritten += 1; + } + } + + tries += 1; + } while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES); + if (num_overwritten != NUM_OS_RANDOM_BYTES) return false; /* If this failed, bailed out after too many tries */ + + // Check that GetPerformanceCounter increases at least during a GetOSRand() call + 1ms sleep. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + uint64_t stop = GetPerformanceCounter(); + if (stop == start) return false; + + // We called GetPerformanceCounter. Use it as entropy. + RAND_add((const unsigned char*)&start, sizeof(start), 1); + RAND_add((const unsigned char*)&stop, sizeof(stop), 1); + + return true; +} + +FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) +{ + if (!fDeterministic) { + return; + } + uint256 seed; + rng.SetKey(seed.begin(), 32); +} + +FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexcept +{ + requires_seed = from.requires_seed; + rng = from.rng; + std::copy(std::begin(from.bytebuf), std::end(from.bytebuf), std::begin(bytebuf)); + bytebuf_size = from.bytebuf_size; + bitbuf = from.bitbuf; + bitbuf_size = from.bitbuf_size; + from.requires_seed = true; + from.bytebuf_size = 0; + from.bitbuf_size = 0; + return *this; +} + +void RandomInit() +{ + RDRandInit(); } diff --git a/src/random.h b/src/random.h index fdddce2fdd4a1..b034bdd12ef1d 100644 --- a/src/random.h +++ b/src/random.h @@ -6,6 +6,8 @@ #ifndef BITCOIN_RANDOM_H #define BITCOIN_RANDOM_H +#include "crypto/chacha20.h" +#include "crypto/common.h" #include "uint256.h" #include @@ -21,6 +23,13 @@ uint64_t GetRand(uint64_t nMax); int GetRandInt(int nMax); uint256 GetRandHash(); +/** + * Add a little bit of randomness to the output of GetStrongRangBytes. + * This sleeps for a millisecond, so should only be called when there is + * no other work to be done. + */ +void RandAddSeedSleep(); + /** * Function to gather random data from multiple sources, failing whenever any * of those source fail to provide a result. @@ -28,25 +37,118 @@ uint256 GetRandHash(); void GetStrongRandBytes(unsigned char* buf, int num); /** - * Seed insecure_rand using the random pool. - * @param Deterministic Use a deterministic seed + * Fast randomness source. This is seeded once with secure random data, but + * is completely deterministic and insecure after that. + * This class is not thread-safe. */ -void seed_insecure_rand(bool fDeterministic = false); +class FastRandomContext { +private: + bool requires_seed; + ChaCha20 rng; -/** - * MWC RNG of George Marsaglia - * This is intended to be fast. It has a period of 2^59.3, though the - * least significant 16 bits only have a period of about 2^30.1. - * - * @return random value + unsigned char bytebuf[64]; + int bytebuf_size; + + uint64_t bitbuf; + int bitbuf_size; + + void RandomSeed(); + + void FillByteBuffer() + { + if (requires_seed) { + RandomSeed(); + } + rng.Output(bytebuf, sizeof(bytebuf)); + bytebuf_size = sizeof(bytebuf); + } + + void FillBitBuffer() + { + bitbuf = rand64(); + bitbuf_size = 64; + } + +public: + explicit FastRandomContext(bool fDeterministic = false); + + /** Initialize with explicit seed (only for testing) */ + explicit FastRandomContext(const uint256& seed); + + // Do not permit copying a FastRandomContext (move it, or create a new one to get reseeded). + FastRandomContext(const FastRandomContext&) = delete; + FastRandomContext(FastRandomContext&&) = delete; + FastRandomContext& operator=(const FastRandomContext&) = delete; + + /** Move a FastRandomContext. If the original one is used again, it will be reseeded. */ + FastRandomContext& operator=(FastRandomContext&& from) noexcept; + + /** Generate a random 64-bit integer. */ + uint64_t rand64() + { + if (bytebuf_size < 8) FillByteBuffer(); + uint64_t ret = ReadLE64(bytebuf + 64 - bytebuf_size); + bytebuf_size -= 8; + return ret; + } + + /** Generate a random (bits)-bit integer. */ + uint64_t randbits(int bits) { + if (bits == 0) { + return 0; + } else if (bits > 32) { + return rand64() >> (64 - bits); + } else { + if (bitbuf_size < bits) FillBitBuffer(); + uint64_t ret = bitbuf & (~(uint64_t)0 >> (64 - bits)); + bitbuf >>= bits; + bitbuf_size -= bits; + return ret; + } + } + + /** Generate a random integer in the range [0..range). */ + uint64_t randrange(uint64_t range) + { + --range; + int bits = CountBits(range); + while (true) { + uint64_t ret = randbits(bits); + if (ret <= range) return ret; + } + } + + /** Generate random bytes. */ + std::vector randbytes(size_t len); + + /** Generate a random 32-bit integer. */ + uint32_t rand32() { return randbits(32); } + + /** generate a random uint256. */ + uint256 rand256(); + + /** Generate a random boolean. */ + bool randbool() { return randbits(1); } +}; + +/* Number of random bytes returned by GetOSRand. + * When changing this constant make sure to change all call sites, and make + * sure that the underlying OS APIs for all platforms support the number. + * (many cap out at 256 bytes). + */ +static const ssize_t NUM_OS_RANDOM_BYTES = 32; + +/** Get 32 bytes of system entropy. Do not use this in application code: use + * GetStrongRandBytes instead. */ -extern uint32_t insecure_rand_Rz; -extern uint32_t insecure_rand_Rw; -static inline uint32_t insecure_rand(void) -{ - insecure_rand_Rz = 36969 * (insecure_rand_Rz & 65535) + (insecure_rand_Rz >> 16); - insecure_rand_Rw = 18000 * (insecure_rand_Rw & 65535) + (insecure_rand_Rw >> 16); - return (insecure_rand_Rw << 16) + insecure_rand_Rz; -} +void GetOSRand(unsigned char *ent32); + +/** Check that OS randomness is available and returning the requested number + * of bytes. + */ +bool Random_SanityCheck(); + +/** Initialize the RNG. */ +void RandomInit(); #endif // BITCOIN_RANDOM_H diff --git a/src/rpc/masternode-budget.cpp b/src/rpc/masternode-budget.cpp index 4899cbb0075f3..524df0de8ca45 100644 --- a/src/rpc/masternode-budget.cpp +++ b/src/rpc/masternode-budget.cpp @@ -50,7 +50,7 @@ void budgetToJSON(CBudgetProposal* pbudgetProposal, UniValue& bObj) } void checkBudgetInputs(const UniValue& params, std::string &strProposalName, std::string &strURL, - int &nPaymentCount, int &nBlockStart, CAmount &nAmount) + int &nPaymentCount, int &nBlockStart, CTxDestination &address, CAmount &nAmount) { int nBlockMin = 0; CBlockIndex* pindexPrev = chainActive.Tip(); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 2ee28893abba3..b32feebcba559 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -175,7 +175,7 @@ UniValue setgenerate(const UniValue& params, bool fHelp) CBlockTemplate* pblocktemplate; try { pblocktemplate = CreateNewBlock(scriptPubKey, pwalletMain, false); - } catch(std::runtime_error err) { + } catch(const std::runtime_error err) { throw JSONRPCError(RPC_MISC_ERROR, err.what()); } CBlock* pblock = &pblocktemplate->block; diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 2752c2f067f6e..07c451cac56ff 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -28,6 +28,8 @@ #include +extern std::vector sporkDefs; + /** * @note Do not add or change anything in the information returned by this @@ -311,22 +313,21 @@ UniValue spork(const UniValue& params, bool fHelp) { if (params.size() == 1 && params[0].get_str() == "show") { UniValue ret(UniValue::VOBJ); - for (int nSporkID = SPORK_START; nSporkID <= SPORK_END; nSporkID++) { - if (sporkManager.GetSporkNameByID(nSporkID) != "Unknown") - ret.push_back(Pair(sporkManager.GetSporkNameByID(nSporkID), GetSporkValue(nSporkID))); + for (const auto& sporkDef : sporkDefs) { + ret.push_back(Pair(sporkDef.name, sporkManager.GetSporkValue(sporkDef.sporkId))); } return ret; } else if (params.size() == 1 && params[0].get_str() == "active") { UniValue ret(UniValue::VOBJ); - for (int nSporkID = SPORK_START; nSporkID <= SPORK_END; nSporkID++) { - if (sporkManager.GetSporkNameByID(nSporkID) != "Unknown") - ret.push_back(Pair(sporkManager.GetSporkNameByID(nSporkID), IsSporkActive(nSporkID))); + for (const auto& sporkDef : sporkDefs) { + ret.push_back(Pair(sporkDef.name, sporkManager.IsSporkActive(sporkDef.sporkId))); } return ret; } else if (params.size() == 2) { - int nSporkID = sporkManager.GetSporkIDByName(params[0].get_str()); - if (nSporkID == -1) { - return "Invalid spork name"; + // advanced mode, update spork values + SporkId nSporkID = sporkManager.GetSporkIDByName(params[0].get_str()); + if (nSporkID == SPORK_INVALID) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid spork name"); } // SPORK VALUE diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 9decae1b4ea44..bacbacdc5b32b 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -18,7 +18,6 @@ #include "version.h" - #include UniValue getconnectioncount(const UniValue& params, bool fHelp) diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 60173ccbcc256..a6c4fecd66942 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -218,7 +218,7 @@ std::string CRPCTable::help(std::string strCommand) const rpcfn_type pfn = pcmd->actor; if (setDone.insert(pfn).second) (*pfn)(params, true); - } catch (std::exception& e) { + } catch (const std::exception& e) { // Help text is returned in an exception std::string strHelp = std::string(e.what()); if (strCommand == "") { @@ -410,6 +410,7 @@ static const CRPCCommand vRPCCommands[] = {"wallet", "getstakingstatus", &getstakingstatus, false, false, true}, {"wallet", "getstakesplitthreshold", &getstakesplitthreshold, false, false, true}, {"wallet", "gettransaction", &gettransaction, false, false, true}, + {"wallet", "abandontransaction", &abandontransaction, false, false, true}, {"wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, false, true}, {"wallet", "getwalletinfo", &getwalletinfo, false, false, true}, {"wallet", "importprivkey", &importprivkey, true, false, true}, @@ -571,7 +572,7 @@ static UniValue JSONRPCExecOne(const UniValue& req) rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); } catch (const UniValue& objError) { rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id); - } catch (std::exception& e) { + } catch (const std::exception& e) { rpc_result = JSONRPCReplyObj(NullUniValue, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); } @@ -607,7 +608,7 @@ UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms try { // Execute return pcmd->actor(params, false); - } catch (std::exception& e) { + } catch (const std::exception& e) { throw JSONRPCError(RPC_MISC_ERROR, e.what()); } diff --git a/src/rpc/server.h b/src/rpc/server.h index 05e84cc93a571..7e50d63a3625f 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -238,6 +238,7 @@ extern UniValue listaddressgroupings(const UniValue& params, bool fHelp); extern UniValue listaccounts(const UniValue& params, bool fHelp); extern UniValue listsinceblock(const UniValue& params, bool fHelp); extern UniValue gettransaction(const UniValue& params, bool fHelp); +extern UniValue abandontransaction(const UniValue& params, bool fHelp); extern UniValue backupwallet(const UniValue& params, bool fHelp); extern UniValue upgradetohd(const UniValue& params, bool fHelp); extern UniValue keypoolrefill(const UniValue& params, bool fHelp); diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 52777b61f96c8..20ee0d5339fe2 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -4,6 +4,7 @@ #include "scheduler.h" +#include "random.h" #include "reverselock.h" #include @@ -37,6 +38,11 @@ void CScheduler::serviceQueue() // is called. while (!shouldStop()) { try { + if (!shouldStop() && taskQueue.empty()) { + reverse_lock > rlock(lock); + // Use this chance to get a tiny bit more entropy + RandAddSeedSleep(); + } while (!shouldStop() && taskQueue.empty()) { // Wait until there is something to do. newTaskScheduled.wait(lock); diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 96f90ebdeb796..56d4deaf18db9 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -269,7 +269,7 @@ bool EvalScript(std::vector >& stack, const CScript& std::vector vfExec; std::vector altstack; set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); - if (script.size() > 10000) + if (script.size() > MAX_SCRIPT_SIZE) return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE); unsigned int nOpCount = 0; bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0; diff --git a/src/script/script.h b/src/script/script.h index 445628db25325..1999a9f970fe1 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -23,6 +23,8 @@ static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes static const unsigned int MAX_OPS_PER_SCRIPT = 201; static const unsigned int MAX_PUBKEYS_PER_MULTISIG = 20; +// Maximum script length in bytes +static const int MAX_SCRIPT_SIZE = 10000; // Threshold for nLockTime: below this value it is interpreted as block number, // otherwise as UNIX timestamp. @@ -629,7 +631,7 @@ class CScript : public std::vector */ bool IsUnspendable() const { - return (size() > 0 && *begin() == OP_RETURN); + return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE); } std::string ToString() const; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 99769e1ede665..372f889087c89 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -13,7 +13,6 @@ #include "util.h" - typedef std::vector valtype; TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} diff --git a/src/script/standard.cpp b/src/script/standard.cpp index d93416a2303f2..64a7236486d2a 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -15,7 +15,6 @@ - typedef std::vector valtype; unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY; diff --git a/src/serialize.h b/src/serialize.h index 3d9350a564d83..d9013fbec8e3b 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -20,6 +20,7 @@ #include #include "libzerocoin/Denominations.h" #include "libzerocoin/SpendType.h" +#include "sporkid.h" class CScript; @@ -290,6 +291,23 @@ inline void Unserialize(Stream& s, libzerocoin::SpendType & a, int, int = 0) a = static_cast(f); } +// Serialization for SporkId +inline unsigned int GetSerializeSize(SporkId sporkID, int, int = 0) { return sizeof(SporkId); } +template +inline void Serialize(Stream& s, SporkId sporkID, int, int = 0) +{ + int32_t f = static_cast(sporkID); + WRITEDATA(s, f); +} + +template +inline void Unserialize(Stream& s, SporkId& sporkID, int, int = 0) +{ + int32_t f=0; + READDATA(s, f); + sporkID = (SporkId) f; +} + /** * Compact Size * size < 253 -- 1 byte diff --git a/src/spork.cpp b/src/spork.cpp index 524e531ed93af..5da7b9ae46dec 100644 --- a/src/spork.cpp +++ b/src/spork.cpp @@ -3,41 +3,57 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "spork.h" -#include "base58.h" #include "consensus/validation.h" -#include "key.h" #include "main.h" #include "masternode-budget.h" #include "net.h" -#include "protocol.h" -#include "sync.h" +#include "spork.h" #include "sporkdb.h" -#include "util.h" -class CSporkMessage; -class CSporkManager; +#define MAKE_SPORK_DEF(name, defaultValue) CSporkDef(name, defaultValue, #name) + +std::vector sporkDefs = { + MAKE_SPORK_DEF(SPORK_2_SWIFTTX, 0), // ON + MAKE_SPORK_DEF(SPORK_3_SWIFTTX_BLOCK_FILTERING, 0), // ON + MAKE_SPORK_DEF(SPORK_5_MAX_VALUE, 1000), // 1000 PIV + MAKE_SPORK_DEF(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT, 4070908800ULL), // OFF + MAKE_SPORK_DEF(SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT, 4070908800ULL), // OFF + MAKE_SPORK_DEF(SPORK_10_MASTERNODE_PAY_UPDATED_NODES, 0), // OFF + MAKE_SPORK_DEF(SPORK_13_ENABLE_SUPERBLOCKS, 4070908800ULL), // OFF + MAKE_SPORK_DEF(SPORK_14_NEW_PROTOCOL_ENFORCEMENT, 4070908800ULL), // OFF + //MAKE_SPORK_DEF(SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2, 4070908800ULL), // OFF + MAKE_SPORK_DEF(SPORK_16_ZEROCOIN_MAINTENANCE_MODE, 4070908800ULL), // OFF + MAKE_SPORK_DEF(SPORK_17_SEGWIT_ACTIVATION, 4070908800ULL), // OFF + //MAKE_SPORK_DEF(SPORK_18_NEW_PROTOCOL_ENFORCEMENT_3, 4070908800ULL), // OFF + MAKE_SPORK_DEF(SPORK_19_SEGWIT_ON_COINBASE, 4070908800ULL), // OFF +}; CSporkManager sporkManager; - std::map mapSporks; -std::map mapSporksActive; + +CSporkManager::CSporkManager() +{ + for (auto& sporkDef : sporkDefs) { + sporkDefsById.emplace(sporkDef.sporkId, &sporkDef); + sporkDefsByName.emplace(sporkDef.name, &sporkDef); + } +} + +void CSporkManager::Clear() +{ + strMasterPrivKey = ""; + mapSporksActive.clear(); +} // Phore: on startup load spork values from previous session if they exist in the sporkDB -void LoadSporksFromDB() +void CSporkManager::LoadSporksFromDB() { - for (int i = SPORK_START; i <= SPORK_END; ++i) { - // Since not all spork IDs are in use, we have to exclude undefined IDs - std::string strSpork = sporkManager.GetSporkNameByID(i); - if (strSpork == "Unknown") { - LogPrintf("%s : unknown spork ID %d\n", __func__, i); - continue; - } + for (const auto& sporkDef : sporkDefs) { // attempt to read spork from sporkDB CSporkMessage spork; - if (!pSporkDB->ReadSpork(i, spork)) { - LogPrintf("%s : no previous value for %s found in database\n", __func__, strSpork); + if (!pSporkDB->ReadSpork(sporkDef.sporkId, spork)) { + LogPrintf("%s : no previous value for %s found in database\n", __func__, sporkDef.name); continue; } @@ -57,49 +73,75 @@ void LoadSporksFromDB() } } -void ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +void CSporkManager::ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { - if (fLiteMode) return; //disable all obfuscation/masternode related functionality + if (fLiteMode) return; // disable all obfuscation/masternode related functionality + + int nChainHeight = 0; + { + LOCK(cs_main); + if (chainActive.Tip() == nullptr) + return; + nChainHeight = chainActive.Height(); + } + + if (strCommand == "spork") { - if (strCommand == NetMsgType::SPORK) { - //LogPrintf("ProcessSpork::spork\n"); - CDataStream vMsg(vRecv); CSporkMessage spork; vRecv >> spork; - if (chainActive.Tip() == NULL) return; - // Ignore spork messages about unknown/deleted sporks std::string strSpork = sporkManager.GetSporkNameByID(spork.nSporkID); if (strSpork == "Unknown") return; + // Do not accept sporks signed way too far into the future + if (spork.nTimeSigned > GetAdjustedTime() + 2 * 60 * 60) { + LOCK(cs_main); + LogPrintf("%s : ERROR: too far into the future\n", __func__); + Misbehaving(pfrom->GetId(), 100); + return; + } + uint256 hash = spork.GetHash(); - if (mapSporksActive.count(spork.nSporkID)) { - if (mapSporksActive[spork.nSporkID].nTimeSigned >= spork.nTimeSigned) { - if (fDebug) LogPrintf("spork - seen %s block %d \n", hash.ToString(), chainActive.Tip()->nHeight); - return; + { + LOCK(cs); + if (mapSporksActive.count(spork.nSporkID)) { + // spork is active + if (mapSporksActive[spork.nSporkID].nTimeSigned >= spork.nTimeSigned) { + // spork in memory has been signed more recently + if (fDebug) LogPrintf("%s : seen %s block %d \n", __func__, hash.ToString(), nChainHeight); + return; + } else { + // update active spork + if (fDebug) LogPrintf("%s : got updated spork %s block %d \n", __func__, hash.ToString(), nChainHeight); + } } else { - if (fDebug) LogPrintf("spork - got updated spork %s block %d \n", hash.ToString(), chainActive.Tip()->nHeight); + // spork is not active + if (fDebug) LogPrintf("%s : got new spork %s block %d \n", __func__, hash.ToString(), nChainHeight); } } - LogPrintf("spork - new %s ID %d Time %d bestHeight %d\n", hash.ToString(), spork.nSporkID, spork.nValue, chainActive.Tip()->nHeight); - - if (!sporkManager.CheckSignature(spork)) { - LogPrintf("spork - invalid signature\n"); + LogPrintf("%s : new %s ID %d Time %d bestHeight %d\n", __func__, hash.ToString(), spork.nSporkID, spork.nValue, nChainHeight); + if (!spork.CheckSignature()) { + LOCK(cs_main); + LogPrintf("%s : Invalid Signature\n", __func__); Misbehaving(pfrom->GetId(), 100); return; } - mapSporks[hash] = spork; - mapSporksActive[spork.nSporkID] = spork; - sporkManager.Relay(spork); + { + LOCK(cs); + mapSporks[hash] = spork; + mapSporksActive[spork.nSporkID] = spork; + } + spork.Relay(); // Phore: add to spork database. pSporkDB->WriteSpork(spork.nSporkID, spork); } - if (strCommand == NetMsgType::GETSPORKS) { - std::map::iterator it = mapSporksActive.begin(); + if (strCommand == "getsporks") { + LOCK(cs); + std::map::iterator it = mapSporksActive.begin(); while (it != mapSporksActive.end()) { pfrom->PushMessage(NetMsgType::SPORK, it->second); @@ -108,199 +150,131 @@ void ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) } } - -// grab the value of the spork on the network, or the default -int64_t GetSporkValue(int nSporkID) +bool CSporkManager::UpdateSpork(SporkId nSporkID, int64_t nValue) { - int64_t r = -1; - if (mapSporksActive.count(nSporkID)) { - r = mapSporksActive[nSporkID].nValue; - } else { - if (nSporkID == SPORK_2_SWIFTTX) r = SPORK_2_SWIFTTX_DEFAULT; - if (nSporkID == SPORK_3_SWIFTTX_BLOCK_FILTERING) r = SPORK_3_SWIFTTX_BLOCK_FILTERING_DEFAULT; - if (nSporkID == SPORK_5_MAX_VALUE) r = SPORK_5_MAX_VALUE_DEFAULT; - if (nSporkID == SPORK_7_MASTERNODE_SCANNING) r = SPORK_7_MASTERNODE_SCANNING_DEFAULT; - if (nSporkID == SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT) r = SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT_DEFAULT; - if (nSporkID == SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT) r = SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT_DEFAULT; - if (nSporkID == SPORK_10_MASTERNODE_PAY_UPDATED_NODES) r = SPORK_10_MASTERNODE_PAY_UPDATED_NODES_DEFAULT; - if (nSporkID == SPORK_13_ENABLE_SUPERBLOCKS) r = SPORK_13_ENABLE_SUPERBLOCKS_DEFAULT; - //if (nSporkID == SPORK_14_NEW_PROTOCOL_ENFORCEMENT) r = SPORK_14_NEW_PROTOCOL_ENFORCEMENT_DEFAULT; - if (nSporkID == SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2) r = SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2_DEFAULT; - if (nSporkID == SPORK_16_ZEROCOIN_MAINTENANCE_MODE) r = SPORK_16_ZEROCOIN_MAINTENANCE_MODE_DEFAULT; - if (nSporkID == SPORK_17_SEGWIT_ACTIVATION) r = SPORK_17_SEGWIT_ACTIVATION_DEFAULT; - //if (nSporkID == SPORK_18_NEW_PROTOCOL_ENFORCEMENT_3) r = SPORK_18_NEW_PROTOCOL_ENFORCEMENT_3_DEFAULT; - if (nSporkID == SPORK_19_SEGWIT_ON_COINBASE) r = SPORK_19_SEGWIT_ON_COINBASE_DEFAULT; - if (nSporkID == SPORK_20_KERNEL_EXTRA_STAKING_CHECK) r = SPORK_20_KERNEL_EXTRA_STAKING_CHECK_DEFAULT; - - if (r == -1) LogPrintf("GetSpork::Unknown Spork %d\n", nSporkID); + CSporkMessage spork = CSporkMessage(nSporkID, nValue, GetTime()); + + if(spork.Sign(strMasterPrivKey)){ + spork.Relay(); + LOCK(cs); + mapSporks[spork.GetHash()] = spork; + mapSporksActive[nSporkID] = spork; + return true; } - return r; + return false; } // grab the spork value, and see if it's off -bool IsSporkActive(int nSporkID) +bool CSporkManager::IsSporkActive(SporkId nSporkID) { - if (Params().NetworkID() == CBaseChainParams::REGTEST) { - if(nSporkID == SPORK_13_ENABLE_SUPERBLOCKS) { - return true; - } - } - int64_t r = GetSporkValue(nSporkID); - if (r == -1) return false; - return r < GetTime(); + return GetSporkValue(nSporkID) < GetAdjustedTime(); } - -void ReprocessBlocks(int nBlocks) +// grab the value of the spork on the network, or the default +int64_t CSporkManager::GetSporkValue(SporkId nSporkID) { - std::map::iterator it = mapRejectedBlocks.begin(); - while (it != mapRejectedBlocks.end()) { - //use a window twice as large as is usual for the nBlocks we want to reset - if ((*it).second > GetTime() - (nBlocks * 60 * 5)) { - BlockMap::iterator mi = mapBlockIndex.find((*it).first); - if (mi != mapBlockIndex.end() && (*mi).second) { - LOCK(cs_main); - - CBlockIndex* pindex = (*mi).second; - LogPrintf("ReprocessBlocks - %s\n", (*it).first.ToString()); - - CValidationState state; - ReconsiderBlock(state, pindex); - } + LOCK(cs); + + if (mapSporksActive.count(nSporkID)) { + return mapSporksActive[nSporkID].nValue; + + } else { + auto it = sporkDefsById.find(nSporkID); + if (it != sporkDefsById.end()) { + return it->second->defaultValue; + } else { + LogPrintf("%s : Unknown Spork %d\n", __func__, nSporkID); } - ++it; } - CValidationState state; - { - LOCK(cs_main); - DisconnectBlocksAndReprocess(nBlocks); + return -1; +} + +SporkId CSporkManager::GetSporkIDByName(std::string strName) +{ + auto it = sporkDefsByName.find(strName); + if (it == sporkDefsByName.end()) { + LogPrintf("%s : Unknown Spork name '%s'\n", __func__, strName); + return SPORK_INVALID; } + return it->second->sporkId; +} - if (state.IsValid()) { - ActivateBestChain(state); +std::string CSporkManager::GetSporkNameByID(SporkId nSporkID) +{ + auto it = sporkDefsById.find(nSporkID); + if (it == sporkDefsById.end()) { + LogPrint("%s : Unknown Spork ID %d\n", __func__, nSporkID); + return "Unknown"; } + return it->second->name; } -bool CSporkManager::CheckSignature(CSporkMessage& spork) +bool CSporkManager::SetPrivKey(std::string strPrivKey) { - //note: need to investigate why this is failing - std::string strMessage = std::to_string(spork.nSporkID) + std::to_string(spork.nValue) + std::to_string(spork.nTimeSigned); - CPubKey pubkeynew(ParseHex(Params().SporkKey())); - std::string errorMessage = ""; - if (obfuScationSigner.VerifyMessage(pubkeynew, spork.vchSig, strMessage, errorMessage)) { + CSporkMessage spork; + + spork.Sign(strPrivKey); + + if (spork.CheckSignature()) { + LOCK(cs); + // Test signing successful, proceed + LogPrintf("%s : Successfully initialized as spork signer\n", __func__); + strMasterPrivKey = strPrivKey; return true; } return false; } -bool CSporkManager::Sign(CSporkMessage& spork) +std::string CSporkManager::ToString() const +{ + LOCK(cs); + return strprintf("Sporks: %llu", mapSporksActive.size()); +} + +bool CSporkMessage::Sign(std::string strSignKey) { - std::string strMessage = std::to_string(spork.nSporkID) + std::to_string(spork.nValue) + std::to_string(spork.nTimeSigned); + std::string strMessage = std::to_string(nSporkID) + std::to_string(nValue) + std::to_string(nTimeSigned); - CKey key2; - CPubKey pubkey2; + CKey key; + CPubKey pubkey; std::string errorMessage = ""; - if (!obfuScationSigner.SetKey(strMasterPrivKey, errorMessage, key2, pubkey2)) { - LogPrintf("CMasternodePayments::Sign - ERROR: Invalid masternodeprivkey: '%s'\n", errorMessage); - return false; + if (!obfuScationSigner.SetKey(strSignKey, errorMessage, key, pubkey)) { + return error("%s : SetKey error: '%s'\n", __func__, errorMessage); } - if (!obfuScationSigner.SignMessage(strMessage, errorMessage, spork.vchSig, key2)) { - LogPrintf("CMasternodePayments::Sign - Sign message failed"); - return false; + if (!obfuScationSigner.SignMessage(strMessage, errorMessage, vchSig, key)) { + return error("%s : Sign message failed", __func__); } - if (!obfuScationSigner.VerifyMessage(pubkey2, spork.vchSig, strMessage, errorMessage)) { - LogPrintf("CMasternodePayments::Sign - Verify message failed"); - return false; + if (!obfuScationSigner.VerifyMessage(pubkey, vchSig, strMessage, errorMessage)) { + return error("%s : Verify message failed", __func__); } return true; } -bool CSporkManager::UpdateSpork(int nSporkID, int64_t nValue) +bool CSporkMessage::CheckSignature() { - CSporkMessage msg; - msg.nSporkID = nSporkID; - msg.nValue = nValue; - msg.nTimeSigned = GetTime(); - - if (Sign(msg)) { - Relay(msg); - mapSporks[msg.GetHash()] = msg; - mapSporksActive[nSporkID] = msg; - return true; - } - - return false; -} - -void CSporkManager::Relay(CSporkMessage& msg) -{ - CInv inv(MSG_SPORK, msg.GetHash()); - RelayInv(inv); -} - -bool CSporkManager::SetPrivKey(std::string strPrivKey) -{ - CSporkMessage msg; - - // Test signing successful, proceed - strMasterPrivKey = strPrivKey; - - Sign(msg); + //note: need to investigate why this is failing + std::string strMessage = std::to_string(nSporkID) + std::to_string(nValue) + std::to_string(nTimeSigned); + CPubKey pubkey(ParseHex(Params().SporkKey())); + std::string strError = ""; - if (CheckSignature(msg)) { - LogPrintf("CSporkManager::SetPrivKey - Successfully initialized as spork signer\n"); - return true; - } else { + if(!obfuScationSigner.VerifyMessage(pubkey, vchSig, strMessage, strError)) { + LogPrintf("CSporkMessage::CheckSignature -- VerifyMessage() failed, error: %s\n", strError); return false; } + return true; } -int CSporkManager::GetSporkIDByName(std::string strName) +void CSporkMessage::Relay() { - if (strName == "SPORK_2_SWIFTTX") return SPORK_2_SWIFTTX; - if (strName == "SPORK_3_SWIFTTX_BLOCK_FILTERING") return SPORK_3_SWIFTTX_BLOCK_FILTERING; - if (strName == "SPORK_5_MAX_VALUE") return SPORK_5_MAX_VALUE; - if (strName == "SPORK_7_MASTERNODE_SCANNING") return SPORK_7_MASTERNODE_SCANNING; - if (strName == "SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT") return SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT; - if (strName == "SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT") return SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT; - if (strName == "SPORK_10_MASTERNODE_PAY_UPDATED_NODES") return SPORK_10_MASTERNODE_PAY_UPDATED_NODES; - if (strName == "SPORK_13_ENABLE_SUPERBLOCKS") return SPORK_13_ENABLE_SUPERBLOCKS; - //if (strName == "SPORK_14_NEW_PROTOCOL_ENFORCEMENT") return SPORK_14_NEW_PROTOCOL_ENFORCEMENT; - if (strName == "SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2") return SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2; - if (strName == "SPORK_16_ZEROCOIN_MAINTENANCE_MODE") return SPORK_16_ZEROCOIN_MAINTENANCE_MODE; - if (strName == "SPORK_17_SEGWIT_ACTIVATION") return SPORK_17_SEGWIT_ACTIVATION; - //if (strName == "SPORK_18_NEW_PROTOCOL_ENFORCEMENT_3") return SPORK_18_NEW_PROTOCOL_ENFORCEMENT_3; - if (strName == "SPORK_19_SEGWIT_ON_COINBASE") return SPORK_19_SEGWIT_ON_COINBASE; - if (strName == "SPORK_20_KERNEL_EXTRA_STAKING_CHECK") return SPORK_20_KERNEL_EXTRA_STAKING_CHECK; - - return -1; + CInv inv(MSG_SPORK, GetHash()); + RelayInv(inv); } -std::string CSporkManager::GetSporkNameByID(int id) -{ - if (id == SPORK_2_SWIFTTX) return "SPORK_2_SWIFTTX"; - if (id == SPORK_3_SWIFTTX_BLOCK_FILTERING) return "SPORK_3_SWIFTTX_BLOCK_FILTERING"; - if (id == SPORK_5_MAX_VALUE) return "SPORK_5_MAX_VALUE"; - if (id == SPORK_7_MASTERNODE_SCANNING) return "SPORK_7_MASTERNODE_SCANNING"; - if (id == SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT) return "SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT"; - if (id == SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT) return "SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT"; - if (id == SPORK_10_MASTERNODE_PAY_UPDATED_NODES) return "SPORK_10_MASTERNODE_PAY_UPDATED_NODES"; - if (id == SPORK_13_ENABLE_SUPERBLOCKS) return "SPORK_13_ENABLE_SUPERBLOCKS"; - //if (id == SPORK_14_NEW_PROTOCOL_ENFORCEMENT) return "SPORK_14_NEW_PROTOCOL_ENFORCEMENT"; - if (id == SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2) return "SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2"; - if (id == SPORK_16_ZEROCOIN_MAINTENANCE_MODE) return "SPORK_16_ZEROCOIN_MAINTENANCE_MODE"; - if (id == SPORK_17_SEGWIT_ACTIVATION) return "SPORK_17_SEGWIT_ACTIVATION"; - //if (id == SPORK_18_NEW_PROTOCOL_ENFORCEMENT_3) return "SPORK_18_NEW_PROTOCOL_ENFORCEMENT_3"; - if (id == SPORK_19_SEGWIT_ON_COINBASE) return "SPORK_19_SEGWIT_ON_COINBASE"; - if (id == SPORK_20_KERNEL_EXTRA_STAKING_CHECK) return "SPORK_20_KERNEL_EXTRA_STAKING_CHECK"; - - return "Unknown"; -} diff --git a/src/spork.h b/src/spork.h index e9225a9047181..1c87e5d4f9c5a 100644 --- a/src/spork.h +++ b/src/spork.h @@ -7,90 +7,46 @@ #define SPORK_H #include "base58.h" +#include "hash.h" #include "key.h" #include "main.h" #include "net.h" +#include "sporkid.h" #include "sync.h" #include "util.h" #include "obfuscation.h" #include "protocol.h" -/* - Don't ever reuse these IDs for other sporks - - This would result in old clients getting confused about which spork is for what - - Sporks 11,12, and 16 to be removed with 1st zerocoin release -*/ -#define SPORK_START 10001 -#define SPORK_END 10019 - -#define SPORK_2_SWIFTTX 10001 -#define SPORK_3_SWIFTTX_BLOCK_FILTERING 10002 -#define SPORK_5_MAX_VALUE 10004 -#define SPORK_7_MASTERNODE_SCANNING 10006 -#define SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT 10007 -#define SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT 10008 -#define SPORK_10_MASTERNODE_PAY_UPDATED_NODES 10009 -//#define SPORK_11_LOCK_INVALID_UTXO 10010 -//#define SPORK_12_RECONSIDER_BLOCKS 10011 -#define SPORK_13_ENABLE_SUPERBLOCKS 10012 -//#define SPORK_14_NEW_PROTOCOL_ENFORCEMENT 10013 -#define SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2 10014 -#define SPORK_16_ZEROCOIN_MAINTENANCE_MODE 10015 -#define SPORK_17_SEGWIT_ACTIVATION 10016 -//#define SPORK_18_NEW_PROTOCOL_ENFORCEMENT_3 10017 -#define SPORK_19_SEGWIT_ON_COINBASE 10018 -#define SPORK_20_KERNEL_EXTRA_STAKING_CHECK 10019 - -#define SPORK_2_SWIFTTX_DEFAULT 978307200 //2001-1-1 -#define SPORK_3_SWIFTTX_BLOCK_FILTERING_DEFAULT 1424217600 //2015-2-18 -#define SPORK_5_MAX_VALUE_DEFAULT 1000 //1000 PHR -#define SPORK_7_MASTERNODE_SCANNING_DEFAULT 978307200 //2001-1-1 -#define SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT_DEFAULT 4070908800 //OFF -#define SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT_DEFAULT 4070908800 //OFF -#define SPORK_10_MASTERNODE_PAY_UPDATED_NODES_DEFAULT 4070908800 //OFF -//#define SPORK_11_LOCK_INVALID_UTXO_DEFAULT 4070908800 //OFF - NOTE: this is block height not time! -#define SPORK_13_ENABLE_SUPERBLOCKS_DEFAULT 4070908800 //OFF -//#define SPORK_14_NEW_PROTOCOL_ENFORCEMENT_DEFAULT 4070908800 //OFF -#define SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2_DEFAULT 4070908800 //OFF -#define SPORK_16_ZEROCOIN_MAINTENANCE_MODE_DEFAULT 4070908800 //OFF -#define SPORK_17_SEGWIT_ACTIVATION_DEFAULT 4070908800 //OFF -//#define SPORK_18_NEW_PROTOCOL_ENFORCEMENT_3_DEFAULT 4070908800 //OFF -#define SPORK_19_SEGWIT_ON_COINBASE_DEFAULT 4070908800 //OFF -#define SPORK_20_KERNEL_EXTRA_STAKING_CHECK_DEFAULT 9999999999 //OFF - class CSporkMessage; class CSporkManager; +extern std::vector sporkDefs; extern std::map mapSporks; -extern std::map mapSporksActive; extern CSporkManager sporkManager; -void LoadSporksFromDB(); -void ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); -int64_t GetSporkValue(int nSporkID); -bool IsSporkActive(int nSporkID); -void ReprocessBlocks(int nBlocks); - // -// Spork Class -// Keeps track of all of the network spork settings +// Spork Classes +// Keep track of all of the network spork settings // class CSporkMessage { -public: +private: std::vector vchSig; - int nSporkID; + +public: + SporkId nSporkID; int64_t nValue; int64_t nTimeSigned; - uint256 GetHash() - { - uint256 n = HashQuark(BEGIN(nSporkID), END(nTimeSigned)); - return n; - } + CSporkMessage(SporkId nSporkID, int64_t nValue, int64_t nTimeSigned) : nSporkID(nSporkID), nValue(nValue), nTimeSigned(nTimeSigned) {} + CSporkMessage() : nSporkID((SporkId)0), nValue(0), nTimeSigned(0) {} + + uint256 GetHash() { return HashQuark(BEGIN(nSporkID), END(nTimeSigned)); } + bool Sign(std::string strSignKey); + bool CheckSignature(); + void Relay(); ADD_SERIALIZE_METHODS; @@ -108,21 +64,38 @@ class CSporkMessage class CSporkManager { private: - std::vector vchSig; + mutable CCriticalSection cs; std::string strMasterPrivKey; + std::map sporkDefsById; + std::map sporkDefsByName; + std::map mapSporksActive; public: - CSporkManager() + CSporkManager(); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(mapSporksActive); + // we don't serialize private key to prevent its leakage } - std::string GetSporkNameByID(int id); - int GetSporkIDByName(std::string strName); - bool UpdateSpork(int nSporkID, int64_t nValue); + void Clear(); + void LoadSporksFromDB(); + + void ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + int64_t GetSporkValue(SporkId nSporkID); + void ExecuteSpork(SporkId nSporkID, int nValue); + bool UpdateSpork(SporkId nSporkID, int64_t nValue); + + bool IsSporkActive(SporkId nSporkID); + std::string GetSporkNameByID(SporkId id); + SporkId GetSporkIDByName(std::string strName); + bool SetPrivKey(std::string strPrivKey); - bool CheckSignature(CSporkMessage& spork); - bool Sign(CSporkMessage& spork); - void Relay(CSporkMessage& msg); + std::string ToString() const; }; #endif diff --git a/src/sporkdb.cpp b/src/sporkdb.cpp index 79be1dc7a78ad..c957cca8aceae 100644 --- a/src/sporkdb.cpp +++ b/src/sporkdb.cpp @@ -7,19 +7,19 @@ CSporkDB::CSporkDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "sporks", nCacheSize, fMemory, fWipe) {} -bool CSporkDB::WriteSpork(const int nSporkId, const CSporkMessage& spork) +bool CSporkDB::WriteSpork(const SporkId nSporkId, const CSporkMessage& spork) { LogPrintf("Wrote spork %s to database\n", sporkManager.GetSporkNameByID(nSporkId)); return Write(nSporkId, spork); } -bool CSporkDB::ReadSpork(const int nSporkId, CSporkMessage& spork) +bool CSporkDB::ReadSpork(const SporkId nSporkId, CSporkMessage& spork) { return Read(nSporkId, spork); } -bool CSporkDB::SporkExists(const int nSporkId) +bool CSporkDB::SporkExists(const SporkId nSporkId) { return Exists(nSporkId); } diff --git a/src/sporkdb.h b/src/sporkdb.h index 9d44110227e79..49de00f94cfaa 100644 --- a/src/sporkdb.h +++ b/src/sporkdb.h @@ -19,9 +19,9 @@ class CSporkDB : public CLevelDBWrapper void operator=(const CSporkDB&); public: - bool WriteSpork(const int nSporkId, const CSporkMessage& spork); - bool ReadSpork(const int nSporkId, CSporkMessage& spork); - bool SporkExists(const int nSporkId); + bool WriteSpork(const SporkId nSporkId, const CSporkMessage& spork); + bool ReadSpork(const SporkId nSporkId, CSporkMessage& spork); + bool SporkExists(const SporkId nSporkId); }; diff --git a/src/sporkid.h b/src/sporkid.h new file mode 100644 index 0000000000000..34e8aa500a2e6 --- /dev/null +++ b/src/sporkid.h @@ -0,0 +1,42 @@ +// Copyright (c) 2014-2016 The Dash developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef SPORKID_H +#define SPORKID_H + +/* + Don't ever reuse these IDs for other sporks + - This would result in old clients getting confused about which spork is for what +*/ + +enum SporkId : int32_t { + SPORK_2_SWIFTTX = 10001, + SPORK_3_SWIFTTX_BLOCK_FILTERING = 10002, + SPORK_5_MAX_VALUE = 10004, + SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT = 10007, + SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT = 10008, + SPORK_10_MASTERNODE_PAY_UPDATED_NODES = 10009, + SPORK_13_ENABLE_SUPERBLOCKS = 10012, + SPORK_14_NEW_PROTOCOL_ENFORCEMENT = 10013, + //SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2 = 10014, + SPORK_16_ZEROCOIN_MAINTENANCE_MODE = 10015, + SPORK_17_SEGWIT_ACTIVATION = 10016, + //SPORK_18_NEW_PROTOCOL_ENFORCEMENT_3 = 10017, + SPORK_19_SEGWIT_ON_COINBASE = 10018, + SPORK_20_KERNEL_EXTRA_STAKING_CHECK = 10019, + + SPORK_INVALID = -1 +}; + +// Default values +struct CSporkDef +{ + CSporkDef(): sporkId(SPORK_INVALID), defaultValue(0) {} + CSporkDef(SporkId id, int64_t val, std::string n): sporkId(id), defaultValue(val), name(n) {} + SporkId sporkId; + int64_t defaultValue; + std::string name; +}; + +#endif diff --git a/src/streams.h b/src/streams.h index e31ec607ea11d..aa3692909e1f2 100644 --- a/src/streams.h +++ b/src/streams.h @@ -264,7 +264,9 @@ class CDataStream CDataStream& ignore(int nSize) { // Ignore from the beginning of the buffer - assert(nSize >= 0); + if (nSize < 0) { + throw std::ios_base::failure("CDataStream::ignore(): nSize negative"); + } unsigned int nReadPosNext = nReadPos + nSize; if (nReadPosNext >= vch.size()) { if (nReadPosNext > vch.size()) @@ -402,6 +404,21 @@ class CAutoFile return (*this); } + CAutoFile& ignore(size_t nSize) + { + if (!file) + throw std::ios_base::failure("CAutoFile::ignore: file handle is NULL"); + unsigned char data[4096]; + while (nSize > 0) { + size_t nNow = std::min(nSize, sizeof(data)); + if (fread(data, 1, nNow, file) != nNow) + throw std::ios_base::failure(feof(file) ? "CAutoFile::ignore: end of file" : "CAutoFile::read: fread failed"); + nSize -= nNow; + } + return (*this); + } + + CAutoFile& write(const char* pch, size_t nSize) { if (!file) diff --git a/src/swifttx.cpp b/src/swifttx.cpp index 6bbcb71da34d4..36276fb0a12d7 100644 --- a/src/swifttx.cpp +++ b/src/swifttx.cpp @@ -19,6 +19,9 @@ #include "validationinterface.h" #include +using namespace std; +using namespace boost; + std::map mapTxLockReq; std::map mapTxLockReqRejected; std::map mapTxLockVote; @@ -37,7 +40,7 @@ int nCompleteTXLocks; void ProcessMessageSwiftTX(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { if (fLiteMode) return; //disable all obfuscation/masternode related functionality - if (!IsSporkActive(SPORK_2_SWIFTTX)) return; + if (!sporkManager.IsSporkActive(SPORK_2_SWIFTTX)) return; if (!masternodeSync.IsBlockchainSynced()) return; if (strCommand == NetMsgType::IX) { @@ -197,7 +200,7 @@ bool IsIXTXValid(const CTransaction& txCollateral) } } - if (nValueOut > GetSporkValue(SPORK_5_MAX_VALUE) * COIN) { + if (nValueOut > sporkManager.GetSporkValue(SPORK_5_MAX_VALUE) * COIN) { LogPrint("swiftx", "IsIXTXValid - Transaction value too high - %s\n", txCollateral.ToString().c_str()); return false; } @@ -461,7 +464,7 @@ void CleanTransactionLocksList() int GetTransactionLockSignatures(uint256 txHash) { if(fLargeWorkForkFound || fLargeWorkInvalidChainFound) return -2; - if (!IsSporkActive(SPORK_2_SWIFTTX)) return -1; + if (!sporkManager.IsSporkActive(SPORK_2_SWIFTTX)) return -1; std::map::iterator it = mapTxLocks.find(txHash); if(it != mapTxLocks.end()) return it->second.CountSignatures(); diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 8eeffd2ee1661..088866590520b 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -111,7 +111,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) CTransaction RandomOrphan() { std::map::iterator it; - it = mapOrphanTransactions.lower_bound(GetRandHash()); + it = mapOrphanTransactions.lower_bound(InsecureRand256()); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); return it->second.tx; @@ -130,7 +130,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) CMutableTransaction tx; tx.vin.resize(1); tx.vin[0].prevout.n = 0; - tx.vin[0].prevout.hash = GetRandHash(); + tx.vin[0].prevout.hash = InsecureRand256(); tx.vin[0].scriptSig << OP_1; tx.vout.resize(1); tx.vout[0].nValue = 1*CENT; diff --git a/src/test/accounting_tests.cpp b/src/test/accounting_tests.cpp index 924edd10539a1..ed9ed9044dd82 100644 --- a/src/test/accounting_tests.cpp +++ b/src/test/accounting_tests.cpp @@ -48,7 +48,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) pwalletMain->AddAccountingEntry(ae, walletdb); wtx.mapValue["comment"] = "z"; - pwalletMain->AddToWallet(wtx); + pwalletMain->AddToWallet(wtx, false, &walletdb); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); vpwtx[0]->nTimeReceived = (unsigned int)1333333335; vpwtx[0]->nOrderPos = -1; @@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) --tx.nLockTime; // Just to change the hash :) *static_cast(&wtx) = CTransaction(tx); } - pwalletMain->AddToWallet(wtx); + pwalletMain->AddToWallet(wtx, false, &walletdb); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); vpwtx[1]->nTimeReceived = (unsigned int)1333333336; @@ -100,7 +100,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) --tx.nLockTime; // Just to change the hash :) *static_cast(&wtx) = CTransaction(tx); } - pwalletMain->AddToWallet(wtx); + pwalletMain->AddToWallet(wtx, false, &walletdb); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); vpwtx[2]->nTimeReceived = (unsigned int)1333333329; vpwtx[2]->nOrderPos = -1; diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index f2aaa78a5bd44..77405ffee0e0b 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -24,7 +24,7 @@ class CAddrManTest : public CAddrMan void MakeDeterministic() { nKey.SetNull(); - seed_insecure_rand(true); + insecure_rand = FastRandomContext(true); } int RandomInt(int nMax) @@ -179,10 +179,11 @@ BOOST_AUTO_TEST_CASE(addrman_select) BOOST_CHECK(addrman.size() == 7); // Test 12: Select pulls from new and tried regardless of port number. - BOOST_CHECK(addrman.Select().ToString() == "250.4.6.6:8333"); - BOOST_CHECK(addrman.Select().ToString() == "250.3.2.2:9999"); - BOOST_CHECK(addrman.Select().ToString() == "250.3.3.3:9999"); - BOOST_CHECK(addrman.Select().ToString() == "250.4.4.4:8333"); + std::set ports; + for (int i = 0; i < 20; ++i) { + ports.insert(addrman.Select().GetPort()); + } + BOOST_CHECK_EQUAL(ports.size(), 3); } BOOST_AUTO_TEST_CASE(addrman_new_collisions) diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index 5ca519f0c855f..58702dc19b60f 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -94,7 +94,7 @@ struct ReadAlerts : public TestingSetup alerts.push_back(alert); } } - catch (std::exception) { } + catch (const std::exception) { } } ~ReadAlerts() { } diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index ae0573dda1cf9..ac8b89578e99b 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -13,7 +13,6 @@ #include "utilstrencodings.h" #include "test/test_phore.h" - #include #include diff --git a/src/test/benchmark_zerocoin.cpp b/src/test/benchmark_zerocoin.cpp index fce0061c760f3..901d5ae52085b 100644 --- a/src/test/benchmark_zerocoin.cpp +++ b/src/test/benchmark_zerocoin.cpp @@ -186,7 +186,7 @@ Testb_GenerateGroupParams() try { group = deriveIntegerGroupParams(calculateSeed(gGetTestModulus(), "test", ZEROCOIN_DEFAULT_SECURITYLEVEL, "TEST GROUP"), pLen, qLen); - } catch (std::runtime_error e) { + } catch (const std::runtime_error& e) { cout << "Caught exception " << e.what() << endl; return false; } @@ -220,7 +220,7 @@ Testb_ParamGen() timer.stop(); cout << "\tPARAMGEN ELAPSED TIME: " << timer.duration() << " ms\t" << timer.duration()*0.001 << " s" << endl; - } catch (runtime_error e) { + } catch (const std::runtime_error& e) { cout << e.what() << endl; result = false; } @@ -411,4 +411,3 @@ BOOST_AUTO_TEST_CASE(benchmark_test) Testb_RunAllTests(); } BOOST_AUTO_TEST_SUITE_END() - diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index e78c80c436a8a..51cd8fa9c391f 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -3,9 +3,10 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "coins.h" -#include "random.h" +#include "script/standard.h" #include "uint256.h" #include "test/test_phore.h" +#include "utilstrencodings.h" #include #include @@ -27,7 +28,7 @@ class CCoinsViewTest : public CCoinsView return false; } coins = it->second; - if (coins.IsPruned() && insecure_rand() % 2 == 0) { + if (coins.IsPruned() && InsecureRandBool() == 0) { // Randomly return false in case of an empty entry. return false; } @@ -46,7 +47,7 @@ class CCoinsViewTest : public CCoinsView { for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) { map_[it->first] = it->second.coins; - if (it->second.coins.IsPruned() && insecure_rand() % 3 == 0) { + if (it->second.coins.IsPruned() && InsecureRandRange(3) == 0) { // Randomly delete empty entries on write. map_.erase(it->first); } @@ -97,25 +98,25 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) std::vector txids; txids.resize(NUM_SIMULATION_ITERATIONS / 8); for (unsigned int i = 0; i < txids.size(); i++) { - txids[i] = GetRandHash(); + txids[i] = InsecureRand256(); } for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { // Do a random modification. { - uint256 txid = txids[insecure_rand() % txids.size()]; // txid we're going to modify in this iteration. + uint256 txid = txids[InsecureRandRange(txids.size())]; // txid we're going to modify in this iteration. CCoins& coins = result[txid]; CCoinsModifier entry = stack.back()->ModifyCoins(txid); BOOST_CHECK(coins == *entry); - if (insecure_rand() % 5 == 0 || coins.IsPruned()) { + if (InsecureRandRange(5) == 0 || coins.IsPruned()) { if (coins.IsPruned()) { added_an_entry = true; } else { updated_an_entry = true; } - coins.nVersion = insecure_rand(); + coins.nVersion = InsecureRand32(); coins.vout.resize(1); - coins.vout[0].nValue = insecure_rand(); + coins.vout[0].nValue = InsecureRand32(); *entry = coins; } else { coins.Clear(); @@ -125,7 +126,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) } // Once every 1000 iterations and at the end, verify the full cache. - if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { + if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { for (std::map::iterator it = result.begin(); it != result.end(); it++) { const CCoins* coins = stack.back()->AccessCoins(it->first); if (coins) { @@ -138,14 +139,14 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) } } - if (insecure_rand() % 100 == 0) { + if (InsecureRandRange(100) == 0) { // Every 100 iterations, change the cache stack. - if (stack.size() > 0 && insecure_rand() % 2 == 0) { + if (stack.size() > 0 && InsecureRandBool() == 0) { stack.back()->Flush(); delete stack.back(); stack.pop_back(); } - if (stack.size() == 0 || (stack.size() < 4 && insecure_rand() % 2)) { + if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) { CCoinsView* tip = &base; if (stack.size() > 0) { tip = stack.back(); @@ -176,4 +177,73 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) BOOST_CHECK(missed_an_entry); } +BOOST_AUTO_TEST_CASE(ccoins_serialization) +{ + // Good example + CDataStream ss1(ParseHex("0108835800816115944e077fe7c803cfa57f29b36bf87c1d35b4934b"), SER_DISK, CLIENT_VERSION); + CCoins cc1; + ss1 >> cc1; + BOOST_CHECK_EQUAL(cc1.nVersion, 1); + BOOST_CHECK_EQUAL(cc1.fCoinBase, false); + BOOST_CHECK_EQUAL(cc1.nHeight, 870987); + BOOST_CHECK_EQUAL(cc1.vout.size(), 2); + BOOST_CHECK_EQUAL(cc1.IsAvailable(0), false); + BOOST_CHECK_EQUAL(cc1.IsAvailable(1), true); + BOOST_CHECK_EQUAL(cc1.vout[1].nValue, 60000000000ULL); + BOOST_CHECK_EQUAL(HexStr(cc1.vout[1].scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35")))))); + + // Good example + CDataStream ss2(ParseHex("0111044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa482d21f"), SER_DISK, CLIENT_VERSION); + CCoins cc2; + ss2 >> cc2; + BOOST_CHECK_EQUAL(cc2.nVersion, 1); + BOOST_CHECK_EQUAL(cc2.fCoinBase, true); + BOOST_CHECK_EQUAL(cc2.nHeight, 59807); + BOOST_CHECK_EQUAL(cc2.vout.size(), 17); + for (int i = 0; i < 17; i++) { + BOOST_CHECK_EQUAL(cc2.IsAvailable(i), i == 4 || i == 16); + } + BOOST_CHECK_EQUAL(cc2.vout[4].nValue, 234925952); + BOOST_CHECK_EQUAL(HexStr(cc2.vout[4].scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("61b01caab50f1b8e9c50a5057eb43c2d9563a4ee")))))); + BOOST_CHECK_EQUAL(cc2.vout[16].nValue, 110397); + BOOST_CHECK_EQUAL(HexStr(cc2.vout[16].scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4")))))); + + // Smallest possible example + CDataStream ssx(SER_DISK, CLIENT_VERSION); + BOOST_CHECK_EQUAL(HexStr(ssx.begin(), ssx.end()), ""); + + CDataStream ss3(ParseHex("0004000600"), SER_DISK, CLIENT_VERSION); + CCoins cc3; + ss3 >> cc3; + BOOST_CHECK_EQUAL(cc3.nVersion, 0); + BOOST_CHECK_EQUAL(cc3.fCoinBase, false); + BOOST_CHECK_EQUAL(cc3.nHeight, 0); + BOOST_CHECK_EQUAL(cc3.vout.size(), 1); + BOOST_CHECK_EQUAL(cc3.IsAvailable(0), true); + BOOST_CHECK_EQUAL(cc3.vout[0].nValue, 0); + BOOST_CHECK_EQUAL(cc3.vout[0].scriptPubKey.size(), 0); + + // scriptPubKey that ends beyond the end of the stream + CDataStream ss4(ParseHex("0004000800"), SER_DISK, CLIENT_VERSION); + try { + CCoins cc4; + ss4 >> cc4; + BOOST_CHECK_MESSAGE(false, "We should have thrown"); + } catch (const std::ios_base::failure& e) { + } + + // Very large scriptPubKey (3*10^9 bytes) past the end of the stream + CDataStream tmp(SER_DISK, CLIENT_VERSION); + uint64_t x = 3000000000ULL; + tmp << VARINT(x); + BOOST_CHECK_EQUAL(HexStr(tmp.begin(), tmp.end()), "8a95c0bb00"); + CDataStream ss5(ParseHex("0002008a95c0bb0000"), SER_DISK, CLIENT_VERSION); + try { + CCoins cc5; + ss5 >> cc5; + BOOST_CHECK_MESSAGE(false, "We should have thrown"); + } catch (const std::ios_base::failure& e) { + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 9f49ee89db31e..7bb1e06df44b1 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "crypto/rfc6979_hmac_sha256.h" +#include "crypto/chacha20.h" #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha256.h" @@ -36,7 +37,7 @@ void TestVector(const Hasher &h, const In &in, const Out &out) { Hasher hasher(h); size_t pos = 0; while (pos < in.size()) { - size_t len = insecure_rand() % ((in.size() - pos + 1) / 2 + 1); + size_t len = InsecureRandRange((in.size() - pos + 1) / 2 + 1); hasher.Write((unsigned char*)&in[pos], len); pos += len; if (pos > 0 && pos + 2 * out.size() > in.size() && pos < in.size()) { @@ -65,6 +66,19 @@ void TestHMACSHA512(const std::string &hexkey, const std::string &hexin, const s TestVector(CHMAC_SHA512(&key[0], key.size()), ParseHex(hexin), ParseHex(hexout)); } +void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout) +{ + std::vector key = ParseHex(hexkey); + ChaCha20 rng(key.data(), key.size()); + rng.SetIV(nonce); + rng.Seek(seek); + std::vector out = ParseHex(hexout); + std::vector outres; + outres.resize(out.size()); + rng.Output(outres.data(), outres.size()); + BOOST_CHECK(out == outres); +} + std::string LongTestString(void) { std::string ret; for (int i=0; i<200000; i++) { @@ -307,4 +321,57 @@ BOOST_AUTO_TEST_CASE(rfc6979_hmac_sha256) ("7597887cbd76321f32e30440679a22cf7f8d9d2eac390e581fea091ce202ba94")); } + +BOOST_AUTO_TEST_CASE(chacha20_testvector) +{ + // Test vector from RFC 7539 + TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1, + "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cb" + "a40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a" + "832c89c167eacd901d7e2bf363"); + + // Test vectors from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 + TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0, 0, + "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b" + "8f41518a11cc387b669b2ee6586"); + TestChaCha20("0000000000000000000000000000000000000000000000000000000000000001", 0, 0, + "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d79" + "2b1c43fea817e9ad275ae546963"); + TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0, + "de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b52770" + "62eb7a0433e445f41e3"); + TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 1, 0, + "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc4" + "97a0b466e7d6bbdb0041b2f586b"); + TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0, + "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3b" + "e59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc1" + "18be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5" + "a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5" + "360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78" + "fab78c9"); +} + +BOOST_AUTO_TEST_CASE(countbits_tests) +{ + FastRandomContext ctx; + for (int i = 0; i <= 64; ++i) { + if (i == 0) { + // Check handling of zero. + BOOST_CHECK_EQUAL(CountBits(0), 0); + } else if (i < 10) { + for (uint64_t j = 1 << (i - 1); (j >> i) == 0; ++j) { + // Exhaustively test up to 10 bits + BOOST_CHECK_EQUAL(CountBits(j), i); + } + } else { + for (int k = 0; k < 1000; k++) { + // Randomly test 1000 samples of each length above 10 bits. + uint64_t j = ((uint64_t)1) << (i - 1) | ctx.randbits(i - 1); + BOOST_CHECK_EQUAL(CountBits(j), i); + } + } + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/libzerocoin_tests.cpp b/src/test/libzerocoin_tests.cpp index 02e6484778f34..c0cfaf84d9edb 100644 --- a/src/test/libzerocoin_tests.cpp +++ b/src/test/libzerocoin_tests.cpp @@ -157,7 +157,7 @@ Test_GenerateGroupParams() try { group = deriveIntegerGroupParams(calculateSeed(GetTestModulus(), "test", ZEROCOIN_DEFAULT_SECURITYLEVEL, "TEST GROUP"), pLen, qLen); - } catch (std::runtime_error e) { + } catch (const std::runtime_error& e) { std::cout << "Caught exception " << e.what() << endl; return false; } @@ -187,7 +187,7 @@ Test_ParamGen() try { // Instantiating testParams runs the parameter generation code ZerocoinParams testParams(GetTestModulus(),ZEROCOIN_DEFAULT_SECURITYLEVEL); - } catch (runtime_error e) { + } catch (const std::runtime_error& e) { std::cout << e.what() << endl; result = false; } @@ -250,7 +250,7 @@ Test_Accumulator() return false; } - } catch (runtime_error e) { + } catch (const std::runtime_error& e) { return false; } @@ -306,7 +306,7 @@ Test_EqualityPoK() return false; } - } catch (runtime_error &e) { + } catch (const std::runtime_error &e) { return false; } } @@ -378,7 +378,7 @@ bool Test_InvalidCoin() return false; } - } catch (runtime_error &e) { + } catch (const std::runtime_error &e) { std::cout << "Caught exception: " << e.what() << endl; return false; } @@ -433,7 +433,7 @@ Test_MintAndSpend() gSerialNumberSize = ceil((double)serialNumber.bitSize() / 8.0); return ret; - } catch (runtime_error &e) { + } catch (const std::runtime_error &e) { std::cout << e.what() << endl; return false; } diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp new file mode 100644 index 0000000000000..57536d8478f03 --- /dev/null +++ b/src/test/merkle_tests.cpp @@ -0,0 +1,135 @@ +// Copyright (c) 2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "consensus/merkle.h" +#include "test/test_pivx.h" + +#include + +BOOST_FIXTURE_TEST_SUITE(merkle_tests, TestingSetup) + +// Older version of the merkle root computation code, for comparison. +static uint256 BlockBuildMerkleTree(const CBlock& block, bool* fMutated, std::vector& vMerkleTree) +{ + vMerkleTree.clear(); + vMerkleTree.reserve(block.vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes. + for (std::vector::const_iterator it(block.vtx.begin()); it != block.vtx.end(); ++it) + vMerkleTree.push_back(it->GetHash()); + int j = 0; + bool mutated = false; + for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + for (int i = 0; i < nSize; i += 2) + { + int i2 = std::min(i+1, nSize-1); + if (i2 == i + 1 && i2 + 1 == nSize && vMerkleTree[j+i] == vMerkleTree[j+i2]) { + // Two identical hashes at the end of the list at a particular level. + mutated = true; + } + vMerkleTree.push_back(Hash(vMerkleTree[j+i].begin(), vMerkleTree[j+i].end(), + vMerkleTree[j+i2].begin(), vMerkleTree[j+i2].end())); + } + j += nSize; + } + if (fMutated) { + *fMutated = mutated; + } + return (vMerkleTree.empty() ? uint256() : vMerkleTree.back()); +} + +// Older version of the merkle branch computation code, for comparison. +static std::vector BlockGetMerkleBranch(const CBlock& block, const std::vector& vMerkleTree, int nIndex) +{ + std::vector vMerkleBranch; + int j = 0; + for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + int i = std::min(nIndex^1, nSize-1); + vMerkleBranch.push_back(vMerkleTree[j+i]); + nIndex >>= 1; + j += nSize; + } + return vMerkleBranch; +} + +static inline int ctz(uint32_t i) { + if (i == 0) return 0; + int j = 0; + while (!(i & 1)) { + j++; + i >>= 1; + } + return j; +} + +BOOST_AUTO_TEST_CASE(merkle_test) +{ + for (int i = 0; i < 32; i++) { + // Try 32 block sizes: all sizes from 0 to 16 inclusive, and then 15 random sizes. + int ntx = (i <= 16) ? i : 17 + (InsecureRandRange(4000)); + // Try up to 3 mutations. + for (int mutate = 0; mutate <= 3; mutate++) { + int duplicate1 = mutate >= 1 ? 1 << ctz(ntx) : 0; // The last how many transactions to duplicate first. + if (duplicate1 >= ntx) break; // Duplication of the entire tree results in a different root (it adds a level). + int ntx1 = ntx + duplicate1; // The resulting number of transactions after the first duplication. + int duplicate2 = mutate >= 2 ? 1 << ctz(ntx1) : 0; // Likewise for the second mutation. + if (duplicate2 >= ntx1) break; + int ntx2 = ntx1 + duplicate2; + int duplicate3 = mutate >= 3 ? 1 << ctz(ntx2) : 0; // And for the the third mutation. + if (duplicate3 >= ntx2) break; + int ntx3 = ntx2 + duplicate3; + // Build a block with ntx different transactions. + CBlock block; + block.vtx.resize(ntx); + for (int j = 0; j < ntx; j++) { + CMutableTransaction mtx; + mtx.nLockTime = j; + block.vtx[j] = mtx; + } + // Compute the root of the block before mutating it. + bool unmutatedMutated = false; + uint256 unmutatedRoot = BlockMerkleRoot(block, &unmutatedMutated); + BOOST_CHECK(unmutatedMutated == false); + // Optionally mutate by duplicating the last transactions, resulting in the same merkle root. + block.vtx.resize(ntx3); + for (int j = 0; j < duplicate1; j++) { + block.vtx[ntx + j] = block.vtx[ntx + j - duplicate1]; + } + for (int j = 0; j < duplicate2; j++) { + block.vtx[ntx1 + j] = block.vtx[ntx1 + j - duplicate2]; + } + for (int j = 0; j < duplicate3; j++) { + block.vtx[ntx2 + j] = block.vtx[ntx2 + j - duplicate3]; + } + // Compute the merkle root and merkle tree using the old mechanism. + bool oldMutated = false; + std::vector merkleTree; + uint256 oldRoot = BlockBuildMerkleTree(block, &oldMutated, merkleTree); + // Compute the merkle root using the new mechanism. + bool newMutated = false; + uint256 newRoot = BlockMerkleRoot(block, &newMutated); + BOOST_CHECK(oldRoot == newRoot); + BOOST_CHECK(newRoot == unmutatedRoot); + BOOST_CHECK((newRoot == uint256()) == (ntx == 0)); + BOOST_CHECK(oldMutated == newMutated); + BOOST_CHECK(newMutated == !!mutate); + // If no mutation was done (once for every ntx value), try up to 16 branches. + if (mutate == 0) { + for (int loop = 0; loop < std::min(ntx, 16); loop++) { + // If ntx <= 16, try all branches. Otherise, try 16 random ones. + int mtx = loop; + if (ntx > 16) { + mtx = InsecureRandRange(ntx); + } + std::vector newBranch = BlockMerkleBranch(block, mtx); + std::vector oldBranch = BlockGetMerkleBranch(block, merkleTree, mtx); + BOOST_CHECK(oldBranch == newBranch); + BOOST_CHECK(ComputeMerkleRootFromBranch(block.vtx[mtx].GetHash(), newBranch, mtx) == oldRoot); + } + } + } + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 39b0cde8af8cf..08f081149f45f 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -4,6 +4,7 @@ #include "consensus/validation.h" #include "init.h" +#include "consensus/merkle.h" #include "main.h" #include "miner.h" #include "pubkey.h" @@ -82,7 +83,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) pblock->vtx[0] = CTransaction(txCoinbase); if (txFirst.size() < 2) txFirst.push_back(new CTransaction(pblock->vtx[0])); - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); pblock->nNonce = blockinfo[i].nonce; CValidationState state; BOOST_CHECK(ProcessNewBlock(state, NULL, pblock)); diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index 8598af620fde8..2832c06670d5a 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -7,9 +7,8 @@ #include "streams.h" #include "uint256.h" #include "version.h" -#include "random.h" #include "test/test_phore.h" - +#include "consensus/merkle.h" #include #include @@ -20,8 +19,8 @@ class CPartialMerkleTreeTester : public CPartialMerkleTree public: // flip one bit in one of the hashes - this should break the authentication void Damage() { - unsigned int n = rand() % vHash.size(); - int bit = rand() % 256; + unsigned int n = InsecureRandRange(vHash.size()); + int bit = InsecureRandBits(8); uint256 &hash = vHash[n]; hash ^= ((uint256)1 << bit); } @@ -45,7 +44,7 @@ BOOST_AUTO_TEST_CASE(pmt_test1) } // calculate actual merkle root and height - uint256 merkleRoot1 = block.BuildMerkleTree(); + uint256 merkleRoot1 = BlockMerkleRoot(block); std::vector vTxid(nTx, 0); for (unsigned int j=0; j vMatch(nTx, false); std::vector vMatchTxid1; for (unsigned int j=0; j + +BOOST_FIXTURE_TEST_SUITE(random_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(osrandom_tests) +{ + BOOST_CHECK(Random_SanityCheck()); +} + +BOOST_AUTO_TEST_CASE(fastrandom_tests) +{ + // Check that deterministic FastRandomContexts are deterministic + FastRandomContext ctx1(true); + FastRandomContext ctx2(true); + + BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32()); + BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32()); + BOOST_CHECK_EQUAL(ctx1.rand64(), ctx2.rand64()); + BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3)); + BOOST_CHECK(ctx1.randbytes(17) == ctx2.randbytes(17)); + BOOST_CHECK(ctx1.rand256() == ctx2.rand256()); + BOOST_CHECK_EQUAL(ctx1.randbits(7), ctx2.randbits(7)); + BOOST_CHECK(ctx1.randbytes(128) == ctx2.randbytes(128)); + BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32()); + BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3)); + BOOST_CHECK(ctx1.rand256() == ctx2.rand256()); + BOOST_CHECK(ctx1.randbytes(50) == ctx2.randbytes(50)); + + // Check that a nondeterministic ones are not + { + FastRandomContext ctx3, ctx4; + BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal + } + { + FastRandomContext ctx3, ctx4; + BOOST_CHECK(ctx3.rand256() != ctx4.rand256()); + } + { + FastRandomContext ctx3, ctx4; + BOOST_CHECK(ctx3.randbytes(7) != ctx4.randbytes(7)); + } +} + +BOOST_AUTO_TEST_CASE(fastrandom_randbits) +{ + FastRandomContext ctx1; + FastRandomContext ctx2; + for (int bits = 0; bits < 63; ++bits) { + for (int j = 0; j < 1000; ++j) { + uint64_t rangebits = ctx1.randbits(bits); + BOOST_CHECK_EQUAL(rangebits >> bits, 0); + uint64_t range = ((uint64_t)1) << bits | rangebits; + uint64_t rand = ctx2.randrange(range); + BOOST_CHECK(rand < range); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index 242108e355228..68f6c86d2caa0 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -45,8 +45,6 @@ static void MicroSleep(uint64_t n) BOOST_AUTO_TEST_CASE(manythreads) { - seed_insecure_rand(false); - // Stress test: hundreds of microsecond-scheduled tasks, // serviced by 10 threads. // @@ -61,7 +59,7 @@ BOOST_AUTO_TEST_CASE(manythreads) boost::mutex counterMutex[10]; int counter[10] = { 0 }; - boost::random::mt19937 rng(insecure_rand()); + boost::random::mt19937 rng(42); boost::random::uniform_int_distribution<> zeroToNine(0, 9); boost::random::uniform_int_distribution<> randomMsec(-11, 1000); boost::random::uniform_int_distribution<> randomDelta(-1000, 1000); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 2834dd3c9f50f..db9cf783b5dea 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -5,7 +5,6 @@ #include "consensus/validation.h" #include "data/sighash.json.h" #include "main.h" -#include "random.h" #include "serialize.h" #include "script/script.h" #include "script/interpreter.h" @@ -86,30 +85,30 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un void static RandomScript(CScript &script) { static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR}; script = CScript(); - int ops = (insecure_rand() % 10); + int ops = (InsecureRandRange(10)); for (int i=0; i #include +extern uint256 insecure_rand_seed; +extern FastRandomContext insecure_rand_ctx; + +static inline void SeedInsecureRand(bool fDeterministic = false) +{ + if (fDeterministic) { + insecure_rand_seed = uint256(); + } else { + insecure_rand_seed = GetRandHash(); + } + insecure_rand_ctx = FastRandomContext(insecure_rand_seed); +} + +static inline uint32_t InsecureRand32() { return insecure_rand_ctx.rand32(); } +static inline uint256 InsecureRand256() { return insecure_rand_ctx.rand256(); } +static inline uint64_t InsecureRandBits(int bits) { return insecure_rand_ctx.randbits(bits); } +static inline uint64_t InsecureRandRange(uint64_t range) { return insecure_rand_ctx.randrange(range); } +static inline bool InsecureRandBool() { return insecure_rand_ctx.randbool(); } +static inline std::vector InsecureRandBytes(size_t len) { return insecure_rand_ctx.randbytes(len); } + /** Basic testing setup. * This just configures logging and chain parameters. */ diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 4354a31d48c70..d8540c9d3cccb 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -6,7 +6,6 @@ #include "clientversion.h" #include "primitives/transaction.h" -#include "random.h" #include "sync.h" #include "utilstrencodings.h" #include "utilmoneystr.h" @@ -243,7 +242,7 @@ BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) int i; int count=0; - seed_insecure_rand(true); + SeedInsecureRand(true); for (int mod=2;mod<11;mod++) { @@ -259,7 +258,7 @@ BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) { uint32_t rval; do{ - rval=insecure_rand()&mask; + rval=InsecureRand32()&mask; }while(rval>=(uint32_t)mod); count += rval==0; } diff --git a/src/test/zerocoin_implementation_tests.cpp b/src/test/zerocoin_implementation_tests.cpp index 378e85f793204..aa20753007b97 100644 --- a/src/test/zerocoin_implementation_tests.cpp +++ b/src/test/zerocoin_implementation_tests.cpp @@ -31,7 +31,7 @@ BOOST_AUTO_TEST_CASE(zcparams_test) SelectParams(CBaseChainParams::MAIN); ZerocoinParams *ZCParams = Params().Zerocoin_Params(); (void)ZCParams; - } catch(std::exception& e) { + } catch(const std::exception& e) { fPassed = false; std::cout << e.what() << "\n"; } diff --git a/src/timedata.cpp b/src/timedata.cpp index ddd30a78447f9..1f60f68ef79b4 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -12,7 +12,6 @@ - static CCriticalSection cs_nTimeOffset; static int64_t nTimeOffset = 0; diff --git a/src/txdb.cpp b/src/txdb.cpp index 2e87f1e27c8ab..dcc2c809399aa 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -165,7 +165,7 @@ bool CCoinsViewDB::GetStats(CCoinsStats& stats) const ss << VARINT(0); } pcursor->Next(); - } catch (std::exception& e) { + } catch (const std::exception& e) { return error("%s : Deserialize or I/O error - %s", __func__, e.what()); } } @@ -336,7 +336,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts() } else { break; // if shutdown requested or finished loading block index } - } catch (std::exception& e) { + } catch (const std::exception& e) { return error("%s : Deserialize or I/O error - %s", __func__, e.what()); } } @@ -432,7 +432,7 @@ bool CZerocoinDB::WipeCoins(std::string strType) } else { break; // if shutdown requested or finished loading block index } - } catch (std::exception& e) { + } catch (const std::exception& e) { return error("%s : Deserialize or I/O error - %s", __func__, e.what()); } } diff --git a/src/txmempool.h b/src/txmempool.h index fc38ee1ddbbd3..76a8d8709cb1d 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -15,6 +15,7 @@ #include "indirectmap.h" #include "primitives/transaction.h" #include "sync.h" +#include "random.h" class CAutoFile; diff --git a/src/util.cpp b/src/util.cpp index 3a2e9a9d76870..6e096b35c36b5 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -23,9 +23,6 @@ #include #include -#include -#include -#include #ifndef WIN32 @@ -119,7 +116,6 @@ bool fDebug = false; bool fPrintToConsole = false; bool fPrintToDebugLog = true; bool fDaemon = false; -bool fServer = false; std::string strMiscWarning; bool fLogTimestamps = false; bool fLogIPs = false; @@ -506,7 +502,7 @@ std::string HelpMessageOpt(const std::string &option, const std::string &message std::string("\n\n"); } -static std::string FormatException(std::exception* pex, const char* pszThread) +static std::string FormatException(const std::exception* pex, const char* pszThread) { #ifdef WIN32 char pszModule[MAX_PATH] = ""; @@ -522,7 +518,7 @@ static std::string FormatException(std::exception* pex, const char* pszThread) "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread); } -void PrintExceptionContinue(std::exception* pex, const char* pszThread) +void PrintExceptionContinue(const std::exception* pex, const char* pszThread) { std::string message = FormatException(pex, pszThread); LogPrintf("\n\n************************\n%s\n", message); @@ -693,7 +689,7 @@ bool TryCreateDirectory(const boost::filesystem::path& p) { try { return boost::filesystem::create_directory(p); - } catch (boost::filesystem::filesystem_error) { + } catch (const boost::filesystem::filesystem_error) { if (!boost::filesystem::exists(p) || !boost::filesystem::is_directory(p)) throw; } diff --git a/src/util.h b/src/util.h index d45091469ebb1..5f8f457026d8b 100644 --- a/src/util.h +++ b/src/util.h @@ -63,7 +63,6 @@ extern std::map > mapMultiArgs; extern bool fDebug; extern bool fPrintToConsole; extern bool fPrintToDebugLog; -extern bool fServer; extern std::string strMiscWarning; extern bool fLogTimestamps; extern bool fLogIPs; @@ -95,7 +94,7 @@ template std::string FormatStringFromLogArgs(const char *fmt, std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \ try { \ _log_msg_ = tfm::format(format, TINYFORMAT_PASSARGS(n)); \ - } catch (std::runtime_error &e) { \ + } catch (const std::runtime_error &e) { \ _log_msg_ = "Error \"" + std::string(e.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(format, TINYFORMAT_PASSARGS(n));\ } \ return LogPrintStr(_log_msg_); \ @@ -107,7 +106,7 @@ template std::string FormatStringFromLogArgs(const char *fmt, std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \ try { \ _log_msg_ = tfm::format(format, TINYFORMAT_PASSARGS(n)); \ - } catch (std::runtime_error &e) { \ + } catch (const std::runtime_error &e) { \ _log_msg_ = "Error \"" + std::string(e.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(format, TINYFORMAT_PASSARGS(n));\ } \ LogPrintStr(std::string("ERROR: ") + _log_msg_ + "\n"); \ @@ -133,7 +132,7 @@ static inline bool error(const char* format) double double_safe_addition(double fValue, double fIncrement); double double_safe_multiplication(double fValue, double fmultiplicator); -void PrintExceptionContinue(std::exception* pex, const char* pszThread); +void PrintExceptionContinue(const std::exception* pex, const char* pszThread); void ParseParameters(int argc, const char* const argv[]); void FileCommit(FILE* fileout); bool TruncateFile(FILE* file, unsigned int length); @@ -245,10 +244,10 @@ void TraceThread(const char* name, Callable func) LogPrintf("%s thread start\n", name); func(); LogPrintf("%s thread exit\n", name); - } catch (boost::thread_interrupted) { + } catch (const boost::thread_interrupted&) { LogPrintf("%s thread interrupt\n", name); throw; - } catch (std::exception& e) { + } catch (const std::exception& e) { PrintExceptionContinue(&e, name); throw; } catch (...) { diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index cf0b9c3e2f1d0..05b8d9edceae0 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -55,7 +55,7 @@ bool validateURL(std::string strURL, std::string& strErr, unsigned int maxSize) // check fronts bool found = false; - for (int i=0; i < reqPre.size() && !found; i++) { + for (int i=0; i < (int) reqPre.size() && !found; i++) { if (strURL.find(reqPre[i]) == 0) found = true; } if ((!found) && (reqPre.size() > 0)) { diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index 9e742553cabe0..23740186117be 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -121,9 +121,7 @@ bool DecryptAES256(const SecureString& sKey, const std::string& sCiphertext, con class CCryptoKeyStore : public CBasicKeyStore { private: - CryptedKeyMap mapCryptedKeys; CHDChain cryptedHDChain; - CKeyingMaterial vMasterKey; //! if fUseCrypto is true, mapKeys must be empty @@ -147,6 +145,8 @@ class CCryptoKeyStore : public CBasicKeyStore bool Unlock(const CKeyingMaterial& vMasterKeyIn); + CryptedKeyMap mapCryptedKeys; + public: CCryptoKeyStore() : fUseCrypto(false), fDecryptionThoroughlyChecked(false) { diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 640be8e554b76..7b115531c053a 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -222,10 +222,11 @@ void CDBEnv::CheckpointLSN(const std::string& strFile) } -CDB::CDB(const std::string& strFilename, const char* pszMode, int nSerVersion) : pdb(NULL), activeTxn(NULL) +CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnCloseIn) : pdb(NULL), activeTxn(NULL) { int ret; fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); + fFlushOnClose = fFlushOnCloseIn; if (strFilename.empty()) return; @@ -264,8 +265,9 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, int nSerVersion) : delete pdb; pdb = NULL; --bitdb.mapFileUseCount[strFile]; + std::string tempCopy(strFile); strFile = ""; - throw std::runtime_error(strprintf("CDB : Error %d, can't open database %s", ret, strFile)); + throw std::runtime_error(strprintf("CDB : Error %d, can't open database %s", ret, tempCopy)); } if (fCreate && !Exists(std::string("version"))) { @@ -302,7 +304,8 @@ void CDB::Close() activeTxn = NULL; pdb = NULL; - Flush(); + if (fFlushOnClose) + Flush(); { LOCK(bitdb.cs_db); diff --git a/src/wallet/db.h b/src/wallet/db.h index 7048da49be684..07ba197e8ccac 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -105,7 +105,9 @@ class CDB bool fReadOnly; int nSerVersion; - explicit CDB(const std::string& strFilename, const char* pszMode = "r+", int nSerVersion = CLIENT_VERSION); + bool fFlushOnClose; + + explicit CDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnCloseIn=true); ~CDB() { Close(); } public: diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 23a03444454a3..f0797ece741b9 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -24,8 +24,6 @@ #include #include -#include -#include #include diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 3f3358767ad6b..653eacd8bf14b 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -56,6 +56,8 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); entry.push_back(Pair("blockindex", wtx.nIndex)); entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); + } else { + entry.push_back(Pair("trusted", wtx.IsTrusted())); } uint256 hash = wtx.GetHash(); entry.push_back(Pair("txid", hash.GetHex())); @@ -644,13 +646,16 @@ CAmount GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, in // Tally wallet transactions for (std::map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (!IsFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) + bool fConflicted; + int depth = wtx.GetDepthAndMempool(fConflicted); + + if (!IsFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || depth < 0 || fConflicted) continue; CAmount nReceived, nSent, nFee; wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); - if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) + if (nReceived != 0 && depth >= nMinDepth) nBalance += nReceived; nBalance -= nSent + nFee; } @@ -711,7 +716,10 @@ UniValue getbalance(const UniValue& params, bool fHelp) CAmount nBalance = 0; for (std::map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (!IsFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) + bool fConflicted; + int depth = wtx.GetDepthAndMempool(fConflicted); + + if (!IsFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || depth < 0 || fConflicted) continue; CAmount allFee; @@ -719,7 +727,7 @@ UniValue getbalance(const UniValue& params, bool fHelp) std::list listReceived; std::list listSent; wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); - if (wtx.GetDepthInMainChain() >= nMinDepth) { + if (depth >= nMinDepth) { for (const COutputEntry& r : listReceived) nBalance += r.amount; } @@ -1081,7 +1089,7 @@ UniValue addwitnessaddress(const UniValue& params, bool fHelp) throw std::runtime_error(msg); } - if (!IsSporkActive(SPORK_17_SEGWIT_ACTIVATION) && !GetBoolArg("-walletprematurewitness", false)) { + if (!sporkManager.IsSporkActive(SPORK_17_SEGWIT_ACTIVATION) && !GetBoolArg("-walletprematurewitness", false)) { throw JSONRPCError(RPC_WALLET_ERROR, "Segregated witness not enabled on network"); } @@ -1350,7 +1358,8 @@ void ListTransactions(const CWalletTx& wtx, const std::string& strAccount, int n } // Received - if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) { + int depth = wtx.GetDepthInMainChain(); + if (listReceived.size() > 0 && depth >= nMinDepth) { for (const COutputEntry& r : listReceived) { std::string account; if (pwalletMain->mapAddressBook.count(r.destination)) @@ -1362,7 +1371,7 @@ void ListTransactions(const CWalletTx& wtx, const std::string& strAccount, int n entry.push_back(Pair("account", account)); MaybePushAddress(entry, r.destination); if (wtx.IsCoinBase()) { - if (wtx.GetDepthInMainChain() < 1) + if (depth < 1) entry.push_back(Pair("category", "orphan")); else if (wtx.GetBlocksToMaturity() > 0) entry.push_back(Pair("category", "immature")); @@ -1429,6 +1438,9 @@ UniValue listtransactions(const UniValue& params, bool fHelp) " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" " 'receive' category of transactions.\n" " \"bcconfirmations\": n, (numeric) The number of blockchain confirmations for the transaction. Available for 'send'\n" + " 'receive' category of transactions. Negative confirmations indicate the\n" + " transation conflicts with the block chain\n" + " \"trusted\": xxx (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n" " and 'receive' category of transactions.\n" " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n" " category of transactions.\n" @@ -1557,8 +1569,9 @@ UniValue listaccounts(const UniValue& params, bool fHelp) std::string strSentAccount; std::list listReceived; std::list listSent; - int nDepth = wtx.GetDepthInMainChain(); - if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) + bool fConflicted; + int nDepth = wtx.GetDepthAndMempool(fConflicted); + if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0 || fConflicted) continue; wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); mapAccountBalances[strSentAccount] -= nFee; @@ -1739,6 +1752,39 @@ UniValue gettransaction(const UniValue& params, bool fHelp) return entry; } +UniValue abandontransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw std::runtime_error( + "abandontransaction \"txid\"\n" + "\nMark in-wallet transaction as abandoned\n" + "This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n" + "for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n" + "It only works on transactions which are not included in a block and are not currently in the mempool.\n" + "It has no effect on transactions which are already conflicted or abandoned.\n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + "\nResult:\n" + "\nExamples:\n" + + HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + + HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + ); + + EnsureWalletIsUnlocked(); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + uint256 hash; + hash.SetHex(params[0].get_str()); + + if (!pwalletMain->mapWallet.count(hash)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + if (!pwalletMain->AbandonTransaction(hash)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment"); + + return NullUniValue; +} + UniValue backupwallet(const UniValue& params, bool fHelp) { @@ -2039,29 +2085,64 @@ UniValue lockunspent(const UniValue& params, bool fHelp) return true; } - UniValue outputs = params[1].get_array(); - for (unsigned int idx = 0; idx < outputs.size(); idx++) { - const UniValue& output = outputs[idx]; + UniValue output_params = params[1].get_array(); + + // Create and validate the COutPoints first. + std::vector outputs; + outputs.reserve(output_params.size()); + + for (unsigned int idx = 0; idx < output_params.size(); idx++) { + const UniValue& output = output_params[idx]; if (!output.isObject()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); const UniValue& o = output.get_obj(); RPCTypeCheckObj(o, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)); - std::string txid = find_value(o, "txid").get_str(); - if (!IsHex(txid)) + const std::string& txid = find_value(o, "txid").get_str(); + if (!IsHex(txid)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); + } - int nOutput = find_value(o, "vout").get_int(); - if (nOutput < 0) + const int nOutput = find_value(o, "vout").get_int(); + if (nOutput < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); + } - COutPoint outpt(uint256(txid), nOutput); + const COutPoint outpt(uint256(txid), nOutput); - if (fUnlock) - pwalletMain->UnlockCoin(outpt); - else - pwalletMain->LockCoin(outpt); + const auto it = pwalletMain->mapWallet.find(outpt.hash); + if (it == pwalletMain->mapWallet.end()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, unknown transaction"); + } + + const CWalletTx& wtx = it->second; + + if (outpt.n >= wtx.vout.size()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds"); + } + + if (pwalletMain->IsSpent(outpt.hash, outpt.n)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output"); + } + + const bool is_locked = pwalletMain->IsLockedCoin(outpt.hash, outpt.n); + + if (fUnlock && !is_locked) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output"); + } + + if (!fUnlock && is_locked) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked"); + } + + outputs.push_back(outpt); + } + + // Atomically set (un)locked status for the outputs. + for (const COutPoint& outpt : outputs) { + if (fUnlock) pwalletMain->UnlockCoin(outpt); + else pwalletMain->LockCoin(outpt); } return true; @@ -2866,7 +2947,8 @@ UniValue mintzerocoin(const UniValue& params, bool fHelp) } int64_t nTime = GetTimeMillis(); - if(GetAdjustedTime() > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE)) + + if(sporkManager.IsSporkActive(SPORK_16_ZEROCOIN_MAINTENANCE_MODE)) throw JSONRPCError(RPC_WALLET_ERROR, "zPHR is currently disabled due to maintenance."); EnsureWalletIsUnlocked(true); @@ -2973,19 +3055,17 @@ UniValue spendzerocoin(const UniValue& params, bool fHelp) HelpExampleRpc("spendzerocoin", "5000 false true 100 \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\"")); LOCK2(cs_main, pwalletMain->cs_wallet); - - if(GetAdjustedTime() > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE)) - throw JSONRPCError(RPC_WALLET_ERROR, "zPHR is currently disabled due to maintenance."); - int64_t nTimeStart = GetTimeMillis(); + if(sporkManager.IsSporkActive(SPORK_16_ZEROCOIN_MAINTENANCE_MODE)) + throw JSONRPCError(RPC_WALLET_ERROR, "zPIV is currently disabled due to maintenance."); + EnsureWalletIsUnlocked(); CAmount nAmount = AmountFromValue(params[0]); // Spending amount bool fMintChange = params[1].get_bool(); // Mint change to zPHR bool fMinimizeChange = params[2].get_bool(); // Minimize change int nSecurityLevel = params[3].get_int(); // Security level - CTxDestination address = CNoDestination(); // Optional sending address. Dummy initialization here. if (params.size() == 5) { // Destination address was supplied as params[4]. Optional parameters MUST be at the end @@ -3619,7 +3699,7 @@ void static SearchThread(CzPHRWallet* zwallet, int nCountStart, int nCountEnd) zwallet->AddToMintPool(std::make_pair(hashPubcoin, i), true); walletDB.WriteMintPoolPair(hashSeed, hashPubcoin, i); } - } catch (std::exception& e) { + } catch (const std::exception& e) { LogPrintf("SearchThread() exception"); } catch (...) { LogPrintf("SearchThread() exception"); @@ -3674,4 +3754,4 @@ UniValue searchdzphr(const UniValue& params, bool fHelp) //todo: better response return "done"; -} +} \ No newline at end of file diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 172df103cb384..12a2e35a45855 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -64,6 +64,8 @@ OutputType g_change_type = OUTPUT_TYPE_NONE; */ CFeeRate CWallet::minTxFee = CFeeRate(10000); +const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); + /** @defgroup mapWallet * * @{ @@ -636,20 +638,27 @@ void CWallet::SyncMetaData(std::pair ran // So: find smallest nOrderPos: int nMinOrderPos = std::numeric_limits::max(); - const CWalletTx* copyFrom = NULL; + const CWalletTx* copyFrom = nullptr; for (TxSpends::iterator it = range.first; it != range.second; ++it) { - const uint256& hash = it->second; - int n = mapWallet[hash].nOrderPos; + const CWalletTx* wtx = &mapWallet.at(it->second); + int n = wtx->nOrderPos; if (n < nMinOrderPos) { nMinOrderPos = n; - copyFrom = &mapWallet[hash]; + copyFrom = wtx; } } + + if (!copyFrom) { + return; + } + // Now copy data from copyFrom to rest: for (TxSpends::iterator it = range.first; it != range.second; ++it) { const uint256& hash = it->second; CWalletTx* copyTo = &mapWallet[hash]; if (copyFrom == copyTo) continue; + assert(copyFrom && "Oldest wallet transaction in range assumed to have been found."); + if (!copyFrom->IsEquivalentTo(*copyTo)) continue; copyTo->mapValue = copyFrom->mapValue; copyTo->vOrderForm = copyFrom->vOrderForm; // fTimeReceivedIsTxTime not copied on purpose @@ -674,8 +683,14 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const for (TxSpends::const_iterator it = range.first; it != range.second; ++it) { const uint256& wtxid = it->second; std::map::const_iterator mit = mapWallet.find(wtxid); - if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0) - return true; // Spent + if (mit != mapWallet.end()) { + bool fConflicted; + const int nDepth = mit->second.GetDepthAndMempool(fConflicted); + // not in mempool txes can spend coins only if not coinstakes + const bool fConflictedCoinstake = fConflicted && mit->second.IsCoinStake(); + if (nDepth > 0 || (nDepth == 0 && !mit->second.isAbandoned() && !fConflictedCoinstake) ) + return true; // Spent + } } return false; } @@ -683,6 +698,8 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid) { mapTxSpends.insert(std::make_pair(outpoint, wtxid)); + setLockedCoins.erase(outpoint); + std::pair range; range = mapTxSpends.equal_range(outpoint); SyncMetaData(range); @@ -1042,7 +1059,7 @@ void CWallet::MarkDirty() } } -bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) +bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb) { uint256 hash = wtxIn.GetHash(); @@ -1062,24 +1079,34 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) if (fInsertedNew) { if (!wtx.nTimeReceived) wtx.nTimeReceived = GetAdjustedTime(); - wtx.nOrderPos = IncOrderPosNext(); + wtx.nOrderPos = IncOrderPosNext(pwalletdb); + wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); wtx.nTimeSmart = ComputeTimeSmart(wtx); AddToSpends(hash); - - // wqking -- fix a bug that listtransactions doesn't return recent transactions. - wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); + for (const CTxIn& txin : wtx.vin) { + if (mapWallet.count(txin.prevout.hash)) { + CWalletTx& prevtx = mapWallet[txin.prevout.hash]; + if (prevtx.nIndex == -1 && !prevtx.hashUnset()) { + MarkConflicted(prevtx.hashBlock, wtx.GetHash()); + } + } + } } bool fUpdated = false; if (!fInsertedNew) { // Merge - if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) { + if (!wtxIn.hashUnset() && wtxIn.hashBlock != wtx.hashBlock) { wtx.hashBlock = wtxIn.hashBlock; fUpdated = true; } - if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) { - wtx.vMerkleBranch = wtxIn.vMerkleBranch; + // If no longer abandoned, update + if (wtxIn.hashBlock.IsNull() && wtx.isAbandoned()) { + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + if (wtxIn.nIndex != -1 && wtxIn.nIndex != wtx.nIndex) { wtx.nIndex = wtxIn.nIndex; fUpdated = true; } @@ -1094,7 +1121,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) // Write to disk if (fInsertedNew || fUpdated) - if (!wtx.WriteToDisk()) + if (!wtx.WriteToDisk(pwalletdb)) return false; // Break debit/credit balance caches: @@ -1123,6 +1150,20 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl { { AssertLockHeld(cs_wallet); + + if (pblock && !tx.IsCoinBase()) { + for (const CTxIn& txin : tx.vin) { + std::pair range = mapTxSpends.equal_range(txin.prevout); + while (range.first != range.second) { + if (range.first->second != tx.GetHash()) { + LogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), pblock->GetHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); + MarkConflicted(pblock->GetHash(), range.first->second); + } + range.first++; + } + } + } + bool fExisted = mapWallet.count(tx.GetHash()) != 0; if (fExisted && !fUpdate) return false; if (fExisted || IsMine(tx) || IsFromMe(tx)) { @@ -1130,12 +1171,127 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl // Get merkle branch if transaction was found in a block if (pblock) wtx.SetMerkleBranch(*pblock); - return AddToWallet(wtx); + // Do not flush the wallet here for performance reasons + // this is safe, as in case of a crash, we rescan the necessary blocks on startup through our SetBestChain-mechanism + CWalletDB walletdb(strWalletFile, "r+", false); + + return AddToWallet(wtx, false, &walletdb); } } return false; } +bool CWallet::AbandonTransaction(const uint256& hashTx) +{ + LOCK2(cs_main, cs_wallet); + + CWalletDB walletdb(strWalletFile, "r+"); + + std::set todo; + std::set done; + + // Can't mark abandoned if confirmed or in mempool + assert(mapWallet.count(hashTx)); + CWalletTx& origtx = mapWallet[hashTx]; + if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) { + return false; + } + + todo.insert(hashTx); + + while (!todo.empty()) { + uint256 now = *todo.begin(); + todo.erase(now); + done.insert(now); + assert(mapWallet.count(now)); + CWalletTx& wtx = mapWallet[now]; + int currentconfirm = wtx.GetDepthInMainChain(); + // If the orig tx was not in block, none of its spends can be + assert(currentconfirm <= 0); + // if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon} + if (currentconfirm == 0 && !wtx.isAbandoned()) { + // If the orig tx was not in block/mempool, none of its spends can be in mempool + assert(!wtx.InMempool()); + wtx.nIndex = -1; + wtx.setAbandoned(); + wtx.MarkDirty(); + wtx.WriteToDisk(&walletdb); + NotifyTransactionChanged(this, wtx.GetHash(), CT_UPDATED); + // Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too + TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0)); + while (iter != mapTxSpends.end() && iter->first.hash == now) { + if (!done.count(iter->second)) { + todo.insert(iter->second); + } + iter++; + } + // If a transaction changes 'conflicted' state, that changes the balance + // available of the outputs it spends. So force those to be recomputed + for (const CTxIn& txin: wtx.vin) + { + if (mapWallet.count(txin.prevout.hash)) + mapWallet[txin.prevout.hash].MarkDirty(); + } + } + } + + return true; +} + +void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) +{ + LOCK2(cs_main, cs_wallet); + + CBlockIndex* pindex; + assert(mapBlockIndex.count(hashBlock)); + pindex = mapBlockIndex[hashBlock]; + int conflictconfirms = 0; + if (chainActive.Contains(pindex)) { + conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); + } + assert(conflictconfirms < 0); + + // Do not flush the wallet here for performance reasons + CWalletDB walletdb(strWalletFile, "r+", false); + + std::set todo; + std::set done; + + todo.insert(hashTx); + + while (!todo.empty()) { + uint256 now = *todo.begin(); + todo.erase(now); + done.insert(now); + assert(mapWallet.count(now)); + CWalletTx& wtx = mapWallet[now]; + int currentconfirm = wtx.GetDepthInMainChain(); + if (conflictconfirms < currentconfirm) { + // Block is 'more conflicted' than current confirm; update. + // Mark transaction as conflicted with this block. + wtx.nIndex = -1; + wtx.hashBlock = hashBlock; + wtx.MarkDirty(); + wtx.WriteToDisk(&walletdb); + // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too + TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0)); + while (iter != mapTxSpends.end() && iter->first.hash == now) { + if (!done.count(iter->second)) { + todo.insert(iter->second); + } + iter++; + } + // If a transaction changes 'conflicted' state, that changes the balance + // available of the outputs it spends. So force those to be recomputed + for (const CTxIn& txin: wtx.vin) + { + if (mapWallet.count(txin.prevout.hash)) + mapWallet[txin.prevout.hash].MarkDirty(); + } + } + } +} + void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock) { LOCK2(cs_main, cs_wallet); @@ -1159,6 +1315,7 @@ void CWallet::EraseFromWallet(const uint256& hash) LOCK(cs_wallet); if (mapWallet.erase(hash)) CWalletDB(strWalletFile).EraseTx(hash); + LogPrintf("%s: Erased wtx %s from wallet\n", __func__, hash.GetHex()); } return; } @@ -1266,7 +1423,7 @@ int CWalletTx::GetRequestCount() const LOCK(pwallet->cs_wallet); if (IsCoinBase()) { // Generated block - if (hashBlock != 0) { + if (!hashUnset()) { std::map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); if (mi != pwallet->mapRequestCount.end()) nRequests = (*mi).second; @@ -1278,7 +1435,7 @@ int CWalletTx::GetRequestCount() const nRequests = (*mi).second; // How about the block it's in? - if (nRequests == 0 && hashBlock != 0) { + if (nRequests == 0 && !hashUnset()) { std::map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); if (mi != pwallet->mapRequestCount.end()) nRequests = (*mi).second; @@ -1612,8 +1769,10 @@ void CWalletTx::GetAccountAmounts(const std::string& strAccount, CAmount& nRecei } -bool CWalletTx::WriteToDisk() +bool CWalletTx::WriteToDisk(CWalletDB *pwalletdb) { + if (pwalletdb) + return pwalletdb->WriteTx(GetHash(), *this); return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this); } @@ -1621,8 +1780,9 @@ bool CWalletTx::WriteToDisk() * Scan the block chain (starting in pindexStart) for transactions * from or to us. If fUpdate is true, found transactions that already * exist in the wallet will be updated. + * @returns -1 if process was cancelled or the number of tx added to the wallet. */ -int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) +int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate, bool fromStartup) { int ret = 0; int64_t nNow = GetTime(); @@ -1648,6 +1808,10 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((Checkpoints::GuessVerificationProgress(pindex, false) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); + if (fromStartup && ShutdownRequested()) { + return -1; + } + CBlock block; ReadBlockFromDisk(block, pindex); for (CTransaction& tx : block.vtx) { @@ -1659,7 +1823,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) if (fCheckZPHR && pindex->nHeight >= Params().Zerocoin_StartHeight()) { std::list listMints; BlockToZerocoinMintList(block, listMints); - + CWalletDB walletdb(strWalletFile); for (auto& m : listMints) { if (IsMyMint(m.GetValue())) { LogPrint("zero", "%s: found mint\n", __func__); @@ -1674,7 +1838,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) CWalletTx wtx(pwalletMain, tx); wtx.nTimeReceived = block.GetBlockTime(); wtx.SetMerkleBranch(block); - pwalletMain->AddToWallet(wtx); + pwalletMain->AddToWallet(wtx, false, &walletdb); setAddedToWallet.insert(txid); } } @@ -1694,7 +1858,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) wtx.SetMerkleBranch(blockSpend); wtx.nTimeReceived = pindexSpend->nTime; - pwalletMain->AddToWallet(wtx); + pwalletMain->AddToWallet(wtx, false, &walletdb); setAddedToWallet.emplace(txidSpend); } } @@ -1712,22 +1876,40 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) return ret; } -void CWallet::ReacceptWalletTransactions() +void CWallet::ReacceptWalletTransactions(bool fFirstLoad) { LOCK2(cs_main, cs_wallet); - for (PAIRTYPE(const uint256, CWalletTx) & item : mapWallet) { + std::map mapSorted; + + // Sort pending wallet transactions based on their initial wallet insertion order + for (PAIRTYPE(const uint256, CWalletTx)& item: mapWallet) { const uint256& wtxid = item.first; CWalletTx& wtx = item.second; assert(wtx.GetHash() == wtxid); int nDepth = wtx.GetDepthInMainChain(); - if (!wtx.IsCoinBase() && nDepth < 0 && !wtx.IsCoinStake()) { + if (!wtx.IsCoinBase() && !wtx.IsCoinStake() && nDepth == 0 && !wtx.isAbandoned()) { + mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); // Try to add to memory pool LOCK(mempool.cs); wtx.AcceptToMemoryPool(false); } } + + // Try to add wallet transactions to memory pool + for (PAIRTYPE(const int64_t, CWalletTx*)& item: mapSorted) + { + CWalletTx& wtx = *(item.second); + + LOCK(mempool.cs); + bool fSuccess = wtx.AcceptToMemoryPool(false); + if (!fSuccess && fFirstLoad && GetTime() - wtx.GetTxTime() > 12*60*60) { + //First load of wallet, failed to accept to mempool, and older than 12 hours... not likely to ever + //make it in to mempool + AbandonTransaction(wtx.GetHash()); + } + } } bool CWalletTx::InMempool() const @@ -1742,8 +1924,8 @@ bool CWalletTx::InMempool() const void CWalletTx::RelayWalletTransaction(std::string strCommand) { LOCK(cs_main); - if (!IsCoinBase()) { - if (GetDepthInMainChain() == 0) { + if (!IsCoinBase() && !IsCoinStake()) { + if (GetDepthInMainChain() == 0 && !isAbandoned()) { uint256 hash = GetHash(); LogPrintf("Relaying wtx %s\n", hash.ToString()); @@ -1917,7 +2099,7 @@ CAmount CWallet::GetUnconfirmedBalance() const LOCK2(cs_main, cs_wallet); for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) + if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) nTotal += pcoin->GetAvailableCredit(); } } @@ -1959,7 +2141,7 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const LOCK2(cs_main, cs_wallet); for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) + if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) nTotal += pcoin->GetAvailableWatchOnlyCredit(); } } @@ -2102,7 +2284,7 @@ static void ApproximateBestSubset(std::vector= nTargetValue) { @@ -2613,6 +2795,7 @@ bool CWallet::CreateTransactionHelper(const std::vectorGetDepthInMainChain(); + assert(age >= 0); if (age != 0) age += 1; dPriority += (double)nCredit * age; @@ -2909,14 +3092,14 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, std: // This is only to keep the database open to defeat the auto-flush for the // duration of this scope. This is the only place where this optimization // maybe makes sense; please don't do it anywhere else. - CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile, "r") : NULL; + CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile, "r+") : NULL; // Take key pair from key pool so it won't be used again reservekey.KeepKey(); // Add tx to wallet, because if it has change it's also ours, // otherwise just for transaction history. - AddToWallet(wtxNew); + AddToWallet(wtxNew, false, pwalletdb); // Notify that old coins are spent if (!wtxNew.IsZerocoinSpend()) { @@ -3006,7 +3189,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { if (!fFileBacked) return DB_LOAD_OK; - fFirstRunRet = false; + DBErrors nLoadWalletRet = CWalletDB(strWalletFile, "cr+").LoadWallet(this); if (nLoadWalletRet == DB_NEED_REWRITE) { if (CDB::Rewrite(strWalletFile, "\x04pool")) { @@ -3018,9 +3201,11 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) } } + // This wallet is in its first run if all of these are empty + fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapMasterKeys.empty() && setWatchOnly.empty() && mapScripts.empty(); + if (nLoadWalletRet != DB_LOAD_OK) return nLoadWalletRet; - fFirstRunRet = !vchDefaultKey.IsValid(); uiInterface.LoadWallet(this); @@ -3092,16 +3277,6 @@ bool CWallet::DelAddressBook(const CTxDestination& address) return CWalletDB(strWalletFile).EraseName(EncodeDestination(address)); } -bool CWallet::SetDefaultKey(const CPubKey& vchPubKey) -{ - if (fFileBacked) { - if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) - return false; - } - vchDefaultKey = vchPubKey; - return true; -} - /** * Mark old keypool keys as used, * and generate all new keys @@ -3331,7 +3506,10 @@ std::map CWallet::GetAddressBalances() if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) continue; - int nDepth = pcoin->GetDepthInMainChain(); + bool fConflicted; + int nDepth = pcoin->GetDepthAndMempool(fConflicted); + if (fConflicted) + continue; if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) continue; @@ -3525,13 +3703,13 @@ bool CWallet::UpdatedTransaction(const uint256& hashTx) return false; } -void CWallet::LockCoin(COutPoint& output) +void CWallet::LockCoin(const COutPoint& output) { AssertLockHeld(cs_wallet); // setLockedCoins setLockedCoins.insert(output); } -void CWallet::UnlockCoin(COutPoint& output) +void CWallet::UnlockCoin(const COutPoint& output) { AssertLockHeld(cs_wallet); // setLockedCoins setLockedCoins.erase(output); @@ -3543,10 +3721,10 @@ void CWallet::UnlockAllCoins() setLockedCoins.clear(); } -bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const +bool CWallet::IsLockedCoin(const uint256& hash, unsigned int n) const { AssertLockHeld(cs_wallet); // setLockedCoins - COutPoint outpt(hash, n); + const COutPoint outpt(hash, n); return (setLockedCoins.count(outpt) > 0); } @@ -3775,7 +3953,7 @@ void CWallet::MarkPreSplitKeys() void CWallet::AutoZeromint() { // Don't bother Autominting if Zerocoin Protocol isn't active - if (GetAdjustedTime() > GetSporkValue(SPORK_16_ZEROCOIN_MAINTENANCE_MODE)) return; + if (sporkManager.IsSporkActive(SPORK_16_ZEROCOIN_MAINTENANCE_MODE)) return; // Wait until blockchain + masternodes are fully synced and wallet is unlocked. if (!masternodeSync.IsSynced() || IsLocked()){ @@ -4122,15 +4300,11 @@ int CMerkleTx::SetMerkleBranch(const CBlock& block) if (block.vtx[nIndex] == *(CTransaction*)this) break; if (nIndex == (int)block.vtx.size()) { - vMerkleBranch.clear(); nIndex = -1; LogPrintf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); return 0; } - // Fill in merkle branch - vMerkleBranch = block.GetMerkleBranch(nIndex); - // Is the tx in a block that's in the main chain BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi == mapBlockIndex.end()) @@ -4142,37 +4316,28 @@ int CMerkleTx::SetMerkleBranch(const CBlock& block) return chainActive.Height() - pindex->nHeight + 1; } -int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex*& pindexRet) const +int CMerkleTx::GetDepthInMainChain(const CBlockIndex*& pindexRet, bool enableIX) const { - if (hashBlock == 0 || nIndex == -1) + if (hashUnset()) return 0; AssertLockHeld(cs_main); + int nResult; // Find the block it claims to be in BlockMap::iterator mi = mapBlockIndex.find(hashBlock); - if (mi == mapBlockIndex.end()) - return 0; - CBlockIndex* pindex = (*mi).second; - if (!pindex || !chainActive.Contains(pindex)) - return 0; - - // Make sure the merkle branch connects to this block - if (!fMerkleVerified) { - if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) - return 0; - fMerkleVerified = true; + if (mi == mapBlockIndex.end()) { + nResult = 0; + } + else { + CBlockIndex* pindex = (*mi).second; + if (!pindex || !chainActive.Contains(pindex)) { + nResult = 0; + } + else { + pindexRet = pindex; + nResult = ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1); + } } - - pindexRet = pindex; - return chainActive.Height() - pindex->nHeight + 1; -} - -int CMerkleTx::GetDepthInMainChain(const CBlockIndex*& pindexRet, bool enableIX) const -{ - AssertLockHeld(cs_main); - int nResult = GetDepthInMainChainINTERNAL(pindexRet); - if (nResult == 0 && !mempool.exists(GetHash())) - return -1; // Not in chain, not in mempool if (enableIX) { if (nResult < 6) { @@ -4207,7 +4372,7 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectInsaneFee, bool int CMerkleTx::GetTransactionLockSignatures() const { if (fLargeWorkForkFound || fLargeWorkInvalidChainFound) return -2; - if (!IsSporkActive(SPORK_2_SWIFTTX)) return -3; + if (!sporkManager.IsSporkActive(SPORK_2_SWIFTTX)) return -3; if (!fEnableSwiftTX) return -1; //compile consessus vote @@ -5241,3 +5406,331 @@ bool CWallet::DatabaseMint(CDeterministicMint& dMint) return true; } +CWallet::CWallet() +{ + SetNull(); +} + +CWallet::CWallet(std::string strWalletFileIn) +{ + SetNull(); + strWalletFile = strWalletFileIn; + fFileBacked = true; +} + +CWallet::~CWallet() +{ + delete pwalletdbEncryption; +} + +void CWallet::SetNull() +{ + nWalletVersion = FEATURE_BASE; + nWalletMaxVersion = FEATURE_BASE; + fFileBacked = false; + nMasterKeyMaxID = 0; + pwalletdbEncryption = NULL; + nOrderPosNext = 0; + nNextResend = 0; + nLastResend = 0; + nTimeFirstKey = 0; + fWalletUnlockAnonymizeOnly = false; + fBackupMints = false; + + // Stake Settings + nHashDrift = 45; + nStakeSplitThreshold = 500; + nHashInterval = 22; + nStakeSetUpdateTime = 300; // 5 minutes + + //MultiSend + vMultiSend.clear(); + fMultiSendStake = false; + fMultiSendMasternodeReward = false; + fMultiSendNotify = false; + strMultiSendChangeAddress = ""; + nLastMultiSendHeight = 0; + vDisabledAddresses.clear(); + + //Auto Combine Dust + fCombineDust = false; + nAutoCombineThreshold = 0; +} + +int CWallet::getZeromintPercentage() +{ + return nZeromintPercentage; +} + +void CWallet::setZWallet(CzPHRWallet* zwallet) +{ + zwalletMain = zwallet; + zphrTracker = std::unique_ptr(new CzPHRTracker(strWalletFile)); +} + +CzPHRWallet* CWallet::getZWallet() +{ + return zwalletMain; +} + + +bool CWallet::isZeromintEnabled() +{ + return fEnableZeromint; +} + +void CWallet::setZPhrAutoBackups(bool fEnabled) +{ + fBackupMints = fEnabled; +} + +bool CWallet::isMultiSendEnabled() +{ + return fMultiSendMasternodeReward || fMultiSendStake; +} + +void CWallet::setMultiSendDisabled() +{ + fMultiSendMasternodeReward = false; + fMultiSendStake = false; +} + +bool CWallet::CanSupportFeature(enum WalletFeature wf) +{ + AssertLockHeld(cs_wallet); + return nWalletMaxVersion >= wf; +} + +bool CWallet::LoadMinVersion(int nVersion) +{ + AssertLockHeld(cs_wallet); + nWalletVersion = nVersion; + nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); + return true; +} + +isminetype CWallet::IsMine(const CTxOut& txout) const +{ + return ::IsMine(*this, txout.scriptPubKey); +} + +CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const +{ + if (!MoneyRange(txout.nValue)) + throw std::runtime_error("CWallet::GetCredit() : value out of range"); + return ((IsMine(txout) & filter) ? txout.nValue : 0); +} + +CAmount CWallet::GetChange(const CTxOut& txout) const +{ + if (!MoneyRange(txout.nValue)) + throw std::runtime_error("CWallet::GetChange() : value out of range"); + return (IsChange(txout) ? txout.nValue : 0); +} +bool CWallet::IsMine(const CTransaction& tx) const +{ + for (const CTxOut& txout : tx.vout) + if (IsMine(txout)) + return true; + return false; +} + +bool CWallet::IsFromMe(const CTransaction& tx) const +{ + return (GetDebit(tx, ISMINE_ALL) > 0); +} +CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) const +{ + CAmount nDebit = 0; + for (const CTxIn& txin : tx.vin) { + nDebit += GetDebit(txin, filter); + if (!MoneyRange(nDebit)) + throw std::runtime_error("CWallet::GetDebit() : value out of range"); + } + return nDebit; +} +CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const +{ + CAmount nCredit = 0; + for (const CTxOut& txout : tx.vout) { + nCredit += GetCredit(txout, filter); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWallet::GetCredit() : value out of range"); + } + return nCredit; +} +CAmount CWallet::GetChange(const CTransaction& tx) const +{ + CAmount nChange = 0; + for (const CTxOut& txout : tx.vout) { + nChange += GetChange(txout); + if (!MoneyRange(nChange)) + throw std::runtime_error("CWallet::GetChange() : value out of range"); + } + return nChange; +} + +void CWallet::Inventory(const uint256& hash) +{ + { + LOCK(cs_wallet); + std::map::iterator mi = mapRequestCount.find(hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } +} + +unsigned int CWallet::GetKeyPoolSize() { + AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool + return setInternalKeyPool.size() + setExternalKeyPool.size(); +} + +int CWallet::GetVersion() +{ + LOCK(cs_wallet); + return nWalletVersion; +} + +CWalletTx::CWalletTx() +{ + Init(NULL); +} + +CWalletTx::CWalletTx(const CWallet* pwalletIn) +{ + Init(pwalletIn); +} + +CWalletTx::CWalletTx(const CWallet* pwalletIn, const CMerkleTx& txIn) : CMerkleTx(txIn) +{ + Init(pwalletIn); +} + +CWalletTx::CWalletTx(const CWallet* pwalletIn, const CTransaction& txIn) : CMerkleTx(txIn) +{ + Init(pwalletIn); +} + +void CWalletTx::Init(const CWallet* pwalletIn) +{ + pwallet = pwalletIn; + mapValue.clear(); + vOrderForm.clear(); + fTimeReceivedIsTxTime = false; + nTimeReceived = 0; + nTimeSmart = 0; + fFromMe = false; + strFromAccount.clear(); + fDebitCached = false; + fCreditCached = false; + fImmatureCreditCached = false; + fAvailableCreditCached = false; + fAnonymizableCreditCached = false; + fAnonymizedCreditCached = false; + fDenomUnconfCreditCached = false; + fDenomConfCreditCached = false; + fWatchDebitCached = false; + fWatchCreditCached = false; + fImmatureWatchCreditCached = false; + fAvailableWatchCreditCached = false; + fChangeCached = false; + nDebitCached = 0; + nCreditCached = 0; + nImmatureCreditCached = 0; + nAvailableCreditCached = 0; + nAnonymizableCreditCached = 0; + nAnonymizedCreditCached = 0; + nDenomUnconfCreditCached = 0; + nDenomConfCreditCached = 0; + nWatchDebitCached = 0; + nWatchCreditCached = 0; + nAvailableWatchCreditCached = 0; + nImmatureWatchCreditCached = 0; + nChangeCached = 0; + nOrderPos = -1; +} + +bool CWalletTx::IsTrusted() const +{ + // Quick answer in most cases + if (!IsFinalTx(*this)) + return false; + + bool fConflicted; + int nDepth = GetDepthAndMempool(fConflicted); + + if (fConflicted) // Don't trust unconfirmed transactions from us unless they are in the mempool. + return false; + if (nDepth >= 1) + return true; + if (nDepth < 0) + return false; + if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit + return false; + + // Trusted if all inputs are from us and are in the mempool: + for (const CTxIn& txin : vin) { + // Transactions not sent by us: not trusted + const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash); + if (parent == NULL) + return false; + const CTxOut& parentOut = parent->vout[txin.prevout.n]; + if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) + return false; + } + return true; +} + +int CWalletTx::GetDepthAndMempool(bool& fConflicted, bool enableIX) const +{ + int ret = GetDepthInMainChain(enableIX); + fConflicted = (ret == 0 && !InMempool()); // not in chain nor in mempool + return ret; +} + +bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const +{ + CMutableTransaction tx1 {*this}; + CMutableTransaction tx2 {_tx}; + for (auto& txin : tx1.vin) txin.scriptSig = CScript(); + for (auto& txin : tx2.vin) txin.scriptSig = CScript(); + return CTransaction(tx1) == CTransaction(tx2); +} + +void CWalletTx::MarkDirty() +{ + fCreditCached = false; + fAvailableCreditCached = false; + fAnonymizableCreditCached = false; + fAnonymizedCreditCached = false; + fDenomUnconfCreditCached = false; + fDenomConfCreditCached = false; + fWatchDebitCached = false; + fWatchCreditCached = false; + fAvailableWatchCreditCached = false; + fImmatureWatchCreditCached = false; + fDebitCached = false; + fChangeCached = false; +} + +void CWalletTx::BindWallet(CWallet* pwalletIn) +{ + pwallet = pwalletIn; + MarkDirty(); +} + +CAmount CWalletTx::GetChange() const +{ + if (fChangeCached) + return nChangeCached; + nChangeCached = pwallet->GetChange(*this); + fChangeCached = true; + return nChangeCached; +} + +bool CWalletTx::IsFromMe(const isminefilter& filter) const +{ + return (GetDebit(filter) > 0); +} + + diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 3f0a69cb4e7cc..c87d3ae737201 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -251,6 +251,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void AddToSpends(const COutPoint& outpoint, const uint256& wtxid); void AddToSpends(const uint256& wtxid); + /* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */ + void MarkConflicted(const uint256& hashBlock, const uint256& hashTx); + void SyncMetaData(std::pair); /* HD derive new child key (on internal or external chain) */ void DeriveNewChildKey(const CKeyMetadata& metadata, CKey& secretRet, uint32_t nAccountIndex, bool fInternal /*= false*/, CWalletDB * walletDB); @@ -351,92 +354,28 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool fCombineDust; CAmount nAutoCombineThreshold; - CWallet() - { - SetNull(); - } + CWallet(); - CWallet(std::string strWalletFileIn) - { - SetNull(); + CWallet(std::string strWalletFileIn); - strWalletFile = strWalletFileIn; - fFileBacked = true; - } + ~CWallet(); - ~CWallet() - { - delete pwalletdbEncryption; - } + void SetNull(); - void SetNull() - { - nWalletVersion = FEATURE_BASE; - nWalletMaxVersion = FEATURE_BASE; - fFileBacked = false; - nMasterKeyMaxID = 0; - pwalletdbEncryption = NULL; - nOrderPosNext = 0; - nNextResend = 0; - nLastResend = 0; - nTimeFirstKey = 0; - fWalletUnlockAnonymizeOnly = false; - fBackupMints = false; - - // Stake Settings - nHashDrift = 45; - nStakeSplitThreshold = 500; - nHashInterval = 22; - nStakeSetUpdateTime = 300; // 5 minutes - - //MultiSend - vMultiSend.clear(); - fMultiSendStake = false; - fMultiSendMasternodeReward = false; - fMultiSendNotify = false; - strMultiSendChangeAddress = ""; - nLastMultiSendHeight = 0; - vDisabledAddresses.clear(); - - //Auto Combine Dust - fCombineDust = false; - nAutoCombineThreshold = 0; - } + int getZeromintPercentage(); - int getZeromintPercentage() - { - return nZeromintPercentage; - } + void setZWallet(CzPHRWallet* zwallet); - void setZWallet(CzPHRWallet* zwallet) - { - zwalletMain = zwallet; - zphrTracker = std::unique_ptr(new CzPHRTracker(strWalletFile)); - } + CzPHRWallet* getZWallet(); - CzPHRWallet* getZWallet() { return zwalletMain; } + bool isZeromintEnabled(); - bool isZeromintEnabled() - { - return fEnableZeromint; - } + void setZPhrAutoBackups(bool fEnabled); - void setZPhrAutoBackups(bool fEnabled) - { - fBackupMints = fEnabled; - } - - bool isMultiSendEnabled() - { - return fMultiSendMasternodeReward || fMultiSendStake; - } + bool isMultiSendEnabled(); - void setMultiSendDisabled() - { - fMultiSendMasternodeReward = false; - fMultiSendStake = false; - } + void setMultiSendDisabled(); std::map mapWallet; std::list laccentries; @@ -450,8 +389,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::map mapAddressBook; - CPubKey vchDefaultKey; - std::set setLockedCoins; int64_t nTimeFirstKey; @@ -464,11 +401,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::string GetUniqueWalletBackupName() const; //! check whether we are allowed to upgrade (or already support) to the named feature - bool CanSupportFeature(enum WalletFeature wf) - { - AssertLockHeld(cs_wallet); - return nWalletMaxVersion >= wf; - } + bool CanSupportFeature(enum WalletFeature wf); void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed = true, const CCoinControl* coinControl = NULL, bool fIncludeZeroValue = false, AvailableCoinsType nCoinType = ALL_COINS, bool fUseIX = false, int nWatchonlyConfig = 1) const; std::map > AvailableCoinsByAddress(bool fConfirmed = true, CAmount maxCoinValue = 0); @@ -481,9 +414,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool IsSpent(const uint256& hash, unsigned int n) const; - bool IsLockedCoin(uint256 hash, unsigned int n) const; - void LockCoin(COutPoint& output); - void UnlockCoin(COutPoint& output); + bool IsLockedCoin(const uint256& hash, unsigned int n) const; + void LockCoin(const COutPoint& output); + void UnlockCoin(const COutPoint& output); void UnlockAllCoins(); void ListLockedCoins(std::vector& vOutpts); CAmount GetTotalValue(std::vector vCoins); @@ -513,13 +446,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! Load metadata (used by LoadWallet) bool LoadKeyMetadata(const CPubKey& pubkey, const CKeyMetadata& metadata); - bool LoadMinVersion(int nVersion) - { - AssertLockHeld(cs_wallet); - nWalletVersion = nVersion; - nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); - return true; - } + bool LoadMinVersion(int nVersion); //! Adds an encrypted key to the store, and saves it to disk. bool AddCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret); @@ -584,12 +511,12 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface int64_t IncOrderPosNext(CWalletDB* pwalletdb = NULL); void MarkDirty(); - bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet = false); + bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb); void SyncTransaction(const CTransaction& tx, const CBlock* pblock); bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate); void EraseFromWallet(const uint256& hash); - int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); - void ReacceptWalletTransactions(); + int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false, bool fromStartup = false); + void ReacceptWalletTransactions(bool fFirstLoad = false); void ResendWalletTransactions(); CAmount GetBalance() const; CAmount GetZerocoinBalance(bool fMatureOnly) const; @@ -650,67 +577,18 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface isminetype IsMine(const CTxIn& txin) const; CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const; - isminetype IsMine(const CTxOut& txout) const - { - return ::IsMine(*this, txout.scriptPubKey); - } + isminetype IsMine(const CTxOut& txout) const; bool IsMyZerocoinSpend(const CBigNum& bnSerial) const; bool IsMyMint(const CBigNum& bnValue) const; - CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const - { - if (!MoneyRange(txout.nValue)) - throw std::runtime_error("CWallet::GetCredit() : value out of range"); - return ((IsMine(txout) & filter) ? txout.nValue : 0); - } + CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const; bool IsChange(const CTxOut& txout) const; - CAmount GetChange(const CTxOut& txout) const - { - if (!MoneyRange(txout.nValue)) - throw std::runtime_error("CWallet::GetChange() : value out of range"); - return (IsChange(txout) ? txout.nValue : 0); - } - bool IsMine(const CTransaction& tx) const - { - for (const CTxOut& txout : tx.vout) - if (IsMine(txout)) - return true; - return false; - } + CAmount GetChange(const CTxOut& txout) const; + bool IsMine(const CTransaction& tx) const; /** should probably be renamed to IsRelevantToMe */ - bool IsFromMe(const CTransaction& tx) const - { - return (GetDebit(tx, ISMINE_ALL) > 0); - } - CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const - { - CAmount nDebit = 0; - for (const CTxIn& txin : tx.vin) { - nDebit += GetDebit(txin, filter); - if (!MoneyRange(nDebit)) - throw std::runtime_error("CWallet::GetDebit() : value out of range"); - } - return nDebit; - } - CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const - { - CAmount nCredit = 0; - for (const CTxOut& txout : tx.vout) { - nCredit += GetCredit(txout, filter); - if (!MoneyRange(nCredit)) - throw std::runtime_error("CWallet::GetCredit() : value out of range"); - } - return nCredit; - } - CAmount GetChange(const CTransaction& tx) const - { - CAmount nChange = 0; - for (const CTxOut& txout : tx.vout) { - nChange += GetChange(txout); - if (!MoneyRange(nChange)) - throw std::runtime_error("CWallet::GetChange() : value out of range"); - } - return nChange; - } + bool IsFromMe(const CTransaction& tx) const; + CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const; + CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const; + CAmount GetChange(const CTransaction& tx) const; void SetBestChain(const CBlockLocator& loc); DBErrors LoadWallet(bool& fFirstRunRet); @@ -722,22 +600,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool NewKeyPool(); bool UpdatedTransaction(const uint256& hashTx); - void Inventory(const uint256& hash) - { - { - LOCK(cs_wallet); - std::map::iterator mi = mapRequestCount.find(hash); - if (mi != mapRequestCount.end()) - (*mi).second++; - } - } - - unsigned int GetKeyPoolSize() { - AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool - return setInternalKeyPool.size() + setExternalKeyPool.size(); - } + void Inventory(const uint256& hash); - bool SetDefaultKey(const CPubKey& vchPubKey); + unsigned int GetKeyPoolSize(); //! signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = NULL, bool fExplicit = false); @@ -746,11 +611,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool SetMaxVersion(int nVersion); //! get the current wallet format (the oldest client version guaranteed to understand this wallet) - int GetVersion() - { - LOCK(cs_wallet); - return nWalletVersion; - } + int GetVersion(); /** * HD Wallet Functions @@ -770,6 +631,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! Get wallet transactions that conflict with given transaction (spend same outputs) std::set GetConflicts(const uint256& txid) const; + /* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */ + bool AbandonTransaction(const uint256& hashTx); + /** * Address book entry changed. * @note called with lock cs_wallet held. @@ -854,17 +718,18 @@ struct COutputEntry { class CMerkleTx : public CTransaction { private: - int GetDepthInMainChainINTERNAL(const CBlockIndex*& pindexRet) const; + /** Constant used in hashBlock to indicate tx has been abandoned */ + static const uint256 ABANDON_HASH; public: uint256 hashBlock; - std::vector vMerkleBranch; + /* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest + * block in the chain we know this or any in-wallet dependency conflicts + * with. Older clients interpret nIndex == -1 as unconfirmed for backward + * compatibility. + */ int nIndex; - // memory only - mutable bool fMerkleVerified; - - CMerkleTx() { Init(); @@ -879,7 +744,6 @@ class CMerkleTx : public CTransaction { hashBlock = 0; nIndex = -1; - fMerkleVerified = false; } ADD_SERIALIZE_METHODS; @@ -887,6 +751,7 @@ class CMerkleTx : public CTransaction template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + std::vector vMerkleBranch; // For compatibility with older versions. READWRITE(*(CTransaction*)this); nVersion = this->nVersion; READWRITE(hashBlock); @@ -899,7 +764,7 @@ class CMerkleTx : public CTransaction /** * Return depth of transaction in blockchain: - * -1 : not in blockchain, and not in memory pool (conflicted transaction) + * <0 : conflicts with a transaction this deep in the blockchain * 0 : in memory pool, waiting to be included in a block * >=1 : this many blocks deep in the main chain */ @@ -912,12 +777,15 @@ class CMerkleTx : public CTransaction bool IsInMainChain() const { const CBlockIndex* pindexRet; - return GetDepthInMainChainINTERNAL(pindexRet) > 0; + return GetDepthInMainChain(pindexRet, false) > 0; } int GetBlocksToMaturity() const; bool AcceptToMemoryPool(bool fLimitFree = true, bool fRejectInsaneFee = true, bool ignoreFees = false); int GetTransactionLockSignatures() const; bool IsTransactionLockTimedOut() const; + bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); } + bool isAbandoned() const { return (hashBlock == ABANDON_HASH); } + void setAbandoned() { hashBlock = ABANDON_HASH; } }; /** @@ -967,64 +835,11 @@ class CWalletTx : public CMerkleTx mutable CAmount nAvailableWatchCreditCached; mutable CAmount nChangeCached; - CWalletTx() - { - Init(NULL); - } - - CWalletTx(const CWallet* pwalletIn) - { - Init(pwalletIn); - } - - CWalletTx(const CWallet* pwalletIn, const CMerkleTx& txIn) : CMerkleTx(txIn) - { - Init(pwalletIn); - } - - CWalletTx(const CWallet* pwalletIn, const CTransaction& txIn) : CMerkleTx(txIn) - { - Init(pwalletIn); - } - - void Init(const CWallet* pwalletIn) - { - pwallet = pwalletIn; - mapValue.clear(); - vOrderForm.clear(); - fTimeReceivedIsTxTime = false; - nTimeReceived = 0; - nTimeSmart = 0; - fFromMe = false; - strFromAccount.clear(); - fDebitCached = false; - fCreditCached = false; - fImmatureCreditCached = false; - fAvailableCreditCached = false; - fAnonymizableCreditCached = false; - fAnonymizedCreditCached = false; - fDenomUnconfCreditCached = false; - fDenomConfCreditCached = false; - fWatchDebitCached = false; - fWatchCreditCached = false; - fImmatureWatchCreditCached = false; - fAvailableWatchCreditCached = false; - fChangeCached = false; - nDebitCached = 0; - nCreditCached = 0; - nImmatureCreditCached = 0; - nAvailableCreditCached = 0; - nAnonymizableCreditCached = 0; - nAnonymizedCreditCached = 0; - nDenomUnconfCreditCached = 0; - nDenomConfCreditCached = 0; - nWatchDebitCached = 0; - nWatchCreditCached = 0; - nAvailableWatchCreditCached = 0; - nImmatureWatchCreditCached = 0; - nChangeCached = 0; - nOrderPos = -1; - } + CWalletTx(); + CWalletTx(const CWallet* pwalletIn); + CWalletTx(const CWallet* pwalletIn, const CMerkleTx& txIn); + CWalletTx(const CWallet* pwalletIn, const CTransaction& txIn); + void Init(const CWallet* pwalletIn); ADD_SERIALIZE_METHODS; @@ -1070,27 +885,10 @@ class CWalletTx : public CMerkleTx } //! make sure balances are recalculated - void MarkDirty() - { - fCreditCached = false; - fAvailableCreditCached = false; - fAnonymizableCreditCached = false; - fAnonymizedCreditCached = false; - fDenomUnconfCreditCached = false; - fDenomConfCreditCached = false; - fWatchDebitCached = false; - fWatchCreditCached = false; - fAvailableWatchCreditCached = false; - fImmatureWatchCreditCached = false; - fDebitCached = false; - fChangeCached = false; - } + void MarkDirty(); + void BindWallet(CWallet* pwalletIn); - void BindWallet(CWallet* pwalletIn) - { - pwallet = pwalletIn; - MarkDirty(); - } + int GetDepthAndMempool(bool& fConflicted, bool enableIX = true) const; //! filter decides which addresses will count towards the debit CAmount GetDebit(const isminefilter& filter) const; @@ -1105,14 +903,7 @@ class CWalletTx : public CMerkleTx CAmount GetAvailableWatchOnlyCredit(const bool& fUseCache = true) const; CAmount GetLockedWatchOnlyCredit() const; - CAmount GetChange() const - { - if (fChangeCached) - return nChangeCached; - nChangeCached = pwallet->GetChange(*this); - fChangeCached = true; - return nChangeCached; - } + CAmount GetChange() const; void GetAmounts(std::list& listReceived, std::list& listSent, @@ -1122,40 +913,16 @@ class CWalletTx : public CMerkleTx void GetAccountAmounts(const std::string& strAccount, CAmount& nReceived, CAmount& nSent, CAmount& nFee, const isminefilter& filter) const; - bool IsFromMe(const isminefilter& filter) const - { - return (GetDebit(filter) > 0); - } + bool IsFromMe(const isminefilter& filter) const; bool InMempool() const; - bool IsTrusted() const - { - // Quick answer in most cases - if (!IsFinalTx(*this)) - return false; - int nDepth = GetDepthInMainChain(); - if (nDepth >= 1) - return true; - if (nDepth < 0) - return false; - if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit - return false; - - // Trusted if all inputs are from us and are in the mempool: - for (const CTxIn& txin : vin) { - // Transactions not sent by us: not trusted - const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash); - if (parent == NULL) - return false; - const CTxOut& parentOut = parent->vout[txin.prevout.n]; - if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) - return false; - } - return true; - } + // True if only scriptSigs are different + bool IsEquivalentTo(const CWalletTx& tx) const; + + bool IsTrusted() const; - bool WriteToDisk(); + bool WriteToDisk(CWalletDB *pwalletdb); int64_t GetTxTime() const; int64_t GetComputedTxTime() const; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index b62659cbe9aa2..361a004f601c6 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -145,12 +145,14 @@ bool CWalletDB::EraseMultiSig(const CScript& dest) bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) { nWalletDBUpdated++; - return Write(std::string("bestblock"), locator); + Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan + return Write(std::string("bestblock_nomerkle"), locator); } bool CWalletDB::ReadBestBlock(CBlockLocator& locator) { - return Read(std::string("bestblock"), locator); + if (Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true; + return Read(std::string("bestblock_nomerkle"), locator); } bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) @@ -232,12 +234,6 @@ bool CWalletDB::WriteAutoCombineSettings(bool fEnable, CAmount nCombineThreshold return Write(std::string("autocombinesettings"), pSettings, true); } -bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey) -{ - nWalletDBUpdated++; - return Write(std::string("defaultkey"), vchPubKey); -} - bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) { return Read(std::make_pair(std::string("pool"), nPool), keypool); @@ -439,7 +435,7 @@ bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CW ssValue >> wtx; CValidationState state; // false because there is no reason to go through the zerocoin checks for our own wallet - if (!(CheckTransaction(wtx, false, false, state, IsSporkActive(SPORK_17_SEGWIT_ACTIVATION)) && (wtx.GetHash() == hash) && state.IsValid())) + if (!(CheckTransaction(wtx, false, false, state, sporkManager.IsSporkActive(SPORK_17_SEGWIT_ACTIVATION)) && (wtx.GetHash() == hash) && state.IsValid())) return false; // Undo serialize changes in 31600 @@ -461,7 +457,7 @@ bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CW if (wtx.nOrderPos == -1) wss.fAnyUnordered = true; - pwallet->AddToWallet(wtx, true); + pwallet->AddToWallet(wtx, true, nullptr); } else if (strType == "acentry") { std::string strAccount; ssKey >> strAccount; @@ -591,7 +587,14 @@ bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CW (keyMeta.nCreateTime < pwallet->nTimeFirstKey)) pwallet->nTimeFirstKey = keyMeta.nCreateTime; } else if (strType == "defaultkey") { - ssValue >> pwallet->vchDefaultKey; + // We don't want or need the default key, but if there is one set, + // we want to make sure that it is valid so that we can detect corruption + CPubKey vchPubKey; + ssValue >> vchPubKey; + if (!vchPubKey.IsValid()) { + strErr = "Error reading wallet database: Default Key corrupt"; + return false; + } } else if (strType == "pool") { int64_t nIndex; ssKey >> nIndex; @@ -705,7 +708,6 @@ static bool IsKeyType(std::string strType) DBErrors CWalletDB::LoadWallet(CWallet* pwallet) { - pwallet->vchDefaultKey = CPubKey(); CWalletScanState wss; bool fNoncriticalErrors = false; DBErrors result = DB_LOAD_OK; @@ -743,7 +745,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) { // losing keys is considered a catastrophic error, anything else // we assume the user can live with: - if (IsKeyType(strType)) + if (IsKeyType(strType) || strType == "defaultkey") result = DB_CORRUPT; else { // Leave other errors alone, if we try to fix them we might make things worse. @@ -757,7 +759,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) LogPrintf("%s\n", strErr); } pcursor->close(); - } catch (boost::thread_interrupted) { + } catch (const boost::thread_interrupted&) { throw; } catch (...) { result = DB_CORRUPT; @@ -804,7 +806,6 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, std::vector& vTxHash, std::vector& vWtx) { - pwallet->vchDefaultKey = CPubKey(); bool fNoncriticalErrors = false; DBErrors result = DB_LOAD_OK; @@ -850,7 +851,7 @@ DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, std::vector& vTxHash } } pcursor->close(); - } catch (boost::thread_interrupted) { + } catch (const boost::thread_interrupted&) { throw; } catch (...) { result = DB_CORRUPT; @@ -1027,7 +1028,7 @@ bool BackupWallet(const CWallet& wallet, const boost::filesystem::path& strDest, boost::filesystem::remove(entry->second); LogPrintf("Old backup deleted: %s\n", (*entry).second); } - } catch (boost::filesystem::filesystem_error& error) { + } catch (const boost::filesystem::filesystem_error& error) { std::string strMessage = strprintf("Failed to delete backup %s\n", error.what()); LogPrint(nullptr, strMessage.data()); NotifyBacked(wallet, false, strMessage); diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 892eec77f3be3..0ced4c5998e7f 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -87,7 +87,7 @@ class CKeyMetadata class CWalletDB : public CDB { public: - CWalletDB(const std::string& strFilename, const char* pszMode = "r+") : CDB(strFilename, pszMode, CLIENT_VERSION) + CWalletDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnClose = true) : CDB(strFilename, pszMode, fFlushOnClose) { } @@ -126,8 +126,6 @@ class CWalletDB : public CDB bool EraseMSDisabledAddresses(std::vector vDisabledAddresses); bool WriteAutoCombineSettings(bool fEnable, CAmount nCombineThreshold); - bool WriteDefaultKey(const CPubKey& vchPubKey); - bool ReadPool(int64_t nPool, CKeyPool& keypool); bool WritePool(int64_t nPool, const CKeyPool& keypool); bool ErasePool(int64_t nPool); diff --git a/src/zphrwallet.cpp b/src/zphrwallet.cpp index 4ab1cac104d16..601afb1b51d7a 100644 --- a/src/zphrwallet.cpp +++ b/src/zphrwallet.cpp @@ -265,7 +265,7 @@ void CzPHRWallet::SyncWithChain(bool fGenerateMintPool) //Fill out wtx so that a transaction record can be created wtx.nTimeReceived = pindex->GetBlockTime(); - pwalletMain->AddToWallet(wtx); + pwalletMain->AddToWallet(wtx, false, &walletdb); setAddedTx.insert(txHash); } @@ -321,7 +321,8 @@ bool CzPHRWallet::SetMintSeen(const CBigNum& bnValue, const int& nHeight, const wtx.SetMerkleBranch(block); wtx.nTimeReceived = pindex->nTime; - pwalletMain->AddToWallet(wtx); + CWalletDB walletdb(strWalletFile); + pwalletMain->AddToWallet(wtx, false, &walletdb); } // Add to zphrTracker which also adds to database diff --git a/test/functional/rpc_budget.py b/test/functional/rpc_budget.py new file mode 100755 index 0000000000000..41041a5d9b0c9 --- /dev/null +++ b/test/functional/rpc_budget.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +# Copyright (c) 2019 The PIVX developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test RPC commands for budget proposal creation, submission, and verification.""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + + +class BudgetProposalTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + + def run_test(self): + budgetcycleblocks = 144 + nextsuperblock = self.nodes[0].getnextsuperblock() + address = self.nodes[0].getnewaddress() + scheme = 'http://' + url = 'test.com/url-with-length-of-57-characters-will-pass-00001' + longurl = 'test.com/url-with-length-of-58-characters-will-not-pass-01' + name = 'proposalwith20chars0' + longname = 'proposalwith21chars01' + numcycles = 2 + cycleamount = 100 + + self.log.info("Test with long name") + assert_raises_rpc_error(-8, "Invalid proposal name, limit of 20 characters.", self.nodes[0].preparebudget, + longname, scheme + url, numcycles, nextsuperblock, address, cycleamount) + + self.log.info("Test with long URL") + assert_raises_rpc_error(-8, "Invalid URL: 65 exceeds limit of 64 characters.", self.nodes[0].preparebudget, + name, scheme + longurl, numcycles, nextsuperblock, address, cycleamount) + + self.log.info("Test with invalid (0) cycles") + assert_raises_rpc_error(-8, "Invalid payment count, must be more than zero.", self.nodes[0].preparebudget, + name, scheme + url, 0, nextsuperblock, address, cycleamount) + + self.log.info("Test with invalid block start") + assert_raises_rpc_error(-8, "Invalid block start", self.nodes[0].preparebudget, + name, scheme + url, numcycles, nextsuperblock - 12, address, cycleamount) + assert_raises_rpc_error(-8, "Invalid block start", self.nodes[0].preparebudget, + name, scheme + url, numcycles, nextsuperblock - budgetcycleblocks, address, cycleamount) + + self.log.info("Test with invalid PIVX address") + assert_raises_rpc_error(-5, "Invalid PIVX address", self.nodes[0].preparebudget, + name, scheme + url, numcycles, nextsuperblock, "DBREvBPNQguwuC4YMoCG5FoH1sA2YntvZm", cycleamount) + + self.log.info("Test with too low amount") + assert_raises_rpc_error(-8, "Invalid amount - Payment of 9.00 is less than minimum 10 PIV allowed", self.nodes[0].preparebudget, + name, scheme + url, numcycles, nextsuperblock, address, 9) + + self.log.info("Test with too high amount") + assert_raises_rpc_error(-8, "Invalid amount - Payment of 648001.00 more than max of 648000.00", self.nodes[0].preparebudget, + name, scheme + url, numcycles, nextsuperblock, address, 648001) + + + self.log.info("Test without URL scheme") + scheme = '' + assert_raises_rpc_error(-8, "Invalid URL, check scheme (e.g. https://)", self.nodes[0].preparebudget, name, scheme + url, 1, nextsuperblock, address, 100) + + self.log.info('Test with invalid URL scheme: ftp://') + scheme = 'ftp://' + assert_raises_rpc_error(-8, "Invalid URL, check scheme (e.g. https://)", self.nodes[0].preparebudget, name, scheme + url, 1, nextsuperblock, address, 100) + + self.log.info("Test with invalid double character scheme: hhttps://") + scheme = 'hhttps://' + url = 'test.com' + assert_raises_rpc_error(-8, "Invalid URL, check scheme (e.g. https://)", self.nodes[0].preparebudget, name, scheme + url, 1, nextsuperblock, address, 100) + + self.log.info("Test with valid scheme: http://") + name = 'testvalid1' + scheme = 'http://' + feehashret = self.nodes[0].preparebudget(name, scheme + url, numcycles, nextsuperblock, address, cycleamount) + txinfo = self.nodes[0].gettransaction(feehashret) + assert_equal(txinfo['amount'], -50.00) + + self.log.info("Generate 7 blocks to confirm fee transaction") + self.nodes[0].generate(7) + + self.log.info("Submit the budget proposal") + submitret = self.nodes[0].submitbudget(name, scheme + url, numcycles, nextsuperblock, address, cycleamount, feehashret) + + self.log.info("Ensure that the budget proposal details are correct") + budgetinfo = self.nodes[0].getbudgetinfo(name)[0] + assert_equal(budgetinfo["Name"], name) + assert_equal(budgetinfo["URL"], scheme + url) + assert_equal(budgetinfo["Hash"], submitret) + assert_equal(budgetinfo["FeeHash"], feehashret) + assert_equal(budgetinfo["BlockStart"], nextsuperblock) + assert_equal(budgetinfo["PaymentAddress"], address) + assert_equal(budgetinfo["MonthlyPayment"], cycleamount) + assert_equal(budgetinfo["TotalPayment"], cycleamount * numcycles) + + +if __name__ == '__main__': + BudgetProposalTest().main() diff --git a/test/functional/rpc_spork.py b/test/functional/rpc_spork.py new file mode 100755 index 0000000000000..0eac7fc38539d --- /dev/null +++ b/test/functional/rpc_spork.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# Copyright (c) 2019 The PIVX developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# -*- coding: utf-8 -*- + +from time import sleep + +from test_framework.mininode import network_thread_start +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import connect_nodes_bi, p2p_port +from fake_stake.util import TestNode + + +class PHORE_RPCSporkTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 2 + self.extra_args = [['-staking=1']] * self.num_nodes + self.extra_args[0].append('-sporkkey=932HEevBSujW2ud7RfB1YF91AFygbBRQj3de3LyaCRqNzKKgWXi') + + + def setup_network(self): + ''' Can't rely on syncing all the nodes when staking=1 + :param: + :return: + ''' + self.setup_nodes() + for i in range(self.num_nodes - 1): + for j in range(i+1, self.num_nodes): + connect_nodes_bi(self.nodes, i, j) + + def init_test(self): + ''' Initializes test parameters + :param: + :return: + ''' + title = "*** Starting %s ***" % self.__class__.__name__ + underline = "-" * len(title) + self.log.info("\n\n%s\n%s\n%s\n", title, underline, self.description) + + # Setup the p2p connections and start up the network thread. + self.test_nodes = [] + for i in range(self.num_nodes): + self.test_nodes.append(TestNode()) + self.test_nodes[i].peer_connect('127.0.0.1', p2p_port(i)) + + network_thread_start() # Start up network handling in another thread + self.node = self.nodes[0] + + # Let the test nodes get in sync + for i in range(self.num_nodes): + self.test_nodes[i].wait_for_verack() + + + def printDict(self, d): + self.log.info("{") + for k in d: + self.log.info(" %s = %d" % (k, d[k])) + self.log.info("}") + + + def run_test(self): + self.description = "Performs tests on the Spork RPC" + # check spork values: + sporks = self.nodes[1].spork("show") + self.printDict(sporks) + active = self.nodes[1].spork("active") + assert(not active["SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT"]) + # activate SPORK 8 + new_value = 1563253447 + res = self.nodes[0].spork("SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT", new_value) + assert(res == "success") + sleep(1) + self.sync_all() + sporks = self.nodes[1].spork("show") + self.printDict(sporks) + assert(sporks["SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT"] == new_value) + active = self.nodes[0].spork("active") + assert (active["SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT"]) + self.log.info("Stopping nodes...") + self.stop_nodes() + self.log.info("Restarting node 1...") + self.start_node(1, []) + sporks = self.nodes[1].spork("show") + self.printDict(sporks) + assert (sporks["SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT"] == new_value) + + + +if __name__ == '__main__': + PIVX_RPCSporkTest().main() + diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py new file mode 100755 index 0000000000000..d77eb46c4d714 --- /dev/null +++ b/test/functional/wallet_abandonconflict.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import urllib.parse + +class AbandonConflictTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 2 + self.setup_clean_chain = True + self.extra_args = [["-minrelaytxfee=0.00001"],[]] + + def run_test(self): + self.nodes[0].generate(5) + sync_blocks(self.nodes) + self.nodes[1].generate(110) + sync_blocks(self.nodes) + balance = self.nodes[0].getbalance() + txA = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 10) + txB = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 10) + txC = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 10) + sync_mempools(self.nodes) + self.nodes[1].generate(1) + + sync_blocks(self.nodes) + newbalance = self.nodes[0].getbalance() + assert(balance - newbalance < Decimal("0.001")) #no more than fees lost + balance = newbalance + + url = urllib.parse.urlparse(self.nodes[1].url) + self.nodes[0].disconnectnode(url.hostname+":"+str(p2p_port(1))) + + # Identify the 10btc outputs + nA = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction(txA, 1)["vout"]) if vout["value"] == 10) + nB = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction(txB, 1)["vout"]) if vout["value"] == 10) + nC = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction(txC, 1)["vout"]) if vout["value"] == 10) + + inputs =[] + # spend 10btc outputs from txA and txB + inputs.append({"txid":txA, "vout":nA}) + inputs.append({"txid":txB, "vout":nB}) + outputs = {} + + outputs[self.nodes[0].getnewaddress()] = 14.99998 + outputs[self.nodes[1].getnewaddress()] = 5 + signed = self.nodes[0].signrawtransaction(self.nodes[0].createrawtransaction(inputs, outputs)) + txAB1 = self.nodes[0].sendrawtransaction(signed["hex"]) + + # Identify the 14.99998btc output + nAB = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction(txAB1, 1)["vout"]) if vout["value"] == Decimal("14.99998")) + + #Create a child tx spending AB1 and C + inputs = [] + inputs.append({"txid":txAB1, "vout":nAB}) + inputs.append({"txid":txC, "vout":nC}) + outputs = {} + outputs[self.nodes[0].getnewaddress()] = 24.9996 + signed2 = self.nodes[0].signrawtransaction(self.nodes[0].createrawtransaction(inputs, outputs)) + txABC2 = self.nodes[0].sendrawtransaction(signed2["hex"]) + + # Create a child tx spending ABC2 + inputs = [] + inputs.append({"txid":txABC2, "vout":0}) + outputs = {} + outputs[self.nodes[0].getnewaddress()] = 24.999 + signed3 = self.nodes[0].signrawtransaction(self.nodes[0].createrawtransaction(inputs, outputs)) + # note tx is never directly referenced, only abandoned as a child of the above + self.nodes[0].sendrawtransaction(signed3["hex"]) + + # In mempool txs from self should increase balance from change + newbalance = self.nodes[0].getbalance() + assert_equal(newbalance, Decimal(round(balance - Decimal("30") + Decimal(24.999), 8))) + balance = newbalance + + # Restart the node with a higher min relay fee so the parent tx is no longer in mempool + # TODO: redo with eviction + # Note had to make sure tx did not have AllowFree priority + self.stop_node(0) + self.start_node(0, extra_args=["-minrelaytxfee=0.0001"]) + + # Verify txs no longer in mempool + assert_equal(len(self.nodes[0].getrawmempool()), 0) + + # Not in mempool txs from self should only reduce balance + # inputs are still spent, but change not received + newbalance = self.nodes[0].getbalance() + assert_equal(newbalance, balance - Decimal("24.999")) + # Unconfirmed received funds that are not in mempool, also shouldn't show + # up in unconfirmed balance + unconfbalance = self.nodes[0].getunconfirmedbalance() + self.nodes[0].getbalance() + assert_equal(unconfbalance, newbalance) + # Also shouldn't show up in listunspent + assert(not txABC2 in [utxo["txid"] for utxo in self.nodes[0].listunspent(0)]) + balance = newbalance + + # Abandon original transaction and verify inputs are available again + # including that the child tx was also abandoned + self.nodes[0].abandontransaction(txAB1) + newbalance = self.nodes[0].getbalance() + assert_equal(newbalance, balance + Decimal("30")) + balance = newbalance + + # Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned + self.stop_node(0) + self.start_node(0, extra_args=["-minrelaytxfee=0.00001"]) + assert_equal(len(self.nodes[0].getrawmempool()), 0) + assert_equal(self.nodes[0].getbalance(), balance) + + # But if its received again then it is unabandoned + # And since now in mempool, the change is available + # But its child tx remains abandoned + self.nodes[0].sendrawtransaction(signed["hex"]) + newbalance = self.nodes[0].getbalance() + assert_equal(newbalance, balance - Decimal("20") + Decimal("14.99998")) + balance = newbalance + + # Send child tx again so its unabandoned + self.nodes[0].sendrawtransaction(signed2["hex"]) + newbalance = self.nodes[0].getbalance() + assert_equal(newbalance, balance - Decimal("10") - Decimal("14.99998") + Decimal("24.9996")) + balance = newbalance + + # Remove using high relay fee again + self.stop_node(0) + self.start_node(0, extra_args=["-minrelaytxfee=0.0001"]) + assert_equal(len(self.nodes[0].getrawmempool()), 0) + newbalance = self.nodes[0].getbalance() + assert_equal(newbalance, balance - Decimal("24.9996")) + balance = newbalance + + # Create a double spend of AB1 by spending again from only A's 10 output + # Mine double spend from node 1 + inputs =[] + inputs.append({"txid":txA, "vout":nA}) + outputs = {} + outputs[self.nodes[1].getnewaddress()] = 9.9999 + tx = self.nodes[0].createrawtransaction(inputs, outputs) + signed = self.nodes[0].signrawtransaction(tx) + self.nodes[1].sendrawtransaction(signed["hex"]) + self.nodes[1].generate(1) + + connect_nodes(self.nodes[0], 1) + sync_blocks(self.nodes) + + # Verify that B and C's 10 BTC outputs are available for spending again because AB1 is now conflicted + newbalance = self.nodes[0].getbalance() + #assert_equal(newbalance, balance + Decimal("20")) + balance = newbalance + + # There is currently a minor bug around this and so this test doesn't work. See Issue #7315 + # Invalidate the block with the double spend and B's 10 BTC output should no longer be available + # Don't think C's should either + self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + newbalance = self.nodes[0].getbalance() + #assert_equal(newbalance, balance - Decimal("10")) + print("If balance has not declined after invalidateblock then out of mempool wallet tx which is no longer") + print("conflicted has not resumed causing its inputs to be seen as spent. See Issue #7315") + print(str(balance) + " -> " + str(newbalance) + " ?") + +if __name__ == '__main__': + AbandonConflictTest().main() diff --git a/test/functional/wallet_reorg-stake.py b/test/functional/wallet_reorg-stake.py new file mode 100755 index 0000000000000..7dbdf028b0b2a --- /dev/null +++ b/test/functional/wallet_reorg-stake.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +# Copyright (c) 2019 The PIVX Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import sync_blocks, sync_mempools, connect_nodes_bi, \ + p2p_port, assert_equal, assert_raises_rpc_error +import urllib.parse + +class ReorgStakeTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 2 + self.setup_clean_chain = True + self.extra_args = [["-minrelaytxfee=0.00001"],[]] + + + def generateBatchBlocks(self, nodeid, limit, batch_size = 5): + i = 0 + while i < limit: + i += batch_size + if i <= limit: + self.nodes[nodeid].generate(batch_size) + else: + self.nodes[nodeid].generate(batch_size-i+limit) + + def findUtxoInList(self, txid, vout, utxo_list): + for x in utxo_list: + if x["txid"] == txid and x["vout"] == vout: + return True, x + return False, None + + + def run_test(self): + # NLAST_POW_BLOCK = 250 - so mine 125 blocks each node (25 consecutive blocks for 5 times) + NMATURITY = 100 + self.log.info("Mining 250 blocks (125 with node 0 and 125 with node 1)...") + for i in range(5): + self.generateBatchBlocks(0, 25) + sync_blocks(self.nodes) + self.generateBatchBlocks(1, 25) + sync_blocks(self.nodes) + sync_mempools(self.nodes) + + # Check balances + balance0 = self.nodes[0].getbalance() + balance1 = self.nodes[1].getbalance() + # Last two 25-blocks bursts (for each node) are not mature: NMATURITY = 2 * (2 * 25) + assert_equal(balance0, 250.0 * (125 - 50)) + assert_equal(balance1, 250.0 * (125 - 50)) + self.log.info("Balances check out (%d, %d)" % (balance0, balance1)) + initial_balance = balance0 + initial_unspent = self.nodes[0].listunspent() + + # PoS start reached (block 250) - disconnect nodes + self.nodes[0].disconnectnode(urllib.parse.urlparse(self.nodes[1].url).hostname + ":" + str(p2p_port(1))) + self.nodes[1].disconnectnode(urllib.parse.urlparse(self.nodes[0].url).hostname + ":" + str(p2p_port(0))) + self.log.info("Nodes disconnected") + + # Stake one block with node-0 and save the stake input + self.log.info("Staking 1 block with node 0...") + self.nodes[0].generate(1) + last_block = self.nodes[0].getblock(self.nodes[0].getbestblockhash()) + assert(len(last_block["tx"]) > 1) # a PoS block has at least two txes + coinstake_txid = last_block["tx"][1] + coinstake_tx = self.nodes[0].getrawtransaction(coinstake_txid, True) + assert(coinstake_tx["vout"][0]["scriptPubKey"]["hex"] == "") # first output of coinstake is empty + stakeinput = coinstake_tx["vin"][0] + + # The stake input was unspent 1 block ago, now it's not + res, utxo = self.findUtxoInList(stakeinput["txid"], stakeinput["vout"], initial_unspent) + assert (res and utxo["spendable"]) + res, utxo = self.findUtxoInList(stakeinput["txid"], stakeinput["vout"], self.nodes[0].listunspent()) + assert (not res or not utxo["spendable"]) + self.log.info("Coinstake input %s...%s-%d is no longer spendable." % ( + stakeinput["txid"][:9], stakeinput["txid"][-4:], stakeinput["vout"])) + + # Stake 10 more blocks with node-0 and check balances + self.log.info("Staking 10 more blocks with node 0...") + self.generateBatchBlocks(0, 10) + balance0 = initial_balance + 500 * 11 # mined blocks matured + staked blocks (250*11 + 250*11) + assert_equal(self.nodes[0].getbalance(), balance0) + self.log.info("Balance for node 0 checks out: %d" % balance0) + + # verify that the stakeinput can't be spent + rawtx_unsigned = self.nodes[0].createrawtransaction( + [{"txid": str(stakeinput["txid"]), "vout": int(stakeinput["vout"])}], + {"xxncEuJK27ygNh7imNfaX8JV6ZQUnoBqzN": 249.99}) + rawtx = self.nodes[0].signrawtransaction(rawtx_unsigned) + assert(rawtx["complete"]) + assert_raises_rpc_error(-25, "Missing inputs",self.nodes[0].sendrawtransaction, rawtx["hex"]) + + # Stake 12 blocks with node-1 + self.log.info("Staking 12 blocks with node 1...") + self.generateBatchBlocks(1, 12) + balance1 += 250 * 12 # staked blocks only (250*12) + assert_equal(self.nodes[1].getbalance(), balance1) + self.log.info("Balance for node 1 checks out: %d" % balance1) + new_best_hash = self.nodes[1].getbestblockhash() + + # re-connect and sync nodes and check that node-0 gets on the other chain + self.log.info("Connecting and syncing nodes...") + connect_nodes_bi(self.nodes, 0, 1) + sync_blocks(self.nodes) + assert_equal(self.nodes[0].getbestblockhash(), new_best_hash) + + # check balance of node-0 + balance0 = initial_balance + 250 * 12 # mined blocks matured (250*12) + assert_equal(self.nodes[0].getbalance(), balance0) # <--- !!! THIS FAILS before PR #1043 + self.log.info("Balance for node 0 checks out: %d" % balance0) + + # check that NOW the original stakeinput is present and spendable + res, utxo = self.findUtxoInList(stakeinput["txid"], stakeinput["vout"], self.nodes[0].listunspent()) + assert (res and utxo["spendable"]) # <--- !!! THIS FAILS before PR #1043 + self.log.info("Coinstake input %s...%s-%d is spendable again." % ( + stakeinput["txid"][:9], stakeinput["txid"][-4:], stakeinput["vout"])) + self.nodes[0].sendrawtransaction(rawtx["hex"]) + self.nodes[1].generate(1) + sync_blocks(self.nodes) + res, utxo = self.findUtxoInList(stakeinput["txid"], stakeinput["vout"], self.nodes[0].listunspent()) + assert (not res or not utxo["spendable"]) + + +if __name__ == '__main__': + ReorgStakeTest().main() \ No newline at end of file diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py new file mode 100755 index 0000000000000..a30048cd7916d --- /dev/null +++ b/test/functional/wallet_txn_clone.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the wallet accounts properly when there are cloned transactions with malleated scriptsigs.""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +class TxnMallTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 4 + + def add_options(self, parser): + parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", + help="Test double-spend of 1-confirmed transaction") + + def setup_network(self): + # Start with split network: + super(TxnMallTest, self).setup_network() + disconnect_nodes(self.nodes[1], 2) + disconnect_nodes(self.nodes[2], 1) + + def run_test(self): + # All nodes should start with 1,250 BTC: + starting_balance = 6250 + for i in range(4): + assert_equal(self.nodes[i].getbalance(), starting_balance) + self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress! + + # Assign coins to foo and bar accounts: + self.nodes[0].settxfee(.001) + + node0_address_foo = self.nodes[0].getnewaddress("foo") + fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, (1219 *5)) + fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid) + + node0_address_bar = self.nodes[0].getnewaddress("bar") + fund_bar_txid = self.nodes[0].sendfrom("", node0_address_bar, (29 *5)) + fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid) + + assert_equal(self.nodes[0].getbalance(""), + starting_balance - (1219 * 5) - (29 * 5) + fund_foo_tx["fee"] + fund_bar_tx["fee"]) + + # Coins are sent to node1_address + node1_address = self.nodes[1].getnewaddress("from0") + + # Send tx1, and another transaction tx2 that won't be cloned + txid1 = self.nodes[0].sendfrom("foo", node1_address, (40 * 5), 0) + txid2 = self.nodes[0].sendfrom("bar", node1_address, (20 * 5), 0) + + # Construct a clone of tx1, to be malleated + rawtx1 = self.nodes[0].getrawtransaction(txid1,1) + clone_inputs = [{"txid":rawtx1["vin"][0]["txid"],"vout":rawtx1["vin"][0]["vout"]}] + clone_outputs = {rawtx1["vout"][0]["scriptPubKey"]["addresses"][0]:float(rawtx1["vout"][0]["value"]), + rawtx1["vout"][1]["scriptPubKey"]["addresses"][0]:float(rawtx1["vout"][1]["value"])} + clone_locktime = rawtx1["locktime"] + clone_raw = self.nodes[0].createrawtransaction(clone_inputs, clone_outputs, clone_locktime) + + # createrawtransaction randomizes the order of its outputs, so swap them if necessary. + # output 0 is at version+#inputs+input+sigstub+sequence+#outputs + # 40 BTC serialized is 00286bee00000000 + pos0 = 2*(4+1+36+1+4+1) + hex40 = "00286bee00000000" + output_len = 16 + 2 + 2 * int("0x" + clone_raw[pos0 + 16 : pos0 + 16 + 2], 0) + if (rawtx1["vout"][0]["value"] == 40 and clone_raw[pos0 : pos0 + 16] != hex40 or + rawtx1["vout"][0]["value"] != 40 and clone_raw[pos0 : pos0 + 16] == hex40): + output0 = clone_raw[pos0 : pos0 + output_len] + output1 = clone_raw[pos0 + output_len : pos0 + 2 * output_len] + clone_raw = clone_raw[:pos0] + output1 + output0 + clone_raw[pos0 + 2 * output_len:] + + # Use a different signature hash type to sign. This creates an equivalent but malleated clone. + # Don't send the clone anywhere yet + tx1_clone = self.nodes[0].signrawtransaction(clone_raw, None, None, "ALL|ANYONECANPAY") + assert_equal(tx1_clone["complete"], True) + + # Have node0 mine a block, if requested: + if (self.options.mine_block): + self.nodes[0].generate(1) + sync_blocks(self.nodes[0:2]) + + tx1 = self.nodes[0].gettransaction(txid1) + tx2 = self.nodes[0].gettransaction(txid2) + + # Node0's balance should be starting balance, plus 50BTC for another + # matured block, minus tx1 and tx2 amounts, and minus transaction fees: + expected = starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"] + if self.options.mine_block: expected += 250 + expected += tx1["amount"] + tx1["fee"] + expected += tx2["amount"] + tx2["fee"] + assert_equal(self.nodes[0].getbalance(), expected) + + # foo and bar accounts should be debited: + assert_equal(self.nodes[0].getbalance("foo", 0), (1219 * 5) + tx1["amount"] + tx1["fee"]) + assert_equal(self.nodes[0].getbalance("bar", 0), (29 * 5) + tx2["amount"] + tx2["fee"]) + + if self.options.mine_block: + assert_equal(tx1["confirmations"], 1) + assert_equal(tx2["confirmations"], 1) + # Node1's "from0" balance should be both transaction amounts: + assert_equal(self.nodes[1].getbalance("from0"), -(tx1["amount"] + tx2["amount"])) + else: + assert_equal(tx1["confirmations"], 0) + assert_equal(tx2["confirmations"], 0) + + # Send clone and its parent to miner + self.nodes[2].sendrawtransaction(fund_foo_tx["hex"]) + txid1_clone = self.nodes[2].sendrawtransaction(tx1_clone["hex"]) + # mine a block... + self.nodes[2].generate(1) + + # Reconnect the split network, and sync chain: + connect_nodes(self.nodes[1], 2) + connect_nodes(self.nodes[0], 2) + connect_nodes(self.nodes[2], 0) + connect_nodes(self.nodes[2], 1) + + self.nodes[2].sendrawtransaction(fund_bar_tx["hex"]) + self.nodes[2].sendrawtransaction(tx2["hex"]) + self.nodes[2].generate(1) # Mine another block to make sure we sync + sync_blocks(self.nodes) + + # Re-fetch transaction info: + tx1 = self.nodes[0].gettransaction(txid1) + tx1_clone = self.nodes[0].gettransaction(txid1_clone) + tx2 = self.nodes[0].gettransaction(txid2) + + # Verify expected confirmations + assert_equal(tx1["confirmations"], -2) + assert_equal(tx1_clone["confirmations"], 2) + assert_equal(tx2["confirmations"], 1) + + # Check node0's total balance; should be same as before the clone, + 100 BTC for 2 matured, + # less possible orphaned matured subsidy + expected += 500 + if (self.options.mine_block): + expected -= 250 + assert_equal(self.nodes[0].getbalance(), expected) + assert_equal(self.nodes[0].getbalance("*", 0), expected) + + # Check node0's individual account balances. + # "foo" should have been debited by the equivalent clone of tx1 + assert_equal(self.nodes[0].getbalance("foo"), (1219 * 5) + tx1["amount"] + tx1["fee"]) + # "bar" should have been debited by (possibly unconfirmed) tx2 + assert_equal(self.nodes[0].getbalance("bar", 0), (29 * 5) + tx2["amount"] + tx2["fee"]) + # "" should have starting balance, less funding txes, plus subsidies + assert_equal(self.nodes[0].getbalance("", 0), starting_balance + - (1219 * 5) + + fund_foo_tx["fee"] + - (29 * 5) + + fund_bar_tx["fee"] + + 500) + + # Node1's "from0" account balance + assert_equal(self.nodes[1].getbalance("from0", 0), -(tx1["amount"] + tx2["amount"])) + +if __name__ == '__main__': + TxnMallTest().main() + diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py new file mode 100755 index 0000000000000..054486a3af243 --- /dev/null +++ b/test/functional/wallet_txn_doublespend.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the wallet accounts properly when there is a double-spend conflict.""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import time + +class TxnMallTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 4 + + def add_options(self, parser): + parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", + help="Test double-spend of 1-confirmed transaction") + + def setup_network(self): + # Start with split network: + super().setup_network() + disconnect_nodes(self.nodes[1], 2) + disconnect_nodes(self.nodes[2], 1) + + def run_test(self): + # All nodes should start with 6,250 PIV: + starting_balance = 6250 + for i in range(4): + assert_equal(self.nodes[i].getbalance(), starting_balance) + self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress! + + # Assign coins to foo and bar accounts: + node0_address_foo = self.nodes[0].getnewaddress("foo") + fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, (1219 * 5)) + fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid) + + node0_address_bar = self.nodes[0].getnewaddress("bar") + fund_bar_txid = self.nodes[0].sendfrom("", node0_address_bar, (29 * 5)) + fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid) + + assert_equal(self.nodes[0].getbalance(""), + starting_balance - (1219 * 5) - (29 * 5) + fund_foo_tx["fee"] + fund_bar_tx["fee"]) + + # Coins are sent to node1_address + node1_address = self.nodes[1].getnewaddress("from0") + + # First: use raw transaction API to send 1240 BTC to node1_address, + # but don't broadcast: + doublespend_fee = Decimal('-.02') + rawtx_input_0 = {} + rawtx_input_0["txid"] = fund_foo_txid + rawtx_input_0["vout"] = find_output(self.nodes[0], fund_foo_txid, (1219 * 5)) + rawtx_input_1 = {} + rawtx_input_1["txid"] = fund_bar_txid + rawtx_input_1["vout"] = find_output(self.nodes[0], fund_bar_txid, (29 * 5)) + inputs = [rawtx_input_0, rawtx_input_1] + change_address = self.nodes[0].getnewaddress() + outputs = {} + outputs[node1_address] = float(1240 * 5) + outputs[change_address] = float((1248 * 5) - (1240 * 5) + doublespend_fee) + rawtx = self.nodes[0].createrawtransaction(inputs, outputs) + doublespend = self.nodes[0].signrawtransaction(rawtx) + assert_equal(doublespend["complete"], True) + + # Create two spends using 1 50 BTC coin each + txid1 = self.nodes[0].sendfrom("foo", node1_address, (40 * 5), 0) + txid2 = self.nodes[0].sendfrom("bar", node1_address, (20 * 5), 0) + + # Have node0 mine a block: + if (self.options.mine_block): + self.nodes[0].generate(1) + sync_blocks(self.nodes[0:2]) + + tx1 = self.nodes[0].gettransaction(txid1) + tx2 = self.nodes[0].gettransaction(txid2) + + # Node0's balance should be starting balance, plus 50BTC for another + # matured block, minus 40, minus 20, and minus transaction fees: + expected = starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"] + if self.options.mine_block: expected += 250 + expected += tx1["amount"] + tx1["fee"] + expected += tx2["amount"] + tx2["fee"] + assert_equal(self.nodes[0].getbalance(), expected) + + # foo and bar accounts should be debited: + assert_equal(self.nodes[0].getbalance("foo", 0), (1219 * 5)+tx1["amount"]+tx1["fee"]) + assert_equal(self.nodes[0].getbalance("bar", 0), (29 * 5)+tx2["amount"]+tx2["fee"]) + + if self.options.mine_block: + assert_equal(tx1["bcconfirmations"], 1) + assert_equal(tx2["bcconfirmations"], 1) + # Node1's "from0" balance should be both transaction amounts: + assert_equal(self.nodes[1].getbalance("from0"), -(tx1["amount"]+tx2["amount"])) + else: + assert_equal(tx1["bcconfirmations"], 0) + assert_equal(tx2["bcconfirmations"], 0) + + # Now give doublespend and its parents to miner: + self.nodes[2].sendrawtransaction(fund_foo_tx["hex"]) + self.nodes[2].sendrawtransaction(fund_bar_tx["hex"]) + doublespend_txid = self.nodes[2].sendrawtransaction(doublespend["hex"]) + + # mine a block... + self.nodes[2].generate(1) + + # Reconnect the split network, and sync chain: + connect_nodes(self.nodes[1], 2) + connect_nodes(self.nodes[0], 2) + connect_nodes(self.nodes[2], 0) + connect_nodes(self.nodes[2], 1) + self.nodes[2].generate(1) # Mine another block to make sure we sync + sync_blocks(self.nodes) + assert_equal(self.nodes[0].gettransaction(doublespend_txid)["confirmations"], 2) + + # Re-fetch transaction info: + tx1 = self.nodes[0].gettransaction(txid1) + tx2 = self.nodes[0].gettransaction(txid2) + + # Both transactions should be conflicted + assert_equal(tx1["bcconfirmations"], -2) + assert_equal(tx2["bcconfirmations"], -2) + + # Node0's total balance should be starting balance, plus 100BTC for + # two more matured blocks, minus 1240 for the double-spend, plus fees (which are + # negative): + expected = starting_balance + 500 - (1240 * 5) + fund_foo_tx["fee"] + fund_bar_tx["fee"] + doublespend_fee + assert_equal(self.nodes[0].getbalance(), expected) + assert_equal(self.nodes[0].getbalance("*"), expected) + + # Final "" balance is starting_balance - amount moved to accounts - doublespend + subsidies + + # fees (which are negative) + assert_equal(self.nodes[0].getbalance("foo"), (1219 * 5)) + #assert_equal(self.nodes[0].getbalance("bar"), (29 * 5)) + #assert_equal(self.nodes[0].getbalance(""), starting_balance + # -(1219 * 5) + # - (29 * 5) + # -(1240 * 5) + # + 500 + # + fund_foo_tx["fee"] + # + fund_bar_tx["fee"] + # + doublespend_fee) + + # Node1's "from0" account balance should be just the doublespend: + assert_equal(self.nodes[1].getbalance("from0"), (1240 * 5)) + +if __name__ == '__main__': + TxnMallTest().main()